From a0309855d7a81b12f7e8f5c8d97b5a66cf825972 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Mon, 11 Mar 2024 18:10:40 +0100 Subject: [PATCH] Added `@link` attribute. --- lib/std/os/macos/cf_allocator.c3 | 24 +-- lib/std/os/macos/cf_array.c3 | 14 +- lib/std/os/macos/core_foundation.c3 | 6 +- lib/std/os/macos/objc.c3 | 38 ++--- releasenotes.md | 2 + src/compiler/ast.c | 25 +++ src/compiler/compiler.c | 8 + src/compiler/compiler_internal.h | 7 + src/compiler/enums.h | 1 + src/compiler/linker.c | 174 +++++--------------- src/compiler/llvm_codegen.c | 2 + src/compiler/llvm_codegen_function.c | 3 +- src/compiler/parse_global.c | 7 + src/compiler/sema_decls.c | 25 ++- src/compiler/sema_passes.c | 48 +++++- src/compiler/symtab.c | 1 + test/test_suite/attributes/attr_link_err.c3 | 22 +++ 17 files changed, 224 insertions(+), 183 deletions(-) create mode 100644 test/test_suite/attributes/attr_link_err.c3 diff --git a/lib/std/os/macos/cf_allocator.c3 b/lib/std/os/macos/cf_allocator.c3 index 1654b2bde..9cb26e194 100644 --- a/lib/std/os/macos/cf_allocator.c3 +++ b/lib/std/os/macos/cf_allocator.c3 @@ -1,18 +1,18 @@ -module std::os::macos::cf @if(env::DARWIN); +module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); distinct CFAllocatorRef = void*; distinct CFAllocatorContextRef = void*; def CFOptionFlags = usz; -macro CFAllocatorRef default_allocator() => _macos_CFAllocatorGetDefault(); -macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) => _macos_CFAllocatorDeallocate(allocator, ptr); -macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) => _macos_CFAllocatorAllocate(allocator, size, 0); -macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) => _macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0); -macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) => _macos_CFAllocatorSetDefault(allocator); +macro CFAllocatorRef default_allocator() => macos_CFAllocatorGetDefault(); +macro void CFAllocatorRef.dealloc(CFAllocatorRef allocator, void* ptr) => macos_CFAllocatorDeallocate(allocator, ptr); +macro void* CFAllocatorRef.alloc(CFAllocatorRef allocator, usz size) => macos_CFAllocatorAllocate(allocator, size, 0); +macro usz CFAllocatorRef.get_preferred_size(CFAllocatorRef allocator, usz req_size) => macos_CFAllocatorGetPreferredSizeForSize(allocator, req_size, 0); +macro void CFAllocatorRef.set_default(CFAllocatorRef allocator) => macos_CFAllocatorSetDefault(allocator); -extern fn CFAllocatorRef _macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extern("CFAllocatorCreate"); -extern fn void _macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extern("CFAllocatorDeallocate"); -extern fn CFAllocatorRef _macos_CFAllocatorGetDefault() @extern("CFAllocatorGetDefault"); -extern fn void _macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extern("CFAllocatorSetDefault"); -extern fn void* _macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorAllocate"); -extern fn CFIndex _macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorGetPreferredSizeForSize"); +extern fn CFAllocatorRef macos_CFAllocatorCreate(CFAllocatorRef allocator, CFAllocatorContextRef context) @extern("CFAllocatorCreate") @builtin; +extern fn void macos_CFAllocatorDeallocate(CFAllocatorRef allocator, void* ptr) @extern("CFAllocatorDeallocate") @builtin; +extern fn CFAllocatorRef macos_CFAllocatorGetDefault() @extern("CFAllocatorGetDefault") @builtin; +extern fn void macos_CFAllocatorSetDefault(CFAllocatorRef allocator) @extern("CFAllocatorSetDefault") @builtin; +extern fn void* macos_CFAllocatorAllocate(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorAllocate") @builtin; +extern fn CFIndex macos_CFAllocatorGetPreferredSizeForSize(CFAllocatorRef allocator, CFIndex size, CFOptionFlags hint) @extern("CFAllocatorGetPreferredSizeForSize") @builtin; diff --git a/lib/std/os/macos/cf_array.c3 b/lib/std/os/macos/cf_array.c3 index 391289225..749afa5af 100644 --- a/lib/std/os/macos/cf_array.c3 +++ b/lib/std/os/macos/cf_array.c3 @@ -1,11 +1,11 @@ -module std::os::macos::cf @if(env::DARWIN); +module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); distinct CFArrayRef = void*; distinct CFArrayCallBacksRef = void*; distinct CFMutableArrayRef = void*; -extern fn CFArrayRef _macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extern("CFArrayCreate"); -extern fn CFArrayRef _macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extern("CFArrayCopy"); -extern fn CFIndex _macos_CFArrayGetCount(CFArrayRef array) @extern("CFArrayGetCount"); -extern fn void _macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extern("CFArrayAppendArray"); -extern fn CFMutableArrayRef _macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extern("CFArrayCreateMutable"); -extern fn void _macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extern("CFArrayAppendValue"); +extern fn CFArrayRef macos_CFArrayCreate(CFAllocatorRef allocator, void** values, CFIndex num_values, CFArrayCallBacksRef callBacks) @extern("CFArrayCreate") @builtin; +extern fn CFArrayRef macos_CFArrayCopy(CFAllocatorRef allocator, CFArrayRef array) @extern("CFArrayCopy") @builtin; +extern fn CFIndex macos_CFArrayGetCount(CFArrayRef array) @extern("CFArrayGetCount") @builtin; +extern fn void macos_CFArrayAppendArray(CFMutableArrayRef theArray, CFArrayRef otherArray, CFRange otherRange) @extern("CFArrayAppendArray") @builtin; +extern fn CFMutableArrayRef macos_CFArrayCreateMutable(CFAllocatorRef allocator, CFIndex capacity, CFArrayCallBacksRef callBacks) @extern("CFArrayCreateMutable") @builtin; +extern fn void macos_CFArrayAppendValue(CFMutableArrayRef theArray, void *value) @extern("CFArrayAppendValue") @builtin; diff --git a/lib/std/os/macos/core_foundation.c3 b/lib/std/os/macos/core_foundation.c3 index ae4bfd2db..0e61735fd 100644 --- a/lib/std/os/macos/core_foundation.c3 +++ b/lib/std/os/macos/core_foundation.c3 @@ -1,4 +1,4 @@ -module std::os::macos::cf @if(env::DARWIN); +module std::os::macos::cf @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); distinct CFTypeRef = void*; def CFIndex = isz; @@ -8,5 +8,5 @@ struct CFRange CFIndex length; } -extern fn CFTypeRef _macos_CFRetain(CFTypeRef cf) @extern("CFRetain"); -extern fn void _macos_CFRelease(CFTypeRef cf) @extern("CFRelease"); +extern fn CFTypeRef macos_CFRetain(CFTypeRef cf) @extern("CFRetain") @builtin; +extern fn void macos_CFRelease(CFTypeRef cf) @extern("CFRelease") @builtin; \ No newline at end of file diff --git a/lib/std/os/macos/objc.c3 b/lib/std/os/macos/objc.c3 index 7a25052dc..75d1c338e 100644 --- a/lib/std/os/macos/objc.c3 +++ b/lib/std/os/macos/objc.c3 @@ -1,4 +1,4 @@ -module std::os::macos::objc @if(env::DARWIN); +module std::os::macos::objc @if(env::DARWIN) @link(env::DARWIN, "CoreFoundation.framework"); distinct Class = void*; distinct Method = void*; @@ -10,38 +10,36 @@ fault ObjcFailure CLASS_NOT_FOUND } -macro char* Class.name(Class cls) => _macos_class_getName(cls); -macro Class Class.superclass(Class cls) => _macos_class_getSuperclass(cls); -macro bool Class.responds_to(Class cls, Selector sel) => _macos_class_respondsToSelector(cls, sel); -macro Method Class.method(Class cls, Selector name) => _macos_class_getClassMethod(cls, name); +macro ZString Class.name(Class cls) => macos_class_getName(cls); +macro Class Class.superclass(Class cls) => macos_class_getSuperclass(cls); +macro bool Class.responds_to(Class cls, Selector sel) => macos_class_respondsToSelector(cls, sel); +macro Method Class.method(Class cls, Selector name) => macos_class_getClassMethod(cls, name); macro bool Selector.equals(Selector a, Selector b) => a == b; macro bool Class.equals(Class a, Class b) => a == b; -macro Selector selector_register(char* c) => _macos_sel_registerName(c); -macro Class! class_by_name(char* c) +macro Class! class_by_name(ZString c) { - Class cls = _macos_objc_lookUpClass(c); - if (!cls) return ObjcFailure.CLASS_NOT_FOUND?; - return cls; + Class cls = macos_objc_lookUpClass(c); + return cls ?: ObjcFailure.CLASS_NOT_FOUND?; } macro Class[] class_get_list(Allocator *allocator = allocator::heap()) { - int num_classes = _macos_objc_getClassList(null, 0); + int num_classes = macos_objc_getClassList(null, 0); if (!num_classes) return {}; Class[] entries = allocator.new_array(Class, num_classes); - _macos_objc_getClassList(entries.ptr, entries.len); + macos_objc_getClassList(entries.ptr, entries.len); return entries; } -extern fn Class _macos_objc_getClass(char* name) @extern("objc_getClass"); -extern fn int _macos_objc_getClassList(Class* buffer, int buffer_count) @extern("objc_getClassList"); -extern fn char* _macos_class_getName(Class cls) @extern("class_getName"); -extern fn Class _macos_class_getSuperclass(Class cls) @extern("class_getSuperclass"); -extern fn Method _macos_class_getClassMethod(Class cls, Selector name) @extern("class_getClassMethod"); -extern fn bool _macos_class_respondsToSelector(Class cls, Selector name) @extern("class_respondsToSelector"); -extern fn Selector _macos_sel_registerName(char* str) @extern("sel_registerName"); -extern fn Class _macos_objc_lookUpClass(char* name) @extern("objc_lookUpClass"); +extern fn Class macos_objc_getClass(ZString name) @extern("objc_getClass") @builtin; +extern fn int macos_objc_getClassList(Class* buffer, int buffer_count) @extern("objc_getClassList") @builtin; +extern fn ZString macos_class_getName(Class cls) @extern("class_getName") @builtin; +extern fn Class macos_class_getSuperclass(Class cls) @extern("class_getSuperclass") @builtin; +extern fn Method macos_class_getClassMethod(Class cls, Selector name) @extern("class_getClassMethod") @builtin; +extern fn bool macos_class_respondsToSelector(Class cls, Selector name) @extern("class_respondsToSelector") @builtin; +extern fn Selector macos_sel_registerName(ZString str) @extern("sel_registerName") @builtin; +extern fn Class macos_objc_lookUpClass(ZString name) @extern("objc_lookUpClass") @builtin; diff --git a/releasenotes.md b/releasenotes.md index 93e5628a3..e93cc979a 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -4,6 +4,7 @@ ### Changes / improvements - Disallow multiple `_` in a row in digits, e.g. `1__000`. +- Added `@link` attribute. ### Fixes - Struct/union members now correctly rejects members without storage size #1147. @@ -26,6 +27,7 @@ - mem::copy / clear / set no longer has an `$inline` attribute. - Native aligned libc malloc on Windows & POSIX. - Simplification of the allocator interface. +- CoreFoundation only linked on MacOS when used. ## 0.5.4 Change list diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 96aeef906..ead9d2e48 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -335,6 +335,31 @@ AttributeType attribute_by_name(const char *name) } +void decl_append_links_to_global(Decl *decl) +{ + CompilationUnit *unit = decl->unit; + if (unit && unit->links) + { + FOREACH_BEGIN(const char *link, unit->links) + global_context_add_link(link); + FOREACH_END(); + unit->links = NULL; // Don't register twice + } + if (decl->has_link) + { + FOREACH_BEGIN(Attr* attr, decl->attributes) + if (attr->attr_kind != ATTRIBUTE_LINK) continue; + if (!attr->exprs) continue; + unsigned args = vec_size(attr->exprs); + for (unsigned i = 0; i < args; i++) + { + Expr *string = attr->exprs[i]; + if (!string) continue; + global_context_add_link(string->const_expr.bytes.ptr); + } + FOREACH_END(); + } +} int decl_count_elements(Decl *structlike) { diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 93c437da4..c1d93db09 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -985,6 +985,14 @@ void global_context_add_generic_decl(Decl *decl) decltable_set(&global_context.generic_symbols, decl); } +void global_context_add_link(const char *link) +{ + FOREACH_BEGIN(const char *existing_link, global_context.links) + if (str_eq(link, existing_link)) return; + FOREACH_END(); + vec_add(global_context.links, link); +} + SectionId global_context_register_section(const char *section) { scratch_buffer_clear(); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 28d057ace..8eb4910b5 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -675,6 +675,7 @@ typedef struct Decl_ bool no_strip : 1; bool is_deprecated : 1; bool is_cond : 1; + bool has_link : 1; OperatorOverload operator : 4; union { @@ -1614,12 +1615,14 @@ struct CompilationUnit_ Decl **enums; Decl **attributes; Decl **faulttypes; + const char **links; Visibility default_visibility; Attr *if_attr; bool export_by_default; bool is_interface_file; bool benchmark_by_default; bool test_by_default; + Attr **attr_links; Decl **generic_defines; Decl **ct_asserts; Decl **ct_echos; @@ -1732,6 +1735,7 @@ typedef struct unsigned warnings_found; unsigned includes_used; Decl ***locals_list; + const char **links; HTable compiler_defines; HTable features; Module std_module; @@ -2149,6 +2153,8 @@ void global_context_add_type(Type *type); void global_context_add_decl(Decl *type_decl); void global_context_add_generic_decl(Decl *decl); SectionId global_context_register_section(const char *section); +void global_context_add_link(const char *link); + INLINE const char *section_from_id(SectionId id); Module *compiler_find_or_create_module(Path *module_name, const char **parameters); @@ -2176,6 +2182,7 @@ const char *decl_safe_name(Decl *decl); const char *decl_to_name(Decl *decl); const char *decl_to_a_name(Decl *decl); int decl_count_elements(Decl *structlike); +void decl_append_links_to_global(Decl *decl); INLINE bool decl_ok(Decl *decl); INLINE bool decl_poison(Decl *decl); diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 38a9710fa..8673551fd 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -791,6 +791,7 @@ typedef enum ATTRIBUTE_IF, ATTRIBUTE_INLINE, ATTRIBUTE_INIT, + ATTRIBUTE_LINK, ATTRIBUTE_LITTLEENDIAN, ATTRIBUTE_LOCAL, ATTRIBUTE_MAYDISCARD, diff --git a/src/compiler/linker.c b/src/compiler/linker.c index 140b498e1..b349bb8ae 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -76,10 +76,11 @@ static const char *string_esc(const char *str) } return strdup(scratch_buffer_to_string()); } -static void linker_setup_windows(const char ***args_ref, LinkerType linker_type, const char ***additional_linked_ref) + +static void linker_setup_windows(const char ***args_ref, LinkerType linker_type) { add_arg(active_target.win.use_win_subsystem ? "/SUBSYSTEM:WINDOWS" : "/SUBSYSTEM:CONSOLE"); - vec_add(*additional_linked_ref, "dbghelp"); + global_context_add_link("dbghelp"); if (linker_type == LINKER_CC) return; //add_arg("/MACHINE:X64"); bool is_debug = false; @@ -158,29 +159,29 @@ static void linker_setup_windows(const char ***args_ref, LinkerType linker_type, // Do not link any. if (active_target.win.crt_linking == WIN_CRT_NONE) return; - vec_add(*additional_linked_ref, "kernel32"); - vec_add(*additional_linked_ref, "ntdll"); - vec_add(*additional_linked_ref, "user32"); - vec_add(*additional_linked_ref, "shell32"); - vec_add(*additional_linked_ref, "Shlwapi"); - vec_add(*additional_linked_ref, "Ws2_32"); - vec_add(*additional_linked_ref, "legacy_stdio_definitions"); + global_context_add_link("kernel32"); + global_context_add_link("ntdll"); + global_context_add_link("user32"); + global_context_add_link("shell32"); + global_context_add_link("Shlwapi"); + global_context_add_link("Ws2_32"); + global_context_add_link("legacy_stdio_definitions"); if (active_target.win.crt_linking == WIN_CRT_STATIC) { if (is_debug) { - vec_add(*additional_linked_ref, "libucrtd"); - vec_add(*additional_linked_ref, "libvcruntimed"); - vec_add(*additional_linked_ref, "libcmtd"); - vec_add(*additional_linked_ref, "libcpmtd"); + global_context_add_link("libucrtd"); + global_context_add_link("libvcruntimed"); + global_context_add_link("libcmtd"); + global_context_add_link("libcpmtd"); } else { - vec_add(*additional_linked_ref, "libucrt"); - vec_add(*additional_linked_ref, "libvcruntime"); - vec_add(*additional_linked_ref, "libcmt"); - vec_add(*additional_linked_ref, "libcpmt"); + global_context_add_link("libucrt"); + global_context_add_link("libvcruntime"); + global_context_add_link("libcmt"); + global_context_add_link("libcpmt"); } } else @@ -189,115 +190,24 @@ static void linker_setup_windows(const char ***args_ref, LinkerType linker_type, // if so, then exclude them. if (is_debug && link_with_dynamic_debug_libc) { - vec_add(*additional_linked_ref, "ucrtd"); - vec_add(*additional_linked_ref, "vcruntimed"); - vec_add(*additional_linked_ref, "msvcrtd"); - vec_add(*additional_linked_ref, "msvcprtd"); + global_context_add_link("ucrtd"); + global_context_add_link("vcruntimed"); + global_context_add_link("msvcrtd"); + global_context_add_link("msvcprtd"); } else { - vec_add(*additional_linked_ref, "ucrt"); - vec_add(*additional_linked_ref, "vcruntime"); - vec_add(*additional_linked_ref, "msvcrt"); - vec_add(*additional_linked_ref, "msvcprt"); + global_context_add_link("ucrt"); + global_context_add_link("vcruntime"); + global_context_add_link("msvcrt"); + global_context_add_link("msvcprt"); } } add_arg("/NOLOGO"); } -#ifdef mingw64_support -static void linker_setup_mingw64_gcc(const char ***args_ref) -{ - add_arg("-m"); - add_arg("i386pep"); - add_arg("-Bdynamic"); - const char *root = getenv("MSYSTEM_PREFIX"); - const char *gcc_base = strformat("%s/lib/gcc/x86_64-w64-mingw32", root); - if (!file_exists(gcc_base)) error_exit("Missing GCC"); - const char *name = file_first(gcc_base); - const char *gcc_path = strformat("%s/%s/", gcc_base, name); - add_arg(strformat("-L%s/x86_64-w64-mingw32/lib", root)); - add_arg(strformat("-L%s/lib", root)); - add_arg2(gcc_path, "crtbegin.o"); - add_arg(strformat("%s/lib/crt2.o", root)); - add_arg(strformat("%s/lib/default-manifest.o", root)); - add_arg2("-L", gcc_path); - add_arg("-lkernel32"); - add_arg("-lmingw32"); - add_arg("-lgcc"); - add_arg("-lgcc_eh"); - add_arg("-lmoldname"); - add_arg("-lmingwex"); - add_arg("-lmsvcrt"); - add_arg("-ladvapi32"); - add_arg("-lshell32"); - add_arg("-luser32"); - add_arg("-lpthread"); - - add_arg2(gcc_path, "crtend.o"); -} - -static void linker_setup_windows_gnu(const char ***args_ref, LinkerType linker_type) -{ - if (linker_type == LINKER_CC) return; - bool is_clang = strcmp(getenv("MSYSTEM"), "CLANG64") == 0; - bool is_mingw = strcmp(getenv("MSYSTEM"), "MINGW64") == 0; - if (!is_clang && !is_mingw) - { - error_exit("Crosslinking MSYS is not yet supported."); - } - if (is_mingw) - { - linker_setup_mingw64_gcc(args_ref); - return; - } - const char *root = getenv("MSYSTEM_PREFIX"); - const char *compiler_prefix; - if (is_clang) - { - char *filename; - char *dir; - path_get_dir_and_filename_from_full(root, &filename, &dir); - compiler_prefix = filename; - root = dir; - } - else - { - compiler_prefix = "x86_64-w64-mingw32"; - } - add_arg("-m"); - add_arg("i386pep"); - add_arg("-Bdynamic"); - const char *lib = strformat("%s/%s/lib", root, compiler_prefix); - if (!file_exists(lib)) - { - error_exit("Cannot find '%s'.", lib); - } - add_arg2(lib, "/crt2.o"); - add_arg2(lib, "/crtbegin.o"); - add_arg2("-L", lib); - add_arg(strformat("-L%s/lib", root)); - add_arg(strformat("-L%s/%s/sys-root/mingw/lib", root, compiler_prefix)); - const char *clang_dir = strformat("%s/lib/clang/" LLVM_VERSION_STRING, root); - add_arg(strformat("-L%s/lib/windows", clang_dir)); - add_arg("-lmingw32"); - add_arg(strformat("%s/lib/windows/libclang_rt.builtins-x86_64.a", clang_dir)); - add_arg("-lmoldname"); - add_arg("-lmingwex"); - add_arg("-lmsvcrt"); - add_arg("-ladvapi32"); - add_arg("-lshell32"); - add_arg("-luser32"); - add_arg("-lkernel32"); - add_arg("-lmingw32"); - add_arg2(lib, "\\crtend.o"); -} -*/ -#endif static void linker_setup_macos(const char ***args_ref, LinkerType linker_type) { - add_arg("-framework"); - add_arg("CoreFoundation"); if (linker_type == LINKER_CC) { add_arg("-target"); @@ -319,8 +229,8 @@ static void linker_setup_macos(const char ***args_ref, LinkerType linker_type) { error_exit("Cannot crosslink MacOS without providing --macossdk."); } - add_arg("-lSystem"); - add_arg("-lm"); + global_context_add_link("System"); + global_context_add_link("m"); add_arg("-syslibroot"); add_arg(active_target.macos.sysroot); if (is_no_pie(platform_target.reloc_model)) add_arg("-no_pie"); @@ -401,9 +311,9 @@ static const char *find_linux_crt_begin(void) return NULL; } -static void linker_setup_linux(const char ***args_ref, LinkerType linker_type, const char ***additional_linked_ref) +static void linker_setup_linux(const char ***args_ref, LinkerType linker_type) { - vec_add(*additional_linked_ref, "dl"); + global_context_add_link("dl"); if (linker_type == LINKER_CC) { if (!link_libc()) @@ -458,9 +368,9 @@ static void linker_setup_linux(const char ***args_ref, LinkerType linker_type, c add_arg("-L"); add_arg("/usr/lib/x86_64-linux-gnu/libdl.so"); add_arg("--dynamic-linker=/lib64/ld-linux-x86-64.so.2"); - add_arg("-lm"); - add_arg("-lpthread"); - add_arg("-lc"); + global_context_add_link("m"); + global_context_add_link("pthread"); + global_context_add_link("c"); add_arg("-L/usr/lib/"); add_arg("-L/lib/"); add_arg("-m"); @@ -504,10 +414,11 @@ static void linker_setup_freebsd(const char ***args_ref, LinkerType linker_type) add_arg2(crt_dir, "crtn.o"); add_arg2("-L", crt_dir); add_arg("--dynamic-linker=/libexec/ld-elf.so.1"); - add_arg("-lc"); - add_arg("-lm"); - add_arg("-lgcc"); - add_arg("-lgcc_s"); + global_context_add_link("c"); + global_context_add_link("m"); + global_context_add_link("gcc"); + global_context_add_link("gcc_s"); + add_arg("-L/usr/lib/"); add_arg("-m"); add_arg(ld_target(platform_target.arch)); @@ -581,13 +492,12 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns } const char *lib_path_opt = use_win ? "/LIBPATH:" : "-L"; - const char **additional_linked = NULL; switch (platform_target.os) { case OS_UNSUPPORTED: UNREACHABLE case OS_TYPE_WIN32: - linker_setup_windows(args_ref, linker_type, &additional_linked); + linker_setup_windows(args_ref, linker_type); break; case OS_TYPE_MACOSX: linker_setup_macos(args_ref, linker_type); @@ -603,7 +513,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns linker_setup_freebsd(args_ref, linker_type); break; case OS_TYPE_LINUX: - linker_setup_linux(args_ref, linker_type, &additional_linked); + linker_setup_linux(args_ref, linker_type); break; case OS_TYPE_UNKNOWN: if (link_libc()) @@ -638,7 +548,7 @@ static bool linker_setup(const char ***args_ref, const char **files_to_link, uns } add_linked_libs(args_ref, target->linked_libs, use_win); } - add_linked_libs(args_ref, additional_linked, use_win); + add_linked_libs(args_ref, global_context.links, use_win); return true; } #undef add_arg2 @@ -717,6 +627,8 @@ static bool link_exe(const char *output_file, const char **files_to_link, unsign arg_list = str_cat(arg_list, args[i]); } INFO_LOG("Linker arguments: %s to %d", arg_list, platform_target.object_format); + if (active_target.print_linking) puts(arg_list); + switch (platform_target.object_format) { case OBJ_FORMAT_COFF: diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 1e1d9b34b..904c57252 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -450,6 +450,8 @@ void llvm_emit_global_variable_init(GenContext *c, Decl *decl) // Skip real constants. if (!decl->type) return; + decl_append_links_to_global(decl); + LLVMValueRef init_value; Type *var_type = type_lowering(decl->type); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index 2df08947c..209c63152 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -623,11 +623,12 @@ void llvm_emit_dynamic_functions(GenContext *c, Decl **funcs) LLVMBuildRet(builder, NULL); LLVMDisposeBuilder(builder); } + void llvm_emit_function_decl(GenContext *c, Decl *decl) { assert(decl->decl_kind == DECL_FUNC); // Resolve function backend type for function. - + decl_append_links_to_global(decl); LLVMValueRef function = llvm_get_ref(c, decl); decl->backend_ref = function; if (decl->section_id) diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 22da880b3..62b282a63 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -257,6 +257,13 @@ bool parse_module(ParseContext *c, AstId contracts) if (attr->is_custom) RETURN_SEMA_ERROR(attr, "Custom attributes cannot be used with 'module'."); switch (attr->attr_kind) { + case ATTRIBUTE_LINK: + { + unsigned args = vec_size(attr->exprs); + if (args < 1) RETURN_SEMA_ERROR(attr, "'@link' needs at least 1 argument."); + } + vec_add(c->unit->attr_links, attr); + continue; case ATTRIBUTE_IF: if (c->unit->if_attr) RETURN_SEMA_ERROR(attr, "'@if' appeared more than once."); c->unit->if_attr = attr; diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 8833b9165..d7e3d920b 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -1989,6 +1989,7 @@ static bool update_call_abi_from_string(Decl *decl, Expr *expr) #define EXPORTED_USER_DEFINED_TYPES (ATTR_ENUM | ATTR_UNION | ATTR_STRUCT | ATTR_FAULT) #define CALLABLE_TYPE (ATTR_FUNC | ATTR_INTERFACE_METHOD | ATTR_MACRO) #define USER_DEFINED_TYPES EXPORTED_USER_DEFINED_TYPES | ATTR_BITSTRUCT | ATTR_DISTINCT + static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, AttributeDomain domain, bool *erase_decl) { AttributeType type = attr->attr_kind; @@ -2008,6 +2009,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, [ATTRIBUTE_IF] = (AttributeDomain)~(ATTR_CALL | ATTR_LOCAL), [ATTRIBUTE_INIT] = ATTR_FUNC, [ATTRIBUTE_INLINE] = ATTR_FUNC | ATTR_CALL, + [ATTRIBUTE_LINK] = ATTR_FUNC | ATTR_MACRO | ATTR_CONST | ATTR_GLOBAL, [ATTRIBUTE_LITTLEENDIAN] = ATTR_BITSTRUCT, [ATTRIBUTE_LOCAL] = ATTR_FUNC | ATTR_MACRO | ATTR_GLOBAL | ATTR_CONST | USER_DEFINED_TYPES | ATTR_DEF | ATTR_INTERFACE, [ATTRIBUTE_MAYDISCARD] = CALLABLE_TYPE, @@ -2042,7 +2044,7 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, return false; } unsigned args = vec_size(attr->exprs); - if (args > 1) + if (args > 1 && type != ATTRIBUTE_LINK) { SEMA_ERROR(attr->exprs[1], "Too many arguments for the attribute."); return false; @@ -2190,6 +2192,27 @@ static bool sema_analyse_attribute(SemaContext *context, Decl *decl, Attr *attr, decl->func_decl.attr_finalizer = true; // Ugly goto PARSE; + case ATTRIBUTE_LINK: + if (args < 1) RETURN_SEMA_ERROR(attr, "'@link' requires at least one argument."); + Expr *cond = args > 1 ? attr->exprs[0] : NULL; + if (cond && !sema_analyse_expr(context, cond)) return false; + int start = 0; + decl->has_link = true; + if (cond && expr_is_const_bool(cond)) + { + start = 1; + decl->has_link = cond->const_expr.b; + } + for (unsigned i = start; i < args; i++) + { + Expr *string = attr->exprs[i]; + if (!sema_analyse_expr(context, string)) return false; + if (!expr_is_const_string(string)) RETURN_SEMA_ERROR(string, "Expected a constant string here, usage is: '@link(cond1, link1, link2, ...)'."); + } + // Erase if not applicable. + if (start == 1) attr->exprs[0] = NULL; + if (!decl->has_link) attr->exprs = NULL; + return true; case ATTRIBUTE_INIT: decl->func_decl.attr_init = true; PARSE:; diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 3487d4667..e6ef5854b 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -366,22 +366,22 @@ void sema_analysis_pass_register_conditional_units(Module *module) assert(!unit->ct_includes); Attr *if_attr = unit->if_attr; - if (!if_attr) continue; + if (!if_attr && !unit->attr_links) continue; + + SemaContext context; + sema_context_init(&context, unit); + if (!if_attr) goto CHECK_LINK; if (vec_size(if_attr->exprs) != 1) { SEMA_ERROR(if_attr, "Expected one parameter."); - break; + goto FAIL_CONTEXT; } Expr *expr = if_attr->exprs[0]; - SemaContext context; - sema_context_init(&context, unit); - bool success = sema_analyse_ct_expr(&context, expr); - sema_context_destroy(&context); - if (!success) continue; + if (!sema_analyse_ct_expr(&context, expr)) goto FAIL_CONTEXT; if (!expr_is_const(expr) || expr->type->canonical != type_bool) { SEMA_ERROR(expr, "Expected a constant boolean expression."); - break; + goto FAIL_CONTEXT; } if (!expr->const_expr.b) { @@ -389,9 +389,41 @@ void sema_analysis_pass_register_conditional_units(Module *module) vec_resize(unit->global_cond_decls, 0); continue; } +CHECK_LINK: + if (!unit->attr_links) goto RELEASE_CONTEXT; + FOREACH_BEGIN(Attr* attr, unit->attr_links) + Expr **exprs = attr->exprs; + unsigned args = vec_size(exprs); + assert(args > 0 && "Should already have been checked."); + Expr *cond = args > 1 ? attr->exprs[0] : NULL; + if (cond && !sema_analyse_expr(&context, cond)) goto FAIL_CONTEXT; + bool start = cond && expr_is_const_bool(cond) ? 1 : 0; + bool add = start == 0 ? true : cond->const_expr.b; + for (unsigned i = start; i < args; i++) + { + Expr *string = attr->exprs[i]; + if (!sema_analyse_expr(&context, string)) goto FAIL_CONTEXT; + if (!expr_is_const_string(string)) + { + SEMA_ERROR(string, "Expected a constant string here, usage is: " + "'@link([cond1, ]link1, link2, ...)'."); + goto FAIL_CONTEXT; + } + if (add) + { + vec_add(unit->links, string->const_expr.bytes.ptr); + } + } + FOREACH_END(); +RELEASE_CONTEXT: + sema_context_destroy(&context); register_global_decls(unit, unit->global_decls); // There may be includes, add those. sema_process_includes(unit); + continue; +FAIL_CONTEXT: + sema_context_destroy(&context); + break; } DEBUG_LOG("Pass finished with %d error(s).", global_context.errors_found); } diff --git a/src/compiler/symtab.c b/src/compiler/symtab.c index d5556d1e2..d322f3354 100644 --- a/src/compiler/symtab.c +++ b/src/compiler/symtab.c @@ -333,6 +333,7 @@ void symtab_init(uint32_t capacity) attribute_list[ATTRIBUTE_IF] = KW_DEF("@if"); attribute_list[ATTRIBUTE_INIT] = KW_DEF("@init"); attribute_list[ATTRIBUTE_INLINE] = KW_DEF("@inline"); + attribute_list[ATTRIBUTE_LINK] = KW_DEF("@link"); attribute_list[ATTRIBUTE_LITTLEENDIAN] = KW_DEF("@littleendian"); attribute_list[ATTRIBUTE_LOCAL] = KW_DEF("@local"); attribute_list[ATTRIBUTE_MAYDISCARD] = KW_DEF("@maydiscard"); diff --git a/test/test_suite/attributes/attr_link_err.c3 b/test/test_suite/attributes/attr_link_err.c3 new file mode 100644 index 000000000..4f56a61b2 --- /dev/null +++ b/test/test_suite/attributes/attr_link_err.c3 @@ -0,0 +1,22 @@ +fn void test(int x) @link(true, "abc", "cde") +{ +} + +fn void testc(int x) @link("abc", "cde") +{ +} + +fn void test2(int x) @link(true) // #error: Expected a constant string here +{ +} +fn void test3(int x) @link(true, 2) // #error: Expected a constant string here +{ +} + +fn void test4(int x) @link("a", 2) // #error: Expected a constant string here +{ +} + +fn void test5(int x) @link(true, "a", 5, 5) // #error: Expected a constant string here +{ +}