Commit 6a93dda3e1

Andrew Kelley <superjoe30@gmail.com>
2017-06-14 06:04:34
progress toward windows hello world working
1 parent 199bbb6
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