diff --git a/tests/internal/test_packed_field.odin b/tests/internal/test_packed_field.odin index 395f906a9..7daa0ead8 100644 --- a/tests/internal/test_packed_field.odin +++ b/tests/internal/test_packed_field.odin @@ -1,5 +1,6 @@ package test_internal +import "core:simd" import "core:testing" U :: union { int, f64 } @@ -47,3 +48,201 @@ test_packed_field_by_reference :: proc(t: ^testing.T) { ptr^ = 7 testing.expect_value(t, p.u, U(7)) } + + +// small loads and stores (<= 64 byte) through a #packed field; +// access must not claim the field's type alignment (here 16), +// because the field is at align 1 +// 1) through a ptr param (the field GEP carries is-packed metadata) +// 2) directly on the global (the GEP folds to ConstantExpr, no metadata) + + +Packed_Small :: struct #packed { + _: u8, + v: #simd[4]f32, // offset 1 + n: i64, // offset 17 +} + +@(export) +p1: Packed_Small + +@(private="file") +swap_v :: proc(p: ^Packed_Small, x: #simd[4]f32) -> #simd[4]f32 { + y := p.v + p.v = x + return y +} + +@(test) +test_packed_field_pointer_access :: proc(t: ^testing.T) { + p1.v = {1, 2, 3, 4} // store through a constant GEP + + old := #force_no_inline swap_v(&p1, {5, 6, 7, 8}) + testing.expect(t, simd.to_array(old) == [4]f32{1, 2, 3, 4}) + testing.expect(t, simd.to_array(p1.v) == [4]f32{5, 6, 7, 8}) +} + + +// element access to an array field of a #packed + +Packed_Array :: struct #packed { + _: u8, + arr: [2]#simd[4]f32, // elems at offsets 1 and 17 +} + +@(export) +p2: Packed_Array + +@(private="file") +read_elem :: proc(p: ^Packed_Array, i: int) -> #simd[4]f32 { + return p.arr[i] +} + +@(private="file") +write_elem :: proc(p: ^Packed_Array, i: int, x: #simd[4]f32) { + p.arr[i] = x +} + +@(test) +test_packed_field_array_element_access :: proc(t: ^testing.T) { + p2.arr[0] = {1, 2, 3, 4} + p2.arr[1] = {5, 6, 7, 8} + + y := #force_no_inline read_elem(&p2, 0) + testing.expect(t, simd.to_array(y) == [4]f32{1, 2, 3, 4}) + + y = #force_no_inline read_elem(&p2, 1) + testing.expect(t, simd.to_array(y) == [4]f32{5, 6, 7, 8}) + + #force_no_inline write_elem(&p2, 0, {9, 10, 11, 12}) + testing.expect(t, simd.to_array(p2.arr[0]) == [4]f32{9, 10, 11, 12}) +} + + +// accesses derived from a #max_field_align struct's field +// (array element, nested struct field) must not assume more +// than the max_field_align cap + +Capped_Inner :: struct { x: i64 } + +Capped :: struct #max_field_align(4) { + a: u16, + b: u64, // offset 4 + arr: [2]u64, // elems at offs 12 and 20 + inner: Capped_Inner, // x at offs 28 +} + +@(export) +c1: Capped + +@(private="file") +swap_elem :: proc(c: ^Capped, i: int, v: u64) -> u64 { + y := c.arr[i] + c.arr[i] = v + return y +} + +@(private="file") +swap_nested :: proc(c: ^Capped, v: i64) -> i64 { + y := c.inner.x + c.inner.x = v + return y +} + +@(test) +test_max_field_align_derived_access :: proc(t: ^testing.T) { + c1.b = 0xCAFECAFE_11223344 + c1.arr[0] = 1 + c1.arr[1] = 2 + c1.inner.x = -1 + + y := #force_no_inline swap_elem(&c1, 1, 5) + testing.expect(t, y == 2) + testing.expect(t, c1.arr[0] == 1) + testing.expect(t, c1.arr[1] == 5) + + z := #force_no_inline swap_nested(&c1, -2) + testing.expect(t, z == -1) + testing.expect(t, c1.inner.x == -2) + testing.expect(t, c1.b == 0xCAFECAFE_11223344) +} + + +// a #min_field_align inner struct placed in a #packed outer struct; +// the outer's #packed provides only align 1; +// accesses must not assume the #min_field_align + +Raised_Inner :: struct #min_field_align(16) { + v: #simd[4]f32, +} + +Packed_Raised :: struct #packed { + _: u8, + inner: Raised_Inner, // offs 1 +} + +@(export) +p3: Packed_Raised + +@(private="file") +swap_raised :: proc(p: ^Packed_Raised, x: #simd[4]f32) -> #simd[4]f32 { + y := p.inner.v + p.inner.v = x + return y +} + +@(test) +test_packed_field_min_align :: proc(t: ^testing.T) { + p3.inner.v = {1, 2, 3, 4} + + y := #force_no_inline swap_raised(&p3, {5, 6, 7, 8}) + testing.expect(t, simd.to_array(y) == [4]f32{1, 2, 3, 4}) + testing.expect(t, simd.to_array(p3.inner.v) == [4]f32{5, 6, 7, 8}) +} + + +// by-reference type switch on a union field of a #packed struct; +// the bound case variable inherits the field GEP's is-packed metadata; +// accesses through it must not assume the variant type alignment + +Packed_Union :: union { + #simd[4]f32, + u8, +} + +Packed_With_Union :: struct #packed { + _: u8, + u: Packed_Union, // offset 1 +} + +Packed_Union_Helper :: struct { + force: #simd[4]f32, // force align to 16 + _: u8, + p: Packed_With_Union, // offset 17, union at 18 +} + +@(export) +p4: Packed_Union_Helper + +@(private="file") +swap_variant_ref :: proc(p: ^Packed_With_Union, x: #simd[4]f32) -> #simd[4]f32 { + #partial switch &v in p.u { + case #simd[4]f32: + y := v + v = x + return y + } + return {} +} + +@(test) +test_packed_field_union_type_switch_ref :: proc(t: ^testing.T) { + p4.p.u = #simd[4]f32{1, 2, 3, 4} + + y := #force_no_inline swap_variant_ref(&p4.p, {5, 6, 7, 8}) + testing.expect(t, simd.to_array(y) == [4]f32{1, 2, 3, 4}) + + v, ok := p4.p.u.(#simd[4]f32) + testing.expect(t, ok) + testing.expect(t, simd.to_array(v) == [4]f32{5, 6, 7, 8}) +} diff --git a/tests/internal/test_transmute_align.odin b/tests/internal/test_transmute_align.odin new file mode 100644 index 000000000..64fd6737f --- /dev/null +++ b/tests/internal/test_transmute_align.odin @@ -0,0 +1,35 @@ +package test_internal + +import "core:testing" + +@(test) +transmute_array_like_to_simd_and_u128 :: proc(t: ^testing.T) { + Named_Lane :: enum { A, B, C, D } + Item :: struct { + tag: u32, + arr: [4]f32, // offs 4 + e: [Named_Lane]u32, // offs 20 + w: [2]u64, + } + + items := make([]Item, 2) + defer delete(items) + items[0] = {1, {1, 2, 3, 4}, {.A = 5, .B = 6, .C = 7, .D = 8}, {9, 10}} + it := &items[0] + testing.expect_value(t, transmute([4]f32)transmute(#simd[4]f32)it.arr, [4]f32{1, 2, 3, 4}) + testing.expect_value(t, transmute([4]u32)transmute(#simd[4]u32)it.e, [4]u32{5, 6, 7, 8}) + testing.expect_value(t, transmute(u128)it.e, u128(8)<<96 | u128(7)<<64 | u128(6)<<32 | 5) + testing.expect_value(t, transmute(u128)it.w, u128(10)<<64 | 9) + + bytes: [32]u8 + for &b, i in bytes { + b = u8(i) + } + odd := (^[16]u8)(&bytes[1]) + testing.expect_value(t, transmute(u128)odd^, u128(0x10_0f_0e_0d_0c_0b_0a_09_08_07_06_05_04_03_02_01)) + + local := [4]f32{1, 2, 3, 4} + testing.expect_value(t, transmute([4]f32)transmute(#simd[4]f32)local, [4]f32{1, 2, 3, 4}) + local_e := [Named_Lane]u32{.A = 1, .B = 2, .C = 3, .D = 4} + testing.expect_value(t, transmute(u128)local_e, u128(4)<<96 | u128(3)<<64 | u128(2)<<32 | 1) +}