From c1aeb47ce5f20904a8bfd6be4381744f0c9e1e94 Mon Sep 17 00:00:00 2001 From: kalsprite Date: Tue, 25 Aug 2026 22:22:33 -0700 Subject: [PATCH] swizzle target multi-assignment --- src/llvm_backend_utility.cpp | 2 +- tests/issues/run.bat | 1 + tests/issues/run.sh | 1 + .../test_issue_swizzle_multi_assign.odin | 80 +++++++++++++++++++ 4 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/issues/test_issue_swizzle_multi_assign.odin diff --git a/src/llvm_backend_utility.cpp b/src/llvm_backend_utility.cpp index 947f7d259..430677aa3 100644 --- a/src/llvm_backend_utility.cpp +++ b/src/llvm_backend_utility.cpp @@ -994,7 +994,7 @@ gb_internal lbAddr lb_find_or_generate_context_ptr(lbProcedure *p) { } gb_internal lbValue lb_address_from_load_or_generate_local(lbProcedure *p, lbValue value) { - if (LLVMIsALoadInst(value.value)) { + if (!p->in_multi_assignment && LLVMIsALoadInst(value.value)) { lbValue res = {}; res.value = LLVMGetOperand(value.value, 0); res.type = alloc_type_pointer(value.type); diff --git a/tests/issues/run.bat b/tests/issues/run.bat index f211e153b..ebdc56495 100644 --- a/tests/issues/run.bat +++ b/tests/issues/run.bat @@ -52,6 +52,7 @@ set COMMON=-define:ODIN_TEST_FANCY=false -file -vet -strict-style -ignore-unused clang -c ..\test_issue_sysv_abi.c -o test_issue_sysv_abi_c.o || exit /b ..\..\..\odin test ..\test_issue_sysv_abi.odin %COMMON% || exit /b ..\..\..\odin build ..\test_issue_7073-1.odin %COMMON% 2>&1 | find /c "Error:" | findstr /x "2" || exit /b +..\..\..\odin test ..\test_issue_swizzle_multi_assign.odin %COMMON% || exit /b @echo off diff --git a/tests/issues/run.sh b/tests/issues/run.sh index 16133d72d..4e442ceaa 100755 --- a/tests/issues/run.sh +++ b/tests/issues/run.sh @@ -97,6 +97,7 @@ $ODIN build ../test_issue_7167.odin $COMMON $ODIN build ../test_issue_7188.odin $COMMON $ODIN check ../test_issue_7260.odin -no-entry-point $COMMON_CHECK $ODIN test ../test_issue_bool_to_be_conversion.odin $COMMON +$ODIN test ../test_issue_swizzle_multi_assign.odin $COMMON $ODIN check ../test_issue_foreign_redeclaration.odin -no-entry-point $COMMON_CHECK if [[ $($ODIN check ../test_issue_foreign_redeclaration_mismatch.odin -no-entry-point $COMMON_CHECK 2>&1 >/dev/null | grep -c "Error:") -eq 1 ]]; then diff --git a/tests/issues/test_issue_swizzle_multi_assign.odin b/tests/issues/test_issue_swizzle_multi_assign.odin new file mode 100644 index 000000000..f16442c6e --- /dev/null +++ b/tests/issues/test_issue_swizzle_multi_assign.odin @@ -0,0 +1,80 @@ +package test_issues + +import "core:testing" + +// A multi-assignment reads every right-hand side before it stores into any target, which is what +// makes `a, b = b, a` a swap. Storing into a swizzle reused the address the source was loaded from +// instead of the value read at that point, so a source that named a target written earlier in the +// same statement read back the new contents. + +@(test) +swizzle_multi_assign_swap :: proc(t: ^testing.T) { + v := [4]f32{1, 2, 3, 4} + v.xy, v.zw = v.zw, v.xy + testing.expect_value(t, v, [4]f32{3, 4, 1, 2}) + + // the other order happened to be correct already + w := [4]f32{1, 2, 3, 4} + w.zw, w.xy = w.xy, w.zw + testing.expect_value(t, w, [4]f32{3, 4, 1, 2}) + + x := [4]f32{1, 2, 3, 4} + x.xy, x.wz = x.wz, x.xy + testing.expect_value(t, x, [4]f32{4, 3, 2, 1}) +} + +@(test) +swizzle_multi_assign_element_types :: proc(t: ^testing.T) { + a := [4]i32{1, 2, 3, 4} + a.xy, a.zw = a.zw, a.xy + testing.expect_value(t, a, [4]i32{3, 4, 1, 2}) + + b := [4]f64{1, 2, 3, 4} + b.xy, b.zw = b.zw, b.xy + testing.expect_value(t, b, [4]f64{3, 4, 1, 2}) + + c := [4]u8{1, 2, 3, 4} + c.xy, c.zw = c.zw, c.xy + testing.expect_value(t, c, [4]u8{3, 4, 1, 2}) +} + +@(test) +swizzle_multi_assign_through_pointer :: proc(t: ^testing.T) { + v := [4]f32{1, 2, 3, 4} + p := &v + p.xy, p.zw = p.zw, p.xy + testing.expect_value(t, v, [4]f32{3, 4, 1, 2}) +} + +@(test) +swizzle_multi_assign_across_variables :: proc(t: ^testing.T) { + // the source names a different variable, which an earlier store still writes to + a := [4]f32{1, 2, 3, 4} + b := [4]f32{5, 6, 7, 8} + a.xy, b.xy = b.xy, a.xy + testing.expect_value(t, a, [4]f32{5, 6, 3, 4}) + testing.expect_value(t, b, [4]f32{1, 2, 7, 8}) +} + +@(test) +swizzle_multi_assign_three_targets :: proc(t: ^testing.T) { + v := [4]f32{1, 2, 3, 4} + v.xy, v.zw, v.x = v.zw, v.xy, v.w + testing.expect_value(t, v, [4]f32{4, 4, 1, 2}) +} + +@(test) +swizzle_single_assign_unchanged :: proc(t: ^testing.T) { + // one target still reads every lane before it writes any of them + v := [4]f32{1, 2, 3, 4} + v.yzw = v.xyz + testing.expect_value(t, v, [4]f32{1, 1, 2, 3}) + + w := [4]f32{1, 2, 3, 4} + w.xy = w.zw + testing.expect_value(t, w, [4]f32{3, 4, 3, 4}) + + x := [4]f32{1, 2, 3, 4} + x.x, x.y = x.y, x.x + testing.expect_value(t, x, [4]f32{2, 1, 3, 4}) +}