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> 2023-08-10 16:10:59-07:00
log77dd64b5f449982bda1bf1871e214a68c535f4c5
tree7c19c7440c7015a57d1ad7cf315d138aa7d344d2
parent9959319d5395a668867c90f63b5f82eb03f0ac9f

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
...@@ -28315,6 +28315,50 @@ fn coerceInMemoryAllowed(...@@ -28315,6 +28315,50 @@ fn coerceInMemoryAllowed(
28315 return .ok;28315 return .ok;
28316 }28316 }
2831728317
28318 // Arrays <-> Vectors
28319 if ((dest_tag == .Vector and src_tag == .Array) or
28320 (dest_tag == .Array and src_tag == .Vector))
28321 {
28322 const dest_len = dest_ty.arrayLen(mod);
28323 const src_len = src_ty.arrayLen(mod);
28324 if (dest_len != src_len) {
28325 return InMemoryCoercionResult{ .array_len = .{
28326 .actual = src_len,
28327 .wanted = dest_len,
28328 } };
28329 }
28330
28331 const dest_elem_ty = dest_ty.childType(mod);
28332 const src_elem_ty = src_ty.childType(mod);
28333 const child = try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, dest_is_mut, target, dest_src, src_src);
28334 if (child != .ok) {
28335 return InMemoryCoercionResult{ .array_elem = .{
28336 .child = try child.dupe(sema.arena),
28337 .actual = src_elem_ty,
28338 .wanted = dest_elem_ty,
28339 } };
28340 }
28341
28342 if (dest_tag == .Array) {
28343 const dest_info = dest_ty.arrayInfo(mod);
28344 if (dest_info.sentinel != null) {
28345 return InMemoryCoercionResult{ .array_sentinel = .{
28346 .actual = Value.@"unreachable",
28347 .wanted = dest_info.sentinel.?,
28348 .ty = dest_info.elem_type,
28349 } };
28350 }
28351 }
28352
28353 // The memory layout of @Vector(N, iM) is the same as the integer type i(N*M),
28354 // that is to say, the padding bits are not in the same place as the array [N]iM.
28355 // If there's no padding, the bitcast is possible.
28356 const elem_bit_size = dest_elem_ty.bitSize(mod);
28357 const elem_abi_byte_size = dest_elem_ty.abiSize(mod);
28358 if (elem_abi_byte_size * 8 == elem_bit_size)
28359 return .ok;
28360 }
28361
28318 // Optionals28362 // Optionals
28319 if (dest_tag == .Optional and src_tag == .Optional) {28363 if (dest_tag == .Optional and src_tag == .Optional) {
28320 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {28364 if ((maybe_dest_ptr_ty != null) != (maybe_src_ptr_ty != null)) {
...@@ -30205,10 +30249,22 @@ fn coerceArrayLike(...@@ -30205,10 +30249,22 @@ fn coerceArrayLike(
30205) !Air.Inst.Ref {30249) !Air.Inst.Ref {
30206 const mod = sema.mod;30250 const mod = sema.mod;
30207 const inst_ty = sema.typeOf(inst);30251 const inst_ty = sema.typeOf(inst);
30208 const inst_len = inst_ty.arrayLen(mod);
30209 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
30210 const target = mod.getTarget();30252 const target = mod.getTarget();
3021130253
30254 // try coercion of the whole array
30255 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_ty, inst_ty, false, target, dest_ty_src, inst_src);
30256 if (in_memory_result == .ok) {
30257 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
30258 // These types share the same comptime value representation.
30259 return sema.coerceInMemory(inst_val, dest_ty);
30260 }
30261 try sema.requireRuntimeBlock(block, inst_src, null);
30262 return block.addBitCast(dest_ty, inst);
30263 }
30264
30265 // otherwise, try element by element
30266 const inst_len = inst_ty.arrayLen(mod);
30267 const dest_len = try sema.usizeCast(block, dest_ty_src, dest_ty.arrayLen(mod));
30212 if (dest_len != inst_len) {30268 if (dest_len != inst_len) {
30213 const msg = msg: {30269 const msg = msg: {
30214 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{30270 const msg = try sema.errMsg(block, inst_src, "expected type '{}', found '{}'", .{
...@@ -30223,17 +30279,6 @@ fn coerceArrayLike(...@@ -30223,17 +30279,6 @@ fn coerceArrayLike(
30223 }30279 }
3022430280
30225 const dest_elem_ty = dest_ty.childType(mod);30281 const dest_elem_ty = dest_ty.childType(mod);
30226 const inst_elem_ty = inst_ty.childType(mod);
30227 const in_memory_result = try sema.coerceInMemoryAllowed(block, dest_elem_ty, inst_elem_ty, false, target, dest_ty_src, inst_src);
30228 if (in_memory_result == .ok) {
30229 if (try sema.resolveMaybeUndefVal(inst)) |inst_val| {
30230 // These types share the same comptime value representation.
30231 return sema.coerceInMemory(inst_val, dest_ty);
30232 }
30233 try sema.requireRuntimeBlock(block, inst_src, null);
30234 return block.addBitCast(dest_ty, inst);
30235 }
30236
30237 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);30282 const element_vals = try sema.arena.alloc(InternPool.Index, dest_len);
30238 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);30283 const element_refs = try sema.arena.alloc(Air.Inst.Ref, dest_len);
30239 var runtime_src: ?LazySrcLoc = null;30284 var runtime_src: ?LazySrcLoc = null;
test/behavior/vector.zig+37
...@@ -176,6 +176,43 @@ test "array to vector" {...@@ -176,6 +176,43 @@ test "array to vector" {
176 try comptime S.doTheTest();176 try comptime S.doTheTest();
177}177}
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
179test "array to vector with element type coercion" {216test "array to vector with element type coercion" {
180 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO217 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_x86_64 and218 if (builtin.zig_backend == .stage2_x86_64 and