diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index b42e01f06..6a350f5c7 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -131,6 +131,7 @@ const bool TESTING = $$TESTING; const MemoryEnvironment MEMORY_ENV = (MemoryEnvironment)$$MEMORY_ENVIRONMENT; const bool TRACK_MEMORY = DEBUG_SYMBOLS && (COMPILER_SAFE_MODE || TESTING); const bool X86_64 = ARCH_TYPE == X86_64; +const bool X86 = ARCH_TYPE == X86; const bool AARCH64 = ARCH_TYPE == AARCH64; const bool NATIVE_STACKTRACE = LINUX || DARWIN || WIN32; const bool LINUX = LIBC && OS_TYPE == LINUX; diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index 0118a28f4..7c94a37c8 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -430,7 +430,7 @@ macro void @pool(TempAllocator* #other_temp = null; @body) @builtin TempAllocator* original = current; if (current == (void*)#other_temp) current = allocator::temp_allocator_next(); $endif - usz mark = current.used; + usz mark = current.mark(); defer { current.reset(mark); @@ -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/cpu_detect.c3 b/lib/std/core/private/cpu_detect.c3 new file mode 100644 index 000000000..3988d6fc1 --- /dev/null +++ b/lib/std/core/private/cpu_detect.c3 @@ -0,0 +1,258 @@ +module std::core::cpudetect @if(env::X86 || env::X86_64); + +struct CpuId +{ + uint eax, ebx, ecx, edx; +} +fn CpuId x86_cpuid(uint eax, uint ecx = 0) +{ + int edx; + int ebx; + asm + { + movl $eax, eax; + movl $ecx, ecx; + cpuid; + movl eax, $eax; + movl ebx, $ebx; + movl ecx, $ecx; + movl edx, $edx; + } + return { eax, ebx, ecx, edx }; +} + +enum X86Feature +{ + ADX, + AES, + AMX_BF16, + AMX_COMPLEX, + AMX_FP16, + AMX_INT8, + AMX_TILE, + AVX, + AVX10_1_256, + AVX10_1_512, + AVX2, + AVX5124FMAPS, + AVX5124VNNIW, + AVX512BF16, + AVX512BITALG, + AVX512BW, + AVX512CD, + AVX512DQ, + AVX512ER, + AVX512F, + AVX512FP16, + AVX512IFMA, + AVX512PF, + AVX512VBMI, + AVX512VBMI2, + AVX512VL, + AVX512VNNI, + AVX512VP2INTERSECT, + AVX512VPOPCNTDQ, + AVXIFMA, + AVXNECONVERT, + AVXVNNI, + AVXVNNIINT16, + AVXVNNIINT8, + BMI, + BMI2, + CLDEMOTE, + CLFLUSHOPT, + CLWB, + CLZERO, + CMOV, + CMPCCXADD, + CMPXCHG16B, + CX8, + ENQCMD, + F16C, + FMA, + FMA4, + FSGSBASE, + FXSR, + GFNI, + HRESET, + INVPCID, + KL, + LWP, + LZCNT, + MMX, + MOVBE, + MOVDIR64B, + MOVDIRI, + MWAITX, + PCLMUL, + PCONFIG, + PKU, + POPCNT, + PREFETCHI, + PREFETCHWT1, + PRFCHW, + PTWRITE, + RAOINT, + RDPID, + RDPRU, + RDRND, + RDSEED, + RTM, + SAHF, + SERIALIZE, + SGX, + SHA, + SHA512, + SHSTK, + SM3, + SM4, + SSE, + SSE2, + SSE3, + SSE4_1, + SSE4_2, + SSE4_A, + SSSE3, + TBM, + TSXLDTRK, + UINTR, + USERMSR, + VAES, + VPCLMULQDQ, + WAITPKG, + WBNOINVD, + WIDEKL, + X87, + XOP, + XSAVE, + XSAVEC, + XSAVEOPT, + XSAVES, +} + +uint128 x86_features; + +fn void add_feature_if_bit(X86Feature feature, uint register, int bit) +{ + if (register & 1U << bit) x86_features |= 1u128 << feature.ordinal; +} + +fn void x86_initialize_cpu_features() +{ + uint max_level = x86_cpuid(0).eax; + CpuId feat = x86_cpuid(1); + CpuId leaf7 = max_level >= 8 ? x86_cpuid(7) : CpuId {}; + CpuId leaf7s1 = leaf7.eax >= 1 ? x86_cpuid(7, 1) : CpuId {}; + CpuId ext1 = x86_cpuid(0x80000000).eax >= 0x80000001 ? x86_cpuid(0x80000001) : CpuId {}; + CpuId ext8 = x86_cpuid(0x80000000).eax >= 0x80000008 ? x86_cpuid(0x80000008) : CpuId {}; + CpuId leaf_d = max_level >= 0xd ? x86_cpuid(0xd, 0x1) : CpuId {}; + CpuId leaf_14 = max_level >= 0x14 ? x86_cpuid(0x14) : CpuId {}; + CpuId leaf_19 = max_level >= 0x19 ? x86_cpuid(0x19) : CpuId {}; + CpuId leaf_24 = max_level >= 0x24 ? x86_cpuid(0x24) : CpuId {}; + add_feature_if_bit(ADX, leaf7.ebx, 19); + add_feature_if_bit(AES, feat.ecx, 25); + add_feature_if_bit(AMX_BF16, leaf7.edx, 22); + add_feature_if_bit(AMX_COMPLEX, leaf7s1.edx, 8); + add_feature_if_bit(AMX_FP16, leaf7s1.eax, 21); + add_feature_if_bit(AMX_INT8, leaf7.edx, 25); + add_feature_if_bit(AMX_TILE, leaf7.edx, 24); + add_feature_if_bit(AVX, feat.ecx, 28); + add_feature_if_bit(AVX10_1_256, leaf7s1.edx, 19); + add_feature_if_bit(AVX10_1_512, leaf_24.ebx, 18); + add_feature_if_bit(AVX2, leaf7.ebx, 5); + add_feature_if_bit(AVX5124FMAPS, leaf7.edx, 3); + add_feature_if_bit(AVX5124VNNIW, leaf7.edx, 2); + add_feature_if_bit(AVX512BF16, leaf7s1.eax, 5); + add_feature_if_bit(AVX512BITALG, leaf7.ecx, 12); + add_feature_if_bit(AVX512BW, leaf7.ebx, 30); + add_feature_if_bit(AVX512CD, leaf7.ebx, 28); + add_feature_if_bit(AVX512DQ, leaf7.ebx, 17); + add_feature_if_bit(AVX512ER, leaf7.ebx, 27); + add_feature_if_bit(AVX512F, leaf7.ebx, 16); + add_feature_if_bit(AVX512FP16, leaf7.edx, 23); + add_feature_if_bit(AVX512IFMA, leaf7.ebx, 21); + add_feature_if_bit(AVX512PF, leaf7.ebx, 26); + add_feature_if_bit(AVX512VBMI, leaf7.ecx, 1); + add_feature_if_bit(AVX512VBMI2, leaf7.ecx, 6); + add_feature_if_bit(AVX512VL, leaf7.ebx, 31); + add_feature_if_bit(AVX512VNNI, leaf7.ecx, 11); + add_feature_if_bit(AVX512VP2INTERSECT, leaf7.edx, 8); + add_feature_if_bit(AVX512VPOPCNTDQ, leaf7.ecx, 14); + add_feature_if_bit(AVXIFMA, leaf7s1.eax, 23); + add_feature_if_bit(AVXNECONVERT, leaf7s1.edx, 5); + add_feature_if_bit(AVXVNNI, leaf7s1.eax, 4); + add_feature_if_bit(AVXVNNIINT16, leaf7s1.edx, 10); + add_feature_if_bit(AVXVNNIINT8, leaf7s1.edx, 4); + add_feature_if_bit(BMI, leaf7.ebx, 3); + add_feature_if_bit(BMI2, leaf7.ebx, 8); + add_feature_if_bit(CLDEMOTE, leaf7.ecx, 25); + add_feature_if_bit(CLFLUSHOPT, leaf7.ebx, 23); + add_feature_if_bit(CLWB, leaf7.ebx, 24); + add_feature_if_bit(CLZERO, ext8.ecx, 0); + add_feature_if_bit(CMOV, feat.edx, 15); + add_feature_if_bit(CMPCCXADD, leaf7s1.eax, 7); + add_feature_if_bit(CMPXCHG16B, feat.ecx, 12); + add_feature_if_bit(CX8, feat.edx, 8); + add_feature_if_bit(ENQCMD, leaf7.ecx, 29); + add_feature_if_bit(F16C, feat.ecx, 29); + add_feature_if_bit(FMA, feat.ecx, 12); + add_feature_if_bit(FMA4, ext1.ecx, 16); + add_feature_if_bit(FSGSBASE, leaf7.ebx, 0); + add_feature_if_bit(FXSR, feat.edx, 24); + add_feature_if_bit(GFNI, leaf7.ecx, 8); + add_feature_if_bit(HRESET, leaf7s1.eax, 22); + add_feature_if_bit(INVPCID, leaf7.ebx, 10); + add_feature_if_bit(KL, leaf7.ecx, 23); + add_feature_if_bit(LWP, ext1.ecx, 15); + add_feature_if_bit(LZCNT, ext1.ecx, 5); + add_feature_if_bit(MMX, feat.edx, 23); + add_feature_if_bit(MOVBE, feat.ecx, 22); + add_feature_if_bit(MOVDIR64B, leaf7.ecx, 28); + add_feature_if_bit(MOVDIRI, leaf7.ecx, 27); + add_feature_if_bit(MWAITX, ext1.ecx, 29); + add_feature_if_bit(PCLMUL, feat.ecx, 1); + add_feature_if_bit(PCONFIG, leaf7.edx, 18); + add_feature_if_bit(PKU, leaf7.ecx, 4); + add_feature_if_bit(POPCNT, feat.ecx, 23); + add_feature_if_bit(PREFETCHI, leaf7s1.edx, 14); + add_feature_if_bit(PREFETCHWT1, leaf7.ecx, 0); + add_feature_if_bit(PRFCHW, ext1.ecx, 8); + add_feature_if_bit(PTWRITE, leaf_14.ebx, 4); + add_feature_if_bit(RAOINT, leaf7s1.eax, 3); + add_feature_if_bit(RDPID, leaf7.ecx, 22); + add_feature_if_bit(RDPRU, ext8.ecx, 4); + add_feature_if_bit(RDRND, feat.ecx, 30); + add_feature_if_bit(RDSEED, leaf7.ebx, 18); + add_feature_if_bit(RTM, leaf7.ebx, 11); + add_feature_if_bit(SAHF, ext1.ecx, 0); + add_feature_if_bit(SERIALIZE, leaf7.edx, 14); + add_feature_if_bit(SGX, leaf7.ebx, 2); + add_feature_if_bit(SHA, leaf7.ebx, 29); + add_feature_if_bit(SHA512, leaf7s1.eax, 0); + add_feature_if_bit(SHSTK, leaf7.ecx, 7); + add_feature_if_bit(SM3, leaf7s1.eax, 1); + add_feature_if_bit(SM4, leaf7s1.eax, 2); + add_feature_if_bit(SSE, feat.edx, 25); + add_feature_if_bit(SSE2, feat.edx, 26); + add_feature_if_bit(SSE3, feat.ecx, 0); + add_feature_if_bit(SSE4_1, feat.ecx, 19); + add_feature_if_bit(SSE4_2, feat.ecx, 20); + add_feature_if_bit(SSE4_A, ext1.ecx, 6); + add_feature_if_bit(SSSE3, feat.ecx, 9); + add_feature_if_bit(TBM, ext1.ecx, 21); + add_feature_if_bit(TSXLDTRK, leaf7.edx, 16); + add_feature_if_bit(UINTR, leaf7.edx, 5); + add_feature_if_bit(USERMSR, leaf7s1.edx, 15); + add_feature_if_bit(VAES, leaf7.ecx, 9); + add_feature_if_bit(VPCLMULQDQ, leaf7.ecx, 10); + add_feature_if_bit(WAITPKG, leaf7.ecx, 5); + add_feature_if_bit(WBNOINVD, ext8.ecx, 9); + add_feature_if_bit(WIDEKL, leaf_19.ebx, 2); + add_feature_if_bit(X87, feat.edx, 0); + add_feature_if_bit(XOP, ext1.ecx, 11); + add_feature_if_bit(XSAVE, feat.ecx, 26); + add_feature_if_bit(XSAVEC, leaf_d.eax, 1); + add_feature_if_bit(XSAVEOPT, leaf_d.eax, 0); + add_feature_if_bit(XSAVES, leaf_d.eax, 3); + +} \ No newline at end of file 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/lib/std/io/formatter.c3 b/lib/std/io/formatter.c3 index d1423dcfb..bf4fa358a 100644 --- a/lib/std/io/formatter.c3 +++ b/lib/std/io/formatter.c3 @@ -98,9 +98,9 @@ fn usz! Formatter.print_with_function(&self, Printable* arg) self.width = old_width; self.prec = old_prec; } - @pool() + @stack_mem(1024; Allocator* mem) { - return self.out_substr(arg.to_new_string(allocator::temp())); + return self.out_substr(arg.to_new_string(mem)); }; } return SearchResult.MISSING?; diff --git a/releasenotes.md b/releasenotes.md index 37bf383c0..011ce2390 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -8,9 +8,12 @@ - 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. +- Fixed clobber on x86 `cpuid` instruction. ### Stdlib changes - Deprecated `Allocator` helper functions. @@ -18,6 +21,7 @@ - Changed `mem::new` / `mem::temp_new` to accept an optional initializer, and will clear by default. - Mem `_clear` and `_zero` variants deprecated. "new_*" functions will clear by default. - Mem "alloc_*" functions replace old "new_*" behaviour. +- Fixed temp memory issue with formatter. ## 0.5.3 Change list diff --git a/src/compiler/asm_target.c b/src/compiler/asm_target.c index 9586969fa..db7f52f13 100644 --- a/src/compiler/asm_target.c +++ b/src/compiler/asm_target.c @@ -384,7 +384,7 @@ static void init_asm_x86(void) reg_instr_clob("subw", rax_cc_mask, "rw:r16/mem, r16/mem/imm16"); reg_instr_clob("subl", rax_cc_mask, "rw:r32/mem, r32/mem/imm32"); reg_instr_clob("subq", rax_cc_mask, "rw:r64/mem, r64/mem/immi32/imm64"); - reg_instr("cpuid", NULL); + reg_instr_clob("cpuid", clobbers_make_from(cc_flag_mask, X86_RAX, X86_RBX, X86_RCX, X86_RDX, -1), NULL); reg_instr("hlt", NULL); reg_instr("in", "w:r8/r16/r32, r16/imm8"); // Actually ensure reg_al_ax and dx reg_instr_clob("incb", cc_flag_mask, "rw:r8/mem"); 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