diff --git a/CMakeLists.txt b/CMakeLists.txt index 70283bb4e3..9cd3d27a63 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -244,7 +244,6 @@ install(FILES "${CMAKE_SOURCE_DIR}/std/special/builtin.zig" DESTINATION "${ZIG_S install(FILES "${CMAKE_SOURCE_DIR}/std/special/compiler_rt.zig" DESTINATION "${ZIG_STD_DEST}/special") install(FILES "${CMAKE_SOURCE_DIR}/std/special/test_runner.zig" DESTINATION "${ZIG_STD_DEST}/special") install(FILES "${CMAKE_SOURCE_DIR}/std/special/zigrt.zig" DESTINATION "${ZIG_STD_DEST}/special") -install(FILES "${CMAKE_SOURCE_DIR}/std/target.zig" DESTINATION "${ZIG_STD_DEST}") if (ZIG_TEST_COVERAGE) add_custom_target(coverage diff --git a/doc/langref.md b/doc/langref.md index 18793a915a..4b3e1a706f 100644 --- a/doc/langref.md +++ b/doc/langref.md @@ -23,9 +23,9 @@ ContainerField = Symbol option(":" Expression) "," UseDecl = "use" Expression ";" -ExternDecl = "extern" (FnProto | VariableDeclaration) ";" +ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" -FnProto = option("coldcc" | "nakedcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) +FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) VisibleMod = "pub" | "export" diff --git a/src/all_types.hpp b/src/all_types.hpp index 090f4f4371..b3e8e6e426 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -304,12 +304,14 @@ struct TldVar { Buf *section_name; AstNode *set_global_linkage_node; GlobalLinkageId linkage; + Buf *extern_lib_name; }; struct TldFn { Tld base; FnTableEntry *fn_entry; + Buf *extern_lib_name; }; struct TldContainer { @@ -387,6 +389,14 @@ struct AstNodeRoot { ZigList top_level_decls; }; +enum CallingConvention { + CallingConventionUnspecified, + CallingConventionC, + CallingConventionCold, + CallingConventionNaked, + CallingConventionStdcall, +}; + struct AstNodeFnProto { VisibMod visib_mod; Buf *name; @@ -395,9 +405,10 @@ struct AstNodeFnProto { bool is_var_args; bool is_extern; bool is_inline; - bool is_coldcc; - bool is_nakedcc; + CallingConvention cc; AstNode *fn_def_node; + // populated if this is an extern declaration + Buf *lib_name; }; struct AstNodeFnDef { @@ -451,6 +462,8 @@ struct AstNodeVariableDeclaration { // one or both of type and expr will be non null AstNode *type; AstNode *expr; + // populated if this is an extern declaration + Buf *lib_name; }; struct AstNodeErrorValueDecl { @@ -879,9 +892,7 @@ struct FnTypeId { size_t param_count; size_t next_param_index; bool is_var_args; - bool is_naked; - bool is_cold; - bool is_extern; + CallingConvention cc; }; uint32_t fn_type_id_hash(FnTypeId*); @@ -1013,7 +1024,6 @@ struct TypeTableEntryFn { FnGenParamInfo *gen_param_info; LLVMTypeRef raw_type_ref; - LLVMCallConv calling_convention; TypeTableEntry *bound_fn_parent; }; @@ -1316,6 +1326,13 @@ enum BuildMode { BuildModeSafeRelease, }; +struct LinkLib { + Buf *name; + Buf *path; + ZigList symbols; // the list of symbols that we depend on from this lib + bool provided_explicitly; +}; + struct CodeGen { LLVMModuleRef module; ZigList errors; @@ -1324,7 +1341,9 @@ struct CodeGen { ZigLLVMDICompileUnit *compile_unit; ZigLLVMDIFile *compile_unit_file; - ZigList link_libs; // non-libc link libs + ZigList link_libs_list; + LinkLib *libc_link_lib; + // add -framework [name] args to linker ZigList darwin_frameworks; // add -rpath [name] args to linker @@ -1344,6 +1363,7 @@ struct CodeGen { HashMap exported_symbol_names; HashMap external_prototypes; + ZigList import_queue; size_t import_queue_index; ZigList resolve_queue; @@ -1402,7 +1422,6 @@ struct CodeGen { bool have_pub_main; bool have_c_main; bool have_pub_panic; - bool link_libc; Buf *libc_lib_dir; Buf *libc_static_lib_dir; Buf *libc_include_dir; diff --git a/src/analyze.cpp b/src/analyze.cpp index ecc1712fc8..85ef997502 100644 --- a/src/analyze.cpp +++ b/src/analyze.cpp @@ -802,6 +802,21 @@ TypeTableEntry *get_bound_fn_type(CodeGen *g, FnTableEntry *fn_entry) { return bound_fn_type; } +bool calling_convention_does_first_arg_return(CallingConvention cc) { + return cc == CallingConventionUnspecified; +} + +static const char *calling_convention_name(CallingConvention cc) { + switch (cc) { + case CallingConventionUnspecified: return "undefined"; + case CallingConventionC: return "ccc"; + case CallingConventionCold: return "coldcc"; + case CallingConventionNaked: return "nakedcc"; + case CallingConventionStdcall: return "stdcallcc"; + default: zig_unreachable(); + } +} + TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { auto table_entry = g->fn_type_table.maybe_get(fn_type_id); if (table_entry) { @@ -813,30 +828,29 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { fn_type->is_copyable = true; fn_type->data.fn.fn_type_id = *fn_type_id; - if (fn_type_id->is_cold) { - // cold calling convention only works on x86. - // but we can add the cold attribute later. - if (g->zig_target.arch.arch == ZigLLVM_x86 || - g->zig_target.arch.arch == ZigLLVM_x86_64) - { - fn_type->data.fn.calling_convention = LLVMColdCallConv; - } else { - fn_type->data.fn.calling_convention = LLVMFastCallConv; - } - } else if (fn_type_id->is_extern) { - fn_type->data.fn.calling_convention = LLVMCCallConv; - } else { - fn_type->data.fn.calling_convention = LLVMFastCallConv; - } - bool skip_debug_info = false; // populate the name of the type buf_resize(&fn_type->name, 0); - const char *extern_str = fn_type_id->is_extern ? "extern " : ""; - const char *naked_str = fn_type_id->is_naked ? "nakedcc " : ""; - const char *cold_str = fn_type_id->is_cold ? "coldcc " : ""; - buf_appendf(&fn_type->name, "%s%s%sfn(", extern_str, naked_str, cold_str); + const char *cc_str; + switch (fn_type->data.fn.fn_type_id.cc) { + case CallingConventionUnspecified: + cc_str = ""; + break; + case CallingConventionC: + cc_str = "extern "; + break; + case CallingConventionCold: + cc_str = "coldcc "; + break; + case CallingConventionNaked: + cc_str = "nakedcc "; + break; + case CallingConventionStdcall: + cc_str = "stdcallcc "; + break; + } + buf_appendf(&fn_type->name, "%sfn(", cc_str); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; @@ -861,7 +875,8 @@ TypeTableEntry *get_fn_type(CodeGen *g, FnTypeId *fn_type_id) { // next, loop over the parameters again and compute debug information // and codegen information if (!skip_debug_info) { - bool first_arg_return = !fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type); + bool first_arg_return = calling_convention_does_first_arg_return(fn_type_id->cc) && + handle_is_ptr(fn_type_id->return_type); // +1 for maybe making the first argument the return value LLVMTypeRef *gen_param_types = allocate(1 + fn_type_id->param_count); // +1 because 0 is the return type and +1 for maybe making first arg ret val @@ -1013,9 +1028,13 @@ void init_fn_type_id(FnTypeId *fn_type_id, AstNode *proto_node, size_t param_cou assert(proto_node->type == NodeTypeFnProto); AstNodeFnProto *fn_proto = &proto_node->data.fn_proto; - fn_type_id->is_extern = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); - fn_type_id->is_naked = fn_proto->is_nakedcc; - fn_type_id->is_cold = fn_proto->is_coldcc; + if (fn_proto->cc == CallingConventionUnspecified) { + bool extern_abi = fn_proto->is_extern || (fn_proto->visib_mod == VisibModExport); + fn_type_id->cc = extern_abi ? CallingConventionC : CallingConventionUnspecified; + } else { + fn_type_id->cc = fn_proto->cc; + } + fn_type_id->param_count = fn_proto->params.length; fn_type_id->param_info = allocate_nonzero(param_count_alloc); fn_type_id->next_param_index = 0; @@ -1037,18 +1056,24 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_comptime) { - if (fn_type_id.is_extern) { + if (fn_type_id.cc != CallingConventionUnspecified) { add_node_error(g, param_node, - buf_sprintf("comptime parameter not allowed in extern function")); + buf_sprintf("comptime parameter not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); } else if (param_is_var_args) { - if (fn_type_id.is_extern) { + if (fn_type_id.cc == CallingConventionC) { fn_type_id.param_count = fn_type_id.next_param_index; continue; - } else { + } else if (fn_type_id.cc == CallingConventionUnspecified) { return get_generic_fn_type(g, &fn_type_id); + } else { + add_node_error(g, param_node, + buf_sprintf("var args not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); + return g->builtin_types.entry_invalid; } } @@ -1066,9 +1091,10 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c buf_sprintf("parameter of type '%s' not allowed", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; case TypeTableEntryIdVar: - if (fn_type_id.is_extern) { + if (fn_type_id.cc != CallingConventionUnspecified) { add_node_error(g, param_node->data.param_decl.type, - buf_sprintf("parameter of type 'var' not allowed in extern function")); + buf_sprintf("parameter of type 'var' not allowed in function with calling convention '%s'", + calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); @@ -1097,7 +1123,7 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdFn: case TypeTableEntryIdEnumTag: ensure_complete_type(g, type_entry); - if (!fn_type_id.is_extern && !type_is_copyable(g, type_entry)) { + if (fn_type_id.cc == CallingConventionUnspecified && !type_is_copyable(g, type_entry)) { add_node_error(g, param_node->data.param_decl.type, buf_sprintf("type '%s' is not copyable; cannot pass by value", buf_ptr(&type_entry->name))); return g->builtin_types.entry_invalid; @@ -1130,10 +1156,11 @@ static TypeTableEntry *analyze_fn_type(CodeGen *g, AstNode *proto_node, Scope *c case TypeTableEntryIdBoundFn: case TypeTableEntryIdVar: case TypeTableEntryIdMetaType: - if (fn_type_id.is_extern) { + if (fn_type_id.cc != CallingConventionUnspecified) { add_node_error(g, fn_proto->return_type, - buf_sprintf("return type '%s' not allowed in extern function", - buf_ptr(&fn_type_id.return_type->name))); + buf_sprintf("return type '%s' not allowed in function with calling convention '%s'", + buf_ptr(&fn_type_id.return_type->name), + calling_convention_name(fn_type_id.cc))); return g->builtin_types.entry_invalid; } return get_generic_fn_type(g, &fn_type_id); @@ -1947,7 +1974,7 @@ static void resolve_decl_fn(CodeGen *g, TldFn *tld_fn) { if (buf_eql_str(&fn_table_entry->symbol_name, "main")) { g->main_fn = fn_table_entry; - if (!g->link_libc && tld_fn->base.visib_mod != VisibModExport) { + if (g->libc_link_lib == nullptr && tld_fn->base.visib_mod != VisibModExport) { TypeTableEntry *err_void = get_error_type(g, g->builtin_types.entry_void); TypeTableEntry *actual_return_type = fn_table_entry->type_entry->data.fn.fn_type_id.return_type; if (actual_return_type != err_void) { @@ -2109,6 +2136,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { VisibMod visib_mod = node->data.variable_declaration.visib_mod; TldVar *tld_var = allocate(1); init_tld(&tld_var->base, TldIdVar, name, visib_mod, node, &decls_scope->base); + tld_var->extern_lib_name = node->data.variable_declaration.lib_name; add_top_level_decl(g, decls_scope, &tld_var->base); break; } @@ -2124,6 +2152,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) { VisibMod visib_mod = node->data.fn_proto.visib_mod; TldFn *tld_fn = allocate(1); init_tld(&tld_fn->base, TldIdFn, fn_name, visib_mod, node, &decls_scope->base); + tld_fn->extern_lib_name = node->data.fn_proto.lib_name; add_top_level_decl(g, decls_scope, &tld_fn->base); ImportTableEntry *import = get_scope_import(&decls_scope->base); @@ -2497,13 +2526,7 @@ bool types_match_const_cast_only(TypeTableEntry *expected_type, TypeTableEntry * if (expected_type->id == TypeTableEntryIdFn && actual_type->id == TypeTableEntryIdFn) { - if (expected_type->data.fn.fn_type_id.is_extern != actual_type->data.fn.fn_type_id.is_extern) { - return false; - } - if (expected_type->data.fn.fn_type_id.is_naked != actual_type->data.fn.fn_type_id.is_naked) { - return false; - } - if (expected_type->data.fn.fn_type_id.is_cold != actual_type->data.fn.fn_type_id.is_cold) { + if (expected_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { return false; } if (expected_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { @@ -2780,11 +2803,6 @@ void define_local_param_variables(CodeGen *g, FnTableEntry *fn_table_entry, Vari add_node_error(g, param_decl_node, buf_sprintf("noalias on non-pointer parameter")); } - if (fn_type_id->is_extern && handle_is_ptr(param_type)) { - add_node_error(g, param_decl_node, - buf_sprintf("byvalue types not yet supported on extern function parameters")); - } - VariableTableEntry *var = add_variable(g, param_decl_node, fn_table_entry->child_scope, param_name, true, create_const_runtime(param_type), nullptr); var->src_arg_index = i; @@ -2849,12 +2867,6 @@ static void analyze_fn_body(CodeGen *g, FnTableEntry *fn_table_entry) { TypeTableEntry *fn_type = fn_table_entry->type_entry; assert(!fn_type->data.fn.is_generic); - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - - if (fn_type_id->is_extern && handle_is_ptr(fn_type_id->return_type)) { - add_node_error(g, return_type_node, - buf_sprintf("byvalue types not yet supported on extern function return values")); - } ir_gen_fn(g, fn_table_entry); if (fn_table_entry->ir_executable.invalid) { @@ -3018,7 +3030,7 @@ ImportTableEntry *add_source_file(CodeGen *g, PackageTableEntry *package, Buf *a g->have_pub_panic = true; } } else if (proto_node->data.fn_proto.visib_mod == VisibModExport && buf_eql_str(proto_name, "main") && - g->link_libc) + g->libc_link_lib != nullptr) { g->have_c_main = true; } @@ -3189,9 +3201,7 @@ bool fn_table_entry_eql(FnTableEntry *a, FnTableEntry *b) { uint32_t fn_type_id_hash(FnTypeId *id) { uint32_t result = 0; - result += id->is_extern ? (uint32_t)3349388391 : 0; - result += id->is_naked ? (uint32_t)608688877 : 0; - result += id->is_cold ? (uint32_t)3605523458 : 0; + result += ((uint32_t)(id->cc)) * (uint32_t)3349388391; result += id->is_var_args ? (uint32_t)1931444534 : 0; result += hash_ptr(id->return_type); for (size_t i = 0; i < id->param_count; i += 1) { @@ -3203,9 +3213,7 @@ uint32_t fn_type_id_hash(FnTypeId *id) { } bool fn_type_id_eql(FnTypeId *a, FnTypeId *b) { - if (a->is_extern != b->is_extern || - a->is_naked != b->is_naked || - a->is_cold != b->is_cold || + if (a->cc != b->cc || a->return_type != b->return_type || a->is_var_args != b->is_var_args || a->param_count != b->param_count) @@ -4344,8 +4352,7 @@ FnTableEntry *get_extern_panic_fn(CodeGen *g) { return g->extern_panic_fn; FnTypeId fn_type_id = {0}; - fn_type_id.is_extern = true; - fn_type_id.is_cold = true; + fn_type_id.cc = CallingConventionCold; fn_type_id.param_count = 2; fn_type_id.param_info = allocate(2); fn_type_id.next_param_index = 0; @@ -4525,3 +4532,42 @@ const char *type_id_name(TypeTableEntryId id) { } zig_unreachable(); } + +LinkLib *create_link_lib(Buf *name) { + LinkLib *link_lib = allocate(1); + link_lib->name = name; + return link_lib; +} + +LinkLib *add_link_lib(CodeGen *g, Buf *name) { + bool is_libc = buf_eql_str(name, "c"); + + if (is_libc && g->libc_link_lib != nullptr) + return g->libc_link_lib; + + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + LinkLib *existing_lib = g->link_libs_list.at(i); + if (buf_eql_buf(existing_lib->name, name)) { + return existing_lib; + } + } + + LinkLib *link_lib = create_link_lib(name); + g->link_libs_list.append(link_lib); + + if (is_libc) + g->libc_link_lib = link_lib; + + return link_lib; +} + +void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name) { + LinkLib *link_lib = add_link_lib(g, lib_name); + for (size_t i = 0; i < link_lib->symbols.length; i += 1) { + Buf *existing_symbol_name = link_lib->symbols.at(i); + if (buf_eql_buf(existing_symbol_name, symbol_name)) { + return; + } + } + link_lib->symbols.append(symbol_name); +} diff --git a/src/analyze.hpp b/src/analyze.hpp index 214227d388..439d6c7eeb 100644 --- a/src/analyze.hpp +++ b/src/analyze.hpp @@ -168,5 +168,9 @@ size_t type_id_len(); size_t type_id_index(TypeTableEntryId id); TypeTableEntry *get_generic_fn_type(CodeGen *g, FnTypeId *fn_type_id); bool type_is_copyable(CodeGen *g, TypeTableEntry *type_entry); +LinkLib *create_link_lib(Buf *name); +bool calling_convention_does_first_arg_return(CallingConvention cc); +LinkLib *add_link_lib(CodeGen *codegen, Buf *lib); +void add_link_lib_symbol(CodeGen *g, Buf *lib_name, Buf *symbol_name); #endif diff --git a/src/ast_render.cpp b/src/ast_render.cpp index a1b443986f..3def04801b 100644 --- a/src/ast_render.cpp +++ b/src/ast_render.cpp @@ -118,6 +118,17 @@ static const char *extern_string(bool is_extern) { return is_extern ? "extern " : ""; } +static const char *calling_convention_string(CallingConvention cc) { + switch (cc) { + case CallingConventionUnspecified: return ""; + case CallingConventionC: return "extern "; + case CallingConventionCold: return "coldcc "; + case CallingConventionNaked: return "nakedcc "; + case CallingConventionStdcall: return "stdcallcc "; + } + zig_unreachable(); +} + static const char *inline_string(bool is_inline) { return is_inline ? "inline " : ""; } @@ -951,10 +962,8 @@ static void ast_render_tld_fn(AstRender *ar, Buf *name, TldFn *tld_fn) { FnTableEntry *fn_entry = tld_fn->fn_entry; FnTypeId *fn_type_id = &fn_entry->type_entry->data.fn.fn_type_id; const char *visib_mod_str = visib_mod_string(tld_fn->base.visib_mod); - const char *extern_str = extern_string(fn_type_id->is_extern); - const char *coldcc_str = fn_type_id->is_cold ? "coldcc " : ""; - const char *nakedcc_str = fn_type_id->is_naked ? "nakedcc " : ""; - fprintf(ar->f, "%s%s%s%sfn %s(", visib_mod_str, extern_str, coldcc_str, nakedcc_str, buf_ptr(&fn_entry->symbol_name)); + const char *cc_str = calling_convention_string(fn_type_id->cc); + fprintf(ar->f, "%s%sfn %s(", visib_mod_str, cc_str, buf_ptr(&fn_entry->symbol_name)); for (size_t i = 0; i < fn_type_id->param_count; i += 1) { FnTypeParamInfo *param_info = &fn_type_id->param_info[i]; if (i != 0) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 8221ce9e11..0e5b8a5ea1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -138,8 +138,7 @@ CodeGen *codegen_create(Buf *root_src_path, const ZigTarget *target, OutType out g->zig_target.os == ZigLLVM_MacOSX || g->zig_target.os == ZigLLVM_IOS) { - g->link_libc = true; - g->link_libs.append(buf_create_from_str("c")); + g->libc_link_lib = create_link_lib(buf_create_from_str("c")); } return g; @@ -234,13 +233,8 @@ void codegen_add_rpath(CodeGen *g, const char *name) { g->rpath_list.append(buf_create_from_str(name)); } -void codegen_add_link_lib(CodeGen *g, const char *lib) { - if (strcmp(lib, "c") == 0) { - if (g->link_libc) - return; - g->link_libc = true; - } - g->link_libs.append(buf_create_from_str(lib)); +LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) { + return add_link_lib(g, name); } void codegen_add_framework(CodeGen *g, const char *framework) { @@ -334,6 +328,33 @@ static Buf *get_mangled_name(CodeGen *g, Buf *original_name, bool external_linka } } +static LLVMCallConv get_llvm_cc(CodeGen *g, CallingConvention cc) { + switch (cc) { + case CallingConventionUnspecified: return LLVMFastCallConv; + case CallingConventionC: return LLVMCCallConv; + case CallingConventionCold: + // cold calling convention only works on x86. + if (g->zig_target.arch.arch == ZigLLVM_x86 || + g->zig_target.arch.arch == ZigLLVM_x86_64) + { + return LLVMColdCallConv; + } else { + return LLVMCCallConv; + } + break; + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionStdcall: + // stdcall calling convention only works on x86. + if (g->zig_target.arch.arch == ZigLLVM_x86) { + return LLVMX86StdcallCallConv; + } else { + return LLVMCCallConv; + } + } + zig_unreachable(); +} + static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (fn_table_entry->llvm_value) return fn_table_entry->llvm_value; @@ -355,6 +376,16 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { } fn_table_entry->llvm_name = LLVMGetValueName(fn_table_entry->llvm_value); + //if (buf_eql_str(&fn_table_entry->symbol_name, "ExitProcess") || + // buf_eql_str(&fn_table_entry->symbol_name, "GetConsoleMode") || + // buf_eql_str(&fn_table_entry->symbol_name, "GetStdHandle") || + // buf_eql_str(&fn_table_entry->symbol_name, "GetFileInformationByHandleEx") || + // buf_eql_str(&fn_table_entry->symbol_name, "GetLastError") || + // buf_eql_str(&fn_table_entry->symbol_name, "WriteFile")) + //{ + // LLVMSetDLLStorageClass(fn_table_entry->llvm_value, LLVMDLLImportStorageClass); + //} + switch (fn_table_entry->fn_inline) { case FnInlineAlways: addLLVMFnAttr(fn_table_entry->llvm_value, "alwaysinline"); @@ -366,8 +397,14 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { case FnInlineAuto: break; } - if (fn_type->data.fn.fn_type_id.is_naked) { + + if (fn_type->data.fn.fn_type_id.cc == CallingConventionNaked) { addLLVMFnAttr(fn_table_entry->llvm_value, "naked"); + } else { + LLVMSetFunctionCallConv(fn_table_entry->llvm_value, get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc)); + if (fn_type->data.fn.fn_type_id.cc == CallingConventionCold) { + ZigLLVMAddFunctionAttrCold(fn_table_entry->llvm_value); + } } switch (fn_table_entry->linkage) { @@ -393,17 +430,13 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) { if (fn_table_entry->body_node != nullptr) { bool want_fn_safety = g->build_mode != BuildModeFastRelease && !fn_table_entry->def_scope->safety_off; if (want_fn_safety) { - if (g->link_libc) { + if (g->libc_link_lib != nullptr) { addLLVMFnAttr(fn_table_entry->llvm_value, "sspstrong"); addLLVMFnAttrStr(fn_table_entry->llvm_value, "stack-protector-buffer-size", "4"); } } } - LLVMSetFunctionCallConv(fn_table_entry->llvm_value, fn_type->data.fn.calling_convention); - if (fn_type->data.fn.fn_type_id.is_cold) { - ZigLLVMAddFunctionAttrCold(fn_table_entry->llvm_value); - } addLLVMFnAttr(fn_table_entry->llvm_value, "nounwind"); if (g->build_mode == BuildModeDebug && fn_table_entry->fn_inline != FnInlineAlways) { ZigLLVMAddFunctionAttr(fn_table_entry->llvm_value, "no-frame-pointer-elim", "true"); @@ -700,7 +733,8 @@ static void gen_panic_raw(CodeGen *g, LLVMValueRef msg_ptr, LLVMValueRef msg_len FnTableEntry *panic_fn = get_extern_panic_fn(g); LLVMValueRef fn_val = fn_llvm_value(g, panic_fn); LLVMValueRef args[] = { msg_ptr, msg_len }; - ZigLLVMBuildCall(g->builder, fn_val, args, 2, panic_fn->type_entry->data.fn.calling_convention, false, ""); + LLVMCallConv llvm_cc = get_llvm_cc(g, panic_fn->type_entry->data.fn.fn_type_id.cc); + ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, false, ""); LLVMBuildUnreachable(g->builder); } @@ -1100,15 +1134,14 @@ static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstruction *instruction) { static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrInstructionReturn *return_instruction) { LLVMValueRef value = ir_llvm_value(g, return_instruction->value); TypeTableEntry *return_type = return_instruction->value->value.type; - bool is_extern = g->cur_fn->type_entry->data.fn.fn_type_id.is_extern; if (handle_is_ptr(return_type)) { - if (is_extern) { - LLVMValueRef by_val_value = LLVMBuildLoad(g->builder, value, ""); - LLVMBuildRet(g->builder, by_val_value); - } else { + if (calling_convention_does_first_arg_return(g->cur_fn->type_entry->data.fn.fn_type_id.cc)) { assert(g->cur_ret_ptr); gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); LLVMBuildRetVoid(g->builder); + } else { + LLVMValueRef by_val_value = LLVMBuildLoad(g->builder, value, ""); + LLVMBuildRet(g->builder, by_val_value); } } else { LLVMBuildRet(g->builder, value); @@ -2059,9 +2092,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr bool want_always_inline = (instruction->fn_entry != nullptr && instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_inline; + LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc); LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values, (unsigned)gen_param_index, fn_type->data.fn.calling_convention, - want_always_inline, ""); + gen_param_values, (unsigned)gen_param_index, llvm_cc, want_always_inline, ""); for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; @@ -3893,7 +3926,7 @@ static void do_code_gen(CodeGen *g) { { addLLVMAttr(fn_val, 0, "nonnull"); } else if (handle_is_ptr(fn_type->data.fn.fn_type_id.return_type) && - !fn_type->data.fn.fn_type_id.is_extern) + calling_convention_does_first_arg_return(fn_type->data.fn.fn_type_id.cc)) { addLLVMArgAttr(fn_val, 0, "sret"); addLLVMArgAttr(fn_val, 0, "nonnull"); @@ -4648,15 +4681,7 @@ static void define_builtin_compile_vars(CodeGen *g) { buf_appendf(contents, "pub const environ = Environ.%s;\n", cur_environ); buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); - - { - buf_appendf(contents, "pub const link_libs = [][]const u8 {\n"); - for (size_t i = 0; i < g->link_libs.length; i += 1) { - Buf *link_lib_buf = g->link_libs.at(i); - buf_appendf(contents, " \"%s\",\n", buf_ptr(link_lib_buf)); - } - buf_appendf(contents, "};\n"); - } + buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); buf_appendf(contents, "pub const __zig_panic_implementation_provided = %s; // overwritten later\n", bool_to_str(false)); diff --git a/src/codegen.hpp b/src/codegen.hpp index cbe9fcfce1..0a4b2db09e 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -33,7 +33,7 @@ void codegen_set_dynamic_linker(CodeGen *g, Buf *dynamic_linker); void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole); void codegen_set_windows_unicode(CodeGen *g, bool municode); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); -void codegen_add_link_lib(CodeGen *codegen, const char *lib); +LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); void codegen_add_framework(CodeGen *codegen, const char *name); void codegen_add_rpath(CodeGen *codegen, const char *name); void codegen_set_mlinker_version(CodeGen *g, Buf *darwin_linker_version); diff --git a/src/ir.cpp b/src/ir.cpp index 3b324352bf..bda6852fdf 100644 --- a/src/ir.cpp +++ b/src/ir.cpp @@ -9044,7 +9044,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal if (comptime_fn_call) { // No special handling is needed for compile time evaluation of generic functions. - if (!fn_entry || fn_entry->type_entry->data.fn.fn_type_id.is_extern) { + if (!fn_entry || fn_entry->body_node == nullptr) { ir_add_error(ira, fn_ref, buf_sprintf("unable to evaluate constant expression")); return ira->codegen->builtin_types.entry_invalid; } @@ -10129,6 +10129,10 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source { TldVar *tld_var = (TldVar *)tld; VariableTableEntry *var = tld_var->var; + if (tld_var->extern_lib_name != nullptr) { + add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name); + } + return ir_analyze_var_ptr(ira, source_instruction, var, false, false); } case TldIdFn: @@ -10147,6 +10151,10 @@ static TypeTableEntry *ir_analyze_decl_ref(IrAnalyze *ira, IrInstruction *source const_val->type = fn_entry->type_entry; const_val->data.x_fn.fn_entry = fn_entry; + if (tld_fn->extern_lib_name != nullptr) { + add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name); + } + bool ptr_is_const = true; bool ptr_is_volatile = false; return ir_analyze_const_ptr(ira, source_instruction, const_val, fn_entry->type_entry, @@ -13167,13 +13175,15 @@ static TypeTableEntry *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstruc bool param_is_var_args = param_node->data.param_decl.is_var_args; if (param_is_var_args) { - if (fn_type_id.is_extern) { + if (fn_type_id.cc == CallingConventionC) { fn_type_id.param_count = fn_type_id.next_param_index; continue; - } else { + } else if (fn_type_id.cc == CallingConventionUnspecified) { ConstExprValue *out_val = ir_build_const_from(ira, &instruction->base); out_val->data.x_type = get_generic_fn_type(ira->codegen, &fn_type_id); return ira->codegen->builtin_types.entry_type; + } else { + zig_unreachable(); } } IrInstruction *param_type_value = instruction->param_types[fn_type_id.next_param_index]->other; @@ -13484,6 +13494,10 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, if (type_is_invalid(var_ptr->value.type)) return ira->codegen->builtin_types.entry_invalid; + if (tld_var->extern_lib_name != nullptr) { + add_link_lib_symbol(ira->codegen, tld_var->extern_lib_name, &var->name); + } + if (lval.is_ptr) { ir_link_new_instruction(var_ptr, &instruction->base); return var_ptr->value.type; @@ -13499,6 +13513,10 @@ static TypeTableEntry *ir_analyze_instruction_decl_ref(IrAnalyze *ira, FnTableEntry *fn_entry = tld_fn->fn_entry; assert(fn_entry->type_entry); + if (tld_fn->extern_lib_name != nullptr) { + add_link_lib_symbol(ira->codegen, tld_fn->extern_lib_name, &fn_entry->symbol_name); + } + IrInstruction *ref_instruction = ir_create_const_fn(&ira->new_irb, instruction->base.scope, instruction->base.source_node, fn_entry); if (lval.is_ptr) { @@ -13896,7 +13914,7 @@ FnTableEntry *ir_create_inline_fn(CodeGen *codegen, Buf *fn_name, VariableTableE assert(src_fn_type->id == TypeTableEntryIdFn); FnTypeId new_fn_type = src_fn_type->data.fn.fn_type_id; - new_fn_type.is_extern = false; + new_fn_type.cc = CallingConventionUnspecified; fn_entry->type_entry = get_fn_type(codegen, &new_fn_type); diff --git a/src/link.cpp b/src/link.cpp index ed1b36703c..1e44d908fb 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -38,12 +38,6 @@ static Buf *build_o(CodeGen *parent_gen, const char *oname) { ZigTarget *child_target = parent_gen->is_native_target ? nullptr : &parent_gen->zig_target; CodeGen *child_gen = codegen_create(full_path, child_target, OutTypeObj, parent_gen->build_mode); - child_gen->link_libc = parent_gen->link_libc; - - child_gen->link_libs.resize(parent_gen->link_libs.length); - for (size_t i = 0; i < parent_gen->link_libs.length; i += 1) { - child_gen->link_libs.items[i] = parent_gen->link_libs.items[i]; - } codegen_set_omit_zigrt(child_gen, true); child_gen->want_h_file = false; @@ -215,13 +209,13 @@ static void construct_linker_job_elf(LinkJob *lj) { if (g->each_lib_rpath) { for (size_t i = 0; i < g->lib_dirs.length; i += 1) { const char *lib_dir = g->lib_dirs.at(i); - for (size_t i = 0; i < g->link_libs.length; i += 1) { - Buf *link_lib = g->link_libs.at(i); - if (buf_eql_str(link_lib, "c")) { + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + LinkLib *link_lib = g->link_libs_list.at(i); + if (buf_eql_str(link_lib->name, "c")) { continue; } bool does_exist; - Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib)); + Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); if (os_file_exists(test_path, &does_exist) != ErrorNone) { zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); } @@ -239,7 +233,7 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(lib_dir); } - if (g->link_libc) { + if (g->libc_link_lib != nullptr) { lj->args.append("-L"); lj->args.append(buf_ptr(g->libc_lib_dir)); @@ -265,7 +259,7 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { + if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { Buf *builtin_o_path = build_o(g, "builtin"); lj->args.append(buf_ptr(builtin_o_path)); @@ -273,25 +267,25 @@ static void construct_linker_job_elf(LinkJob *lj) { lj->args.append(buf_ptr(compiler_rt_o_path)); } - for (size_t i = 0; i < g->link_libs.length; i += 1) { - Buf *link_lib = g->link_libs.at(i); - if (buf_eql_str(link_lib, "c")) { + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + LinkLib *link_lib = g->link_libs_list.at(i); + if (buf_eql_str(link_lib->name, "c")) { continue; } Buf *arg; - if (buf_starts_with_str(link_lib, "/") || buf_ends_with_str(link_lib, ".a") || - buf_ends_with_str(link_lib, ".so")) + if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || + buf_ends_with_str(link_lib->name, ".so")) { - arg = link_lib; + arg = link_lib->name; } else { - arg = buf_sprintf("-l%s", buf_ptr(link_lib)); + arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); } lj->args.append(buf_ptr(arg)); } // libc dep - if (g->link_libc) { + if (g->libc_link_lib != nullptr) { if (g->is_static) { lj->args.append("--start-group"); lj->args.append("-lgcc"); @@ -394,7 +388,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); } - if (g->link_libc) { + if (g->libc_link_lib != nullptr) { lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_lib_dir)))); lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(g->libc_static_lib_dir)))); } @@ -403,7 +397,7 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - if (!g->link_libc && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { + if (g->libc_link_lib == nullptr && (g->out_type == OutTypeExe || g->out_type == OutTypeLib)) { Buf *builtin_o_path = build_o(g, "builtin"); lj->args.append(buf_ptr(builtin_o_path)); @@ -411,17 +405,35 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(compiler_rt_o_path)); } - - for (size_t i = 0; i < g->link_libs.length; i += 1) { - Buf *link_lib = g->link_libs.at(i); - if (buf_eql_str(link_lib, "c")) { + Buf *def_contents = buf_alloc(); + for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { + LinkLib *link_lib = g->link_libs_list.at(lib_i); + if (buf_eql_str(link_lib->name, "c")) { continue; } - Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib)); - lj->args.append(buf_ptr(arg)); + if (link_lib->provided_explicitly) { + Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); + lj->args.append(buf_ptr(arg)); + } else { + buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name)); + for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) { + Buf *symbol_name = link_lib->symbols.at(exp_i); + buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name)); + } + buf_appendf(def_contents, "\n"); + } + } + if (buf_len(def_contents) != 0) { + Buf *dll_path = buf_alloc(); + os_path_join(g->cache_dir, buf_create_from_str("all.dll"), dll_path); + ZigLLDDefToLib(def_contents, dll_path); + + Buf *all_lib_path = buf_alloc(); + os_path_join(g->cache_dir, buf_create_from_str("all.lib"), all_lib_path); + lj->args.append(buf_ptr(all_lib_path)); } - if (g->link_libc) { + if (g->libc_link_lib != nullptr) { if (g->is_static) { lj->args.append("--start-group"); } @@ -664,12 +676,12 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); } - for (size_t i = 0; i < g->link_libs.length; i += 1) { - Buf *link_lib = g->link_libs.at(i); - if (buf_eql_str(link_lib, "c")) { + for (size_t i = 0; i < g->link_libs_list.length; i += 1) { + LinkLib *link_lib = g->link_libs_list.at(i); + if (buf_eql_str(link_lib->name, "c")) { continue; } - Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib)); + Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); lj->args.append(buf_ptr(arg)); } @@ -771,7 +783,7 @@ void codegen_link(CodeGen *g, const char *out_file) { return; } - lj.link_in_crt = (g->link_libc && g->out_type == OutTypeExe); + lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); construct_linker_job(&lj); diff --git a/src/main.cpp b/src/main.cpp index ae0f747d1e..51f8d17205 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -601,7 +601,8 @@ int main(int argc, char **argv) { codegen_add_lib_dir(g, lib_dirs.at(i)); } for (size_t i = 0; i < link_libs.length; i += 1) { - codegen_add_link_lib(g, link_libs.at(i)); + LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); + link_lib->provided_explicitly = true; } for (size_t i = 0; i < frameworks.length; i += 1) { codegen_add_framework(g, frameworks.at(i)); diff --git a/src/parseh.cpp b/src/parseh.cpp index 9eb851c01a..ddf8cb11e8 100644 --- a/src/parseh.cpp +++ b/src/parseh.cpp @@ -477,8 +477,7 @@ static TypeTableEntry *resolve_type_with_table(Context *c, const Type *ty, const } FnTypeId fn_type_id = {0}; - fn_type_id.is_naked = false; - fn_type_id.is_extern = true; + fn_type_id.cc = CallingConventionC; fn_type_id.is_var_args = fn_proto_ty->isVariadic(); fn_type_id.param_count = fn_proto_ty->getNumParams(); @@ -619,7 +618,7 @@ static void visit_fn_decl(Context *c, const FunctionDecl *fn_decl) { buf_init_from_buf(&fn_entry->symbol_name, fn_name); fn_entry->type_entry = fn_type; - assert(!fn_type->data.fn.fn_type_id.is_naked); + assert(fn_type->data.fn.fn_type_id.cc != CallingConventionNaked); size_t arg_count = fn_type->data.fn.fn_type_id.param_count; fn_entry->param_names = allocate(arg_count); diff --git a/src/parser.cpp b/src/parser.cpp index 3dd4fdf418..529d5e73a7 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2123,25 +2123,29 @@ static AstNode *ast_parse_block(ParseContext *pc, size_t *token_index, bool mand } /* -FnProto = option("coldcc" | "nakedcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) +FnProto = option("coldcc" | "nakedcc" | "stdcallcc") "fn" option(Symbol) ParamDeclList option("->" TypeExpr) */ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *first_token = &pc->tokens->at(*token_index); Token *fn_token; - bool is_coldcc = false; - bool is_nakedcc = false; + CallingConvention cc; if (first_token->id == TokenIdKeywordColdCC) { *token_index += 1; fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); - is_coldcc = true; + cc = CallingConventionCold; } else if (first_token->id == TokenIdKeywordNakedCC) { *token_index += 1; fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); - is_nakedcc = true; + cc = CallingConventionNaked; + } else if (first_token->id == TokenIdKeywordStdcallCC) { + *token_index += 1; + fn_token = ast_eat_token(pc, token_index, TokenIdKeywordFn); + cc = CallingConventionStdcall; } else if (first_token->id == TokenIdKeywordFn) { fn_token = first_token; *token_index += 1; + cc = CallingConventionUnspecified; } else if (mandatory) { ast_expect_token(pc, first_token, TokenIdKeywordFn); zig_unreachable(); @@ -2151,8 +2155,7 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, size_t *token_index, bool m AstNode *node = ast_create_node(pc, NodeTypeFnProto, fn_token); node->data.fn_proto.visib_mod = visib_mod; - node->data.fn_proto.is_coldcc = is_coldcc; - node->data.fn_proto.is_nakedcc = is_nakedcc; + node->data.fn_proto.cc = cc; Token *fn_name = &pc->tokens->at(*token_index); if (fn_name->id == TokenIdSymbol) { @@ -2220,7 +2223,7 @@ static AstNode *ast_parse_fn_def(ParseContext *pc, size_t *token_index, bool man } /* -ExternDecl = "extern" (FnProto | VariableDeclaration) ";" +ExternDecl = "extern" option(String) (FnProto | VariableDeclaration) ";" */ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, bool mandatory, VisibMod visib_mod) { Token *extern_kw = &pc->tokens->at(*token_index); @@ -2233,11 +2236,19 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo } *token_index += 1; + Token *lib_name_tok = &pc->tokens->at(*token_index); + Buf *lib_name = nullptr; + if (lib_name_tok->id == TokenIdStringLiteral) { + lib_name = token_buf(lib_name_tok); + *token_index += 1; + } + AstNode *fn_proto_node = ast_parse_fn_proto(pc, token_index, false, visib_mod); if (fn_proto_node) { ast_eat_token(pc, token_index, TokenIdSemicolon); fn_proto_node->data.fn_proto.is_extern = true; + fn_proto_node->data.fn_proto.lib_name = lib_name; return fn_proto_node; } @@ -2247,6 +2258,7 @@ static AstNode *ast_parse_extern_decl(ParseContext *pc, size_t *token_index, boo ast_eat_token(pc, token_index, TokenIdSemicolon); var_decl_node->data.variable_declaration.is_extern = true; + var_decl_node->data.variable_declaration.lib_name = lib_name; return var_decl_node; } diff --git a/src/tokenizer.cpp b/src/tokenizer.cpp index 344cf5341c..7853d7f74c 100644 --- a/src/tokenizer.cpp +++ b/src/tokenizer.cpp @@ -133,6 +133,7 @@ static const struct ZigKeyword zig_keywords[] = { {"packed", TokenIdKeywordPacked}, {"pub", TokenIdKeywordPub}, {"return", TokenIdKeywordReturn}, + {"stdcallcc", TokenIdKeywordStdcallCC}, {"struct", TokenIdKeywordStruct}, {"switch", TokenIdKeywordSwitch}, {"test", TokenIdKeywordTest}, @@ -1471,6 +1472,7 @@ const char * token_name(TokenId id) { case TokenIdKeywordPacked: return "packed"; case TokenIdKeywordPub: return "pub"; case TokenIdKeywordReturn: return "return"; + case TokenIdKeywordStdcallCC: return "stdcallcc"; case TokenIdKeywordStruct: return "struct"; case TokenIdKeywordSwitch: return "switch"; case TokenIdKeywordTest: return "test"; diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp index 36420127b5..a5c81f7694 100644 --- a/src/tokenizer.hpp +++ b/src/tokenizer.hpp @@ -71,6 +71,7 @@ enum TokenId { TokenIdKeywordPacked, TokenIdKeywordPub, TokenIdKeywordReturn, + TokenIdKeywordStdcallCC, TokenIdKeywordStruct, TokenIdKeywordSwitch, TokenIdKeywordTest, diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index 29accd59cc..1eeac98421 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -791,3 +792,151 @@ bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_ } zig_unreachable(); } + +// workaround for LLD not exposing ability to convert .def to .lib + +#include + +namespace lld { +namespace coff { + +class SymbolBody; +class StringChunk; +struct Symbol; + +struct Export { + StringRef Name; // N in /export:N or /export:E=N + StringRef ExtName; // E in /export:E=N + SymbolBody *Sym = nullptr; + uint16_t Ordinal = 0; + bool Noname = false; + bool Data = false; + bool Private = false; + + // If an export is a form of /export:foo=dllname.bar, that means + // that foo should be exported as an alias to bar in the DLL. + // ForwardTo is set to "dllname.bar" part. Usually empty. + StringRef ForwardTo; + StringChunk *ForwardChunk = nullptr; + + // True if this /export option was in .drectves section. + bool Directives = false; + StringRef SymbolName; + StringRef ExportName; // Name in DLL + + bool operator==(const Export &E) { + return (Name == E.Name && ExtName == E.ExtName && + Ordinal == E.Ordinal && Noname == E.Noname && + Data == E.Data && Private == E.Private); + } +}; + +enum class DebugType { + None = 0x0, + CV = 0x1, /// CodeView + PData = 0x2, /// Procedure Data + Fixup = 0x4, /// Relocation Table +}; + +struct Configuration { + enum ManifestKind { SideBySide, Embed, No }; + llvm::COFF::MachineTypes Machine = llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN; + bool Verbose = false; + llvm::COFF::WindowsSubsystem Subsystem = llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN; + SymbolBody *Entry = nullptr; + bool NoEntry = false; + std::string OutputFile; + bool DoGC = true; + bool DoICF = true; + bool Relocatable = true; + bool Force = false; + bool Debug = false; + bool WriteSymtab = true; + unsigned DebugTypes = static_cast(DebugType::None); + StringRef PDBPath; + + // Symbols in this set are considered as live by the garbage collector. + std::set GCRoot; + + std::set NoDefaultLibs; + bool NoDefaultLibAll = false; + + // True if we are creating a DLL. + bool DLL = false; + StringRef Implib; + std::vector Exports; + std::set DelayLoads; + std::map DLLOrder; + SymbolBody *DelayLoadHelper = nullptr; + + // Used for SafeSEH. + Symbol *SEHTable = nullptr; + Symbol *SEHCount = nullptr; + + // Used for /opt:lldlto=N + unsigned LTOOptLevel = 2; + + // Used for /opt:lldltojobs=N + unsigned LTOJobs = 1; + + // Used for /merge:from=to (e.g. /merge:.rdata=.text) + std::map Merge; + + // Used for /section=.name,{DEKPRSW} to set section attributes. + std::map Section; + + // Options for manifest files. + ManifestKind Manifest = SideBySide; + int ManifestID = 1; + StringRef ManifestDependency; + bool ManifestUAC = true; + std::vector ManifestInput; + StringRef ManifestLevel = "'asInvoker'"; + StringRef ManifestUIAccess = "'false'"; + StringRef ManifestFile; + + // Used for /failifmismatch. + std::map MustMatch; + + // Used for /alternatename. + std::map AlternateNames; + + uint64_t ImageBase = -1; + uint64_t StackReserve = 1024 * 1024; + uint64_t StackCommit = 4096; + uint64_t HeapReserve = 1024 * 1024; + uint64_t HeapCommit = 4096; + uint32_t MajorImageVersion = 0; + uint32_t MinorImageVersion = 0; + uint32_t MajorOSVersion = 6; + uint32_t MinorOSVersion = 0; + bool DynamicBase = true; + bool AllowBind = true; + bool NxCompat = true; + bool AllowIsolation = true; + bool TerminalServerAware = true; + bool LargeAddressAware = false; + bool HighEntropyVA = false; + + // This is for debugging. + bool DebugPdb = false; + bool DumpPdb = false; +}; + +extern Configuration *Config; + +void writeImportLibrary(); +void parseModuleDefs(MemoryBufferRef MB); + +} // namespace coff +} // namespace lld + +// writes the output to dll_path with .dll replaced with .lib +void ZigLLDDefToLib(Buf *def_contents, Buf *dll_path) { + lld::coff::Config = new lld::coff::Configuration; + auto mem_buf = MemoryBuffer::getMemBuffer(buf_ptr(def_contents)); + MemoryBufferRef mbref(*mem_buf); + lld::coff::parseModuleDefs(mbref); + lld::coff::Config->OutputFile = buf_ptr(dll_path); + lld::coff::writeImportLibrary(); +} diff --git a/src/zig_llvm.hpp b/src/zig_llvm.hpp index 2acffeaba6..413d158e6d 100644 --- a/src/zig_llvm.hpp +++ b/src/zig_llvm.hpp @@ -359,5 +359,6 @@ void ZigLLVMGetNativeTarget(ZigLLVM_ArchType *arch_type, ZigLLVM_SubArchType *su ZigLLVM_VendorType *vendor_type, ZigLLVM_OSType *os_type, ZigLLVM_EnvironmentType *environ_type, ZigLLVM_ObjectFormatType *oformat); +void ZigLLDDefToLib(Buf *def_contents, Buf *dll_path); #endif diff --git a/std/c/darwin.zig b/std/c/darwin.zig index 2095139b91..9d4961fe00 100644 --- a/std/c/darwin.zig +++ b/std/c/darwin.zig @@ -1,4 +1,4 @@ -pub extern fn getrandom(buf_ptr: &u8, buf_len: usize) -> c_int; +pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize) -> c_int; +fn extern "c" __error() -> &c_int; -extern fn __error() -> &c_int; pub const _errno = __error; diff --git a/std/c/index.zig b/std/c/index.zig index a3e8a3fc9d..091458d789 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -9,7 +9,6 @@ pub use switch(builtin.os) { else => empty_import, }; -pub extern fn abort() -> noreturn; - +pub extern "c" fn abort() -> noreturn; const empty_import = @import("../empty.zig"); diff --git a/std/c/linux.zig b/std/c/linux.zig index fe5476a20f..2a18ce46bd 100644 --- a/std/c/linux.zig +++ b/std/c/linux.zig @@ -1,4 +1,3 @@ -pub extern fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) -> c_int; - -extern fn __errno_location() -> &c_int; +pub extern "c" fn getrandom(buf_ptr: &u8, buf_len: usize, flags: c_uint) -> c_int; +extern "c" fn __errno_location() -> &c_int; pub const _errno = __errno_location; diff --git a/std/c/windows.zig b/std/c/windows.zig index de77f67f6c..6df1067ebf 100644 --- a/std/c/windows.zig +++ b/std/c/windows.zig @@ -1 +1 @@ -pub extern fn _errno() -> &c_int; +pub extern "c" fn _errno() -> &c_int; diff --git a/std/debug.zig b/std/debug.zig index 44923ae082..d8567aae6e 100644 --- a/std/debug.zig +++ b/std/debug.zig @@ -16,7 +16,9 @@ pub fn assert(ok: bool) { var panicking = false; /// This is the default panic implementation. -pub coldcc fn panic(comptime format: []const u8, args: ...) -> noreturn { +pub fn panic(comptime format: []const u8, args: ...) -> noreturn { + // TODO an intrinsic that labels this as unlikely to be reached + // TODO // if (@atomicRmw(AtomicOp.XChg, &panicking, true, AtomicOrder.SeqCst)) { } if (panicking) { diff --git a/std/index.zig b/std/index.zig index b8b6029b55..c164a66740 100644 --- a/std/index.zig +++ b/std/index.zig @@ -22,7 +22,6 @@ pub const net = @import("net.zig"); pub const os = @import("os/index.zig"); pub const rand = @import("rand.zig"); pub const sort = @import("sort.zig"); -pub const target = @import("target.zig"); test "std" { // run tests from these @@ -50,5 +49,4 @@ test "std" { _ = @import("os/index.zig"); _ = @import("rand.zig"); _ = @import("sort.zig"); - _ = @import("target.zig"); } diff --git a/std/os/index.zig b/std/os/index.zig index de877f94c3..d07cb6fff3 100644 --- a/std/os/index.zig +++ b/std/os/index.zig @@ -24,7 +24,6 @@ const debug = @import("../debug.zig"); const assert = debug.assert; const errno = @import("errno.zig"); -const linking_libc = @import("../target.zig").linking_libc; const c = @import("../c/index.zig"); const mem = @import("../mem.zig"); @@ -60,14 +59,14 @@ pub fn getRandomBytes(buf: []u8) -> %void { while (true) { const err = switch (builtin.os) { Os.linux => { - if (linking_libc) { + if (builtin.link_libc) { if (c.getrandom(buf.ptr, buf.len, 0) == -1) *c._errno() else 0 } else { posix.getErrno(posix.getrandom(buf.ptr, buf.len, 0)) } }, Os.darwin, Os.macosx, Os.ios => { - if (linking_libc) { + if (builtin.link_libc) { if (posix.getrandom(buf.ptr, buf.len) == -1) *c._errno() else 0 } else { posix.getErrno(posix.getrandom(buf.ptr, buf.len)) @@ -103,7 +102,7 @@ pub fn getRandomBytes(buf: []u8) -> %void { /// If linking against libc, this calls the abort() libc function. Otherwise /// it uses the zig standard library implementation. pub coldcc fn abort() -> noreturn { - if (linking_libc) { + if (builtin.link_libc) { c.abort(); } switch (builtin.os) { diff --git a/std/os/windows/index.zig b/std/os/windows/index.zig index eb60f392fb..36e9e4e8af 100644 --- a/std/os/windows/index.zig +++ b/std/os/windows/index.zig @@ -1,39 +1,50 @@ pub const ERROR = @import("error.zig"); -pub extern fn CryptAcquireContext(phProv: &HCRYPTPROV, pszContainer: LPCTSTR, +pub extern "kernel32" stdcallcc fn CryptAcquireContext(phProv: &HCRYPTPROV, pszContainer: LPCTSTR, pszProvider: LPCTSTR, dwProvType: DWORD, dwFlags: DWORD) -> bool; -pub extern fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> bool; +pub extern "kernel32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) -> bool; -pub extern fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: &BYTE) -> bool; +pub extern "kernel32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: &BYTE) -> bool; -pub extern fn ExitProcess(exit_code: UINT) -> noreturn; +pub extern "kernel32" fn ExitProcess(exit_code: UINT) -> noreturn; -pub extern fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) -> bool; +pub extern "kernel32" stdcallcc fn GetCommandLine() -> LPTSTR; + +pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: &DWORD) -> bool; /// Retrieves the calling thread's last-error code value. The last-error code is maintained on a per-thread basis. /// Multiple threads do not overwrite each other's last-error code. -pub extern fn GetLastError() -> DWORD; +pub extern "kernel32" stdcallcc fn GetLastError() -> DWORD; /// Retrieves file information for the specified file. -pub extern fn GetFileInformationByHandleEx(in_hFile: HANDLE, in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, - out_lpFileInformation: &c_void, in_dwBufferSize: DWORD) -> bool; +pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(in_hFile: HANDLE, + in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS, out_lpFileInformation: &c_void, + in_dwBufferSize: DWORD) -> bool; /// Retrieves a handle to the specified standard device (standard input, standard output, or standard error). -pub extern fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE; +pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) -> ?HANDLE; /// Reads data from the specified file or input/output (I/O) device. Reads occur at the position specified by the file pointer if supported by the device. /// This function is designed for both synchronous and asynchronous operations. For a similar function designed solely for asynchronous operation, see ReadFileEx. -pub extern fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID, in_nNumberOfBytesToRead: DWORD, - out_lpNumberOfBytesRead: &DWORD, in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; +pub extern "kernel32" stdcallcc fn ReadFile(in_hFile: HANDLE, out_lpBuffer: LPVOID, + in_nNumberOfBytesToRead: DWORD, out_lpNumberOfBytesRead: &DWORD, + in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; /// Writes data to the specified file or input/output (I/O) device. /// This function is designed for both synchronous and asynchronous operation. For a similar function designed solely for asynchronous operation, see WriteFileEx. -pub extern fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, in_nNumberOfBytesToWrite: DWORD, - out_lpNumberOfBytesWritten: ?&DWORD, in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; +pub extern "kernel32" stdcallcc fn WriteFile(in_hFile: HANDLE, in_lpBuffer: &const c_void, + in_nNumberOfBytesToWrite: DWORD, out_lpNumberOfBytesWritten: ?&DWORD, + in_out_lpOverlapped: ?&OVERLAPPED) -> BOOL; pub const PROV_RSA_FULL = 1; +pub const UNICODE = false; +pub const LPTSTR = if (unicode) LPWSTR else LPSTR; +pub const LPWSTR = &WCHAR; +pub const LPSTR = &CHAR; +pub const CHAR = u8; + pub const BOOL = bool; pub const BYTE = u8; @@ -45,12 +56,13 @@ pub const LPCTSTR = &const TCHAR; pub const LPDWORD = &DWORD; pub const LPVOID = &c_void; pub const PVOID = &c_void; -pub const TCHAR = u8; // TODO something about unicode WCHAR vs char +pub const TCHAR = if (UNICODE) WCHAR else u8; pub const UINT = c_uint; pub const ULONG_PTR = usize; pub const WCHAR = u16; pub const LPCVOID = &const c_void; + /// The standard input device. Initially, this is the console input buffer, CONIN$. pub const STD_INPUT_HANDLE = @maxValue(DWORD) - 10 + 1; diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index 3c1fe86ceb..d2554fd8df 100644 --- a/std/special/bootstrap.zig +++ b/std/special/bootstrap.zig @@ -5,19 +5,23 @@ const root = @import("@root"); const std = @import("std"); const builtin = @import("builtin"); -const want_main_symbol = std.target.linking_libc; +const want_main_symbol = builtin.link_libc; const want_start_symbol = !want_main_symbol; -const posix_exit = std.os.posix.exit; - var argc_ptr: &usize = undefined; +const is_windows = builtin.os == builtin.Os.windows; + export nakedcc fn _start() -> noreturn { if (!want_start_symbol) { @setGlobalLinkage(_start, builtin.GlobalLinkage.Internal); unreachable; } + if (is_windows) { + windowsCallMainAndExit() + } + switch (builtin.arch) { builtin.Arch.x86_64 => { argc_ptr = asm("lea (%%rsp), %[argc]": [argc] "=r" (-> &usize)); @@ -27,23 +31,21 @@ export nakedcc fn _start() -> noreturn { }, else => @compileError("unsupported arch"), } - callMainAndExit() + posixCallMainAndExit() } -fn callMainAndExit() -> noreturn { +fn windowsCallMainAndExit() -> noreturn { + std.debug.user_main_fn = root.main; + root.main() %% std.os.windows.ExitProcess(1); + std.os.windows.ExitProcess(0); +} + +fn posixCallMainAndExit() -> noreturn { const argc = *argc_ptr; const argv = @ptrCast(&&u8, &argc_ptr[1]); const envp = @ptrCast(&?&u8, &argv[argc + 1]); - callMain(argc, argv, envp) %% exit(true); - exit(false); -} - -fn exit(failure: bool) -> noreturn { - if (builtin.os == builtin.Os.windows) { - std.os.windows.ExitProcess(c_uint(failure)); - } else { - posix_exit(i32(failure)); - } + callMain(argc, argv, envp) %% std.os.posix.exit(1); + std.os.posix.exit(0); } fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void { diff --git a/std/target.zig b/std/target.zig deleted file mode 100644 index db37d57e0e..0000000000 --- a/std/target.zig +++ /dev/null @@ -1,16 +0,0 @@ -const mem = @import("mem.zig"); -const builtin = @import("builtin"); - -pub const linking_libc = linkingLibrary("c"); - -pub fn linkingLibrary(lib_name: []const u8) -> bool { - // TODO shouldn't need this if - if (builtin.link_libs.len != 0) { - for (builtin.link_libs) |link_lib| { - if (mem.eql(u8, link_lib, lib_name)) { - return true; - } - } - } - return false; -} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index fd07dbb2b1..03e29e3450 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -409,18 +409,6 @@ pub fn addCases(cases: &tests.CompileErrorContext) { ".tmp_source.zig:2:1: error: redefinition of 'a'", ".tmp_source.zig:1:1: note: previous definition is here"); - cases.add("byvalue struct parameter in exported function", - \\const A = struct { x : i32, }; - \\export fn f(a : A) {} - , ".tmp_source.zig:2:13: error: byvalue types not yet supported on extern function parameters"); - - cases.add("byvalue struct return value in exported function", - \\const A = struct { x: i32, }; - \\export fn f() -> A { - \\ A {.x = 1234 } - \\} - , ".tmp_source.zig:2:18: error: byvalue types not yet supported on extern function return values"); - cases.add("duplicate field in struct value expression", \\const A = struct { \\ x : i32, @@ -1070,7 +1058,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\export fn foo(comptime x: i32, y: i32) -> i32{ \\ x + y \\} - , ".tmp_source.zig:1:15: error: comptime parameter not allowed in extern function"); + , ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'"); cases.add("extern function with comptime parameter", \\extern fn foo(comptime x: i32, y: i32) -> i32; @@ -1078,7 +1066,7 @@ pub fn addCases(cases: &tests.CompileErrorContext) { \\ foo(1, 2) \\} \\export fn entry() -> usize { @sizeOf(@typeOf(f)) } - , ".tmp_source.zig:1:15: error: comptime parameter not allowed in extern function"); + , ".tmp_source.zig:1:15: error: comptime parameter not allowed in function with calling convention 'ccc'"); cases.add("convert fixed size array to slice with invalid size", \\export fn f() {