progress toward windows hello world working

This commit is contained in:
Andrew Kelley 2017-06-14 00:04:34 -04:00
parent 199bbb6292
commit 6a93dda3e1
28 changed files with 515 additions and 235 deletions

View File

@ -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

View File

@ -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"

View File

@ -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;

View File

@ -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);
}

View File

@ -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

View File

@ -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) {

View File

@ -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));

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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));

View File

@ -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);

View File

@ -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;
}

View File

@ -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";

View File

@ -71,6 +71,7 @@ enum TokenId {
TokenIdKeywordPacked,
TokenIdKeywordPub,
TokenIdKeywordReturn,
TokenIdKeywordStdcallCC,
TokenIdKeywordStruct,
TokenIdKeywordSwitch,
TokenIdKeywordTest,

View File

@ -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();
}

View File

@ -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

View File

@ -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;

View File

@ -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");

View File

@ -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;

View File

@ -1 +1 @@
pub extern fn _errno() -> &c_int;
pub extern "c" fn _errno() -> &c_int;

View File

@ -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) {

View File

@ -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");
}

View File

@ -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) {

View File

@ -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;

View File

@ -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 {

View File

@ -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;
}

View File

@ -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() {