diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index e04839b9b..12a220ca4 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -393,42 +393,69 @@ macro void @stack_pool(usz $size; @body) @builtin }; } -macro void @pool(;@body) @builtin +macro void @pool(...; @body) @builtin { - TempAllocator* allocator = temp(); - usz mark = allocator.used; - defer allocator.reset(mark); + bool $has_arg = $vacount == 1; + assert($vacount <= 1); + TempAllocator* current = temp(); + $if $has_arg: + TempAllocator* original = current; + if (current == $vaarg(0)) current = temp_allocator_next(); + $endif + usz mark = current.used; + defer + { + current.reset(mark); + $if $has_arg: + thread_temp_allocator = original; + $endif; + } @body(); } -macro void @allocating_pool(Allocator* using; @body(bool is_temp)) @builtin -{ - TempAllocator* allocator = temp(); - usz mark = allocator.used; - bool is_temp = allocator == using; - defer if (!is_temp) allocator.reset(mark); - @body(is_temp); -} - tlocal Allocator* thread_allocator @private = allocator::LIBC_ALLOCATOR; tlocal TempAllocator* thread_temp_allocator @private = null; +tlocal TempAllocator*[2] temp_allocator_pair @private; -macro TempAllocator* temp_allocator() => temp(); +macro TempAllocator* create_default_sized_temp_allocator() @local +{ + $switch (env::MEMORY_ENV) + $case NORMAL: + return allocator::new_temp(1024 * 256, thread_allocator)!!; + $case SMALL: + return allocator::new_temp(1024 * 16, thread_allocator)!!; + $case TINY: + return allocator::new_temp(1024 * 2, thread_allocator)!!; + $case NONE: + unreachable("Temp allocator must explicitly created when memory-env is set to 'none'."); + $endswitch +} + +fn TempAllocator *temp_allocator_next() @private +{ + if (!thread_temp_allocator) + { + init_default_temp_allocators(); + return thread_temp_allocator; + } + usz index = thread_temp_allocator == temp_allocator_pair[0] ? 1 : 0; + return thread_temp_allocator = temp_allocator_pair[index]; +} + +import libc; + +fn void init_default_temp_allocators() @private +{ + temp_allocator_pair[0] = create_default_sized_temp_allocator(); + temp_allocator_pair[1] = create_default_sized_temp_allocator(); + thread_temp_allocator = temp_allocator_pair[0]; +} macro TempAllocator* temp() { if (!thread_temp_allocator) { - $switch (env::MEMORY_ENV) - $case NORMAL: - thread_temp_allocator = allocator::new_temp(1024 * 256, thread_allocator)!!; - $case SMALL: - thread_temp_allocator = allocator::new_temp(1024 * 16, thread_allocator)!!; - $case TINY: - thread_temp_allocator = allocator::new_temp(1024 * 2, thread_allocator)!!; - $case NONE: - unreachable("Temp allocator must explicitly created when memory-env is set to 'none'."); - $endswitch + init_default_temp_allocators(); } return thread_temp_allocator; } diff --git a/lib/std/core/string.c3 b/lib/std/core/string.c3 index 4e039ea47..4b06e99d4 100644 --- a/lib/std/core/string.c3 +++ b/lib/std/core/string.c3 @@ -33,10 +33,10 @@ fault NumberConversion macro String printf(String fmt, ..., Allocator* using = mem::heap()) { - @stack_mem(256; Allocator* mem) + @pool(using) { DString str; - str.init(.using = mem); + str.tinit(); str.printf(fmt, $vasplat()); return str.copy_str(using); }; diff --git a/lib/std/io/path.c3 b/lib/std/io/path.c3 index 6f7ac8164..e082e67df 100644 --- a/lib/std/io/path.c3 +++ b/lib/std/io/path.c3 @@ -146,9 +146,9 @@ fn Path! Path.append(self, String filename, Allocator* using = mem::heap()) if (!self.path_string.len) return new(filename, using, self.env)!; assert(!is_separator(self.path_string[^1], self.env)); - @stack_mem(256; Allocator* mem) + @pool(using) { - DString dstr = dstring::new_with_capacity(self.path_string.len + 1 + filename.len, .using = mem); + DString dstr = dstring::tnew_with_capacity(self.path_string.len + 1 + filename.len); dstr.append(self.path_string); dstr.append(PREFERRED_SEPARATOR); dstr.append(filename); diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3835ed3e6..4c821775d 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -1477,15 +1477,14 @@ AlignSize llvm_abi_alignment(GenContext *c, LLVMTypeRef type) -void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len) +LLVMValueRef llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len) { assert(dest_align && src_align); if (len <= UINT32_MAX) { - LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_uint, len)); - return; + return LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_uint, len)); } - LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_ulong, len)); + return LLVMBuildMemCpy(c->builder, dest, dest_align, source, src_align, llvm_const_int(c, type_ulong, len)); } void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment) diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index 0208bfb25..0145406b5 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -1162,7 +1162,7 @@ static inline void llvm_emit_access_addr(GenContext *c, BEValue *be_value, Expr if (flat_type->type_kind == TYPE_ENUM) { llvm_value_rvalue(c, be_value); - if (!member->backend_ref) llvm_get_typeid(c, parent->type); + if (!flat_type->decl->backend_ref) llvm_get_typeid(c, parent->type); assert(member->backend_ref); LLVMTypeRef value_type = llvm_get_type(c, type_get_array(member->type, vec_size(flat_type->decl->enums.values))); AlignSize align = LLVMGetAlignment(member->backend_ref); diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 367524b0d..d4a790be9 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -445,7 +445,7 @@ INLINE LLVMValueRef llvm_emit_or_raw(GenContext *c, LLVMValueRef lhs, LLVMValueR // -- Mem ops -- LLVMValueRef llvm_emit_memclear_size_align(GenContext *c, LLVMValueRef ptr, uint64_t size, AlignSize align); -void llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len); +LLVMValueRef llvm_emit_memcpy(GenContext *c, LLVMValueRef dest, unsigned dest_align, LLVMValueRef source, unsigned src_align, uint64_t len); void llvm_emit_memcpy_to_decl(GenContext *c, Decl *decl, LLVMValueRef source, unsigned source_alignment); // -- ABI -- diff --git a/src/compiler/llvm_codegen_storeload.c b/src/compiler/llvm_codegen_storeload.c index a9ab56cad..51cc60cad 100644 --- a/src/compiler/llvm_codegen_storeload.c +++ b/src/compiler/llvm_codegen_storeload.c @@ -43,18 +43,7 @@ LLVMValueRef llvm_store_to_ptr_aligned(GenContext *c, LLVMValueRef destination, case BE_ADDRESS_OPTIONAL: UNREACHABLE case BE_ADDRESS: - { - // Here we do an optimized(?) memcopy. - ByteSize size = type_size(value->type); - LLVMValueRef copy_size = llvm_const_int(c, size <= UINT32_MAX ? type_uint : type_usz, size); - LLVMValueRef copy = LLVMBuildMemCpy(c->builder, - destination, - alignment, - value->value, - value->alignment ? value->alignment : type_abi_alignment(value->type), - copy_size); - return copy; - } + return llvm_emit_memcpy(c, destination, alignment, value->value, value->alignment ? value->alignment : type_abi_alignment(value->type), type_size(value->type)); } UNREACHABLE } diff --git a/src/version.h b/src/version.h index 439ce647b..ae8d77a77 100644 --- a/src/version.h +++ b/src/version.h @@ -1 +1 @@ -#define COMPILER_VERSION "0.4.571" \ No newline at end of file +#define COMPILER_VERSION "0.4.572" \ No newline at end of file diff --git a/test/test_suite/stdlib/map.c3t b/test/test_suite/stdlib/map.c3t index 1f89af543..495bf3267 100644 --- a/test/test_suite/stdlib/map.c3t +++ b/test/test_suite/stdlib/map.c3t @@ -79,7 +79,6 @@ entry: %14 = load { ptr, i64 }, ptr %result, align 8 ret { ptr, i64 } %14 } - ; Function Attrs: nounwind define void @test.main() #0 { entry: @@ -118,18 +117,14 @@ entry: %retparam44 = alloca i64, align 8 %varargslots45 = alloca [1 x %any], align 16 %result46 = alloca %"double[]", align 8 - %allocator = alloca ptr, align 8 - %error_var = alloca i64, align 8 - %retparam49 = alloca ptr, align 8 - %varargslots52 = alloca [1 x %any], align 16 - %indirectarg = alloca %"any[]", align 8 + %current = alloca ptr, align 8 %mark = alloca i64, align 8 %map3 = alloca %HashMap.0, align 8 - %retparam53 = alloca i64, align 8 - %varargslots54 = alloca [1 x %any], align 16 - %result55 = alloca %"int[]", align 8 - %mark58 = alloca i64, align 8 - %retparam59 = alloca ptr, align 8 + %retparam49 = alloca i64, align 8 + %varargslots50 = alloca [1 x %any], align 16 + %result51 = alloca %"int[]", align 8 + %mark54 = alloca i64, align 8 + %retparam55 = alloca ptr, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map, i8 0, i64 40, i1 false) %0 = load ptr, ptr @std.core.mem.thread_allocator, align 8 call void @"std.collections.map$int$test.Foo$.HashMap.init"(ptr %map, i32 16, float 7.500000e-01, ptr %0) @@ -173,7 +168,6 @@ entry: %not_err = icmp eq i64 %26, 0 %27 = call i1 @llvm.expect.i1(i1 %not_err, i1 true) br i1 %27, label %after_check, label %after_check12 - after_check: ; preds = %entry %28 = getelementptr inbounds %Foo, ptr %retparam10, i32 0, i32 0 %29 = insertvalue %any undef, ptr %28, 0 @@ -184,7 +178,6 @@ after_check: ; preds = %entry %not_err11 = icmp eq i64 %32, 0 %33 = call i1 @llvm.expect.i1(i1 %not_err11, i1 true) br i1 %33, label %after_check12, label %after_check12 - after_check12: ; preds = %entry, %after_check, %after_check %34 = call i8 @"std.collections.map$int$test.Foo$.HashMap.has_key"(ptr %map, i32 1) store i8 %34, ptr %taddr, align 1 @@ -255,65 +248,37 @@ after_check12: ; preds = %entry, %after_check %80 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 %not = icmp eq ptr %80, null br i1 %not, label %if.then, label %if.exit - if.then: ; preds = %after_check12 - %81 = load ptr, ptr @std.core.mem.thread_allocator, align 8 - %82 = call i64 @std.core.mem.allocator.new_temp(ptr %retparam49, i64 262144, ptr %81) - %not_err50 = icmp eq i64 %82, 0 - %83 = call i1 @llvm.expect.i1(i1 %not_err50, i1 true) - br i1 %83, label %after_check51, label %assign_optional - -assign_optional: ; preds = %if.then - store i64 %82, ptr %error_var, align 8 - br label %panic_block - -after_check51: ; preds = %if.then - %84 = load ptr, ptr %retparam49, align 8 - br label %noerr_block - -panic_block: ; preds = %assign_optional - %85 = insertvalue %any undef, ptr %error_var, 0 - %86 = insertvalue %any %85, i64 ptrtoint (ptr @"$ct.anyfault" to i64), 1 - %87 = getelementptr inbounds [1 x %any], ptr %varargslots52, i64 0, i64 0 - store %any %86, ptr %87, align 16 - %88 = insertvalue %"any[]" undef, ptr %varargslots52, 0 - %89 = insertvalue %"any[]" %88, i64 1, 1 - store %"any[]" %89, ptr %indirectarg, align 8 - call void @std.core.builtin.panicf(ptr @.panic_msg, i64 36, ptr @.file, i64 6, ptr @.func, i64 4, i32 424, ptr byval(%"any[]") align 8 %indirectarg) - unreachable - -noerr_block: ; preds = %after_check51 - store ptr %84, ptr @std.core.mem.thread_temp_allocator, align 8 + call void @std.core.mem.init_default_temp_allocators() br label %if.exit - -if.exit: ; preds = %noerr_block, %after_check12 - %90 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 - store ptr %90, ptr %allocator, align 8 - %91 = load ptr, ptr %allocator, align 8 - %92 = getelementptr inbounds %TempAllocator, ptr %91, i32 0, i32 3 - %93 = load i64, ptr %92, align 8 - store i64 %93, ptr %mark, align 8 +if.exit: ; preds = %if.then, %after_check12 + %81 = load ptr, ptr @std.core.mem.thread_temp_allocator, align 8 + store ptr %81, ptr %current, align 8 + %82 = load ptr, ptr %current, align 8 + %83 = getelementptr inbounds %TempAllocator, ptr %82, i32 0, i32 3 + %84 = load i64, ptr %83, align 8 + store i64 %84, ptr %mark, align 8 call void @llvm.memset.p0.i64(ptr align 8 %map3, i8 0, i64 40, i1 false) - %94 = load ptr, ptr @std.core.mem.thread_allocator, align 8 - call void @"std.collections.map$int$double$.HashMap.init"(ptr %map3, i32 16, float 7.500000e-01, ptr %94) - %95 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00) - %96 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) - %97 = load ptr, ptr @std.core.mem.thread_allocator, align 8 - %98 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, ptr %97) - store { ptr, i64 } %98, ptr %result55, align 8 - %99 = insertvalue %any undef, ptr %result55, 0 - %100 = insertvalue %any %99, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 - %101 = getelementptr inbounds [1 x %any], ptr %varargslots54, i64 0, i64 0 - store %any %100, ptr %101, align 16 - %102 = call i64 @std.io.printfn(ptr %retparam53, ptr @.str.11, i64 2, ptr %varargslots54, i64 1) - %103 = load ptr, ptr %allocator, align 8 - %104 = getelementptr inbounds %TempAllocator, ptr %103, i32 0, i32 0 - %105 = load i64, ptr %mark, align 8 - store i64 %105, ptr %mark58, align 8 - %106 = getelementptr inbounds %Allocator, ptr %104, i32 0, i32 0 - %107 = load ptr, ptr %106, align 8 - %108 = load i64, ptr %mark58, align 8 - %109 = call i64 %107(ptr %retparam59, ptr %104, i64 %108, i64 0, i64 0, ptr null, i32 8) + %85 = load ptr, ptr @std.core.mem.thread_allocator, align 8 + call void @"std.collections.map$int$double$.HashMap.init"(ptr %map3, i32 16, float 7.500000e-01, ptr %85) + %86 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 5, double 3.200000e+00) + %87 = call i8 @"std.collections.map$int$double$.HashMap.set"(ptr %map3, i32 7, double 5.200000e+00) + %88 = load ptr, ptr @std.core.mem.thread_allocator, align 8 + %89 = call { ptr, i64 } @"std.collections.map$int$double$.HashMap.key_list"(ptr %map3, ptr %88) + store { ptr, i64 } %89, ptr %result51, align 8 + %90 = insertvalue %any undef, ptr %result51, 0 + %91 = insertvalue %any %90, i64 ptrtoint (ptr @"$ct.sa$int" to i64), 1 + %92 = getelementptr inbounds [1 x %any], ptr %varargslots50, i64 0, i64 0 + store %any %91, ptr %92, align 16 + %93 = call i64 @std.io.printfn(ptr %retparam49, ptr @.str.11, i64 2, ptr %varargslots50, i64 1) + %94 = load ptr, ptr %current, align 8 + %95 = getelementptr inbounds %TempAllocator, ptr %94, i32 0, i32 0 + %96 = load i64, ptr %mark, align 8 + store i64 %96, ptr %mark54, align 8 + %97 = getelementptr inbounds %Allocator, ptr %95, i32 0, i32 0 + %98 = load ptr, ptr %97, align 8 + %99 = load i64, ptr %mark54, align 8 + %100 = call i64 %98(ptr %retparam55, ptr %95, i64 %99, i64 0, i64 0, ptr null, i32 8) ret void } define internal void @.static_initialize.0() { diff --git a/test/unit/stdlib/mem/temp_mem.c3 b/test/unit/stdlib/mem/temp_mem.c3 new file mode 100644 index 000000000..8257ed81b --- /dev/null +++ b/test/unit/stdlib/mem/temp_mem.c3 @@ -0,0 +1,68 @@ +module test; + +fn String add(String s, Allocator* a, int x) +{ + if (x < 0) return s.copy(a); + String tmp; + @pool(a) + { + tmp = "foo".tconcat(s); + tmp = add(tmp, a, x - 1); + }; + ulong* y = malloc(ulong, .using = mem::temp()); + *y = 0xAAAA_AAAA_AAAA_AAAA; + return tmp.concat("a", .using = a); +} + +fn String breakit(String s, Allocator* a) +{ + @pool(a) + { + return inner2("foo".concat(s, mem::temp()), a); + }; +} + +fn String inner2(String s, Allocator* a) +{ + @pool(a) + { + ulong* z1 = tmalloc(ulong); + *z1 = 0xAAAA_AAAA_AAAA_AAAA; + String y = inner3(s, a); + ulong* z = tmalloc(ulong); + *z = 0xAAAA_AAAA_AAAA_AAAA; + return y; + }; +} + +fn String inner3(String s, Allocator* a) +{ + @pool(a) + { + ulong* z1 = tmalloc(ulong); + *z1 = 0xAAAA_AAAA_AAAA_AAAA; + String y = inner4(s, a); + ulong* z = tmalloc(ulong); + *z = 0xAAAA_AAAA_AAAA_AAAA; + return y; + }; +} + +fn String inner4(String s, Allocator* a) +{ + @pool(a) + { + String y = s.concat("xy**********", mem::temp()).copy(a); + return y; + }; +} + +fn void! test_temp_allocator() @test +{ + assert("foofoofoofoofoofooabcaaaaaa" == add("abc", mem::temp(), 5), "was %s", add("abc", mem::temp(), 5)); +} + +fn void! test_temp_allocator2() @test +{ + assert("fooxyz0123456789xy**********" == breakit("xyz0123456789", mem::temp())); +}