authorgravatar for me@brianferri.devBrian Ferri <me@brianferri.dev> 2026-10-04 11:50:49+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-10-04 18:20:54+02:00
log292e6b99017dcbcc67e12b1eaf83217f9efce42a
tree3789906afc86c20861369a74ad7a97f6ff40f7a7
parent245d477879b3b1646ef42c53317e94464325ccaa
signature Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

spirv: fix vector casts, packed field pointers and debug names of comptime-known values (#37040)

I'm writing a small numeric library whose kernels run on the CPU, on CUDA and on Vulkan through the self-hosted SPIR-V backend, and I was making them generic over the float type. `f64` needs its own `exp` and `log` on the GPU, so I wrote them as polynomial series, and the `f64` Vulkan module stopped validating. This fixes the three backend bugs underneath that, and a crash I ran into after merging master. The series take floats apart with `@bitCast` on vectors. Between vectors of the same length this reaches the Vulkan/OpenGL fallback in `bitCast`, which stores the source into a function variable of the destination type: ```zig export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { var x: @Vector(2, f32) = .{ 1, 2 }; _ = &x; const bits: @Vector(2, i32) = @bitCast(x); _ = bits; } ``` ``` $ zig build-exe -fno-llvm -target spirv64-vulkan -mcpu vulkan_v1_2 bitcast.zig -femit-bin=bitcast.spv $ spirv-val --target-env vulkan1.2 bitcast.spv error: line 45: OpStore Pointer <id> '36[%36]'s type does not match Object <id> '29[%29]'s type. ``` An elementwise vector bitcast is now a scalar `bitCast` per lane, the way `buildConvert` and friends already unroll vectors. Going through the scalar path also normalizes lanes of strange integers: a single `OpBitcast` on the whole vector validates for `@Vector(n, u3)` to `@Vector(n, i3)`, but leaves lanes that don't compare equal to the negative values they should hold. The series also convert between integer and float lanes. A vector of one lane, or of more than four without `vector16`, is an array in SPIR-V, and `airFloatFromInt` and `airIntFromFloat` emitted a single `OpConvert*` with that array as its result type: ```zig export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { var x: @Vector(1, i32) = .{1}; _ = &x; const y: @Vector(1, f32) = @floatFromInt(x); _ = y; } ``` ``` $ spirv-val --target-env vulkan1.2 convert.spv error: line 40: Expected float scalar or vector type as Result Type: ConvertSToF %y = OpConvertSToF %_arr_f32_u32_1 %26 ``` `@floatCast` and `@intCast` already go through `buildConvert`, which unrolls the vector; these two now do as well, with `buildConvert` picking the int/float conversion opcodes. The coefficients sit in a comptime array that the series take as a comptime slice, and that alone fails to compile, although nothing reads the slice at runtime: ```zig const weights = [_]f32{ 1, 2, 3 }; inline fn first(comptime w: []const f32) f32 { return w[0]; } export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { var x: f32 = first(&weights); _ = &x; } ``` ``` $ zig build-exe -fno-llvm -target spirv64-vulkan -mcpu vulkan_v1_2 dbgvar.zig -femit-bin=dbgvar.spv dbgvar.zig:7:8: error: TODO (SPIR-V): pointer to constant 'dbgvar.weights' ``` It appears `airDbgVar` lowers the operand only to attach an `OpName` to it, and a pointer to a non-extern constant is still a TODO. A comptime-known value has no runtime variable of its own to name, so `airDbgVar` now leaves interned operands alone. The same thing held back 58 of the behavior tests skipped on SPIR-V, and the test commit un-skips them. After merging master, four of those tests crashed the compiler in `structFieldPtr`, which has `unreachable` for packed containers since ebb8683ef1 ([#36996](https://codeberg.org/ziglang/zig/pulls/36996)): ```zig const Flags = packed struct { a: bool, b: bool }; export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { var flags: Flags = .{ .a = true, .b = false }; const b = &flags.b; b.* = true; } ``` ``` thread 3961624 panic: reached unreachable code src/codegen/spirv/CodeGen.zig:6906:27: 0x4767157 in structFieldPtr (main.zig) .@"packed" => unreachable, ``` My reading is that the `unreachable` holds for the pointer derivations it was written for, since `constantPtr` now turns packed pointers into pointers to their host integer first, but `airStructFieldPtr` and `airStructFieldPtrIndex` still pass packed structs through. A pointer to a packed field is a bit-pointer into the struct's own host integer, so it is the struct pointer: that is what the old code computed, and what the C backend now does with `.begin`. Packed unions get back the TODO error they had before. I may be missing a plan for packed field pointers here, and I'm happy to change it if so. The commit also un-skips three packed tests that crash on master the same way. I suppose nothing caught this because those tests are skipped on SPIR-V and the `spirv64-vulkan` behavior target is commented out in `test/tests.zig`. I ran the behavior tests through `tools/unskip_spirv_tests.zig` and [zig-spirv-test-executor](https://github.com/Snektron/zig-spirv-test-executor) on a Radeon 680M, and the new and un-skipped tests on an RTX 4060 as well. Every fix has tests that fail on master and pass with it; the vector ones compare lane by lane, since `expectEqual` on vectors doesn't compile for SPIR-V yet. Running each behavior file whole, nothing that passes on master fails. Five more skips already pass on master without these changes, so I left them out, but I can add them. The new vector tests copy the aarch64, arm, sparc64 and riscv64 skips of the tests around them, which I couldn't check. Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37040 Reviewed-by: Ali Cheraghi <alichraghi@noreply.codeberg.org>

20 files changed, 119 insertions(+), 137 deletions(-)

src/codegen/spirv/CodeGen.zig+40-43
......@@ -3423,8 +3423,8 @@ fn vectorization(cg: *CodeGen, args: anytype) Vectorization {
34233423 return v;
34243424}
34253425
3426/// This function builds an OpSConvert of OpUConvert depending on the
3427/// signedness of the types.
3426/// This function builds the conversion between two numeric types: OpFConvert, OpSConvert or
3427/// OpUConvert within floats or integers, and OpConvert{S,U}ToF or OpConvertFTo{S,U} across them.
34283428fn buildConvert(cg: *CodeGen, dst_ty: Type, src: Temporary) !Temporary {
34293429 const zcu = cg.zcu;
34303430
......@@ -3453,8 +3453,16 @@ fn buildConvert(cg: *CodeGen, dst_ty: Type, src: Temporary) !Temporary {
34533453 const op_result_ty_id = try cg.resolveType(op_result_ty, .direct);
34543454
34553455 const opcode: Opcode = blk: {
3456 if (dst_ty.scalarType(zcu).isAnyFloat()) break :blk .OpFConvert;
3457 if (dst_ty.scalarType(zcu).isSignedInt(zcu)) break :blk .OpSConvert;
3456 if (dst_scalar.isAnyFloat()) {
3457 if (src_scalar.isAnyFloat()) break :blk .OpFConvert;
3458 if (src_scalar.isSignedInt(zcu)) break :blk .OpConvertSToF;
3459 break :blk .OpConvertUToF;
3460 }
3461 if (src_scalar.isAnyFloat()) {
3462 if (dst_scalar.isSignedInt(zcu)) break :blk .OpConvertFToS;
3463 break :blk .OpConvertFToU;
3464 }
3465 if (dst_scalar.isSignedInt(zcu)) break :blk .OpSConvert;
34583466 break :blk .OpUConvert;
34593467 };
34603468
......@@ -5801,6 +5809,23 @@ fn bitCast(
58015809 // TODO: Some more cases are missing here
58025810 // See fn bitCast in llvm.zig
58035811
5812 // An elementwise vector bitcast is a scalar bitcast per lane, which also normalizes lanes
5813 // of strange integers.
5814 if (src_ty.isVector(zcu) and dst_ty.isVector(zcu) and
5815 src_ty.vectorLen(zcu) == dst_ty.vectorLen(zcu) and
5816 src_ty.childType(zcu).isNumeric(zcu) and dst_ty.childType(zcu).isNumeric(zcu))
5817 {
5818 const src_elem_ty = src_ty.childType(zcu);
5819 const dst_elem_ty = dst_ty.childType(zcu);
5820 const scratch_top = cg.id_scratch.items.len;
5821 defer cg.id_scratch.shrinkRetainingCapacity(scratch_top);
5822 for (0..src_ty.vectorLen(zcu)) |i| {
5823 const src_lane_id = try cg.extractVectorComponent(src_elem_ty, src_id, @intCast(i));
5824 try cg.id_scratch.append(gpa, try cg.bitCast(dst_elem_ty, src_elem_ty, src_lane_id));
5825 }
5826 break :blk try cg.constructComposite(dst_ty_id, cg.id_scratch.items[scratch_top..]);
5827 }
5828
58045829 if (src_ty.zigTypeTag(zcu) == .int and dst_ty.isPtrAtRuntime(zcu)) {
58055830 if (target.os.tag != .opencl) {
58065831 if (dst_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
......@@ -6139,50 +6164,19 @@ fn intFromPtr(cg: *CodeGen, operand_id: Id) !Id {
61396164}
61406165
61416166fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6142 const gpa = cg.gpa;
61436167 const ty_op = cg.air.instructions.items(.data)[@backingInt(inst)].ty_op;
6144 const operand_ty = cg.typeOf(ty_op.operand);
6145 const operand_id = try cg.resolve(ty_op.operand);
6168 const operand = try cg.temporary(ty_op.operand);
61466169 const result_ty = cg.typeOfIndex(inst);
6147 const operand_info = cg.arithmeticTypeInfo(operand_ty);
6148 const result_id = cg.allocId();
6149 const result_ty_id = try cg.resolveType(result_ty, .direct);
6150 switch (operand_info.signedness) {
6151 .signed => try cg.body.emit(gpa, .OpConvertSToF, .{
6152 .id_result_type = result_ty_id,
6153 .id_result = result_id,
6154 .signed_value = operand_id,
6155 }),
6156 .unsigned => try cg.body.emit(gpa, .OpConvertUToF, .{
6157 .id_result_type = result_ty_id,
6158 .id_result = result_id,
6159 .unsigned_value = operand_id,
6160 }),
6161 }
6162 return result_id;
6170 const result = try cg.buildConvert(result_ty, operand);
6171 return try result.materialize(cg);
61636172}
61646173
61656174fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
6166 const gpa = cg.gpa;
61676175 const ty_op = cg.air.instructions.items(.data)[@backingInt(inst)].ty_op;
6168 const operand_id = try cg.resolve(ty_op.operand);
6176 const operand = try cg.temporary(ty_op.operand);
61696177 const result_ty = cg.typeOfIndex(inst);
6170 const result_info = cg.arithmeticTypeInfo(result_ty);
6171 const result_ty_id = try cg.resolveType(result_ty, .direct);
6172 const result_id = cg.allocId();
6173 switch (result_info.signedness) {
6174 .signed => try cg.body.emit(gpa, .OpConvertFToS, .{
6175 .id_result_type = result_ty_id,
6176 .id_result = result_id,
6177 .float_value = operand_id,
6178 }),
6179 .unsigned => try cg.body.emit(gpa, .OpConvertFToU, .{
6180 .id_result_type = result_ty_id,
6181 .id_result = result_id,
6182 .float_value = operand_id,
6183 }),
6184 }
6185 return result_id;
6178 const result = try cg.buildConvert(result_ty, operand);
6179 return try result.materialize(cg);
61866180}
61876181
61886182fn airFloatCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
......@@ -6903,14 +6897,14 @@ fn structFieldPtr(
69036897 return cg.accessChain(result_ptr_ty_id, object_ptr, &.{field_index});
69046898 },
69056899 .@"struct" => switch (object_ty.containerLayout(zcu)) {
6906 .@"packed" => unreachable,
6900 .@"packed" => return cg.todo("implement field access for packed structs", .{}),
69076901 .auto, .@"extern" => {
69086902 const member_index = cg.memberIndex(object_ty, field_index);
69096903 return try cg.accessChain(result_ptr_ty_id, object_ptr, &.{member_index});
69106904 },
69116905 },
69126906 .@"union" => switch (object_ty.containerLayout(zcu)) {
6913 .@"packed" => unreachable,
6907 .@"packed" => return cg.todo("implement field access for packed unions", .{}),
69146908 .auto => {
69156909 if (!field_ty.hasRuntimeBits(zcu)) return try cg.constUndef(result_ptr_ty_id);
69166910 return try cg.accessChain(result_ptr_ty_id, object_ptr, &.{field_index});
......@@ -8483,6 +8477,9 @@ fn airDbgInlineBlock(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
84838477
84848478fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void {
84858479 const pl_op = cg.air.instructions.items(.data)[@backingInt(inst)].pl_op;
8480 // A comptime-known value has no runtime variable of its own to name, and lowering it only to
8481 // name it can fail, as for a slice of a global constant.
8482 if (pl_op.operand.toInterned() != null) return;
84868483 const target_id = switch (try cg.resolvePtr(pl_op.operand)) {
84878484 .tracked => return,
84888485 .id => |id| id,
test/behavior/align.zig-2
......@@ -639,8 +639,6 @@ test "function pointer @intFromPtr/@ptrFromInt roundtrip" {
639639}
640640
641641test "function pointer align mask" {
642 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
643
644642 const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020;
645643 const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int);
646644 const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned);
test/behavior/array.zig-8
......@@ -836,8 +836,6 @@ test "pointer to sentinel-terminated array initialized through reference to anon
836836}
837837
838838test "tuple initialized through reference to anonymous array init provides result types" {
839 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
840
841839 const Tuple = struct { u64, *const u32 };
842840 const foo: *const Tuple = &.{
843841 @intCast(12345),
......@@ -978,15 +976,11 @@ test "runtime index of array of zero-bit values" {
978976}
979977
980978test "initialize slice with reference to empty array initializer" {
981 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
982
983979 const a: []const u8 = &.{};
984980 comptime assert(a.len == 0);
985981}
986982
987983test "initialize many-pointer with reference to empty array initializer" {
988 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
989
990984 const a: [*]const u8 = &.{};
991985 _ = a; // nothing meaningful to test; points to zero bits
992986}
......@@ -1012,8 +1006,6 @@ test "pass pointer to empty array initializer to anytype parameter" {
10121006}
10131007
10141008test "initialize pointer to anyopaque with reference to empty array initializer" {
1015 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1016
10171009 const ptr: *const anyopaque = &.{};
10181010 // The above acts like an untyped initializer, since the `.{}` has no result type.
10191011 // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`).
test/behavior/basic.zig-8
......@@ -297,7 +297,6 @@ const global_a: i32 = 1234;
297297const global_b: *const i32 = &global_a;
298298const global_c: *const f32 = @as(*const f32, @ptrCast(global_b));
299299test "compile time global reinterpret" {
300 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
301300 const d = @as(*const i32, @ptrCast(global_c));
302301 try expect(d.* == 1234);
303302}
......@@ -637,7 +636,6 @@ fn emptyFn() void {}
637636
638637const addr1 = @as(*const u8, @ptrCast(&emptyFn));
639638test "comptime cast fn to ptr" {
640 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
641639 const addr2 = @as(*const u8, @ptrCast(&emptyFn));
642640 comptime assert(addr1 == addr2);
643641}
......@@ -915,7 +913,6 @@ test "labeled block with runtime branch forwards its result location type to bre
915913
916914test "try in labeled block doesn't cast to wrong type" {
917915 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
918 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
919916
920917 const S = struct {
921918 a: u32,
......@@ -1001,8 +998,6 @@ test "generic function uses return type of other generic function" {
1001998}
1002999
10031000test "const alloc with comptime-known initializer is made comptime-known" {
1004 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1005
10061001 const S = struct {
10071002 a: bool,
10081003 b: [2]u8,
......@@ -1276,7 +1271,6 @@ test "@Int returned from block" {
12761271}
12771272
12781273test "comptime variable initialized with addresses of literals" {
1279 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
12801274 comptime var st = .{
12811275 .foo = &1,
12821276 .bar = &2,
......@@ -1326,8 +1320,6 @@ test "proper value is returned from labeled block" {
13261320}
13271321
13281322test "const inferred array of slices" {
1329 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1330
13311323 const T = struct { v: bool };
13321324
13331325 const decls = [_][]const T{
test/behavior/bitcast.zig+30
......@@ -311,6 +311,36 @@ test "bitcast vector to integer and back" {
311311 try expect(int_again == 0b1111_1111_1111_1101);
312312}
313313
314test "bitcast vector elementwise" {
315 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
316 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
317 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
318 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
319
320 const S = struct {
321 fn doTheTest(comptime len: comptime_int) !void {
322 var floats: @Vector(len, f32) = std.simd.iota(f32, len) + @as(@Vector(len, f32), @splat(0.5));
323 _ = &floats;
324 const bits: @Vector(len, i32) = @bitCast(floats);
325 inline for (0..len) |i| try expect(bits[i] == @as(i32, @bitCast(@as(f32, @floatFromInt(i)) + 0.5)));
326 const floats_again: @Vector(len, f32) = @bitCast(bits);
327 inline for (0..len) |i| try expect(floats_again[i] == @as(f32, @floatFromInt(i)) + 0.5);
328
329 var unsigned: @Vector(len, u3) = std.simd.iota(u3, len);
330 _ = &unsigned;
331 const signed: @Vector(len, i3) = @bitCast(unsigned);
332 inline for (0..len) |i| try expect(signed[i] == @as(i3, @bitCast(@as(u3, i))));
333 const unsigned_again: @Vector(len, u3) = @bitCast(signed);
334 inline for (0..len) |i| try expect(unsigned_again[i] == i);
335 }
336 };
337
338 inline for (.{ 1, 2, 3, 4, 8 }) |len| {
339 try S.doTheTest(len);
340 try comptime S.doTheTest(len);
341 }
342}
343
314344fn bitCastWrapper16(x: f16) u16 {
315345 return @as(u16, @bitCast(x));
316346}
test/behavior/cast.zig+30-13
......@@ -9,8 +9,6 @@ const maxInt = std.math.maxInt;
99const native_endian = builtin.target.cpu.arch.endian();
1010
1111test "int to ptr cast" {
12 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
13
1412 const x = @as(usize, 13);
1513 const y = @as(*u8, @ptrFromInt(x));
1614 const z = @intFromPtr(y);
......@@ -18,8 +16,6 @@ test "int to ptr cast" {
1816}
1917
2018test "integer literal to pointer cast" {
21 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
22
2319 const vga_mem = @as(*u16, @ptrFromInt(0xB8000));
2420 try expect(@intFromPtr(vga_mem) == 0xB8000);
2521}
......@@ -71,8 +67,6 @@ test "implicit cast comptime_int to comptime_float" {
7167}
7268
7369test "comptime_int @floatFromInt" {
74 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
75
7670 {
7771 const result = @as(f16, @floatFromInt(1234));
7872 try expect(@TypeOf(result) == f16);
......@@ -538,8 +532,6 @@ test "return u8 coercing into ?u32 return type" {
538532}
539533
540534test "cast from ?[*]T to ??[*]T" {
541 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
542
543535 const a: ??[*]u8 = @as(?[*]u8, null);
544536 try expect(a != null and a.? == null);
545537}
......@@ -1423,7 +1415,6 @@ test "comptime float casts" {
14231415}
14241416
14251417test "pointer reinterpret const float to int" {
1426 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
14271418 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
14281419
14291420 // The hex representation is 0x3fe3333333333303.
......@@ -1843,8 +1834,6 @@ test "coerce between pointers of compatible differently-named floats" {
18431834}
18441835
18451836test "peer type resolution of const and non-const pointer to array" {
1846 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1847
18481837 const a = @as(*[1024]u8, @ptrFromInt(42));
18491838 const b = @as(*const [1024]u8, @ptrFromInt(42));
18501839 try std.testing.expect(@TypeOf(a, b) == *const [1024]u8);
......@@ -1909,8 +1898,6 @@ test "optional pointer coerced to optional allowzero pointer" {
19091898}
19101899
19111900test "optional slice coerced to allowzero many pointer" {
1912 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1913
19141901 const a: ?[]const u32 = null;
19151902 const b: [*]allowzero const u8 = @ptrCast(a);
19161903 const c = @intFromPtr(b);
......@@ -2932,6 +2919,36 @@ test "@intFromFloat on vector" {
29322919 try comptime S.doTheTest();
29332920}
29342921
2922test "@floatFromInt and @intFromFloat on vectors of any length" {
2923 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2924 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2925 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2926 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
2927
2928 const S = struct {
2929 fn doTheTest(comptime len: comptime_int) !void {
2930 var signed: @Vector(len, i32) = std.simd.iota(i32, len) - @as(@Vector(len, i32), @splat(4));
2931 _ = &signed;
2932 const from_signed: @Vector(len, f32) = @floatFromInt(signed);
2933 inline for (0..len) |i| try expect(from_signed[i] == @as(f32, @floatFromInt(@as(i32, i) - 4)));
2934 const to_signed: @Vector(len, i32) = @intFromFloat(from_signed);
2935 inline for (0..len) |i| try expect(to_signed[i] == @as(i32, i) - 4);
2936
2937 var unsigned: @Vector(len, u32) = std.simd.iota(u32, len) * @as(@Vector(len, u32), @splat(3));
2938 _ = &unsigned;
2939 const from_unsigned: @Vector(len, f32) = @floatFromInt(unsigned);
2940 inline for (0..len) |i| try expect(from_unsigned[i] == @as(f32, @floatFromInt(i * 3)));
2941 const to_unsigned: @Vector(len, u32) = @intFromFloat(from_unsigned);
2942 inline for (0..len) |i| try expect(to_unsigned[i] == i * 3);
2943 }
2944 };
2945
2946 inline for (.{ 1, 2, 3, 4, 8 }) |len| {
2947 try S.doTheTest(len);
2948 try comptime S.doTheTest(len);
2949 }
2950}
2951
29352952test "@intFromBool on vector" {
29362953 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
29372954 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
test/behavior/comptime_memory.zig-10
......@@ -407,8 +407,6 @@ test "mutate entire slice at comptime" {
407407}
408408
409409test "dereference undefined pointer to zero-bit type" {
410 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
411
412410 const p0: *void = undefined;
413411 try testing.expectEqual({}, p0.*);
414412
......@@ -424,16 +422,12 @@ test "type pun extern struct" {
424422}
425423
426424test "type pun @ptrFromInt" {
427 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
428
429425 const p: *u8 = @ptrFromInt(42);
430426 // note that expectEqual hides the bug
431427 try testing.expect(@as(*const [*]u8, @ptrCast(&p)).* == @as([*]u8, @ptrFromInt(42)));
432428}
433429
434430test "type pun null pointer-like optional" {
435 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
436
437431 const p: ?*u8 = null;
438432 // note that expectEqual hides the bug
439433 try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null);
......@@ -557,8 +551,6 @@ test "comptime store of packed struct with void field into array" {
557551}
558552
559553test "comptime store of reinterpreted zero-bit type" {
560 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
561
562554 const S = struct {
563555 fn doTheTest(comptime T: type) void {
564556 comptime var buf: T = undefined;
......@@ -590,8 +582,6 @@ test "comptime store to extern struct reinterpreted as byte array" {
590582}
591583
592584test "reinterpret sentinel-terminated array as packed struct" {
593 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
594
595585 const S = packed struct(u16) { lo: u8, hi: u8 };
596586 const data: [2:0]u8 = .{ 0x12, 0x34 };
597587 const ptr: *align(1) const S = @ptrCast(&data);
test/behavior/error.zig-2
......@@ -555,7 +555,6 @@ test "return result loc as peer result loc in inferred error set function" {
555555}
556556
557557test "error payload type is correctly resolved" {
558 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
559558 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
560559 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
561560
......@@ -1108,7 +1107,6 @@ test "'if' ignores error via local while 'else' ignores error directly" {
11081107}
11091108
11101109test "@errorCast into own inferred error set" {
1111 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
11121110 const static = struct {
11131111 fn foo(b: bool) !void {
11141112 if (b) {
test/behavior/eval.zig-6
......@@ -876,16 +876,12 @@ test "comptime pointer load through elem_ptr" {
876876}
877877
878878test "debug variable type resolved through indirect zero-bit types" {
879 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
880
881879 const T = struct { key: []void };
882880 const slice: []const T = &[_]T{};
883881 _ = slice;
884882}
885883
886884test "const local with comptime init through array init" {
887 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
888
889885 const E1 = enum {
890886 A,
891887 pub fn a() void {}
......@@ -1163,8 +1159,6 @@ test "repeated value is correctly expanded" {
11631159}
11641160
11651161test "value in if block is comptime-known" {
1166 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1167
11681162 const first = blk: {
11691163 const s = if (false) "a" else "b";
11701164 break :blk "foo" ++ s;
test/behavior/floatop.zig-2
......@@ -349,7 +349,6 @@ fn testDifferentSizedFloatComparisons() !void {
349349test "negative f128 intFromFloat at compile-time" {
350350 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
351351 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
352 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
353352
354353 const a: f128 = -2;
355354 var b: i64 = @intFromFloat(a);
......@@ -1550,7 +1549,6 @@ test "comptime fixed-width float zero divided by zero produces NaN" {
15501549test "comptime fixed-width float non-zero divided by zero produces signed Inf" {
15511550 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15521551 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1553 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
15541552
15551553 inline for (.{ f16, f32, f64, f80, f128 }) |F| {
15561554 const pos = @as(F, 1) / @as(F, 0);
test/behavior/for.zig-3
......@@ -453,7 +453,6 @@ test "inline for with counter as the comptime-known" {
453453test "inline for on tuple pointer" {
454454 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
455455 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
456 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
457456
458457 const S = struct { u32, u32, u32 };
459458 var s: S = .{ 100, 200, 300 };
......@@ -522,8 +521,6 @@ test "return from inline for" {
522521}
523522
524523test "for loop 0 length range" {
525 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
526
527524 const map: []const u8 = &.{};
528525 for (map, 0..map.len) |i, j| {
529526 _ = i;
test/behavior/math.zig-1
......@@ -2506,7 +2506,6 @@ test "runtime comparison to NaN is comptime-known" {
25062506test "runtime int comparison to inf is comptime-known" {
25072507 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
25082508 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
2509 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
25102509 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
25112510
25122511 const S = struct {
test/behavior/pointers.zig-14
......@@ -266,8 +266,6 @@ test "implicit cast error unions with non-optional to optional pointer" {
266266}
267267
268268test "compare equality of optional and non-optional pointer" {
269 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
270
271269 const a = @as(*const usize, @ptrFromInt(0x12345678));
272270 const b = @as(?*usize, @ptrFromInt(0x12345678));
273271 try expect(a == b);
......@@ -453,8 +451,6 @@ test "pointer sentinel with +inf" {
453451}
454452
455453test "pointer to array at fixed address" {
456 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
457
458454 const array = @as(*volatile [2]u32, @ptrFromInt(0x10));
459455 // Silly check just to reference `array`
460456 try expect(@intFromPtr(&array[0]) == 0x10);
......@@ -496,8 +492,6 @@ test "pointer-integer arithmetic affects the alignment" {
496492}
497493
498494test "@intFromPtr on null optional at comptime" {
499 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
500
501495 {
502496 const pointer = @as(?*u8, @ptrFromInt(0x000));
503497 const x = @intFromPtr(pointer);
......@@ -642,8 +636,6 @@ test "result type preserved through multiple references" {
642636}
643637
644638test "result type found through optional pointer" {
645 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
646
647639 const ptr1: ?*const u32 = &@intCast(123);
648640 const ptr2: ?[]const u8 = &.{ @intCast(123), @truncate(0xABCD) };
649641 try expect(ptr1.?.* == 123);
......@@ -710,8 +702,6 @@ test "pointer-to-array constness for zero-size elements, const" {
710702}
711703
712704test "cast pointers with zero sized elements" {
713 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
714
715705 const a: *void = undefined;
716706 const b: *[1]void = a;
717707 _ = b;
......@@ -721,8 +711,6 @@ test "cast pointers with zero sized elements" {
721711}
722712
723713test "comptime pointer equality through distinct fields with well-defined layout" {
724 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
725
726714 const A = extern struct {
727715 x: u32,
728716 z: u16,
......@@ -785,8 +773,6 @@ test "pointers to elements of many-ptr to zero-bit type" {
785773}
786774
787775test "comptime C pointer to optional pointer" {
788 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
789
790776 const opt: ?*u8 = @ptrFromInt(0x1000);
791777 const outer_ptr: [*c]const ?*u8 = &opt;
792778 const inner_ptr = &outer_ptr.*.?;
test/behavior/ptrcast.zig-9
......@@ -24,7 +24,6 @@ fn testReinterpretBytesAsInteger() !void {
2424test "reinterpret an array over multiple elements, with no well-defined layout" {
2525 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
2626 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
27 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
2827
2928 try testReinterpretWithOffsetAndNoWellDefinedLayout();
3029 try comptime testReinterpretWithOffsetAndNoWellDefinedLayout();
......@@ -282,8 +281,6 @@ test "@ptrCast undefined value at comptime" {
282281}
283282
284283test "comptime @ptrCast with packed struct leaves value unmodified" {
285 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
286
287284 const S = packed struct { three: u3 };
288285 const st: S = .{ .three = 6 };
289286 try expect(st.three == 6);
......@@ -293,8 +290,6 @@ test "comptime @ptrCast with packed struct leaves value unmodified" {
293290}
294291
295292test "@ptrCast restructures comptime-only array" {
296 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
297
298293 {
299294 const a3a2: [3][2]comptime_int = .{
300295 .{ 1, 2 },
......@@ -561,8 +556,6 @@ test "@ptrCast single-item pointer to slice of bytes" {
561556}
562557
563558test "@ptrCast array pointer removing sentinel" {
564 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
565
566559 const in: *const [4:0]u8 = &.{ 1, 2, 3, 4 };
567560 const out: []const i8 = @ptrCast(in);
568561 comptime assert(out.len == 4);
......@@ -573,8 +566,6 @@ test "@ptrCast array pointer removing sentinel" {
573566}
574567
575568test "@ptrcast larger type to smaller one" {
576 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
577
578569 const T = packed struct { x: u17 };
579570 const a: u32 = 0;
580571 const b: *const T = @ptrCast(&a);
test/behavior/ptrfromint.zig-1
......@@ -44,7 +44,6 @@ test "@ptrFromInt creates null pointer" {
4444test "@ptrFromInt creates allowzero zero pointer" {
4545 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
4646 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
47 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
4847
4948 const ptr = @as(*allowzero u32, @ptrFromInt(0));
5049 try expectEqual(@as(usize, 0), @intFromPtr(ptr));
test/behavior/slice.zig+19-2
......@@ -385,8 +385,6 @@ test "obtaining a null terminated slice" {
385385}
386386
387387test "empty array to slice" {
388 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
389
390388 const S = struct {
391389 fn doTheTest() !void {
392390 const empty: []align(16) u8 = &[_]u8{};
......@@ -403,6 +401,25 @@ test "empty array to slice" {
403401 try comptime S.doTheTest();
404402}
405403
404test "comptime-known slice of a global constant" {
405 const S = struct {
406 const weights = [_]f32{ 1, 2, 3 };
407
408 inline fn first(comptime w: []const f32) f32 {
409 return w[0];
410 }
411 };
412
413 var from_param: f32 = S.first(&S.weights);
414 _ = &from_param;
415 try expect(from_param == 1);
416
417 const w: []const f32 = &S.weights;
418 var from_local: f32 = w[1];
419 _ = &from_local;
420 try expect(from_local == 2);
421}
422
406423test "@ptrCast slice to pointer" {
407424 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
408425 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
test/behavior/struct.zig-3
......@@ -1631,8 +1631,6 @@ test "struct init with no result pointer sets field result types" {
16311631}
16321632
16331633test "runtime side-effects in comptime-known struct init" {
1634 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1635
16361634 var side_effects: u4 = 0;
16371635 const S = struct { a: u4, b: u4, c: u4, d: u4 };
16381636 const init = S{
......@@ -2275,7 +2273,6 @@ test "struct contains underaligned field with overaligned pointer to itself" {
22752273}
22762274
22772275test "struct contains pointer to function accepting that struct" {
2278 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
22792276 const S = struct {
22802277 const FnPtr = ?*const fn (@This()) void;
22812278 fn_ptr: FnPtr,
test/behavior/type.zig-2
......@@ -406,8 +406,6 @@ test "matching captures causes opaque equivalence" {
406406}
407407
408408test "reify enum where fields refers to part of array" {
409 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
410
411409 const field_names: [3][]const u8 = .{ "foo", "bar", undefined };
412410 const field_values: [3]u8 = .{ undefined, 0, 1 };
413411 const E = @Enum(u8, .exhaustive, field_names[0..2], field_values[1..3]);
test/behavior/type_info.zig-4
......@@ -310,8 +310,6 @@ const TestStruct = struct {
310310};
311311
312312test "type info: packed struct info" {
313 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
314
315313 try testPackedStruct();
316314 try comptime testPackedStruct();
317315}
......@@ -596,8 +594,6 @@ test "value from struct @typeInfo default_value_ptr can be loaded at comptime" {
596594}
597595
598596test "type info of tuple of string literal default value" {
599 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
600
601597 const struct_info = @typeInfo(@TypeOf(.{"hi"})).@"struct";
602598 const struct_field_attrs = struct_info.field_attrs[0];
603599 const struct_field_type = struct_info.field_types[0];
test/behavior/union.zig-4
......@@ -1109,8 +1109,6 @@ test "union with a large struct field" {
11091109}
11101110
11111111test "comptime equality of extern unions with same tag" {
1112 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1113
11141112 const S = struct {
11151113 const U = extern union {
11161114 a: i32,
......@@ -1845,8 +1843,6 @@ test "inner struct initializer uses packed union layout" {
18451843}
18461844
18471845test "extern union initialized via reintepreted struct field initializer" {
1848 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1849
18501846 const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd };
18511847
18521848 const U = extern union {