diff --git a/README.md b/README.md index ee230e1b14..ab15d1e63e 100644 --- a/README.md +++ b/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) diff --git a/doc/vim/syntax/zig.vim b/doc/vim/syntax/zig.vim index 720a7175f0..05ce1533e7 100644 --- a/doc/vim/syntax/zig.vim +++ b/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" diff --git a/test/hello.zig b/example/hello.zig similarity index 79% rename from test/hello.zig rename to example/hello.zig index a7098aa16a..9e6257eaff 100644 --- a/test/hello.zig +++ b/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); } diff --git a/src/codegen.cpp b/src/codegen.cpp index 6e9c590a2e..ab801ab0b1 100644 --- a/src/codegen.cpp +++ b/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 errors; LLVMBuilderRef builder; @@ -228,6 +229,7 @@ static void find_declarations(CodeGen *g, AstNode *node) { FnTableEntry *fn_table_entry = allocate(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(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 *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); } diff --git a/src/codegen.hpp b/src/codegen.hpp index 49ee599c96..bfc880b2cd 100644 --- a/src/codegen.hpp +++ b/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); diff --git a/src/main.cpp b/src/main.cpp index bfde1d5bda..8e1ced7cbf 100644 --- a/src/main.cpp +++ b/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); diff --git a/src/parser.cpp b/src/parser.cpp index 4b965e1fbf..d9f488a254 100644 --- a/src/parser.cpp +++ b/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 *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_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; + 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 *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>(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 *top_level_decls) -{ +static void ast_parse_top_level_decls(ParseContext *pc, int *token_index, ZigList *top_level_decls) { for (;;) { - Token *token = &pc->tokens->at(token_index); - if (token->id == TokenIdNumberSign) { - assert(!pc->directive_list); - pc->directive_list = allocate>(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>(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); - top_level_decls->append(extern_node); - } else { - *new_token_index = token_index; - return; + continue; } + + AstNode *extern_node = ast_parse_extern_block(pc, token_index, false); + if (extern_node) { + top_level_decls->append(extern_node); + 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 *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; diff --git a/src/parser.hpp b/src/parser.hpp index 319c444f2e..8d92a40a75 100644 --- a/src/parser.hpp +++ b/src/parser.hpp @@ -34,7 +34,15 @@ struct AstNodeRoot { ZigList top_level_decls; }; +enum FnProtoVisibMod { + FnProtoVisibModPrivate, + FnProtoVisibModPub, + FnProtoVisibModExport, +}; + struct AstNodeFnProto { + ZigList *directives; + FnProtoVisibMod visib_mod; Buf name; ZigList params; AstNode *return_type; diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index f56c1d8fb9..a83881e807 100644 --- a/src/tokenizer.cpp +++ b/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"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index fbd085cecf..c3a276778d 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -19,6 +19,8 @@ enum TokenId { TokenIdKeywordConst, TokenIdKeywordExtern, TokenIdKeywordUnreachable, + TokenIdKeywordPub, + TokenIdKeywordExport, TokenIdLParen, TokenIdRParen, TokenIdComma, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index a43e0a541b..89d0563b97 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -10,7 +10,19 @@ #include #include #include - +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include 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(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(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)); +} diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index 1a7a6ef736..fda8e371ec 100644 --- a/src/zig_llvm.hpp +++ b/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 diff --git a/test/standalone.cpp b/test/standalone.cpp index a4848db127..a220e1251e 100644 --- a/test/standalone.cpp +++ b/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); }