From 4961d0433fb0a9d86847e7e801bf0a561af3daa7 Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 12 Feb 2025 12:50:30 +0100 Subject: [PATCH] - Circumvent Aarch64 miscompilations of atomics. - Fixes to ByteBuffer allocation/free. - Fix issue where compiling both for asm and object file would corrupt the obj file output. --- .github/workflows/main.yml | 13 ++--- lib/std/io/stream/bytebuffer.c3 | 16 +++--- releasenotes.md | 3 ++ src/compiler/llvm_codegen.c | 82 ++++++++++++++++--------------- src/compiler/target.c | 17 +++++-- test/unit/stdlib/io/bytebuffer.c3 | 2 +- test/unit/stdlib/io/varint.c3 | 2 +- 7 files changed, 74 insertions(+), 61 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 91e30d6c7..0569f5210 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -87,15 +87,16 @@ jobs: ..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_snake.c3 ..\build\${{ matrix.build_type }}\c3c.exe compile --lib raylib5 --print-linking examples\raylib\raylib_tetris.c3 + - name: Compile run unit tests + run: | + cd test + ..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 + - name: run compiler tests run: | cd test python3.exe src/tester.py ..\build\${{ matrix.build_type }}\c3c.exe test_suite/ - - name: Compile run unit tests - run: | - cd test - ..\build\${{ matrix.build_type }}\c3c.exe compile-test unit -O1 - name: Test python script run: | @@ -486,7 +487,7 @@ jobs: - name: Compile run unit tests run: | cd test - ../build/c3c compile-test unit + ../build/c3c compile-test unit --sanitize=address - name: Build testproject run: | @@ -660,7 +661,7 @@ jobs: - name: Compile run unit tests run: | cd test - ../build/c3c compile-test unit + ../build/c3c compile-test unit -O1 - name: Test WASM run: | diff --git a/lib/std/io/stream/bytebuffer.c3 b/lib/std/io/stream/bytebuffer.c3 index a5f64b19c..46e1512e2 100644 --- a/lib/std/io/stream/bytebuffer.c3 +++ b/lib/std/io/stream/bytebuffer.c3 @@ -16,15 +16,15 @@ struct ByteBuffer (InStream, OutStream) max_read defines how many bytes might be kept before its internal buffer is shrinked. @require self.bytes.len == 0 "Buffer already initialized." *> -fn ByteBuffer*! ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap()) +fn ByteBuffer* ByteBuffer.new_init(&self, usz max_read, usz initial_capacity = 16, Allocator allocator = allocator::heap()) { *self = { .allocator = allocator, .max_read = max_read }; initial_capacity = max(initial_capacity, 16); - self.grow(initial_capacity)!; + self.grow(initial_capacity); return self; } -fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16) +fn ByteBuffer* ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = 16) { return self.new_init(max_read, initial_capacity, allocator::temp()); } @@ -33,7 +33,7 @@ fn ByteBuffer*! ByteBuffer.temp_init(&self, usz max_read, usz initial_capacity = @require buf.len > 0 @require self.bytes.len == 0 "Buffer already initialized." *> -fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf) +fn ByteBuffer* ByteBuffer.init_with_buffer(&self, char[] buf) { *self = { .max_read = buf.len, .bytes = buf }; return self; @@ -48,7 +48,7 @@ fn void ByteBuffer.free(&self) fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic { usz cap = self.bytes.len - self.write_idx; - if (cap < bytes.len) self.grow(bytes.len)!; + if (cap < bytes.len) self.grow(bytes.len); self.bytes[self.write_idx:bytes.len] = bytes[..]; self.write_idx += bytes.len; return bytes.len; @@ -57,7 +57,7 @@ fn usz! ByteBuffer.write(&self, char[] bytes) @dynamic fn void! ByteBuffer.write_byte(&self, char c) @dynamic { usz cap = self.bytes.len - self.write_idx; - if (cap == 0) self.grow(1)!; + if (cap == 0) self.grow(1); self.bytes[self.write_idx] = c; self.write_idx++; } @@ -128,10 +128,10 @@ fn usz! ByteBuffer.available(&self) @inline @dynamic return self.write_idx - self.read_idx; } -fn void! ByteBuffer.grow(&self, usz n) +fn void ByteBuffer.grow(&self, usz n) { n = math::next_power_of_2(n); - char* p = allocator::realloc_aligned(self.allocator, self.bytes, n, alignment: char.alignof)!; + char* p = allocator::realloc(self.allocator, self.bytes, n); self.bytes = p[:n]; } diff --git a/releasenotes.md b/releasenotes.md index 08569fba5..a1e070a55 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -63,6 +63,9 @@ - Fix issue where aligned bitstructs did not store/load with the given alignment. - Fix issue in GrowableBitSet with sanitizers. - Fix issue in List with sanitizers. +- Circumvent Aarch64 miscompilations of atomics. +- Fixes to ByteBuffer allocation/free. +- Fix issue where compiling both for asm and object file would corrupt the obj file output. ### Stdlib changes - Added '%h' and '%H' for printing out binary data in hexadecimal using the formatter. diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index 3e7d49528..5038b8a75 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -664,47 +664,48 @@ static void gencontext_verify_ir(GenContext *context) } } - -static void gencontext_emit_object_file(GenContext *context) +static void llvm_emit_file(GenContext *c, const char *filename, LLVMCodeGenFileType llvm_codegen_type, bool clone_module) { - char *err = ""; DEBUG_LOG("Target: %s", compiler.platform.target_triple); - LLVMSetTarget(context->module, compiler.platform.target_triple); - char *layout = LLVMCopyStringRepOfTargetData(context->target_data); - LLVMSetDataLayout(context->module, layout); + LLVMModuleRef module = clone_module ? LLVMCloneModule(c->module) : c->module; + LLVMSetTarget(module, compiler.platform.target_triple); + char *layout = LLVMCopyStringRepOfTargetData(c->target_data); + LLVMSetDataLayout(module, layout); LLVMDisposeMessage(layout); - if (context->asm_filename) + char *err = ""; + FILE *file = NULL; + LLVMMemoryBufferRef buffer = NULL; + if (LLVMTargetMachineEmitToMemoryBuffer(c->machine, module, llvm_codegen_type, &err, &buffer)) { - // Generate .s file - if (LLVMTargetMachineEmitToFile(context->machine, context->module, (char *)context->asm_filename, LLVMAssemblyFile, &err)) + goto ERR; + } + file = fopen(filename, "wb"); + if (!file) + { + err = "File could not be opened"; + goto ERR; + } + size_t len = LLVMGetBufferSize(buffer); + const char *ptr = LLVMGetBufferStart(buffer); + while (len > 0) + { + size_t written = fwrite(ptr, 1, len, file); + if (written == 0) { - error_exit("Could not emit asm file: %s", err); + err = "Failed to write to file"; + goto ERR; } + ptr += written; + len -= written; } - - // Generate .o or .obj file - if (LLVMTargetMachineEmitToFile(context->machine, context->module, (char *)context->object_filename, LLVMObjectFile, &err)) - { - error_exit("Could not emit object file: %s", err); - } - -} - -static void llvm_emit_asm_file(GenContext *context) -{ - char *err = ""; - DEBUG_LOG("Target: %s", compiler.platform.target_triple); - LLVMSetTarget(context->module, compiler.platform.target_triple); - char *layout = LLVMCopyStringRepOfTargetData(context->target_data); - LLVMSetDataLayout(context->module, layout); - LLVMDisposeMessage(layout); - - // Generate .s file - if (LLVMTargetMachineEmitToFile(context->machine, context->module, (char *)context->asm_filename, LLVMAssemblyFile, &err)) - { - error_exit("Could not emit asm file: %s", err); - } + fclose(file); + LLVMDisposeMemoryBuffer(buffer); + return; +ERR: + if (file) fclose(file); + if (buffer) LLVMDisposeMemoryBuffer(buffer); + error_exit("Could not emit '%s': %s", filename, err); } void gencontext_print_llvm_ir(GenContext *context) @@ -1067,15 +1068,16 @@ const char *llvm_codegen(void *context) } const char *object_name = NULL; - if (compiler.build.emit_object_files) - { - gencontext_emit_object_file(c); - object_name = c->object_filename; - } - if (compiler.build.emit_asm) { - llvm_emit_asm_file(context); + // Clone if there will be object file output. + llvm_emit_file(c, c->asm_filename, LLVMAssemblyFile, compiler.build.emit_object_files); + } + + if (compiler.build.emit_object_files) + { + llvm_emit_file(c, c->object_filename, LLVMObjectFile, false); + object_name = c->object_filename; } gencontext_end_module(c); diff --git a/src/compiler/target.c b/src/compiler/target.c index 34a9945fe..0546c0128 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -159,6 +159,16 @@ static bool os_target_signed_c_char_type(OsType os, ArchType arch) } } +static inline void target_setup_aarch64_abi(void) +{ + compiler.platform.aarch.is_darwin = os_is_apple(compiler.platform.os); + compiler.platform.aarch.is_win32 = compiler.platform.os == OS_TYPE_WIN32; + compiler.platform.abi = ABI_AARCH64; + // TODO improve Aarch64 functionality support. + scratch_buffer_clear(); + scratch_buffer_append("+crc,+lse,+rdm,+fp-armv8,+neon"); + compiler.platform.features = scratch_buffer_copy(); +} static inline void target_setup_arm_abi(void) { compiler.platform.abi = ABI_ARM; @@ -836,8 +846,7 @@ static void x86_features_from_host(X86Features *cpu_features) #if LLVM_AVAILABLE char *features = LLVMGetHostCPUFeatures(); INFO_LOG("Detected the following host features: %s", features); - INFO_LOG("For %s", - LLVMGetHostCPUName()); + INFO_LOG("For %s", LLVMGetHostCPUName()); char *tok = strtok(features, ","); *cpu_features = x86_feature_zero; @@ -1994,9 +2003,7 @@ void target_setup(BuildTarget *target) break; case ARCH_TYPE_AARCH64: case ARCH_TYPE_AARCH64_BE: - compiler.platform.aarch.is_darwin = os_is_apple(compiler.platform.os); - compiler.platform.aarch.is_win32 = compiler.platform.os == OS_TYPE_WIN32; - compiler.platform.abi = ABI_AARCH64; + target_setup_aarch64_abi(); break; case ARCH_TYPE_XTENSA: compiler.platform.abi = ABI_XTENSA; diff --git a/test/unit/stdlib/io/bytebuffer.c3 b/test/unit/stdlib/io/bytebuffer.c3 index 0f8abe2ea..eafbe9ec9 100644 --- a/test/unit/stdlib/io/bytebuffer.c3 +++ b/test/unit/stdlib/io/bytebuffer.c3 @@ -4,7 +4,7 @@ import std::io; fn void write_read() { ByteBuffer buffer; - buffer.new_init(0)!!; + buffer.new_init(0); defer buffer.free(); buffer.write("hello")!!; diff --git a/test/unit/stdlib/io/varint.c3 b/test/unit/stdlib/io/varint.c3 index 1dcb7ffa4..391f04b12 100644 --- a/test/unit/stdlib/io/varint.c3 +++ b/test/unit/stdlib/io/varint.c3 @@ -4,7 +4,7 @@ import std::io; fn void write_read() { ByteBuffer buf; - buf.temp_init(16)!!; + buf.temp_init(16); usz n; uint x; uint y;