From e901a3de5576c2493dfb8a49745f81b3fcee5fbc Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Sun, 25 Jan 2026 23:05:43 +0100 Subject: [PATCH] - Fix alignment for uint128 to 16 with WASM targets. - Incorrect assert in struct alignment checking #2841 - Packed structs sometimes not lowered as such. --- lib/std/core/env.c3 | 3 +- lib/std/core/mem.c3 | 2 +- releasenotes.md | 3 ++ src/compiler/llvm_codegen_type.c | 1 + src/compiler/sema_decls.c | 4 +-- src/compiler/target.c | 4 +-- .../struct/struct_pack_and_align.c3t | 7 ++-- test/test_suite/struct/struct_pack_error.c3t | 2 +- test/test_suite/struct/struct_packed.c3t | 35 +++++++++++++++++++ 9 files changed, 50 insertions(+), 11 deletions(-) create mode 100644 test/test_suite/struct/struct_packed.c3t diff --git a/lib/std/core/env.c3 b/lib/std/core/env.c3 index 6d25075f9..27c8ab19c 100644 --- a/lib/std/core/env.c3 +++ b/lib/std/core/env.c3 @@ -154,12 +154,13 @@ const bool FREEBSD = LIBC && OS_TYPE == FREEBSD; const bool NETBSD = LIBC && OS_TYPE == NETBSD; const bool BSD_FAMILY = env::FREEBSD || env::OPENBSD || env::NETBSD; const bool WASI = LIBC && OS_TYPE == WASI; +const bool WASM = ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64; const bool ANDROID = LIBC && OS_TYPE == ANDROID; const bool WASM_NOLIBC @builtin @deprecated("Use 'FREESTANDING_WASM' instead") = !LIBC && ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64; const bool FREESTANDING_PE32 = NO_LIBC && OS_TYPE == WIN32; const bool FREESTANDING_MACHO = NO_LIBC && OS_TYPE == MACOS; const bool FREESTANDING_ELF = NO_LIBC && !env::FREESTANDING_PE32 && !env::FREESTANDING_MACHO && !env::FREESTANDING_WASM; -const bool FREESTANDING_WASM = NO_LIBC && (ARCH_TYPE == ArchType.WASM32 || ARCH_TYPE == ArchType.WASM64); +const bool FREESTANDING_WASM = NO_LIBC && WASM; const bool FREESTANDING = env::FREESTANDING_PE32 || env::FREESTANDING_MACHO || env::FREESTANDING_ELF || env::FREESTANDING_WASM; const bool ADDRESS_SANITIZER = $$ADDRESS_SANITIZER; const bool MEMORY_SANITIZER = $$MEMORY_SANITIZER; diff --git a/lib/std/core/mem.c3 b/lib/std/core/mem.c3 index a401f19b6..15ef3bf1e 100644 --- a/lib/std/core/mem.c3 +++ b/lib/std/core/mem.c3 @@ -7,7 +7,7 @@ import std::os::posix, std::os::win32; import std::math; const MAX_MEMORY_ALIGNMENT = 0x1000_0000; -const DEFAULT_MEM_ALIGNMENT = (void*.alignof) * 2; +const DEFAULT_MEM_ALIGNMENT = env::WASM ? 16 : (void*.alignof) * 2; const ulong KB = 1024; const ulong MB = KB * 1024; const ulong GB = MB * 1024; diff --git a/releasenotes.md b/releasenotes.md index b1d8ac374..f953a3955 100644 --- a/releasenotes.md +++ b/releasenotes.md @@ -134,6 +134,9 @@ - Lowering of optional in && was incorrect #2843 - Resolving &X.b when X is a const incorrectly checked for runtime constness #2842 - Alignment param on $$unaligned_* not checked for zero #2844 +- Fix alignment for uint128 to 16 with WASM targets. +- Incorrect assert in struct alignment checking #2841 +- Packed structs sometimes not lowered as such. ### Stdlib changes - Add `ThreadPool` join function to wait for all threads to finish in the pool without destroying the threads. diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index 2e45550ad..be1b0a133 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -49,6 +49,7 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl) vec_add(types, llvm_const_padding_type(c, decl->strukt.padding)); } LLVMStructSetBody(type, types, vec_size(types), decl->is_packed); + ASSERT_SPAN(decl, llvm_abi_size(c, type) == type_size(decl->type)); return type; } case DECL_UNION: diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index aac81fa66..537973b8e 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -679,7 +679,7 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) } if (is_unaligned && size > offset) { - ASSERT(!decl->strukt.padding); + ASSERT(!decl->strukt.padding || decl->strukt.padding == size - offset); decl->strukt.padding = size - offset; } @@ -703,7 +703,7 @@ static bool sema_analyse_struct_members(SemaContext *context, Decl *decl) } } - decl->is_packed = is_unaligned; + decl->is_packed |= is_unaligned; // Strip padding if we are aligned. if (!decl->is_packed && is_naturally_aligned) { diff --git a/src/compiler/target.c b/src/compiler/target.c index a98b6b43e..8b0ad2f98 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -1683,9 +1683,7 @@ static AlignData os_target_alignment_of_int(OsType os, ArchType arch, uint32_t b case ARCH_TYPE_PPC64: case ARCH_TYPE_PPC: case ARCH_TYPE_PPC64LE: - case ARCH_TYPE_WASM64: case ARCH_TYPE_RISCV32: - case ARCH_TYPE_WASM32: case ARCH_TYPE_XTENSA: return (AlignData) { MIN(64u, bits), MIN(64u, bits) }; case ARCH_TYPE_X86_64: @@ -1694,6 +1692,8 @@ static AlignData os_target_alignment_of_int(OsType os, ArchType arch, uint32_t b #else FALLTHROUGH; #endif + case ARCH_TYPE_WASM64: + case ARCH_TYPE_WASM32: case ARCH_TYPE_RISCV64: return (AlignData) { bits, bits }; case ARCH_TYPE_AARCH64: diff --git a/test/test_suite/struct/struct_pack_and_align.c3t b/test/test_suite/struct/struct_pack_and_align.c3t index db00f4275..56fc173ca 100644 --- a/test/test_suite/struct/struct_pack_and_align.c3t +++ b/test/test_suite/struct/struct_pack_and_align.c3t @@ -58,7 +58,7 @@ fn int test5(ichar x) return y.foo + y.bar; } -// { i32, i16, i16 } +// <{ i32, i16, i16 }> struct Foo6 @packed { int a; @@ -76,16 +76,15 @@ Foo6 foo6 = { 1, 2, 3 }; %Foo3 = type <{ i8, i64, [7 x i8] }> %Foo4 = type <{ i8, i64 }> %Foo5 = type { i32, [12 x i8], i8, [15 x i8] } -%Foo6 = type { i32, i16, i16 } +%Foo6 = type <{ i32, i16, i16 }> @struct2.foo1 = local_unnamed_addr global %Foo1 <{ i64 1, i8 2, [3 x i8] undef }>, align 4 @struct2.foo2 = local_unnamed_addr global %Foo2 <{ i8 1, i64 2, [3 x i8] undef }>, align 4 @struct2.foo3 = local_unnamed_addr global %Foo3 <{ i8 1, i64 2, [7 x i8] undef }>, align 8 @struct2.foo4 = local_unnamed_addr global %Foo4 <{ i8 1, i64 2 }>, align 1 @struct2.foo5 = local_unnamed_addr global %Foo5 { i32 1, [12 x i8] undef, i8 2, [15 x i8] undef }, align 16 -@struct2.foo6 = local_unnamed_addr global %Foo6 { i32 1, i16 2, i16 3 }, align 1 +@struct2.foo6 = local_unnamed_addr global %Foo6 <{ i32 1, i16 2, i16 3 }>, align 1 -; Function Attrs: define i32 @struct2.test5(i8 signext %0) #0 { entry: %y = alloca %Foo5, align 16 diff --git a/test/test_suite/struct/struct_pack_error.c3t b/test/test_suite/struct/struct_pack_error.c3t index 233b624fa..a7423e1a8 100644 --- a/test/test_suite/struct/struct_pack_error.c3t +++ b/test/test_suite/struct/struct_pack_error.c3t @@ -36,7 +36,7 @@ fn void main() /* #expect: test.ll %Client = type <{ i32, %MsgHeader, i16, [2 x i8], %"ElasticArray{ulong, 1}" }> -%MsgHeader = type { i64 } +%MsgHeader = type <{ i64 }> %"ElasticArray{ulong, 1}" = type { i64, [1 x i64] } define void @test.main() #0 { diff --git a/test/test_suite/struct/struct_packed.c3t b/test/test_suite/struct/struct_packed.c3t new file mode 100644 index 000000000..9a1f8f9eb --- /dev/null +++ b/test/test_suite/struct/struct_packed.c3t @@ -0,0 +1,35 @@ +// #target: macos-x64 +module test; +struct Foo3 @align(8) +{ + char foo; + Foo6 bar; +} +struct Foo6 @packed +{ + short c; +} + +fn int main() +{ + Foo3 x; + return 0; +} + +/* #expect: test.ll + +%Foo3 = type <{ i8, %Foo6, [5 x i8] }> +%Foo6 = type <{ i16 }> + +@"$ct.test.Foo3" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 8, i64 0, i64 2, [0 x i64] zeroinitializer }, align 8 +@"$ct.test.Foo6" = linkonce global %.introspect { i8 10, i64 0, ptr null, i64 2, i64 0, i64 1, [0 x i64] zeroinitializer }, align 8 + +define i32 @main() #0 { +entry: + %x = alloca %Foo3, align 8 + store i8 0, ptr %x, align 8 + %ptradd = getelementptr inbounds i8, ptr %x, i64 1 + store i16 0, ptr %ptradd, align 1 + ret i32 0 +} +