authorgravatar for xavierb@gmail.comXavier Bouchoux <xavierb@gmail.com> 2023-07-28 08:15:20+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2024-01-29 14:29:14-07:00
logc0fd64af0323ec5da7c79a7f36072d483eac738b
tree7d82c078fa32aa182ff0de3f33b5f058eb65746c
parentd1dd5aeb07e3b9d5c642533f1dd174aa445c0bd5

Sema: fix coerceArrayLike() for vectors with padding

as explainded at https://llvm.org/docs/LangRef.html#vector-type : "In general vector elements are laid out in memory in the same way as array types. Such an analogy works fine as long as the vector elements are byte sized. However, when the elements of the vector aren’t byte sized it gets a bit more complicated. One way to describe the layout is by describing what happens when a vector such as <N x iM> is bitcasted to an integer type with N*M bits, and then following the rules for storing such an integer to memory." "When <N*M> isn’t evenly divisible by the byte size the exact memory layout is unspecified (just like it is for an integral type of the same size)."

2 files changed, 95 insertions(+), 13 deletions(-)

src/Sema.zig+58-13
......@@ -28115,6 +28115,50 @@ fn coerceInMemoryAllowed(
2811528115 return .ok;
2811628116 }
2811728117
28118 // Arrays <-> Vectors
28119 if ((dest_tag == .Vector and src_tag == .Array) or
28120 (dest_tag == .Array and src_tag == .Vector))
28121 {
28122 const dest_len = dest_ty.arrayLen(mod);
28123 const src_len = src_ty.arrayLen(mod);
28124 if (dest_len != src_len) {
28125 return InMemoryCoercionResult{ .array_len = .{
28126 .actual = src_len,
28127 .wanted = dest_len,
28128 } };
28129 }
28130
28131 const dest_elem_ty = dest_ty.childType(mod);
28132 const src_elem_ty = src_ty.childType(mod);
28133 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
28134 if (child != .ok) {
28135 return InMemoryCoercionResult{ .array_elem = .{
28136 .child = try child.dupe(sema.arena),
28137 .actual = src_elem_ty,
28138 .wanted = dest_elem_ty,
28139 } };
28140 }
28141
28142 if (dest_tag == .Array) {
28143 const dest_info = dest_ty.arrayInfo(mod);
28144 if (dest_info.sentinel != null) {
28145 return InMemoryCoercionResult{ .array_sentinel = .{
28146 .actual = Value.@"unreachable",
28147 .wanted = dest_info.sentinel.?,
28148 .ty = dest_info.elem_type,
28149 } };
28150 }
28151 }
28152
28153 // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M),
28154 // that is to say, the padding bits are not in the same place as the array [N]iM.
28155 // If there's no padding, the bitcast is possible.
28156 const elem_bit_size = dest_elem_ty.bitSize(mod);
28157 const elem_abi_byte_size = dest_elem_ty.abiSize(mod);
28158 if (elem_abi_byte_size * 8 == elem_bit_size)
28159 return .ok;
28160 }
28161
2811828162 // Optionals
2811928163 if (dest_tag == .Optional and src_tag == .Optional) {
2812028164 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
......@@ -30005,10 +30049,22 @@ fn coerceArrayLike(
3000530049) !Air.Inst.Ref {
3000630050 const mod = sema.mod;
3000730051 const inst_ty = sema.typeOf(inst);
30008 const inst_len = inst_ty.arrayLen(mod);
30009 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
3001030052 const target = mod.getTarget();
3001130053
30054 // try coercion of the whole array
30055 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
30056 if (in_memory_result == .ok) {
30057 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
30058 // These types share the same comptime value representation.
30059 return sema.coerceInMemory(inst_val, dest_ty);
30060 }
30061 try sema.requireRuntimeBlock(block, inst_src, null);
30062 return block.addBitCast(dest_ty, inst);
30063 }
30064
30065 // otherwise, try element by element
30066 const inst_len = inst_ty.arrayLen(mod);
30067 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
3001230068 if (dest_len != inst_len) {
3001330069 const msg = msg: {
3001430070 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
......@@ -30023,17 +30079,6 @@ fn coerceArrayLike(
3002330079 }
3002430080
3002530081 const dest_elem_ty = dest_ty.childType(mod);
30026 const inst_elem_ty = inst_ty.childType(mod);
30027 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
30028 if (in_memory_result == .ok) {
30029 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
30030 // These types share the same comptime value representation.
30031 return sema.coerceInMemory(inst_val, dest_ty);
30032 }
30033 try sema.requireRuntimeBlock(block, inst_src, null);
30034 return block.addBitCast(dest_ty, inst);
30035 }
30036
3003730082 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
3003830083 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
3003930084 var runtime_src: ?LazySrcLoc = null;
test/behavior/vector.zig+37
......@@ -176,6 +176,43 @@ test "array to vector" {
176176 try comptime S.doTheTest();
177177}
178178
179test "array vector coercion - odd sizes" {
180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
182 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
183 if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
184 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
185 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
186
187 const S = struct {
188 fn doTheTest() !void {
189 var foo1: i48 = 124578;
190 var vec1: @Vector(2, i48) = [2]i48{ foo1, 1 };
191 var arr1: [2]i48 = vec1;
192 try expect(vec1[0] == foo1 and vec1[1] == 1);
193 try expect(arr1[0] == foo1 and arr1[1] == 1);
194
195 var foo2: u4 = 5;
196 var vec2: @Vector(2, u4) = [2]u4{ foo2, 1 };
197 var arr2: [2]u4 = vec2;
198 try expect(vec2[0] == foo2 and vec2[1] == 1);
199 try expect(arr2[0] == foo2 and arr2[1] == 1);
200
201 var foo3: u13 = 13;
202 var vec3: @Vector(3, u13) = [3]u13{ foo3, 0, 1 };
203 var arr3: [3]u13 = vec3;
204 try expect(vec3[0] == foo3 and vec3[1] == 0 and vec3[2] == 1);
205 try expect(arr3[0] == foo3 and arr3[1] == 0 and arr3[2] == 1);
206
207 var arr4 = [4:0]u24{ foo3, foo2, 0, 1 };
208 var vec4: @Vector(4, u24) = arr4;
209 try expect(vec4[0] == foo3 and vec4[1] == foo2 and vec4[2] == 0 and vec4[3] == 1);
210 }
211 };
212 try S.doTheTest();
213 try comptime S.doTheTest();
214}
215
179216test "array to vector with element type coercion" {
180217 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
181218 if (builtin.zig_backend == .stage2_x86_64 and