From 5b29d39543bad9ff9477104a3811a691dbc056a8 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Tue, 23 Jan 2024 21:28:03 +0100 Subject: [PATCH] 0.5.4: Hash variables accept designated initializers. @safemacro overrides the need for `@` in macro names. Fixes to macro context evaluation. Updated allocator api. Removed install_win_reqs.bat. Deterministic @init for MacOS. --- lib/std/core/mem.c3 | 4 +- lib/std/core/private/macho_runtime.c3 | 254 +++++++++++++++++ releasenotes.md | 2 + src/compiler/enums.h | 1 - src/compiler/llvm_codegen.c | 42 +++ src/compiler/llvm_codegen_function.c | 48 ++-- src/compiler/llvm_codegen_internal.h | 3 + src/compiler/llvm_codegen_module.c | 3 + .../{inherit.c3t => inherit_linux.c3t} | 27 +- test/test_suite/dynamic/inherit_macos.c3t | 163 +++++++++++ ...ion.c3t => overlapping_function_linux.c3t} | 21 +- .../dynamic/overlapping_function_macos.c3t | 176 ++++++++++++ .../stdlib/{map.c3t => map_linux.c3t} | 8 +- test/test_suite/stdlib/map_macos.c3t | 269 ++++++++++++++++++ 14 files changed, 978 insertions(+), 43 deletions(-) create mode 100644 lib/std/core/private/macho_runtime.c3 rename test/test_suite/dynamic/{inherit.c3t => inherit_linux.c3t} (91%) create mode 100644 test/test_suite/dynamic/inherit_macos.c3t rename test/test_suite/dynamic/{overlapping_function.c3t => overlapping_function_linux.c3t} (91%) create mode 100644 test/test_suite/dynamic/overlapping_function_macos.c3t rename test/test_suite/stdlib/{map.c3t => map_linux.c3t} (97%) create mode 100644 test/test_suite/stdlib/map_macos.c3t diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 0118a28f4..f9755276b 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -557,12 +557,12 @@ macro new_temp_clear($Type) @deprecated("use mem::temp_new") macro new_array($Type, usz elements) @nodiscard { - return allocator::new_array(heap(), $Type, elements); + return allocator::new_array(allocator::heap(), $Type, elements); } macro alloc_array($Type, usz elements) @nodiscard { - return allocator::alloc_array(heap(), $Type, elements); + return allocator::alloc_array(allocator::heap(), $Type, elements); } macro talloc_array($Type, usz elements) @nodiscard @deprecated("use mem::temp_alloc_array") diff --git a/lib/std/core/private/macho_runtime.c3 b/lib/std/core/private/macho_runtime.c3 new file mode 100644 index 000000000..93ebb93c2 --- /dev/null +++ b/lib/std/core/private/macho_runtime.c3 @@ -0,0 +1,254 @@ +module std::core::machoruntime @if(env::DARWIN) @private; + +struct SegmentCommand64 +{ + uint cmd; + uint cmdsize; + char[16] segname; + ulong vmaddr; + ulong vmsize; + ulong fileoff; + ulong filesize; + uint maxprot; + uint initprot; + uint nsects; + uint flags; +} + +struct LoadCommand +{ + uint cmd; + uint cmdsize; +} + +struct Section64 +{ + char[16] sectname; + char[16] segname; + ulong addr; + ulong size; + uint offset; + uint align; + uint reloff; + uint nreloc; + uint flags; + uint reserved1; + uint reserved2; + uint reserved3; +} + +struct MachHeader +{ + uint magic; + uint cputype; + uint cpusubtype; + uint filetype; + uint ncmds; + uint sizeofcmds; + uint flags; +} + +struct MachHeader64 +{ + inline MachHeader header; + uint reserved; +} + +const LC_SEGMENT_64 = 0x19; + +fault MachoSearch +{ + NOT_FOUND +} +fn bool name_cmp(char* a, char[16]* b) +{ + for (usz i = 0; i < 16; i++) + { + if (a[i] != (*b)[i]) return false; + if (a[i] == '\0') return true; + } + return false; +} + +fn SegmentCommand64*! find_segment(MachHeader* header, char* segname) +{ + LoadCommand* command = (void*)header + MachHeader64.sizeof; + for (uint i = 0; i < header.ncmds; i++) + { + if (command.cmd == LC_SEGMENT_64) + { + SegmentCommand64* segment = (SegmentCommand64*)command; + if (name_cmp(segname, &segment.segname)) return segment; + } + command = (void*)command + command.cmdsize; + } + return MachoSearch.NOT_FOUND?; +} +fn Section64*! find_section(SegmentCommand64* command, char* sectname) +{ + Section64* section = (void*)command + SegmentCommand64.sizeof; + for (uint i = 0; i < command.nsects; i++) + { + if (name_cmp(sectname, §ion.sectname)) return section; + section++; + } + return MachoSearch.NOT_FOUND?; +} + +macro find_segment_section_body(MachHeader* header, char* segname, char* sectname, $Type) +{ + + Section64*! section = find_section(find_segment(header, segname), sectname); + if (catch section) + { + return $Type[] {}; + } + $Type* ptr = (void*)header + section.offset; + return ptr[:section.size / $Type.sizeof]; +} + +def DyldCallback = fn void (MachHeader* mh, isz vmaddr_slide); + +extern fn void _dyld_register_func_for_add_image(DyldCallback); + + +struct DlInfo +{ + char* dli_fname; + void* dli_fbase; + char* dli_sname; + void* dli_saddr; +} + +extern fn void printf(char*, ...); +extern fn int dladdr(MachHeader* mh, DlInfo* dlinfo); +extern fn void* realloc(void* ptr, usz size); +extern fn void* malloc(usz size); +extern fn void free(void* ptr); + +def CallbackFn = fn void(); +struct Callback +{ + uint priority; + CallbackFn xtor; + Callback* next; +} +struct DynamicMethod +{ + void* fn_ptr; + char* sel; + union + { + DynamicMethod* next; + TypeId* type; + } +} + +enum StartupState +{ + NOT_STARTED, + INIT, + RUN_CTORS, + READ_DYLIB, + RUN_DYLIB_CTORS, + RUN_DTORS, + SHUTDOWN +} + +StartupState runtime_state = NOT_STARTED; + +Callback* ctor_first; +Callback* dtor_first; + +fn void runtime_startup() @public @export("__c3_runtime_startup") +{ + if (runtime_state != NOT_STARTED) return; + runtime_state = INIT; + _dyld_register_func_for_add_image(&dl_reg_callback); + assert(runtime_state == INIT); + runtime_state = RUN_CTORS; + Callback* ctor = ctor_first; + while (ctor) + { + ctor.xtor(); + ctor = ctor.next; + } + assert(runtime_state == RUN_CTORS); + runtime_state = READ_DYLIB; + ctor = null; +} + +fn void runtime_finalize() @public @export("__c3_runtime_finalize") +{ + if (runtime_state != READ_DYLIB) return; + runtime_state = RUN_DTORS; + Callback* dtor = dtor_first; + while (dtor) + { + dtor.xtor(); + dtor = dtor.next; + } + assert(runtime_state == RUN_DTORS); + runtime_state = SHUTDOWN; +} + +fn void append_xxlizer(Callback** ref, Callback* cb) +{ + while (Callback* current = *ref, current) + { + if (current.priority > cb.priority) + { + cb.next = current; + break; + } + ref = ¤t.next; + } + *ref = cb; +} + +struct TypeId +{ + char type; + TypeId* parentof; + DynamicMethod* dtable; + usz sizeof; + TypeId* inner; + usz len; + typeid[*] additional; +} + +fn void dl_reg_callback(MachHeader* mh, isz vmaddr_slide) +{ + usz size = 0; + assert(runtime_state == INIT || runtime_state == READ_DYLIB, "State was %s", runtime_state); + foreach (&dm : find_segment_section_body(mh, "__DATA", "__c3_dynamic", DynamicMethod)) + { + TypeId* type = dm.type; + dm.next = type.dtable; + type.dtable = dm; + DynamicMethod* m = dm; + while (m) + { + m = m.next; + } + } + foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3dtor", Callback)) + { + append_xxlizer(&dtor_first, cb); + } + foreach (&cb : find_segment_section_body(mh, "__DATA", "__c3ctor", Callback)) + { + append_xxlizer(&ctor_first, cb); + } + if (runtime_state != READ_DYLIB) return; + runtime_state = RUN_DYLIB_CTORS; + Callback* ctor = ctor_first; + ctor_first = null; + while (ctor) + { + ctor.xtor(); + ctor = ctor.next; + } + assert(runtime_state == RUN_DYLIB_CTORS); + runtime_state = READ_DYLIB; +} diff --git a/releasenotes.md b/releasenotes.md index 37bf383c0..d388ce9dc 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -8,9 +8,11 @@ - More information available with debug log in non debug builds. - Removed install_win_reqs.bat which didn't work well. - Support `**` to mean `./**` +- MacOS init/finalizer now respects priority. ### Fixes - Fixes to macro context evaluation with macro varargs. +- Dynamic methods registered before init functions on MacOS. ### Stdlib changes - Deprecated `Allocator` helper functions. diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 86a9b8a9c..38a9710fa 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -850,7 +850,6 @@ typedef enum typedef enum { - BUILTIN_ABS, BUILTIN_ANY_MAKE, BUILTIN_ATOMIC_LOAD, diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 271fe8551..2334dea64 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -134,8 +134,50 @@ LLVMValueRef llvm_get_selector(GenContext *c, const char *name) return selector; } + +void llvm_emit_macho_xtor(GenContext *c, LLVMValueRef *list, const char *name) +{ + unsigned len = vec_size(list); + if (!len) return; + scratch_buffer_clear(); + scratch_buffer_append(".list$"); + scratch_buffer_append(name); + LLVMValueRef array = LLVMConstArray(c->xtor_entry_type, list, vec_size(list)); + LLVMValueRef global = LLVMAddGlobal(c->module, LLVMTypeOf(array), scratch_buffer_to_string()); + scratch_buffer_clear(); + scratch_buffer_append("__DATA,__"); + scratch_buffer_append(name); + LLVMSetLinkage(global, LLVMInternalLinkage); + LLVMSetInitializer(global, array); + LLVMSetSection(global, scratch_buffer_to_string()); + LLVMSetAlignment(global, llvm_abi_alignment(c, c->xtor_entry_type)); +} + void llvm_emit_constructors_and_destructors(GenContext *c) { + if (platform_target.object_format == OBJ_FORMAT_MACHO) + { + llvm_emit_macho_xtor(c, c->constructors, "c3ctor"); + llvm_emit_macho_xtor(c, c->destructors, "c3dtor"); + + LLVMValueRef runtime_start = LLVMGetNamedFunction(c->module, "__c3_runtime_startup"); + if (!runtime_start || !LLVMGetFirstBasicBlock(runtime_start)) return; + LLVMValueRef vals[3] = { llvm_const_int(c, type_int, 65535), runtime_start, llvm_get_zero(c, type_voidptr) }; + LLVMValueRef entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3); + LLVMValueRef array = LLVMConstArray(c->xtor_entry_type, &entry, 1); + LLVMValueRef global_ctor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_ctors"); + LLVMSetLinkage(global_ctor, LLVMAppendingLinkage); + LLVMSetInitializer(global_ctor, array); + LLVMValueRef runtime_end = LLVMGetNamedFunction(c->module, "__c3_runtime_finalize"); + if (!runtime_end || !LLVMGetFirstBasicBlock(runtime_end)) error_exit("Failed to find __c3_runtime_finalize in the same module as __c3_runtime_startup."); + vals[1] = runtime_end; + entry = LLVMConstNamedStruct(c->xtor_entry_type, vals, 3); + array = LLVMConstArray(c->xtor_entry_type, &entry, 1); + LLVMValueRef global_dtor = LLVMAddGlobal(c->module, LLVMTypeOf(array), "llvm.global_dtors"); + LLVMSetLinkage(global_dtor, LLVMAppendingLinkage); + LLVMSetInitializer(global_dtor, array); + return; + } llvm_emit_xtor(c, c->constructors, "llvm.global_ctors"); llvm_emit_xtor(c, c->destructors, "llvm.global_dtors"); } diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index b1c453422..2df08947c 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -545,27 +545,43 @@ static void llvm_append_xxlizer(GenContext *c, unsigned priority, bool is_initi { LLVMValueRef **array_ref = is_initializer ? &c->constructors : &c->destructors; LLVMValueRef vals[3] = { llvm_const_int(c, type_int, priority), function, llvm_get_zero(c, type_voidptr) }; - vec_add(*array_ref, LLVMConstStructInContext(c->context, vals, 3, false)); -} - -static LLVMValueRef llvm_add_xxlizer(GenContext *c, unsigned priority, bool is_initializer) -{ - LLVMTypeRef initializer_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false); - LLVMValueRef **array_ref = is_initializer ? &c->constructors : &c->destructors; - scratch_buffer_clear(); - scratch_buffer_printf(is_initializer ? ".static_initialize.%u" : ".static_finalize.%u", vec_size(*array_ref)); - LLVMValueRef function = LLVMAddFunction(c->module, scratch_buffer_to_string(), initializer_type); - LLVMSetLinkage(function, LLVMInternalLinkage); - LLVMValueRef vals[3] = { llvm_const_int(c, type_int, priority), function, llvm_get_zero(c, type_voidptr) }; - vec_add(*array_ref, LLVMConstStructInContext(c->context, vals, 3, false)); - return function; + vec_add(*array_ref, LLVMConstNamedStruct(c->xtor_entry_type, vals, 3)); } void llvm_emit_dynamic_functions(GenContext *c, Decl **funcs) { - if (!vec_size(funcs)) return; - LLVMValueRef initializer = llvm_add_xxlizer(c, 1, true); + size_t len = vec_size(funcs); + if (!len) return; + if (platform_target.object_format == OBJ_FORMAT_MACHO) + { + LLVMTypeRef types[3] = { c->ptr_type, c->ptr_type, c->typeid_type }; + LLVMTypeRef entry_type = LLVMStructType(types, 3, false); + c->dyn_section_type = entry_type; + LLVMValueRef *entries = VECNEW(LLVMValueRef, len); + FOREACH_BEGIN(Decl *func, funcs) + Type *type = typeget(func->func_decl.type_parent); + Decl *proto = declptrzero(func->func_decl.interface_method); + LLVMValueRef proto_ref = proto ? llvm_get_ref(c, proto) : llvm_get_selector(c, func->name); + LLVMValueRef vals[3] = { llvm_get_ref(c, func), proto_ref, llvm_get_typeid(c, type) }; + LLVMValueRef entry = LLVMConstNamedStruct(entry_type, vals, 3); + vec_add(entries, entry); + FOREACH_END(); + LLVMValueRef array = LLVMConstArray(entry_type, entries, len); + LLVMValueRef global = LLVMAddGlobal(c->module, LLVMTypeOf(array), "$c3_dynamic"); + LLVMSetLinkage(global, LLVMInternalLinkage); + LLVMSetInitializer(global, array); + LLVMSetSection(global, "__DATA,__c3_dynamic"); + LLVMSetAlignment(global, llvm_abi_alignment(c, c->xtor_entry_type)); + return; + } + + LLVMValueRef initializer = LLVMAddFunction(c->module, ".c3_dynamic_register", c->xtor_func_type); + LLVMSetLinkage(initializer, LLVMInternalLinkage); + LLVMSetAlignment(initializer, 8); + LLVMValueRef vals_fn[3] = { llvm_const_int(c, type_int, 1), initializer, llvm_get_zero(c, type_voidptr) }; + vec_add(c->constructors, LLVMConstNamedStruct(c->xtor_entry_type, vals_fn, 3)); + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(c->context, initializer, "entry"); LLVMBuilderRef builder = llvm_create_builder(c); LLVMPositionBuilderAtEnd(builder, entry); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 3a652532c..12da0242d 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -101,6 +101,8 @@ typedef struct GenContext_ LLVMTypeRef dtable_type; LLVMTypeRef ptr_type; LLVMTypeRef chars_type; + LLVMTypeRef xtor_entry_type; + LLVMTypeRef xtor_func_type; Decl *panic_var; Decl *panicf; struct @@ -121,6 +123,7 @@ typedef struct GenContext_ Decl **dynamic_functions; LLVMValueRef dyn_find_function; LLVMTypeRef dyn_find_function_type; + LLVMTypeRef dyn_section_type; } GenContext; // LLVM Intrinsics diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 31cb0f795..203c08cdd 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -137,6 +137,9 @@ void gencontext_begin_module(GenContext *c) LLVMTypeRef dtable_type[3] = { c->ptr_type, c->ptr_type, c->ptr_type }; c->dtable_type = LLVMStructTypeInContext(c->context, dtable_type, 3, false); c->chars_type = llvm_get_type(c, type_chars); + LLVMTypeRef ctor_type[3] = { LLVMInt32TypeInContext(c->context), c->ptr_type, c->ptr_type }; + c->xtor_entry_type = LLVMStructTypeInContext(c->context, ctor_type, 3, false); + c->xtor_func_type = LLVMFunctionType(LLVMVoidTypeInContext(c->context), NULL, 0, false); c->introspect_type = create_introspection_type(c); c->fault_type = create_fault_type(c); if (c->panic_var) c->panic_var->backend_ref = NULL; diff --git a/test/test_suite/dynamic/inherit.c3t b/test/test_suite/dynamic/inherit_linux.c3t similarity index 91% rename from test/test_suite/dynamic/inherit.c3t rename to test/test_suite/dynamic/inherit_linux.c3t index 75895c538..d60666056 100644 --- a/test/test_suite/dynamic/inherit.c3t +++ b/test/test_suite/dynamic/inherit_linux.c3t @@ -1,4 +1,4 @@ -// #target: macos-x64 +// #target: linux-x64 module inherit; import std::io; @@ -41,18 +41,21 @@ fn void main() %.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] } %"any*" = type { ptr, i64 } -@"$ct.inherit.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 -@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1 +$.dyn_search = comdat any +$"$ct.inherit.Test" = comdat any +$"$sel.tesT" = comdat any +$"$sel.hello" = comdat any +@"$ct.inherit.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, comdat, align 8 +@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", comdat, align 1 @.panic_msg = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1 -@.file = internal constant [11 x i8] c"inherit.c3\00", align 1 + @.func = internal constant [5 x i8] c"main\00", align 1 @std.core.builtin.panic = external global ptr, align 8 @"$ct.dyn.inherit.Test.tesT" = global { ptr, ptr, ptr } { ptr @inherit.Test.tesT, ptr @"$sel.tesT", ptr null }, align 8 @"$ct.dyn.inherit.Test.hello" = global { ptr, ptr, ptr } { ptr @inherit.Test.hello, ptr @"$sel.hello", ptr null }, align 8 -@"$sel.hello" = linkonce_odr constant [6 x i8] c"hello\00", align 1 -@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.static_initialize.0, ptr null }] +@"$sel.hello" = linkonce_odr constant [6 x i8] c"hello\00", comdat, align 1 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.c3_dynamic_register, ptr null }] -; Function Attrs: define void @inherit.Test.tesT(ptr %0) #0 { entry: ret void @@ -101,7 +104,7 @@ cache_hit: ; preds = %entry br i1 %9, label %missing_function, label %match missing_function: ; preds = %8 %10 = load ptr, ptr @std.core.builtin.panic, align 8 - call void %10(ptr @.panic_msg, i64 41, ptr @.file, i64 10, ptr @.func, i64 4, i32 34) + call void %10(ptr @.panic_msg, i64 41, ptr @.file unreachable match: ; preds = %8 %11 = load ptr, ptr %z, align 8 @@ -130,7 +133,7 @@ cache_hit8: ; preds = %match br i1 %19, label %missing_function11, label %match12 missing_function11: ; preds = %18 %20 = load ptr, ptr @std.core.builtin.panic, align 8 - call void %20(ptr @.panic_msg, i64 41, ptr @.file, i64 10, ptr @.func, i64 4, i32 36) + call void %20(ptr @.panic_msg, i64 41, ptr @.file unreachable match12: ; preds = %18 %21 = load ptr, ptr %w, align 8 @@ -142,7 +145,8 @@ entry: call void @inherit.main() ret i32 0 } -define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr { + +define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr comdat { entry: br label %check check: ; preds = %no_match, %entry @@ -164,7 +168,7 @@ no_match: ; preds = %compare %9 = load ptr, ptr %8, align 8 br label %check } -define internal void @.static_initialize.0() { +define internal void @.c3_dynamic_register() align 8 { entry: br label %dtable_check dtable_check: ; preds = %dtable_next, %entry @@ -190,4 +194,3 @@ dtable_found6: ; preds = %dtable_check1 store ptr @"$ct.dyn.inherit.Test.hello", ptr %dtable_ref2, align 8 ret void } -} \ No newline at end of file diff --git a/test/test_suite/dynamic/inherit_macos.c3t b/test/test_suite/dynamic/inherit_macos.c3t new file mode 100644 index 000000000..a6fb4a1c5 --- /dev/null +++ b/test/test_suite/dynamic/inherit_macos.c3t @@ -0,0 +1,163 @@ +// #target: macos-x64 +module inherit; +import std::io; + +interface Base +{ + fn void tesT(); +} + +interface TestProto2 : Base +{ +} + +interface TestProto : Base +{ + fn void hello(); +} + +fn void Test.tesT(&self) @dynamic +{ +} + +fn void Test.hello(&self) @dynamic +{ +} + +struct Test (TestProto, TestProto2) +{ + void* abc; +} + +fn void main() +{ + TestProto* z = mem::alloc(Test); + z.tesT(); + Base* w = z; + w.tesT(); +} + +/* #expect: inherit.ll + +%.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] } +%"any*" = type { ptr, i64 } +@"$ct.inherit.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1 +@.panic_msg = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1 +@.func = internal constant [5 x i8] c"main\00", align 1 +@std.core.builtin.panic = external global ptr, align 8 +@"$sel.hello" = linkonce_odr constant [6 x i8] c"hello\00", align 1 +@"$c3_dynamic" = internal global [2 x { ptr, ptr, i64 }] [{ ptr, ptr, i64 } { ptr @inherit.Test.tesT, ptr @"$sel.tesT", i64 ptrtoint (ptr @"$ct.inherit.Test" to i64) }, { ptr, ptr, i64 } { ptr @inherit.Test.hello, ptr @"$sel.hello", i64 ptrtoint (ptr @"$ct.inherit.Test" to i64) }], section "__DATA,__c3_dynamic", align 8 + +; Function Attrs: +define void @inherit.Test.tesT(ptr %0) #0 { +entry: + ret void +} + +; Function Attrs: +define void @inherit.Test.hello(ptr %0) #0 { +entry: + ret void +} + +; Function Attrs: +define void @inherit.main() #0 { +entry: + %z = alloca %"any*", align 8 + %.inlinecache = alloca ptr, align 8 + %.cachedtype = alloca ptr, align 8 + %w = alloca %"any*", align 8 + %.inlinecache3 = alloca ptr, align 8 + %.cachedtype4 = alloca ptr, align 8 + store ptr null, ptr %.cachedtype4, align 8 + store ptr null, ptr %.cachedtype, align 8 + %0 = call ptr @std.core.mem.malloc(i64 8) #1 + %1 = insertvalue %"any*" undef, ptr %0, 0 + %2 = insertvalue %"any*" %1, i64 ptrtoint (ptr @"$ct.inherit.Test" to i64), 1 + store %"any*" %2, ptr %z, align 8 + %ptradd = getelementptr inbounds i8, ptr %z, i64 8 + %3 = load i64, ptr %ptradd, align 8 + %4 = inttoptr i64 %3 to ptr + %type = load ptr, ptr %.cachedtype, align 8 + %5 = icmp eq ptr %4, %type + br i1 %5, label %cache_hit, label %cache_miss +cache_miss: ; preds = %entry + %ptradd1 = getelementptr inbounds i8, ptr %4, i64 16 + %6 = load ptr, ptr %ptradd1, align 8 + %7 = call ptr @.dyn_search(ptr %6, ptr @"$sel.tesT") + store ptr %7, ptr %.inlinecache, align 8 + store ptr %4, ptr %.cachedtype, align 8 + br label %8 +cache_hit: ; preds = %entry + %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 + br label %8 +8: ; preds = %cache_hit, %cache_miss + %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %7, %cache_miss ] + %9 = icmp eq ptr %fn_phi, null + br i1 %9, label %missing_function, label %match +missing_function: ; preds = %8 + %10 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %10(ptr @.panic_msg, i64 41, + unreachable +match: ; preds = %8 + %11 = load ptr, ptr %z, align 8 + call void %fn_phi(ptr %11) + %12 = load %"any*", ptr %z, align 8 + store %"any*" %12, ptr %w, align 8 + %ptradd2 = getelementptr inbounds i8, ptr %w, i64 8 + %13 = load i64, ptr %ptradd2, align 8 + %14 = inttoptr i64 %13 to ptr + %type5 = load ptr, ptr %.cachedtype4, align 8 + %15 = icmp eq ptr %14, %type5 + br i1 %15, label %cache_hit8, label %cache_miss6 +cache_miss6: ; preds = %match + %ptradd7 = getelementptr inbounds i8, ptr %14, i64 16 + %16 = load ptr, ptr %ptradd7, align 8 + %17 = call ptr @.dyn_search(ptr %16, ptr @"$sel.tesT") + store ptr %17, ptr %.inlinecache3, align 8 + store ptr %14, ptr %.cachedtype4, align 8 + br label %18 +cache_hit8: ; preds = %match + %cache_hit_fn9 = load ptr, ptr %.inlinecache3, align 8 + br label %18 +18: ; preds = %cache_hit8, %cache_miss6 + %fn_phi10 = phi ptr [ %cache_hit_fn9, %cache_hit8 ], [ %17, %cache_miss6 ] + %19 = icmp eq ptr %fn_phi10, null + br i1 %19, label %missing_function11, label %match12 +missing_function11: ; preds = %18 + %20 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %20(ptr @.panic_msg, i64 41 + unreachable +match12: ; preds = %18 + %21 = load ptr, ptr %w, align 8 + call void %fn_phi10(ptr %21) + ret void +} +define i32 @main(i32 %0, ptr %1) #0 { +entry: + call void @inherit.main() + ret i32 0 +} +define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr { +entry: + br label %check +check: ; preds = %no_match, %entry + %2 = phi ptr [ %0, %entry ], [ %9, %no_match ] + %3 = icmp eq ptr %2, null + br i1 %3, label %missing_function, label %compare +missing_function: ; preds = %check + ret ptr null +compare: ; preds = %check + %4 = getelementptr inbounds { ptr, ptr, ptr }, ptr %2, i32 0, i32 1 + %5 = load ptr, ptr %4, align 8 + %6 = icmp eq ptr %5, %1 + br i1 %6, label %match, label %no_match +match: ; preds = %compare + %7 = load ptr, ptr %2, align 8 + ret ptr %7 +no_match: ; preds = %compare + %8 = getelementptr inbounds { ptr, ptr, ptr }, ptr %2, i32 0, i32 2 + %9 = load ptr, ptr %8, align 8 + br label %check +} diff --git a/test/test_suite/dynamic/overlapping_function.c3t b/test/test_suite/dynamic/overlapping_function_linux.c3t similarity index 91% rename from test/test_suite/dynamic/overlapping_function.c3t rename to test/test_suite/dynamic/overlapping_function_linux.c3t index 4e0bb7a41..3534d6dac 100644 --- a/test/test_suite/dynamic/overlapping_function.c3t +++ b/test/test_suite/dynamic/overlapping_function_linux.c3t @@ -1,4 +1,4 @@ -// #target: macos-x64 +// #target: linux-x64 module overlap; import std::io; @@ -35,16 +35,16 @@ fn void main() %.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] } %"any*" = type { ptr, i64 } -@"$ct.overlap.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 -@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1 +@"$ct.overlap.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, comdat, align 8 +@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", comdat, align 1 @.panic_msg = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1 -@.file = internal constant [24 x i8] c"overlapping_function.c3\00", align 1 +@.file = internal constant [30 x i8] c"overlapping_function_linux.c3\00", align 1 @.func = internal constant [5 x i8] c"main\00", align 1 @std.core.builtin.panic = external global ptr, align 8 @"$ct.dyn.overlap.Test.tesT" = global { ptr, ptr, ptr } { ptr @overlap.Test.tesT, ptr @"$sel.tesT", ptr null }, align 8 @"$ct.dyn.overlap.Test.foo" = global { ptr, ptr, ptr } { ptr @overlap.Test.foo, ptr @"$sel.foo", ptr null }, align 8 -@"$sel.foo" = linkonce_odr constant [4 x i8] c"foo\00", align 1 -@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.static_initialize.0, ptr null }] +@"$sel.foo" = linkonce_odr constant [4 x i8] c"foo\00", comdat, align 1 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.c3_dynamic_register, ptr null }] ; Function Attrs: nounwind uwtable define void @overlap.Test.tesT(ptr %0) #0 { entry: @@ -94,7 +94,7 @@ cache_hit: ; preds = %entry br i1 %9, label %missing_function, label %match missing_function: ; preds = %8 %10 = load ptr, ptr @std.core.builtin.panic, align 8 - call void %10(ptr @.panic_msg, i64 41, ptr @.file, i64 23, ptr @.func, i64 4, i32 28) + call void %10(ptr @.panic_msg, i64 41, ptr @.file unreachable match: ; preds = %8 %11 = load ptr, ptr %z, align 8 @@ -123,7 +123,7 @@ cache_hit8: ; preds = %match br i1 %19, label %missing_function11, label %match12 missing_function11: ; preds = %18 %20 = load ptr, ptr @std.core.builtin.panic, align 8 - call void %20(ptr @.panic_msg, i64 41, ptr @.file, i64 23, ptr @.func, i64 4, i32 30) + call void %20(ptr @.panic_msg, i64 41, ptr @.file unreachable match12: ; preds = %18 %21 = load ptr, ptr %w, align 8 @@ -138,7 +138,8 @@ entry: ret i32 0 } -define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr { + +define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr comdat { entry: br label %check check: ; preds = %no_match, %entry @@ -160,7 +161,7 @@ no_match: ; preds = %compare %9 = load ptr, ptr %8, align 8 br label %check } -define internal void @.static_initialize.0() { +define internal void @.c3_dynamic_register() align 8 { entry: br label %dtable_check dtable_check: ; preds = %dtable_next, %entry diff --git a/test/test_suite/dynamic/overlapping_function_macos.c3t b/test/test_suite/dynamic/overlapping_function_macos.c3t new file mode 100644 index 000000000..1da4e2d3f --- /dev/null +++ b/test/test_suite/dynamic/overlapping_function_macos.c3t @@ -0,0 +1,176 @@ +// #target: macos-x64 +module overlap; +import std::io; + +interface TestProto +{ + fn void tesT(); +} + +interface TestProto2 +{ + fn void tesT(); +} + +fn void Test.tesT(&self) @dynamic +{ +} + +fn void Test.foo(&self) @dynamic {} + +struct Test (TestProto, TestProto2) +{ + void* abc; +} + +fn void main() +{ + TestProto* z = mem::alloc(Test); + z.tesT(); + TestProto2* w = (TestProto2*)z; + w.tesT(); +} + +/* #expect: overlap.ll +%.introspect = type { i8, i64, ptr, i64, i64, i64, [0 x i64] } +%"any*" = type { ptr, i64 } + +@"$ct.overlap.Test" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 +@"$sel.tesT" = linkonce_odr constant [5 x i8] c"tesT\00", align 1 +@.panic_msg = internal constant [42 x i8] c"No method 'tesT' could be found on target\00", align 1 +@.file = internal constant [30 x i8] c"overlapping_function_macos.c3\00", align 1 +@.func = internal constant [5 x i8] c"main\00", align 1 +@std.core.builtin.panic = external global ptr, align 8 +@"$sel.foo" = linkonce_odr constant [4 x i8] c"foo\00", align 1 +@"$c3_dynamic" = internal global [2 x { ptr, ptr, i64 }] [{ ptr, ptr, i64 } { ptr @overlap.Test.tesT, ptr @"$sel.tesT", i64 ptrtoint (ptr @"$ct.overlap.Test" to i64) }, { ptr, ptr, i64 } { ptr @overlap.Test.foo, ptr @"$sel.foo", i64 ptrtoint (ptr @"$ct.overlap.Test" to i64) }], section "__DATA,__c3_dynamic", align 8 + +; Function Attrs: nounwind uwtable +define void @overlap.Test.tesT(ptr %0) #0 { +entry: + ret void +} + +; Function Attrs: +define void @overlap.Test.foo(ptr %0) #0 { +entry: + ret void +} + +; Function Attrs: +define void @overlap.main() #0 { +entry: + %z = alloca %"any*", align 8 + %.inlinecache = alloca ptr, align 8 + %.cachedtype = alloca ptr, align 8 + %w = alloca %"any*", align 8 + %.inlinecache3 = alloca ptr, align 8 + %.cachedtype4 = alloca ptr, align 8 + store ptr null, ptr %.cachedtype4, align 8 + store ptr null, ptr %.cachedtype, align 8 + %0 = call ptr @std.core.mem.malloc(i64 8) #1 + %1 = insertvalue %"any*" undef, ptr %0, 0 + %2 = insertvalue %"any*" %1, i64 ptrtoint (ptr @"$ct.overlap.Test" to i64), 1 + store %"any*" %2, ptr %z, align 8 + %ptradd = getelementptr inbounds i8, ptr %z, i64 8 + %3 = load i64, ptr %ptradd, align 8 + %4 = inttoptr i64 %3 to ptr + %type = load ptr, ptr %.cachedtype, align 8 + %5 = icmp eq ptr %4, %type + br i1 %5, label %cache_hit, label %cache_miss + +cache_miss: ; preds = %entry + %ptradd1 = getelementptr inbounds i8, ptr %4, i64 16 + %6 = load ptr, ptr %ptradd1, align 8 + %7 = call ptr @.dyn_search(ptr %6, ptr @"$sel.tesT") + store ptr %7, ptr %.inlinecache, align 8 + store ptr %4, ptr %.cachedtype, align 8 + br label %8 + +cache_hit: ; preds = %entry + %cache_hit_fn = load ptr, ptr %.inlinecache, align 8 + br label %8 + +8: ; preds = %cache_hit, %cache_miss + %fn_phi = phi ptr [ %cache_hit_fn, %cache_hit ], [ %7, %cache_miss ] + %9 = icmp eq ptr %fn_phi, null + br i1 %9, label %missing_function, label %match + +missing_function: ; preds = %8 + %10 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %10(ptr @.panic_msg, i64 41, ptr @.file + unreachable + +match: ; preds = %8 + %11 = load ptr, ptr %z, align 8 + call void %fn_phi(ptr %11) + %12 = load %"any*", ptr %z, align 8 + store %"any*" %12, ptr %w, align 8 + %ptradd2 = getelementptr inbounds i8, ptr %w, i64 8 + %13 = load i64, ptr %ptradd2, align 8 + %14 = inttoptr i64 %13 to ptr + %type5 = load ptr, ptr %.cachedtype4, align 8 + %15 = icmp eq ptr %14, %type5 + br i1 %15, label %cache_hit8, label %cache_miss6 + +cache_miss6: ; preds = %match + %ptradd7 = getelementptr inbounds i8, ptr %14, i64 16 + %16 = load ptr, ptr %ptradd7, align 8 + %17 = call ptr @.dyn_search(ptr %16, ptr @"$sel.tesT") + store ptr %17, ptr %.inlinecache3, align 8 + store ptr %14, ptr %.cachedtype4, align 8 + br label %18 + +cache_hit8: ; preds = %match + %cache_hit_fn9 = load ptr, ptr %.inlinecache3, align 8 + br label %18 + +18: ; preds = %cache_hit8, %cache_miss6 + %fn_phi10 = phi ptr [ %cache_hit_fn9, %cache_hit8 ], [ %17, %cache_miss6 ] + %19 = icmp eq ptr %fn_phi10, null + br i1 %19, label %missing_function11, label %match12 + +missing_function11: ; preds = %18 + %20 = load ptr, ptr @std.core.builtin.panic, align 8 + call void %20(ptr @.panic_msg, i64 41, ptr @.file + unreachable + +match12: ; preds = %18 + %21 = load ptr, ptr %w, align 8 + call void %fn_phi10(ptr %21) + ret void +} + +; Function Attrs: +define i32 @main(i32 %0, ptr %1) #0 { +entry: + call void @overlap.main() + ret i32 0 +} + +define weak_odr ptr @.dyn_search(ptr %0, ptr %1) unnamed_addr { +entry: + br label %check + +check: ; preds = %no_match, %entry + %2 = phi ptr [ %0, %entry ], [ %9, %no_match ] + %3 = icmp eq ptr %2, null + br i1 %3, label %missing_function, label %compare + +missing_function: ; preds = %check + ret ptr null + +compare: ; preds = %check + %4 = getelementptr inbounds { ptr, ptr, ptr }, ptr %2, i32 0, i32 1 + %5 = load ptr, ptr %4, align 8 + %6 = icmp eq ptr %5, %1 + br i1 %6, label %match, label %no_match + +match: ; preds = %compare + %7 = load ptr, ptr %2, align 8 + ret ptr %7 + +no_match: ; preds = %compare + %8 = getelementptr inbounds { ptr, ptr, ptr }, ptr %2, i32 0, i32 2 + %9 = load ptr, ptr %8, align 8 + br label %check +} diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map_linux.c3t similarity index 97% rename from test/test_suite/stdlib/map.c3t rename to test/test_suite/stdlib/map_linux.c3t index daa30e1ad..12c19cb63 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map_linux.c3t @@ -1,4 +1,4 @@ -// #target: macos-x64 +// #target: linux-x64 module test; import std::io; @@ -52,6 +52,9 @@ fn void main() /* #expect: test.ll +@"$sel.to_new_string" = linkonce_odr constant [14 x i8] c"to_new_string\00", comdat, align 1 +@llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.c3_dynamic_register, ptr null }] + define { ptr, i64 } @test.Foo.to_new_string(ptr %0, i64 %1, ptr %2) #0 { entry: %allocator = alloca %"any*", align 8 @@ -264,7 +267,8 @@ if.exit: ; preds = %if.then, %after_che call void @std.core.mem.allocator.TempAllocator.reset(ptr %61, i64 %62) ret void } -define internal void @.static_initialize.0() { + +define internal void @.c3_dynamic_register() align 8 { entry: br label %dtable_check diff --git a/test/test_suite/stdlib/map_macos.c3t b/test/test_suite/stdlib/map_macos.c3t new file mode 100644 index 000000000..dd76b0685 --- /dev/null +++ b/test/test_suite/stdlib/map_macos.c3t @@ -0,0 +1,269 @@ +// #target: macos-x64 + +module test; +import std::io; +import std::collections::map; + +struct Foo (Printable) { int x; void* bar; } + +def IntFooMap = HashMap(); +def IntDoubleMap = HashMap(); + +fn String Foo.to_new_string(Foo* foo, Allocator* allocator = allocator::heap()) @dynamic +{ + DString s = dstring::new_with_capacity(128, allocator); + s.appendf("{%s, %p}", foo.x, foo.bar); + return s.str_view(); +} + + + +fn void main() +{ + IntFooMap map; + map.new_init(); + io::printfn("Map size: %d", map.count); + map.set(1, Foo { 1, null }); + io::printfn("Map size: %d", map.count); + map.set(1, Foo { 2, null }); + io::printfn("Map size: %d", map.count); + (void)io::printfn("Val: %d", map.get(1).x); + io::printfn("Has 1: %s", map.has_key(1)); + io::printfn("Has 2: %s", map.has_key(2)); + map.set(7, Foo { 4, null }); + io::printfn("Values: %s", map.value_new_list()); + IntDoubleMap map2; + map2.new_init(); + map2.set(4, 1.3); + io::printfn("Map find: %s", map2.has_value(1.3)); + io::printfn("Map find: %s", map2.has_value(1.2)); + map2.set(100, 3.4); + io::printfn("%s", map2.key_new_list()); + io::printfn("%s", map2.value_new_list()); + @pool() + { + IntDoubleMap map3; + map3.new_init(); + map3.set(5, 3.2); + map3.set(7, 5.2); + io::printfn("%s", map3.key_new_list()); + }; +} + +/* #expect: test.ll + +@"$sel.to_new_string" = linkonce_odr constant [14 x i8] c"to_new_string\00", align 1 +@"$c3_dynamic" = internal global [1 x { ptr, ptr, i64 }] [{ ptr, ptr, i64 } { ptr @test.Foo.to_new_string, ptr @"$sel.to_new_string", i64 ptrtoint (ptr @"$ct.test.Foo" to i64) }], section "__DATA,__c3_dynamic", align 8 + +define { ptr, i64 } @test.Foo.to_new_string(ptr %0, i64 %1, ptr %2) #0 { +entry: + %allocator = alloca %"any*", align 8 + %s = alloca ptr, align 8 + %varargslots = alloca [2 x %"any*"], align 16 + %retparam = alloca i64, align 8 + %result = alloca %"char[]", align 8 + store i64 %1, ptr %allocator, align 8 + %ptradd = getelementptr inbounds i8, ptr %allocator, i64 8 + store ptr %2, ptr %ptradd, align 8 + %lo = load i64, ptr %allocator, align 8 + %ptradd1 = getelementptr inbounds i8, ptr %allocator, i64 8 + %hi = load ptr, ptr %ptradd1, align 8 + %3 = call ptr @std.core.dstring.new_with_capacity(i64 128, i64 %lo, ptr %hi) + store ptr %3, ptr %s, align 8 + %4 = insertvalue %"any*" undef, ptr %0, 0 + %5 = insertvalue %"any*" %4, i64 ptrtoint (ptr @"$ct.int" to i64), 1 + store %"any*" %5, ptr %varargslots, align 16 + %ptradd2 = getelementptr inbounds i8, ptr %0, i64 8 + %6 = insertvalue %"any*" undef, ptr %ptradd2, 0 + %7 = insertvalue %"any*" %6, i64 ptrtoint (ptr @"$ct.p$void" to i64), 1 + %ptradd3 = getelementptr inbounds i8, ptr %varargslots, i64 16 + store %"any*" %7, ptr %ptradd3, align 16 + %8 = call i64 @std.core.dstring.DString.appendf(ptr %retparam, ptr %s, ptr @.str.14, i64 8, ptr %varargslots, i64 2) + %9 = load ptr, ptr %s, align 8 + %10 = call { ptr, i64 } @std.core.dstring.DString.str_view(ptr %9) + store { ptr, i64 } %10, ptr %result, align 8 + %11 = load { ptr, i64 }, ptr %result, align 8 + ret { ptr, i64 } %11 +} + +; Function Attrs: +define void @test.main() #0 { +entry: + %map = alloca %HashMap, align 8 + %varargslots = alloca [1 x %"any*"], align 16 + %retparam = alloca i64, align 8 + %literal = alloca %Foo, align 8 + %varargslots4 = alloca [1 x %"any*"], align 16 + %retparam6 = alloca i64, align 8 + %literal7 = alloca %Foo, align 8 + %varargslots11 = alloca [1 x %"any*"], align 16 + %retparam13 = alloca i64, align 8 + %varargslots14 = alloca [1 x %"any*"], align 16 + %retparam15 = alloca %Foo, align 8 + %retparam16 = alloca i64, align 8 + %varargslots19 = alloca [1 x %"any*"], align 16 + %taddr = alloca i8, align 1 + %retparam20 = alloca i64, align 8 + %varargslots23 = alloca [1 x %"any*"], align 16 + %taddr24 = alloca i8, align 1 + %retparam25 = alloca i64, align 8 + %literal28 = alloca %Foo, align 8 + %varargslots32 = alloca [1 x %"any*"], align 16 + %result = alloca %"Foo[]", align 8 + %retparam35 = alloca i64, align 8 + %map2 = alloca %HashMap.0, align 8 + %varargslots40 = alloca [1 x %"any*"], align 16 + %taddr41 = alloca i8, align 1 + %retparam42 = alloca i64, align 8 + %varargslots45 = alloca [1 x %"any*"], align 16 + %taddr46 = alloca i8, align 1 + %retparam47 = alloca i64, align 8 + %varargslots50 = alloca [1 x %"any*"], align 16 + %result53 = alloca %"int[]", align 8 + %retparam54 = alloca i64, align 8 + %varargslots57 = alloca [1 x %"any*"], align 16 + %result60 = alloca %"double[]", align 8 + %retparam61 = alloca i64, align 8 + %current = alloca ptr, align 8 + %mark = alloca i64, align 8 + %map3 = alloca %HashMap.0, align 8 + %varargslots67 = alloca [1 x %"any*"], align 16 + %result70 = alloca %"int[]", align 8 + %retparam71 = alloca i64, align 8 + call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 48, i1 false) + %lo = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %0 = call ptr @"std.collections.map$int$test.Foo$.HashMap.new_init"(ptr %map, i32 16, float 7.500000e-01, i64 %lo, ptr %hi) + %ptradd = getelementptr inbounds i8, ptr %map, i64 32 + %1 = insertvalue %"any*" undef, ptr %ptradd, 0 + %2 = insertvalue %"any*" %1, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 + store %"any*" %2, ptr %varargslots, align 16 + %3 = call i64 @std.io.printfn(ptr %retparam, ptr @.str, i64 12, ptr %varargslots, i64 1) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal, ptr align 8 @.__const, i32 16, i1 false) + %lo1 = load i64, ptr %literal, align 8 + %ptradd2 = getelementptr inbounds i8, ptr %literal, i64 8 + %hi3 = load ptr, ptr %ptradd2, align 8 + %4 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo1, ptr %hi3) + %ptradd5 = getelementptr inbounds i8, ptr %map, i64 32 + %5 = insertvalue %"any*" undef, ptr %ptradd5, 0 + %6 = insertvalue %"any*" %5, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 + store %"any*" %6, ptr %varargslots4, align 16 + %7 = call i64 @std.io.printfn(ptr %retparam6, ptr @.str.1, i64 12, ptr %varargslots4, i64 1) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal7, ptr align 8 @.__const.2, i32 16, i1 false) + %lo8 = load i64, ptr %literal7, align 8 + %ptradd9 = getelementptr inbounds i8, ptr %literal7, i64 8 + %hi10 = load ptr, ptr %ptradd9, align 8 + %8 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 1, i64 %lo8, ptr %hi10) + %ptradd12 = getelementptr inbounds i8, ptr %map, i64 32 + %9 = insertvalue %"any*" undef, ptr %ptradd12, 0 + %10 = insertvalue %"any*" %9, i64 ptrtoint (ptr @"$ct.uint" to i64), 1 + store %"any*" %10, ptr %varargslots11, align 16 + %11 = call i64 @std.io.printfn(ptr %retparam13, ptr @.str.3, i64 12, ptr %varargslots11, i64 1) + %12 = call i64 @"std.collections.map$int$test.Foo$.HashMap.get"(ptr %retparam15, ptr %map, i32 1) + %not_err = icmp eq i64 %12, 0 + %13 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) + br i1 %13, label %after_check, label %after_check18 + +after_check: ; preds = %entry + %14 = insertvalue %"any*" undef, ptr %retparam15, 0 + %15 = insertvalue %"any*" %14, i64 ptrtoint (ptr @"$ct.int" to i64), 1 + store %"any*" %15, ptr %varargslots14, align 16 + %16 = call i64 @std.io.printfn(ptr %retparam16, ptr @.str.4, i64 7, ptr %varargslots14, i64 1) + %not_err17 = icmp eq i64 %16, 0 + %17 = call i1 @llvm.expect.i1(i1 %not_err17, i1 true) + br i1 %17, label %after_check18, label %after_check18 + +after_check18: ; preds = %entry, %after_check, %after_check + %18 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1) + store i8 %18, ptr %taddr, align 1 + %19 = insertvalue %"any*" undef, ptr %taddr, 0 + %20 = insertvalue %"any*" %19, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + store %"any*" %20, ptr %varargslots19, align 16 + %21 = call i64 @std.io.printfn(ptr %retparam20, ptr @.str.5, i64 9, ptr %varargslots19, i64 1) + %22 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 2) + store i8 %22, ptr %taddr24, align 1 + %23 = insertvalue %"any*" undef, ptr %taddr24, 0 + %24 = insertvalue %"any*" %23, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + store %"any*" %24, ptr %varargslots23, align 16 + %25 = call i64 @std.io.printfn(ptr %retparam25, ptr @.str.6, i64 9, ptr %varargslots23, i64 1) + call void @llvm.memcpy.p0.p0.i32(ptr align 8 %literal28, ptr align 8 @.__const.7, i32 16, i1 false) + %lo29 = load i64, ptr %literal28, align 8 + %ptradd30 = getelementptr inbounds i8, ptr %literal28, i64 8 + %hi31 = load ptr, ptr %ptradd30, align 8 + %26 = call i8 @"std.collections.map$int$test.Foo$.HashMap.set"(ptr %map, i32 7, i64 %lo29, ptr %hi31) + %lo33 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi34 = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %27 = call { ptr, i64 } @"std.collections.map$int$test.Foo$.HashMap.value_new_list"(ptr %map, i64 %lo33, ptr %hi34) + store { ptr, i64 } %27, ptr %result, align 8 + %28 = insertvalue %"any*" undef, ptr %result, 0 + %29 = insertvalue %"any*" %28, i64 ptrtoint (ptr @"$ct.sa$test.Foo" to i64), 1 + store %"any*" %29, ptr %varargslots32, align 16 + %30 = call i64 @std.io.printfn(ptr %retparam35, ptr @.str.8, i64 10, ptr %varargslots32, i64 1) + call void @llvm.memset.p0.i64(ptr align 8 %map2, i8 0, i64 48, i1 false) + %lo38 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi39 = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %31 = call ptr @"std.collections.map$int$double$.HashMap.new_init"(ptr %map2, i32 16, float 7.500000e-01, i64 %lo38, ptr %hi39) + %32 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 4, double 1.300000e+00) + %33 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.300000e+00) + store i8 %33, ptr %taddr41, align 1 + %34 = insertvalue %"any*" undef, ptr %taddr41, 0 + %35 = insertvalue %"any*" %34, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + store %"any*" %35, ptr %varargslots40, align 16 + %36 = call i64 @std.io.printfn(ptr %retparam42, ptr @.str.9, i64 12, ptr %varargslots40, i64 1) + %37 = call i8 @"std.collections.map$int$double$.HashMap.has_value"(ptr %map2, double 1.200000e+00) + store i8 %37, ptr %taddr46, align 1 + %38 = insertvalue %"any*" undef, ptr %taddr46, 0 + %39 = insertvalue %"any*" %38, i64 ptrtoint (ptr @"$ct.bool" to i64), 1 + store %"any*" %39, ptr %varargslots45, align 16 + %40 = call i64 @std.io.printfn(ptr %retparam47, ptr @.str.10, i64 12, ptr %varargslots45, i64 1) + %41 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map2, i32 100, double 3.400000e+00) + %lo51 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi52 = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %42 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_new_list"(ptr %map2, i64 %lo51, ptr %hi52) + store { ptr, i64 } %42, ptr %result53, align 8 + %43 = insertvalue %"any*" undef, ptr %result53, 0 + %44 = insertvalue %"any*" %43, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 + store %"any*" %44, ptr %varargslots50, align 16 + %45 = call i64 @std.io.printfn(ptr %retparam54, ptr @.str.11, i64 2, ptr %varargslots50, i64 1) + %lo58 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi59 = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %46 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.value_new_list"(ptr %map2, i64 %lo58, ptr %hi59) + store { ptr, i64 } %46, ptr %result60, align 8 + %47 = insertvalue %"any*" undef, ptr %result60, 0 + %48 = insertvalue %"any*" %47, i64 ptrtoint (ptr @"$ct.sa$double" to i64), 1 + store %"any*" %48, ptr %varargslots57, align 16 + %49 = call i64 @std.io.printfn(ptr %retparam61, ptr @.str.12, i64 2, ptr %varargslots57, i64 1) + %50 = load ptr, ptr @std.core.mem.allocator.thread_temp_allocator, align 8 + %not = icmp eq ptr %50, null + br i1 %not, label %if.then, label %if.exit + +if.then: ; preds = %after_check18 + call void @std.core.mem.allocator.init_default_temp_allocators() + br label %if.exit + +if.exit: ; preds = %if.then, %after_check18 + %51 = load ptr, ptr @std.core.mem.allocator.thread_temp_allocator, align 8 + store ptr %51, ptr %current, align 8 + %52 = load ptr, ptr %current, align 8 + %ptradd64 = getelementptr inbounds i8, ptr %52, i64 24 + %53 = load i64, ptr %ptradd64, align 8 + store i64 %53, ptr %mark, align 8 + call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 48, i1 false) + %lo65 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi66 = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %54 = call ptr @"std.collections.map$int$double$.HashMap.new_init"(ptr %map3, i32 16, float 7.500000e-01, i64 %lo65, ptr %hi66) + %55 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00) + %56 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) + %lo68 = load i64, ptr @std.core.mem.allocator.thread_allocator, align 8 + %hi69 = load ptr, ptr getelementptr inbounds (i8, ptr @std.core.mem.allocator.thread_allocator, i64 8), align 8 + %57 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_new_list"(ptr %map3, i64 %lo68, ptr %hi69) + store { ptr, i64 } %57, ptr %result70, align 8 + %58 = insertvalue %"any*" undef, ptr %result70, 0 + %59 = insertvalue %"any*" %58, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 + store %"any*" %59, ptr %varargslots67, align 16 + %60 = call i64 @std.io.printfn(ptr %retparam71, ptr @.str.13, i64 2, ptr %varargslots67, i64 1) + %61 = load ptr, ptr %current, align 8 + %62 = load i64, ptr %mark, align 8 + call void @std.core.mem.allocator.TempAllocator.reset(ptr %61, i64 %62) + ret void +} \ No newline at end of file