Commit 024052b448

Andrew Kelley <superjoe30@gmail.com>
2015-11-27 23:46:06
add pub and export visibility modifiers and optimization
1 parent 9ca9a2c
doc/vim/syntax/zig.vim
@@ -7,7 +7,7 @@ if exists("b:current_syntax")
   finish
 endif
 
-syn keyword zigKeyword fn return mut const extern unreachable
+syn keyword zigKeyword fn return mut const extern unreachable export pub
 
 let b:current_syntax = "zig"
 
test/hello.zig → example/hello.zig
@@ -4,7 +4,7 @@ extern {
     fn exit(code: i32) -> unreachable;
 }
 
-fn _start() -> unreachable {
+export fn _start() -> unreachable {
     puts("Hello, world!");
     exit(0);
 }
src/codegen.cpp
@@ -26,6 +26,7 @@ struct FnTableEntry {
     AstNode *fn_def_node;
     bool is_extern;
     bool internal_linkage;
+    unsigned calling_convention;
 };
 
 enum TypeId {
@@ -51,7 +52,7 @@ struct TypeTableEntry {
 };
 
 struct CodeGen {
-    LLVMModuleRef mod;
+    LLVMModuleRef module;
     AstNode *root;
     ZigList<ErrorMsg> errors;
     LLVMBuilderRef builder;
@@ -228,6 +229,7 @@ static void find_declarations(CodeGen *g, AstNode *node) {
                 FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
                 fn_table_entry->proto_node = fn_proto;
                 fn_table_entry->is_extern = true;
+                fn_table_entry->calling_convention = LLVMCCallConv;
                 g->fn_table.put(name, fn_table_entry);
             }
             break;
@@ -244,6 +246,12 @@ static void find_declarations(CodeGen *g, AstNode *node) {
                     FnTableEntry *fn_table_entry = allocate<FnTableEntry>(1);
                     fn_table_entry->proto_node = proto_node;
                     fn_table_entry->fn_def_node = node;
+                    fn_table_entry->internal_linkage = proto_node->data.fn_proto.visib_mod != FnProtoVisibModExport;
+                    if (fn_table_entry->internal_linkage) {
+                        fn_table_entry->calling_convention = LLVMFastCallConv;
+                    } else {
+                        fn_table_entry->calling_convention = LLVMCCallConv;
+                    }
                     g->fn_table.put(proto_name, fn_table_entry);
                     g->fn_defs.append(fn_table_entry);
 
@@ -512,12 +520,12 @@ void semantic_analyze(CodeGen *g) {
     g->target_data_ref = LLVMGetTargetMachineData(g->target_machine);
 
 
-    g->mod = LLVMModuleCreateWithName("ZigModule");
+    g->module = LLVMModuleCreateWithName("ZigModule");
 
     g->pointer_size_bytes = LLVMPointerSize(g->target_data_ref);
 
     g->builder = LLVMCreateBuilder();
-    g->dbuilder = new llvm::DIBuilder(*llvm::unwrap(g->mod), true);
+    g->dbuilder = new llvm::DIBuilder(*llvm::unwrap(g->module), true);
 
 
     add_types(g);
@@ -550,8 +558,8 @@ static LLVMValueRef gen_fn_call(CodeGen *g, AstNode *fn_call_node) {
     }
 
     add_debug_source_node(g, fn_call_node);
-    LLVMValueRef result = LLVMBuildCall(g->builder, fn_table_entry->fn_value,
-            param_values, actual_param_count, "");
+    LLVMValueRef result = LLVMZigBuildCall(g->builder, fn_table_entry->fn_value,
+            param_values, actual_param_count, fn_table_entry->calling_convention, "");
 
     if (type_is_unreachable(fn_table_entry->proto_node->data.fn_proto.return_type)) {
         return LLVMBuildUnreachable(g->builder);
@@ -566,7 +574,7 @@ static LLVMValueRef find_or_create_string(CodeGen *g, Buf *str) {
         return entry->value;
     }
     LLVMValueRef text = LLVMConstString(buf_ptr(str), buf_len(str), false);
-    LLVMValueRef global_value = LLVMAddGlobal(g->mod, LLVMTypeOf(text), "");
+    LLVMValueRef global_value = LLVMAddGlobal(g->module, LLVMTypeOf(text), "");
     LLVMSetLinkage(global_value, LLVMPrivateLinkage);
     LLVMSetInitializer(global_value, text);
     LLVMSetGlobalConstant(global_value, true);
@@ -615,6 +623,8 @@ static void gen_block(CodeGen *g, AstNode *block_node, bool add_implicit_return)
             g->di_file, block_node->line + 1, block_node->column + 1);
     g->block_scopes.append(di_block);
 
+    add_debug_source_node(g, block_node);
+
     for (int i = 0; i < block_node->data.block.statements.length; i += 1) {
         AstNode *statement_node = block_node->data.block.statements.at(i);
         switch (statement_node->type) {
@@ -714,16 +724,15 @@ void code_gen(CodeGen *g) {
             param_types[param_decl_i] = to_llvm_type(type_node);
         }
         LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, fn_proto->params.length, 0);
-        LLVMValueRef fn = LLVMAddFunction(g->mod, buf_ptr(&fn_proto->name), function_type);
+        LLVMValueRef fn = LLVMAddFunction(g->module, buf_ptr(&fn_proto->name), function_type);
 
-        LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMPrivateLinkage : LLVMExternalLinkage);
+        LLVMSetLinkage(fn, fn_table_entry->internal_linkage ? LLVMInternalLinkage : LLVMExternalLinkage);
 
         if (type_is_unreachable(fn_proto->return_type)) {
             LLVMAddFunctionAttr(fn, LLVMNoReturnAttribute);
         }
-        if (fn_table_entry->is_extern) {
-            LLVMSetFunctionCallConv(fn, LLVMCCallConv);
-        } else {
+        LLVMSetFunctionCallConv(fn, fn_table_entry->calling_convention);
+        if (!fn_table_entry->is_extern) {
             LLVMAddFunctionAttr(fn, LLVMNoUnwindAttribute);
         }
 
@@ -768,10 +777,19 @@ void code_gen(CodeGen *g) {
 
     g->dbuilder->finalize();
 
-    LLVMDumpModule(g->mod);
+    LLVMDumpModule(g->module);
 
+    // in release mode, we're sooooo confident that we've generated correct ir,
+    // that we skip the verify module step in order to get better performance.
+#ifndef NDEBUG
     char *error = nullptr;
-    LLVMVerifyModule(g->mod, LLVMAbortProcessAction, &error);
+    LLVMVerifyModule(g->module, LLVMAbortProcessAction, &error);
+#endif
+}
+
+void code_gen_optimize(CodeGen *g) {
+    LLVMZigOptimizeModule(g->target_machine, g->module);
+    LLVMDumpModule(g->module);
 }
 
 ZigList<ErrorMsg> *codegen_error_messages(CodeGen *g) {
@@ -907,7 +925,9 @@ void code_gen_link(CodeGen *g, const char *out_file) {
     buf_append_str(&out_file_o, ".o");
 
     char *err_msg = nullptr;
-    if (LLVMTargetMachineEmitToFile(g->target_machine, g->mod, buf_ptr(&out_file_o), LLVMObjectFile, &err_msg)) {
+    if (LLVMZigTargetMachineEmitToFile(g->target_machine, g->module, buf_ptr(&out_file_o),
+                LLVMObjectFile, &err_msg))
+    {
         zig_panic("unable to write object file: %s", err_msg);
     }
 
src/codegen.hpp
@@ -33,6 +33,8 @@ void codegen_set_strip(CodeGen *codegen, bool strip);
 
 void semantic_analyze(CodeGen *g);
 
+void code_gen_optimize(CodeGen *g);
+
 void code_gen(CodeGen *g);
 
 void code_gen_link(CodeGen *g, const char *out_file);
src/main.cpp
@@ -118,6 +118,12 @@ static int build(const char *arg0, const char *in_file, const char *out_file,
     fprintf(stderr, "------------------\n");
     code_gen(codegen);
 
+    if (release) {
+        fprintf(stderr, "\nOptimization:\n");
+        fprintf(stderr, "---------------\n");
+        code_gen_optimize(codegen);
+    }
+
     fprintf(stderr, "\nLink:\n");
     fprintf(stderr, "-------\n");
     code_gen_link(codegen, out_file);
src/parser.cpp
@@ -268,6 +268,53 @@ static void ast_expect_token(ParseContext *pc, Token *token, TokenId token_id) {
     }
 }
 
+static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) {
+    Token *number_sign = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, number_sign, TokenIdNumberSign);
+
+    AstNode *node = ast_create_node(NodeTypeDirective, number_sign);
+
+    Token *name_symbol = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, name_symbol, TokenIdSymbol);
+
+    ast_buf_from_token(pc, name_symbol, &node->data.directive.name);
+
+    Token *l_paren = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, l_paren, TokenIdLParen);
+
+    Token *param_str = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, param_str, TokenIdStringLiteral);
+
+    parse_string_literal(pc, param_str, &node->data.directive.param);
+
+    Token *r_paren = &pc->tokens->at(token_index);
+    token_index += 1;
+    ast_expect_token(pc, r_paren, TokenIdRParen);
+
+    *new_token_index = token_index;
+    return node;
+}
+
+static void ast_parse_directives(ParseContext *pc, int *token_index,
+        ZigList<AstNode *> *directives)
+{
+    for (;;) {
+        Token *token = &pc->tokens->at(*token_index);
+        if (token->id == TokenIdNumberSign) {
+            AstNode *directive_node = ast_parse_directive(pc, *token_index, token_index);
+            directives->append(directive_node);
+        } else {
+            return;
+        }
+    }
+    zig_unreachable();
+}
+
+
 /*
 Type : token(Symbol) | PointerType | token(Unreachable)
 PointerType : token(Star) token(Const) Type  | token(Star) token(Mut) Type;
@@ -500,48 +547,74 @@ static AstNode *ast_parse_block(ParseContext *pc, int token_index, int *new_toke
 }
 
 /*
-FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
+FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
 */
-static AstNode *ast_parse_fn_proto(ParseContext *pc, int token_index, int *new_token_index) {
-    Token *fn_token = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, fn_token, TokenIdKeywordFn);
+static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *token = &pc->tokens->at(*token_index);
 
-    AstNode *node = ast_create_node(NodeTypeFnProto, fn_token);
+    FnProtoVisibMod visib_mod;
 
+    if (token->id == TokenIdKeywordPub) {
+        visib_mod = FnProtoVisibModPub;
+        *token_index += 1;
 
-    Token *fn_name = &pc->tokens->at(token_index);
-    token_index += 1;
+        Token *fn_token = &pc->tokens->at(*token_index);
+        *token_index += 1;
+        ast_expect_token(pc, fn_token, TokenIdKeywordFn);
+    } else if (token->id == TokenIdKeywordExport) {
+        visib_mod = FnProtoVisibModExport;
+        *token_index += 1;
+
+        Token *fn_token = &pc->tokens->at(*token_index);
+        *token_index += 1;
+        ast_expect_token(pc, fn_token, TokenIdKeywordFn);
+    } else if (token->id == TokenIdKeywordFn) {
+        visib_mod = FnProtoVisibModPrivate;
+        *token_index += 1;
+    } else if (mandatory) {
+        ast_invalid_token_error(pc, token);
+    } else {
+        return nullptr;
+    }
+
+    AstNode *node = ast_create_node(NodeTypeFnProto, token);
+    node->data.fn_proto.visib_mod = visib_mod;
+    node->data.fn_proto.directives = pc->directive_list;
+    pc->directive_list = nullptr;
+
+
+    Token *fn_name = &pc->tokens->at(*token_index);
+    *token_index += 1;
     ast_expect_token(pc, fn_name, TokenIdSymbol);
 
     ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name);
 
 
-    ast_parse_param_decl_list(pc, token_index, &token_index, &node->data.fn_proto.params);
+    ast_parse_param_decl_list(pc, *token_index, token_index, &node->data.fn_proto.params);
 
-    Token *arrow = &pc->tokens->at(token_index);
+    Token *arrow = &pc->tokens->at(*token_index);
     if (arrow->id == TokenIdArrow) {
-        token_index += 1;
-        node->data.fn_proto.return_type = ast_parse_type(pc, token_index, &token_index);
+        *token_index += 1;
+        node->data.fn_proto.return_type = ast_parse_type(pc, *token_index, token_index);
     } else {
         node->data.fn_proto.return_type = ast_create_void_type_node(pc, arrow);
     }
 
-    *new_token_index = token_index;
     return node;
 }
 
 /*
 FnDef : FnProto Block
 */
-static AstNode *ast_parse_fn_def(ParseContext *pc, int token_index, int *new_token_index) {
-    AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, &token_index);
+static AstNode *ast_parse_fn_def(ParseContext *pc, int *token_index, bool mandatory) {
+    AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, mandatory);
+    if (!fn_proto)
+        return nullptr;
     AstNode *node = ast_create_node_with_node(NodeTypeFnDef, fn_proto);
 
     node->data.fn_def.fn_proto = fn_proto;
-    node->data.fn_def.body = ast_parse_block(pc, token_index, &token_index);
+    node->data.fn_def.body = ast_parse_block(pc, *token_index, token_index);
 
-    *new_token_index = token_index;
     return node;
 }
 
@@ -549,7 +622,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, int token_index, int *new_tok
 FnDecl : FnProto token(Semicolon)
 */
 static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_token_index) {
-    AstNode *fn_proto = ast_parse_fn_proto(pc, token_index, &token_index);
+    AstNode *fn_proto = ast_parse_fn_proto(pc, &token_index, true);
     AstNode *node = ast_create_node_with_node(NodeTypeFnDecl, fn_proto);
 
     node->data.fn_decl.fn_proto = fn_proto;
@@ -565,78 +638,45 @@ static AstNode *ast_parse_fn_decl(ParseContext *pc, int token_index, int *new_to
 /*
 Directive : token(NumberSign) token(Symbol) token(LParen) token(String) token(RParen)
 */
-static AstNode *ast_parse_directive(ParseContext *pc, int token_index, int *new_token_index) {
-    Token *number_sign = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, number_sign, TokenIdNumberSign);
-
-    AstNode *node = ast_create_node(NodeTypeDirective, number_sign);
-
-    Token *name_symbol = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, name_symbol, TokenIdSymbol);
-
-    ast_buf_from_token(pc, name_symbol, &node->data.directive.name);
-
-    Token *l_paren = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, l_paren, TokenIdLParen);
-
-    Token *param_str = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, param_str, TokenIdStringLiteral);
-
-    parse_string_literal(pc, param_str, &node->data.directive.param);
-
-    Token *r_paren = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, r_paren, TokenIdRParen);
-
-    *new_token_index = token_index;
-    return node;
-}
-
-static void ast_parse_directives(ParseContext *pc, int token_index, int *new_token_index,
-        ZigList<AstNode *> *directives)
-{
-    for (;;) {
-        Token *token = &pc->tokens->at(token_index);
-        if (token->id == TokenIdNumberSign) {
-            AstNode *directive_node = ast_parse_directive(pc, token_index, &token_index);
-            directives->append(directive_node);
-        } else {
-            *new_token_index = token_index;
-            return;
-        }
-    }
-    zig_unreachable();
-}
-
 /*
 ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnProtoDecl) token(RBrace)
 */
-static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *new_token_index) {
-    Token *extern_kw = &pc->tokens->at(token_index);
-    token_index += 1;
-    ast_expect_token(pc, extern_kw, TokenIdKeywordExtern);
+static AstNode *ast_parse_extern_block(ParseContext *pc, int *token_index, bool mandatory) {
+    Token *extern_kw = &pc->tokens->at(*token_index);
+    if (extern_kw->id != TokenIdKeywordExtern) {
+        if (mandatory)
+            ast_invalid_token_error(pc, extern_kw);
+        else
+            return nullptr;
+    }
+    *token_index += 1;
 
     AstNode *node = ast_create_node(NodeTypeExternBlock, extern_kw);
 
     node->data.extern_block.directives = pc->directive_list;
     pc->directive_list = nullptr;
 
-    Token *l_brace = &pc->tokens->at(token_index);
-    token_index += 1;
+    Token *l_brace = &pc->tokens->at(*token_index);
+    *token_index += 1;
     ast_expect_token(pc, l_brace, TokenIdLBrace);
 
     for (;;) {
-        Token *token = &pc->tokens->at(token_index);
+        Token *directive_token = &pc->tokens->at(*token_index);
+        assert(!pc->directive_list);
+        pc->directive_list = allocate<ZigList<AstNode*>>(1);
+        ast_parse_directives(pc, token_index, pc->directive_list);
+
+        Token *token = &pc->tokens->at(*token_index);
         if (token->id == TokenIdRBrace) {
-            token_index += 1;
-            *new_token_index = token_index;
+            if (pc->directive_list->length > 0) {
+                ast_error(directive_token, "invalid directive");
+            }
+            pc->directive_list = nullptr;
+
+            *token_index += 1;
             return node;
         } else {
-            AstNode *child = ast_parse_fn_decl(pc, token_index, &token_index);
+            AstNode *child = ast_parse_fn_decl(pc, *token_index, token_index);
             node->data.extern_block.fn_decls.append(child);
         }
     }
@@ -645,25 +685,31 @@ static AstNode *ast_parse_extern_block(ParseContext *pc, int token_index, int *n
     zig_unreachable();
 }
 
-static void ast_parse_top_level_decls(ParseContext *pc, int token_index, int *new_token_index,
-        ZigList<AstNode *> *top_level_decls)
-{
+static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList<AstNode *> *top_level_decls) {
     for (;;) {
-        Token *token = &pc->tokens->at(token_index);
-        if (token->id == TokenIdNumberSign) {
-            assert(!pc->directive_list);
-            pc->directive_list = allocate<ZigList<AstNode*>>(1);
-            ast_parse_directives(pc, token_index, &token_index, pc->directive_list);
-        } else if (token->id == TokenIdKeywordFn) {
-            AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, &token_index);
+        Token *directive_token = &pc->tokens->at(*token_index);
+        assert(!pc->directive_list);
+        pc->directive_list = allocate<ZigList<AstNode*>>(1);
+        ast_parse_directives(pc, token_index, pc->directive_list);
+
+        AstNode *fn_decl_node = ast_parse_fn_def(pc, token_index, false);
+        if (fn_decl_node) {
             top_level_decls->append(fn_decl_node);
-        } else if (token->id == TokenIdKeywordExtern) {
-            AstNode *extern_node = ast_parse_extern_block(pc, token_index, &token_index);
+            continue;
+        }
+
+        AstNode *extern_node = ast_parse_extern_block(pc, token_index, false);
+        if (extern_node) {
             top_level_decls->append(extern_node);
-        } else {
-            *new_token_index = token_index;
-            return;
+            continue;
         }
+
+        if (pc->directive_list->length > 0) {
+            ast_error(directive_token, "invalid directive");
+        }
+        pc->directive_list = nullptr;
+
+        return;
     }
     zig_unreachable();
 }
@@ -674,11 +720,11 @@ AstNode *ast_parse(Buf *buf, ZigList<Token> *tokens) {
     pc.root = ast_create_node(NodeTypeRoot, &tokens->at(0));
     pc.tokens = tokens;
 
-    int new_token_index;
-    ast_parse_top_level_decls(&pc, 0, &new_token_index, &pc.root->data.root.top_level_decls);
+    int token_index = 0;
+    ast_parse_top_level_decls(&pc, &token_index, &pc.root->data.root.top_level_decls);
 
-    if (new_token_index != tokens->length - 1) {
-        ast_invalid_token_error(&pc, &tokens->at(new_token_index));
+    if (token_index != tokens->length - 1) {
+        ast_invalid_token_error(&pc, &tokens->at(token_index));
     }
 
     return pc.root;
src/parser.hpp
@@ -34,7 +34,15 @@ struct AstNodeRoot {
     ZigList<AstNode *> top_level_decls;
 };
 
+enum FnProtoVisibMod {
+    FnProtoVisibModPrivate,
+    FnProtoVisibModPub,
+    FnProtoVisibModExport,
+};
+
 struct AstNodeFnProto {
+    ZigList<AstNode *> *directives;
+    FnProtoVisibMod visib_mod;
     Buf name;
     ZigList<AstNode *> params;
     AstNode *return_type;
src/tokenizer.cpp
@@ -163,6 +163,10 @@ static void end_token(Tokenize *t) {
         t->cur_tok->id = TokenIdKeywordExtern;
     } else if (mem_eql_str(token_mem, token_len, "unreachable")) {
         t->cur_tok->id = TokenIdKeywordUnreachable;
+    } else if (mem_eql_str(token_mem, token_len, "pub")) {
+        t->cur_tok->id = TokenIdKeywordPub;
+    } else if (mem_eql_str(token_mem, token_len, "export")) {
+        t->cur_tok->id = TokenIdKeywordExport;
     }
 
     t->cur_tok = nullptr;
@@ -407,6 +411,8 @@ static const char * token_name(Token *token) {
         case TokenIdKeywordReturn: return "Return";
         case TokenIdKeywordExtern: return "Extern";
         case TokenIdKeywordUnreachable: return "Unreachable";
+        case TokenIdKeywordPub: return "Pub";
+        case TokenIdKeywordExport: return "Export";
         case TokenIdLParen: return "LParen";
         case TokenIdRParen: return "RParen";
         case TokenIdComma: return "Comma";
src/tokenizer.hpp
@@ -19,6 +19,8 @@ enum TokenId {
     TokenIdKeywordConst,
     TokenIdKeywordExtern,
     TokenIdKeywordUnreachable,
+    TokenIdKeywordPub,
+    TokenIdKeywordExport,
     TokenIdLParen,
     TokenIdRParen,
     TokenIdComma,
src/zig_llvm.cpp
@@ -10,7 +10,19 @@
 #include <llvm/InitializePasses.h>
 #include <llvm/PassRegistry.h>
 #include <llvm/MC/SubtargetFeature.h>
-
+#include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/FileSystem.h>
+#include <llvm/Target/TargetMachine.h>
+#include <llvm/IR/LegacyPassManager.h>
+#include <llvm/IR/Module.h>
+#include <llvm/IR/Verifier.h>
+#include <llvm/IR/Instructions.h>
+#include <llvm/IR/IRBuilder.h>
+#include <llvm/Analysis/TargetLibraryInfo.h>
+#include <llvm/Analysis/TargetTransformInfo.h>
+#include <llvm/Transforms/IPO.h>
+#include <llvm/Transforms/IPO/PassManagerBuilder.h>
+#include <llvm/Transforms/Scalar.h>
 
 using namespace llvm;
 
@@ -42,3 +54,123 @@ char *LLVMZigGetNativeFeatures(void) {
 
     return strdup(features.getString().c_str());
 }
+
+static void addAddDiscriminatorsPass(const PassManagerBuilder &Builder, legacy::PassManagerBase &PM) {
+  PM.add(createAddDiscriminatorsPass());
+}
+
+
+void LLVMZigOptimizeModule(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref) {
+    TargetMachine* target_machine = reinterpret_cast<TargetMachine*>(targ_machine_ref);
+    Module* module = unwrap(module_ref);
+    TargetLibraryInfoImpl tlii(Triple(module->getTargetTriple()));
+
+    PassManagerBuilder *PMBuilder = new PassManagerBuilder();
+    PMBuilder->OptLevel = target_machine->getOptLevel();
+    PMBuilder->SizeLevel = 0;
+    PMBuilder->BBVectorize = true;
+    PMBuilder->SLPVectorize = true;
+    PMBuilder->LoopVectorize = true;
+
+    PMBuilder->DisableUnitAtATime = false;
+    PMBuilder->DisableUnrollLoops = false;
+    PMBuilder->MergeFunctions = true;
+    PMBuilder->PrepareForLTO = true;
+    PMBuilder->RerollLoops = true;
+
+    PMBuilder->addExtension(PassManagerBuilder::EP_EarlyAsPossible, addAddDiscriminatorsPass);
+
+    PMBuilder->LibraryInfo = &tlii;
+
+    PMBuilder->Inliner = createFunctionInliningPass(PMBuilder->OptLevel, PMBuilder->SizeLevel);
+
+    // Set up the per-function pass manager.
+    legacy::FunctionPassManager *FPM = new legacy::FunctionPassManager(module);
+    FPM->add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
+#ifndef NDEBUG
+    bool verify_module = true;
+#else
+    bool verify_module = false;
+#endif
+    if (verify_module) {
+        FPM->add(createVerifierPass());
+    }
+    PMBuilder->populateFunctionPassManager(*FPM);
+
+    // Set up the per-module pass manager.
+    legacy::PassManager *MPM = new legacy::PassManager();
+    MPM->add(createTargetTransformInfoWrapperPass(target_machine->getTargetIRAnalysis()));
+
+    PMBuilder->populateModulePassManager(*MPM);
+
+
+    // run per function optimization passes
+    FPM->doInitialization();
+    for (Function &F : *module)
+      if (!F.isDeclaration())
+        FPM->run(F);
+    FPM->doFinalization();
+
+    // run per module optimization passes
+    MPM->run(*module);
+}
+
+static LLVMBool LLVMZigTargetMachineEmit(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
+        raw_pwrite_stream &out_stream, LLVMCodeGenFileType codegen, char **err_msg)
+{
+    TargetMachine* target_machine = reinterpret_cast<TargetMachine*>(targ_machine_ref);
+    Module* module = unwrap(module_ref);
+    TargetLibraryInfoImpl tlii(Triple(module->getTargetTriple()));
+
+    legacy::PassManager pass;
+
+    pass.add(new TargetLibraryInfoWrapperPass(tlii));
+
+    const DataLayout *td = target_machine->getDataLayout();
+
+    if (!td) {
+        *err_msg = strdup("No DataLayout in TargetMachine");
+        return true;
+    }
+    module->setDataLayout(*td);
+
+
+    TargetMachine::CodeGenFileType ft;
+    switch (codegen) {
+        case LLVMAssemblyFile:
+            ft = TargetMachine::CGFT_AssemblyFile;
+            break;
+        default:
+            ft = TargetMachine::CGFT_ObjectFile;
+            break;
+    }
+    if (target_machine->addPassesToEmitFile(pass, out_stream, ft)) {
+        *err_msg = strdup("TargetMachine can't emit a file of this type");
+        return true;
+    }
+
+    pass.run(*module);
+
+    out_stream.flush();
+    return false;
+}
+
+LLVMBool LLVMZigTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
+        char* filename, LLVMCodeGenFileType codegen, char** err_msg)
+{
+    std::error_code error_code;
+    raw_fd_ostream dest(filename, error_code, sys::fs::F_None);
+    if (error_code) {
+        *err_msg = strdup(error_code.message().c_str());
+        return true;
+    }
+    return LLVMZigTargetMachineEmit(targ_machine_ref, module_ref, dest, codegen, err_msg);
+}
+
+LLVMValueRef LLVMZigBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
+        unsigned NumArgs, unsigned CC, const char *Name)
+{
+    CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);
+    call_inst->setCallingConv(CC);
+    return wrap(unwrap(B)->Insert(call_inst));
+}
src/zig_llvm.hpp
@@ -21,4 +21,12 @@ void LLVMZigInitializeUnreachableBlockElimPass(LLVMPassRegistryRef R);
 char *LLVMZigGetHostCPUName(void);
 char *LLVMZigGetNativeFeatures(void);
 
+LLVMBool LLVMZigTargetMachineEmitToFile(LLVMTargetMachineRef target_machine, LLVMModuleRef module,
+        char* filename, LLVMCodeGenFileType codegen, char** error_msg);
+
+void LLVMZigOptimizeModule(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref);
+
+LLVMValueRef LLVMZigBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
+        unsigned NumArgs, unsigned CC, const char *Name);
+
 #endif
test/standalone.cpp
@@ -53,7 +53,7 @@ static void add_all_test_cases(void) {
             fn exit(code: i32) -> unreachable;
         }
 
-        fn _start() -> unreachable {
+        export fn _start() -> unreachable {
             puts("Hello, world!");
             exit(0);
         }
@@ -69,7 +69,7 @@ static void add_all_test_cases(void) {
         fn empty_function_1() {}
         fn empty_function_2() { return; }
 
-        fn _start() -> unreachable {
+        export fn _start() -> unreachable {
             empty_function_1();
             empty_function_2();
             this_is_a_function();
@@ -95,7 +95,7 @@ static void add_all_test_cases(void) {
 
         /// this is a documentation comment
         /// doc comment line 2
-        fn _start() -> unreachable {
+        export fn _start() -> unreachable {
             puts(/* mid-line comment /* nested */ */ "OK");
             exit(0);
         }
README.md
@@ -27,12 +27,11 @@ readable, safe, optimal, and concise code to solve any computing problem.
  * Source code is UTF-8.
  * Shebang line OK so language can be used for "scripting" as well.
  * Ability to mark functions as test and automatically run them in test mode.
+   This mode should automatically provide test coverage.
  * Memory zeroed by default, unless you initialize with "uninitialized".
 
 ## Roadmap
 
- * pub/private/export functions
- * make sure that release mode optimizes out empty private functions
  * test framework to test for compile errors
  * Simple .so library
  * Multiple files
@@ -69,11 +68,13 @@ TopLevelDecl : FnDef | ExternBlock
 
 ExternBlock : many(Directive) token(Extern) token(LBrace) many(FnDecl) token(RBrace)
 
-FnProto : token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
+FnProto : many(Directive) option(FnVisibleMod) token(Fn) token(Symbol) ParamDeclList option(token(Arrow) Type)
+
+FnVisibleMod : token(Pub) | token(Export)
 
 FnDecl : FnProto token(Semicolon)
 
-FnDef : many(Directive) FnProto Block
+FnDef : FnProto Block
 
 ParamDeclList : token(LParen) list(ParamDecl, token(Comma)) token(RParen)