Commit 6a93dda3e1
Changed files (28)
doc
src
std
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"
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<AstNode *> 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<Buf *> symbols; // the list of symbols that we depend on from this lib
+ bool provided_explicitly;
+};
+
struct CodeGen {
LLVMModuleRef module;
ZigList<ErrorMsg*> errors;
@@ -1324,7 +1341,9 @@ struct CodeGen {
ZigLLVMDICompileUnit *compile_unit;
ZigLLVMDIFile *compile_unit_file;
- ZigList<Buf *> link_libs; // non-libc link libs
+ ZigList<LinkLib *> link_libs_list;
+ LinkLib *libc_link_lib;
+
// add -framework [name] args to linker
ZigList<Buf *> darwin_frameworks;
// add -rpath [name] args to linker
@@ -1344,6 +1363,7 @@ struct CodeGen {
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> exported_symbol_names;
HashMap<Buf *, Tld *, buf_hash, buf_eql_buf> external_prototypes;
+
ZigList<ImportTableEntry *> import_queue;
size_t import_queue_index;
ZigList<Tld *> 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;
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<LLVMTypeRef>(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<FnTypeParamInfo>(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<TldVar>(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<TldFn>(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<FnTypeParamInfo>(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<LinkLib>(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);
+}
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
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) {
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));
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);
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);
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);
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));
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<Buf *>(arg_count);
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;
}
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";
src/tokenizer.hpp
@@ -71,6 +71,7 @@ enum TokenId {
TokenIdKeywordPacked,
TokenIdKeywordPub,
TokenIdKeywordReturn,
+ TokenIdKeywordStdcallCC,
TokenIdKeywordStruct,
TokenIdKeywordSwitch,
TokenIdKeywordTest,
src/zig_llvm.cpp
@@ -38,6 +38,7 @@
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/TargetParser.h>
#include <llvm/Support/raw_ostream.h>
+#include <llvm/Support/COFF.h>
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
@@ -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 <set>
+
+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<unsigned>(DebugType::None);
+ StringRef PDBPath;
+
+ // Symbols in this set are considered as live by the garbage collector.
+ std::set<SymbolBody *> GCRoot;
+
+ std::set<StringRef> NoDefaultLibs;
+ bool NoDefaultLibAll = false;
+
+ // True if we are creating a DLL.
+ bool DLL = false;
+ StringRef Implib;
+ std::vector<Export> Exports;
+ std::set<std::string> DelayLoads;
+ std::map<std::string, int> 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<StringRef, StringRef> Merge;
+
+ // Used for /section=.name,{DEKPRSW} to set section attributes.
+ std::map<StringRef, uint32_t> Section;
+
+ // Options for manifest files.
+ ManifestKind Manifest = SideBySide;
+ int ManifestID = 1;
+ StringRef ManifestDependency;
+ bool ManifestUAC = true;
+ std::vector<std::string> ManifestInput;
+ StringRef ManifestLevel = "'asInvoker'";
+ StringRef ManifestUIAccess = "'false'";
+ StringRef ManifestFile;
+
+ // Used for /failifmismatch.
+ std::map<StringRef, StringRef> MustMatch;
+
+ // Used for /alternatename.
+ std::map<StringRef, StringRef> 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();
+}
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
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;
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");
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;
std/c/windows.zig
@@ -1,1 +1,1 @@
-pub extern fn _errno() -> &c_int;
+pub extern "c" fn _errno() -> &c_int;
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;
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) {
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 windowsCallMainAndExit() -> noreturn {
+ std.debug.user_main_fn = root.main;
+ root.main() %% std.os.windows.ExitProcess(1);
+ std.os.windows.ExitProcess(0);
}
-fn callMainAndExit() -> noreturn {
+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 {
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) {
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");
}
std/target.zig
@@ -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;
-}
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() {
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