From 292e6b99017dcbcc67e12b1eaf83217f9efce42a Mon Sep 17 00:00:00 2001 From: Brian Ferri Date: Sun, 4 Oct 2026 11:50:49 +0200 Subject: [PATCH] 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 '36[%36]'s type does not match Object '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 --- src/codegen/spirv/CodeGen.zig | 83 +++++++++++++++---------------- test/behavior/align.zig | 2 - test/behavior/array.zig | 8 --- test/behavior/basic.zig | 8 --- test/behavior/bitcast.zig | 30 +++++++++++ test/behavior/cast.zig | 43 +++++++++++----- test/behavior/comptime_memory.zig | 10 ---- test/behavior/error.zig | 2 - test/behavior/eval.zig | 6 --- test/behavior/floatop.zig | 2 - test/behavior/for.zig | 3 -- test/behavior/math.zig | 1 - test/behavior/pointers.zig | 14 ------ test/behavior/ptrcast.zig | 9 ---- test/behavior/ptrfromint.zig | 1 - test/behavior/slice.zig | 21 +++++++- test/behavior/struct.zig | 3 -- test/behavior/type.zig | 2 - test/behavior/type_info.zig | 4 -- test/behavior/union.zig | 4 -- 20 files changed, 119 insertions(+), 137 deletions(-) diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index 24d5fac0c6357222e593a996d8cff7152f899e22..69cb0da04a19d707aa653b51ee13b4794a409985 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -3423,8 +3423,8 @@ fn vectorization(cg: *CodeGen, args: anytype) Vectorization { return v; } -/// This function builds an OpSConvert of OpUConvert depending on the -/// signedness of the types. +/// This function builds the conversion between two numeric types: OpFConvert, OpSConvert or +/// OpUConvert within floats or integers, and OpConvert{S,U}ToF or OpConvertFTo{S,U} across them. fn buildConvert(cg: *CodeGen, dst_ty: Type, src: Temporary) !Temporary { const zcu = cg.zcu; @@ -3453,8 +3453,16 @@ fn buildConvert(cg: *CodeGen, dst_ty: Type, src: Temporary) !Temporary { const op_result_ty_id = try cg.resolveType(op_result_ty, .direct); const opcode: Opcode = blk: { - if (dst_ty.scalarType(zcu).isAnyFloat()) break :blk .OpFConvert; - if (dst_ty.scalarType(zcu).isSignedInt(zcu)) break :blk .OpSConvert; + if (dst_scalar.isAnyFloat()) { + if (src_scalar.isAnyFloat()) break :blk .OpFConvert; + if (src_scalar.isSignedInt(zcu)) break :blk .OpConvertSToF; + break :blk .OpConvertUToF; + } + if (src_scalar.isAnyFloat()) { + if (dst_scalar.isSignedInt(zcu)) break :blk .OpConvertFToS; + break :blk .OpConvertFToU; + } + if (dst_scalar.isSignedInt(zcu)) break :blk .OpSConvert; break :blk .OpUConvert; }; @@ -5801,6 +5809,23 @@ fn bitCast( // TODO: Some more cases are missing here // See fn bitCast in llvm.zig + // An elementwise vector bitcast is a scalar bitcast per lane, which also normalizes lanes + // of strange integers. + if (src_ty.isVector(zcu) and dst_ty.isVector(zcu) and + src_ty.vectorLen(zcu) == dst_ty.vectorLen(zcu) and + src_ty.childType(zcu).isNumeric(zcu) and dst_ty.childType(zcu).isNumeric(zcu)) + { + const src_elem_ty = src_ty.childType(zcu); + const dst_elem_ty = dst_ty.childType(zcu); + const scratch_top = cg.id_scratch.items.len; + defer cg.id_scratch.shrinkRetainingCapacity(scratch_top); + for (0..src_ty.vectorLen(zcu)) |i| { + const src_lane_id = try cg.extractVectorComponent(src_elem_ty, src_id, @intCast(i)); + try cg.id_scratch.append(gpa, try cg.bitCast(dst_elem_ty, src_elem_ty, src_lane_id)); + } + break :blk try cg.constructComposite(dst_ty_id, cg.id_scratch.items[scratch_top..]); + } + if (src_ty.zigTypeTag(zcu) == .int and dst_ty.isPtrAtRuntime(zcu)) { if (target.os.tag != .opencl) { if (dst_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) { @@ -6139,50 +6164,19 @@ fn intFromPtr(cg: *CodeGen, operand_id: Id) !Id { } fn airFloatFromInt(cg: *CodeGen, inst: Air.Inst.Index) !?Id { - const gpa = cg.gpa; const ty_op = cg.air.instructions.items(.data)[@backingInt(inst)].ty_op; - const operand_ty = cg.typeOf(ty_op.operand); - const operand_id = try cg.resolve(ty_op.operand); + const operand = try cg.temporary(ty_op.operand); const result_ty = cg.typeOfIndex(inst); - const operand_info = cg.arithmeticTypeInfo(operand_ty); - const result_id = cg.allocId(); - const result_ty_id = try cg.resolveType(result_ty, .direct); - switch (operand_info.signedness) { - .signed => try cg.body.emit(gpa, .OpConvertSToF, .{ - .id_result_type = result_ty_id, - .id_result = result_id, - .signed_value = operand_id, - }), - .unsigned => try cg.body.emit(gpa, .OpConvertUToF, .{ - .id_result_type = result_ty_id, - .id_result = result_id, - .unsigned_value = operand_id, - }), - } - return result_id; + const result = try cg.buildConvert(result_ty, operand); + return try result.materialize(cg); } fn airIntFromFloat(cg: *CodeGen, inst: Air.Inst.Index) !?Id { - const gpa = cg.gpa; const ty_op = cg.air.instructions.items(.data)[@backingInt(inst)].ty_op; - const operand_id = try cg.resolve(ty_op.operand); + const operand = try cg.temporary(ty_op.operand); const result_ty = cg.typeOfIndex(inst); - const result_info = cg.arithmeticTypeInfo(result_ty); - const result_ty_id = try cg.resolveType(result_ty, .direct); - const result_id = cg.allocId(); - switch (result_info.signedness) { - .signed => try cg.body.emit(gpa, .OpConvertFToS, .{ - .id_result_type = result_ty_id, - .id_result = result_id, - .float_value = operand_id, - }), - .unsigned => try cg.body.emit(gpa, .OpConvertFToU, .{ - .id_result_type = result_ty_id, - .id_result = result_id, - .float_value = operand_id, - }), - } - return result_id; + const result = try cg.buildConvert(result_ty, operand); + return try result.materialize(cg); } fn airFloatCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id { @@ -6903,14 +6897,14 @@ fn structFieldPtr( return cg.accessChain(result_ptr_ty_id, object_ptr, &.{field_index}); }, .@"struct" => switch (object_ty.containerLayout(zcu)) { - .@"packed" => unreachable, + .@"packed" => return cg.todo("implement field access for packed structs", .{}), .auto, .@"extern" => { const member_index = cg.memberIndex(object_ty, field_index); return try cg.accessChain(result_ptr_ty_id, object_ptr, &.{member_index}); }, }, .@"union" => switch (object_ty.containerLayout(zcu)) { - .@"packed" => unreachable, + .@"packed" => return cg.todo("implement field access for packed unions", .{}), .auto => { if (!field_ty.hasRuntimeBits(zcu)) return try cg.constUndef(result_ptr_ty_id); 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 { fn airDbgVar(cg: *CodeGen, inst: Air.Inst.Index) !void { const pl_op = cg.air.instructions.items(.data)[@backingInt(inst)].pl_op; + // A comptime-known value has no runtime variable of its own to name, and lowering it only to + // name it can fail, as for a slice of a global constant. + if (pl_op.operand.toInterned() != null) return; const target_id = switch (try cg.resolvePtr(pl_op.operand)) { .tracked => return, .id => |id| id, diff --git a/test/behavior/align.zig b/test/behavior/align.zig index b3333c2d233d7ed448338dea9a9b9a6220594244..83a100533d77a81a0a77ef38def5e61cb2a29f79 100644 --- a/test/behavior/align.zig +++ b/test/behavior/align.zig @@ -639,8 +639,6 @@ test "function pointer @intFromPtr/@ptrFromInt roundtrip" { } test "function pointer align mask" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020; const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int); const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned); diff --git a/test/behavior/array.zig b/test/behavior/array.zig index 603e1784aaf0ddf1230d606bdb3b3f864dff11d3..10cbdd6f731658795f030c4167c6d204079767bc 100644 --- a/test/behavior/array.zig +++ b/test/behavior/array.zig @@ -836,8 +836,6 @@ test "pointer to sentinel-terminated array initialized through reference to anon } test "tuple initialized through reference to anonymous array init provides result types" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const Tuple = struct { u64, *const u32 }; const foo: *const Tuple = &.{ @intCast(12345), @@ -978,15 +976,11 @@ test "runtime index of array of zero-bit values" { } test "initialize slice with reference to empty array initializer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a: []const u8 = &.{}; comptime assert(a.len == 0); } test "initialize many-pointer with reference to empty array initializer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a: [*]const u8 = &.{}; _ = a; // nothing meaningful to test; points to zero bits } @@ -1012,8 +1006,6 @@ test "pass pointer to empty array initializer to anytype parameter" { } test "initialize pointer to anyopaque with reference to empty array initializer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const ptr: *const anyopaque = &.{}; // The above acts like an untyped initializer, since the `.{}` has no result type. // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`). diff --git a/test/behavior/basic.zig b/test/behavior/basic.zig index 07bf89c9348748786f7b337e5cee3084d8f18b98..cb81cbd68b23bb706a1c288671185e485080aa6e 100644 --- a/test/behavior/basic.zig +++ b/test/behavior/basic.zig @@ -297,7 +297,6 @@ const global_a: i32 = 1234; const global_b: *const i32 = &global_a; const global_c: *const f32 = @as(*const f32, @ptrCast(global_b)); test "compile time global reinterpret" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const d = @as(*const i32, @ptrCast(global_c)); try expect(d.* == 1234); } @@ -637,7 +636,6 @@ fn emptyFn() void {} const addr1 = @as(*const u8, @ptrCast(&emptyFn)); test "comptime cast fn to ptr" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const addr2 = @as(*const u8, @ptrCast(&emptyFn)); comptime assert(addr1 == addr2); } @@ -915,7 +913,6 @@ test "labeled block with runtime branch forwards its result location type to bre test "try in labeled block doesn't cast to wrong type" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { a: u32, @@ -1001,8 +998,6 @@ test "generic function uses return type of other generic function" { } test "const alloc with comptime-known initializer is made comptime-known" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const S = struct { a: bool, b: [2]u8, @@ -1276,7 +1271,6 @@ test "@Int returned from block" { } test "comptime variable initialized with addresses of literals" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; comptime var st = .{ .foo = &1, .bar = &2, @@ -1326,8 +1320,6 @@ test "proper value is returned from labeled block" { } test "const inferred array of slices" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const T = struct { v: bool }; const decls = [_][]const T{ diff --git a/test/behavior/bitcast.zig b/test/behavior/bitcast.zig index e0bac5c59aa8caca367af85a394b61d32bc94058..98df69510bf23d1c477ca85f25e8c0e49219d0f3 100644 --- a/test/behavior/bitcast.zig +++ b/test/behavior/bitcast.zig @@ -311,6 +311,36 @@ test "bitcast vector to integer and back" { try expect(int_again == 0b1111_1111_1111_1101); } +test "bitcast vector elementwise" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const S = struct { + fn doTheTest(comptime len: comptime_int) !void { + var floats: @Vector(len, f32) = std.simd.iota(f32, len) + @as(@Vector(len, f32), @splat(0.5)); + _ = &floats; + const bits: @Vector(len, i32) = @bitCast(floats); + inline for (0..len) |i| try expect(bits[i] == @as(i32, @bitCast(@as(f32, @floatFromInt(i)) + 0.5))); + const floats_again: @Vector(len, f32) = @bitCast(bits); + inline for (0..len) |i| try expect(floats_again[i] == @as(f32, @floatFromInt(i)) + 0.5); + + var unsigned: @Vector(len, u3) = std.simd.iota(u3, len); + _ = &unsigned; + const signed: @Vector(len, i3) = @bitCast(unsigned); + inline for (0..len) |i| try expect(signed[i] == @as(i3, @bitCast(@as(u3, i)))); + const unsigned_again: @Vector(len, u3) = @bitCast(signed); + inline for (0..len) |i| try expect(unsigned_again[i] == i); + } + }; + + inline for (.{ 1, 2, 3, 4, 8 }) |len| { + try S.doTheTest(len); + try comptime S.doTheTest(len); + } +} + fn bitCastWrapper16(x: f16) u16 { return @as(u16, @bitCast(x)); } diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index f229ec8af57feae23e2aafd57007f0ed1a6bf1b6..46d7b1758e0229a10bd4f1163becd58826cd6d9c 100644 --- a/test/behavior/cast.zig +++ b/test/behavior/cast.zig @@ -9,8 +9,6 @@ const maxInt = std.math.maxInt; const native_endian = builtin.target.cpu.arch.endian(); test "int to ptr cast" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const x = @as(usize, 13); const y = @as(*u8, @ptrFromInt(x)); const z = @intFromPtr(y); @@ -18,8 +16,6 @@ test "int to ptr cast" { } test "integer literal to pointer cast" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const vga_mem = @as(*u16, @ptrFromInt(0xB8000)); try expect(@intFromPtr(vga_mem) == 0xB8000); } @@ -71,8 +67,6 @@ test "implicit cast comptime_int to comptime_float" { } test "comptime_int @floatFromInt" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - { const result = @as(f16, @floatFromInt(1234)); try expect(@TypeOf(result) == f16); @@ -538,8 +532,6 @@ test "return u8 coercing into ?u32 return type" { } test "cast from ?[*]T to ??[*]T" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a: ??[*]u8 = @as(?[*]u8, null); try expect(a != null and a.? == null); } @@ -1423,7 +1415,6 @@ test "comptime float casts" { } test "pointer reinterpret const float to int" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO // The hex representation is 0x3fe3333333333303. @@ -1843,8 +1834,6 @@ test "coerce between pointers of compatible differently-named floats" { } test "peer type resolution of const and non-const pointer to array" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a = @as(*[1024]u8, @ptrFromInt(42)); const b = @as(*const [1024]u8, @ptrFromInt(42)); try std.testing.expect(@TypeOf(a, b) == *const [1024]u8); @@ -1909,8 +1898,6 @@ test "optional pointer coerced to optional allowzero pointer" { } test "optional slice coerced to allowzero many pointer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a: ?[]const u32 = null; const b: [*]allowzero const u8 = @ptrCast(a); const c = @intFromPtr(b); @@ -2932,6 +2919,36 @@ test "@intFromFloat on vector" { try comptime S.doTheTest(); } +test "@floatFromInt and @intFromFloat on vectors of any length" { + if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; + if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO + if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; + + const S = struct { + fn doTheTest(comptime len: comptime_int) !void { + var signed: @Vector(len, i32) = std.simd.iota(i32, len) - @as(@Vector(len, i32), @splat(4)); + _ = &signed; + const from_signed: @Vector(len, f32) = @floatFromInt(signed); + inline for (0..len) |i| try expect(from_signed[i] == @as(f32, @floatFromInt(@as(i32, i) - 4))); + const to_signed: @Vector(len, i32) = @intFromFloat(from_signed); + inline for (0..len) |i| try expect(to_signed[i] == @as(i32, i) - 4); + + var unsigned: @Vector(len, u32) = std.simd.iota(u32, len) * @as(@Vector(len, u32), @splat(3)); + _ = &unsigned; + const from_unsigned: @Vector(len, f32) = @floatFromInt(unsigned); + inline for (0..len) |i| try expect(from_unsigned[i] == @as(f32, @floatFromInt(i * 3))); + const to_unsigned: @Vector(len, u32) = @intFromFloat(from_unsigned); + inline for (0..len) |i| try expect(to_unsigned[i] == i * 3); + } + }; + + inline for (.{ 1, 2, 3, 4, 8 }) |len| { + try S.doTheTest(len); + try comptime S.doTheTest(len); + } +} + test "@intFromBool on vector" { if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; diff --git a/test/behavior/comptime_memory.zig b/test/behavior/comptime_memory.zig index 75bbca0a12b98ce34454281b17f07c0d59c9ffba..63044d026e18e7d77e9e049805867d8dc6749fc2 100644 --- a/test/behavior/comptime_memory.zig +++ b/test/behavior/comptime_memory.zig @@ -407,8 +407,6 @@ test "mutate entire slice at comptime" { } test "dereference undefined pointer to zero-bit type" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const p0: *void = undefined; try testing.expectEqual({}, p0.*); @@ -424,16 +422,12 @@ test "type pun extern struct" { } test "type pun @ptrFromInt" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const p: *u8 = @ptrFromInt(42); // note that expectEqual hides the bug try testing.expect(@as(*const [*]u8, @ptrCast(&p)).* == @as([*]u8, @ptrFromInt(42))); } test "type pun null pointer-like optional" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const p: ?*u8 = null; // note that expectEqual hides the bug try testing.expect(@as(*const ?*i8, @ptrCast(&p)).* == null); @@ -557,8 +551,6 @@ test "comptime store of packed struct with void field into array" { } test "comptime store of reinterpreted zero-bit type" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const S = struct { fn doTheTest(comptime T: type) void { comptime var buf: T = undefined; @@ -590,8 +582,6 @@ test "comptime store to extern struct reinterpreted as byte array" { } test "reinterpret sentinel-terminated array as packed struct" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const S = packed struct(u16) { lo: u8, hi: u8 }; const data: [2:0]u8 = .{ 0x12, 0x34 }; const ptr: *align(1) const S = @ptrCast(&data); diff --git a/test/behavior/error.zig b/test/behavior/error.zig index 53c4614d6965927691943ade8f359833f74d5a73..46f7871a8281f76841f15f85b8d78f9f3db9f6bf 100644 --- a/test/behavior/error.zig +++ b/test/behavior/error.zig @@ -555,7 +555,6 @@ test "return result loc as peer result loc in inferred error set function" { } test "error payload type is correctly resolved" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO @@ -1108,7 +1107,6 @@ test "'if' ignores error via local while 'else' ignores error directly" { } test "@errorCast into own inferred error set" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const static = struct { fn foo(b: bool) !void { if (b) { diff --git a/test/behavior/eval.zig b/test/behavior/eval.zig index 50e264c1df6fbfc7bdbc3c8c8e0784f33f642ed3..4aaeb641d4621ddeb545e2c8285ebc7c1c4c5a2c 100644 --- a/test/behavior/eval.zig +++ b/test/behavior/eval.zig @@ -876,16 +876,12 @@ test "comptime pointer load through elem_ptr" { } test "debug variable type resolved through indirect zero-bit types" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const T = struct { key: []void }; const slice: []const T = &[_]T{}; _ = slice; } test "const local with comptime init through array init" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const E1 = enum { A, pub fn a() void {} @@ -1163,8 +1159,6 @@ test "repeated value is correctly expanded" { } test "value in if block is comptime-known" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const first = blk: { const s = if (false) "a" else "b"; break :blk "foo" ++ s; diff --git a/test/behavior/floatop.zig b/test/behavior/floatop.zig index cb6910fdaf2c1caf3c95737f538a1deea88fe2ff..62920d845fa59b54aecfb3c9b499a2048ab2ec28 100644 --- a/test/behavior/floatop.zig +++ b/test/behavior/floatop.zig @@ -349,7 +349,6 @@ fn testDifferentSizedFloatComparisons() !void { test "negative f128 intFromFloat at compile-time" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const a: f128 = -2; var b: i64 = @intFromFloat(a); @@ -1550,7 +1549,6 @@ test "comptime fixed-width float zero divided by zero produces NaN" { test "comptime fixed-width float non-zero divided by zero produces signed Inf" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; inline for (.{ f16, f32, f64, f80, f128 }) |F| { const pos = @as(F, 1) / @as(F, 0); diff --git a/test/behavior/for.zig b/test/behavior/for.zig index f64271480a65d0e7a1e8125b37f78106ba4b71a0..f2a8cba44665dccff95b013778a45f810f7e2664 100644 --- a/test/behavior/for.zig +++ b/test/behavior/for.zig @@ -453,7 +453,6 @@ test "inline for with counter as the comptime-known" { test "inline for on tuple pointer" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { u32, u32, u32 }; var s: S = .{ 100, 200, 300 }; @@ -522,8 +521,6 @@ test "return from inline for" { } test "for loop 0 length range" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const map: []const u8 = &.{}; for (map, 0..map.len) |i, j| { _ = i; diff --git a/test/behavior/math.zig b/test/behavior/math.zig index 88d83ba60d052ef5da1c23f8226325e41c885f8c..9d35984b7c1cb2ebbd405727138cd13826c570aa 100644 --- a/test/behavior/math.zig +++ b/test/behavior/math.zig @@ -2506,7 +2506,6 @@ test "runtime comparison to NaN is comptime-known" { test "runtime int comparison to inf is comptime-known" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest; const S = struct { diff --git a/test/behavior/pointers.zig b/test/behavior/pointers.zig index 23b976f02ac63e4bdd418fd6d8a512e930fb8dd7..67316d9619890852230d6d4928e38a7823d4dc41 100644 --- a/test/behavior/pointers.zig +++ b/test/behavior/pointers.zig @@ -266,8 +266,6 @@ test "implicit cast error unions with non-optional to optional pointer" { } test "compare equality of optional and non-optional pointer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a = @as(*const usize, @ptrFromInt(0x12345678)); const b = @as(?*usize, @ptrFromInt(0x12345678)); try expect(a == b); @@ -453,8 +451,6 @@ test "pointer sentinel with +inf" { } test "pointer to array at fixed address" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const array = @as(*volatile [2]u32, @ptrFromInt(0x10)); // Silly check just to reference `array` try expect(@intFromPtr(&array[0]) == 0x10); @@ -496,8 +492,6 @@ test "pointer-integer arithmetic affects the alignment" { } test "@intFromPtr on null optional at comptime" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - { const pointer = @as(?*u8, @ptrFromInt(0x000)); const x = @intFromPtr(pointer); @@ -642,8 +636,6 @@ test "result type preserved through multiple references" { } test "result type found through optional pointer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const ptr1: ?*const u32 = &@intCast(123); const ptr2: ?[]const u8 = &.{ @intCast(123), @truncate(0xABCD) }; try expect(ptr1.?.* == 123); @@ -710,8 +702,6 @@ test "pointer-to-array constness for zero-size elements, const" { } test "cast pointers with zero sized elements" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const a: *void = undefined; const b: *[1]void = a; _ = b; @@ -721,8 +711,6 @@ test "cast pointers with zero sized elements" { } test "comptime pointer equality through distinct fields with well-defined layout" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const A = extern struct { x: u32, z: u16, @@ -785,8 +773,6 @@ test "pointers to elements of many-ptr to zero-bit type" { } test "comptime C pointer to optional pointer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const opt: ?*u8 = @ptrFromInt(0x1000); const outer_ptr: [*c]const ?*u8 = &opt; const inner_ptr = &outer_ptr.*.?; diff --git a/test/behavior/ptrcast.zig b/test/behavior/ptrcast.zig index 123b9be3d8d4f28767dc3ed7a1ec636811ec7693..d468fd7ceb962c8b26dc6775330a98bf678dedcc 100644 --- a/test/behavior/ptrcast.zig +++ b/test/behavior/ptrcast.zig @@ -24,7 +24,6 @@ fn testReinterpretBytesAsInteger() !void { test "reinterpret an array over multiple elements, with no well-defined layout" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; try testReinterpretWithOffsetAndNoWellDefinedLayout(); try comptime testReinterpretWithOffsetAndNoWellDefinedLayout(); @@ -282,8 +281,6 @@ test "@ptrCast undefined value at comptime" { } test "comptime @ptrCast with packed struct leaves value unmodified" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const S = packed struct { three: u3 }; const st: S = .{ .three = 6 }; try expect(st.three == 6); @@ -293,8 +290,6 @@ test "comptime @ptrCast with packed struct leaves value unmodified" { } test "@ptrCast restructures comptime-only array" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - { const a3a2: [3][2]comptime_int = .{ .{ 1, 2 }, @@ -561,8 +556,6 @@ test "@ptrCast single-item pointer to slice of bytes" { } test "@ptrCast array pointer removing sentinel" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const in: *const [4:0]u8 = &.{ 1, 2, 3, 4 }; const out: []const i8 = @ptrCast(in); comptime assert(out.len == 4); @@ -573,8 +566,6 @@ test "@ptrCast array pointer removing sentinel" { } test "@ptrcast larger type to smaller one" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const T = packed struct { x: u17 }; const a: u32 = 0; const b: *const T = @ptrCast(&a); diff --git a/test/behavior/ptrfromint.zig b/test/behavior/ptrfromint.zig index 480c773c156af7d49087d25a03a781ad553e1601..f72e64f87f74ce2f177273a9d7ead40ff5495678 100644 --- a/test/behavior/ptrfromint.zig +++ b/test/behavior/ptrfromint.zig @@ -44,7 +44,6 @@ test "@ptrFromInt creates null pointer" { test "@ptrFromInt creates allowzero zero pointer" { if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const ptr = @as(*allowzero u32, @ptrFromInt(0)); try expectEqual(@as(usize, 0), @intFromPtr(ptr)); diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index a08e134cb75a554598372275d7381c973ed435fb..9ba9014c66200f546c90a9e9fd1c3d6c72416559 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -385,8 +385,6 @@ test "obtaining a null terminated slice" { } test "empty array to slice" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const S = struct { fn doTheTest() !void { const empty: []align(16) u8 = &[_]u8{}; @@ -403,6 +401,25 @@ test "empty array to slice" { try comptime S.doTheTest(); } +test "comptime-known slice of a global constant" { + const S = struct { + const weights = [_]f32{ 1, 2, 3 }; + + inline fn first(comptime w: []const f32) f32 { + return w[0]; + } + }; + + var from_param: f32 = S.first(&S.weights); + _ = &from_param; + try expect(from_param == 1); + + const w: []const f32 = &S.weights; + var from_local: f32 = w[1]; + _ = &from_local; + try expect(from_local == 2); +} + test "@ptrCast slice to pointer" { if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; diff --git a/test/behavior/struct.zig b/test/behavior/struct.zig index b65833a251ae69f60cdef52c7e0f9eacc0e89e8d..4c12786161707d848fa11627cc952c6b0a72b10c 100644 --- a/test/behavior/struct.zig +++ b/test/behavior/struct.zig @@ -1631,8 +1631,6 @@ test "struct init with no result pointer sets field result types" { } test "runtime side-effects in comptime-known struct init" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - var side_effects: u4 = 0; const S = struct { a: u4, b: u4, c: u4, d: u4 }; const init = S{ @@ -2275,7 +2273,6 @@ test "struct contains underaligned field with overaligned pointer to itself" { } test "struct contains pointer to function accepting that struct" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; const S = struct { const FnPtr = ?*const fn (@This()) void; fn_ptr: FnPtr, diff --git a/test/behavior/type.zig b/test/behavior/type.zig index 5f310bd4729aeb51b755e50a1eafa4d38d8e5c82..875e77ffcb037bce22bf124d5ecea3689913d163 100644 --- a/test/behavior/type.zig +++ b/test/behavior/type.zig @@ -406,8 +406,6 @@ test "matching captures causes opaque equivalence" { } test "reify enum where fields refers to part of array" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const field_names: [3][]const u8 = .{ "foo", "bar", undefined }; const field_values: [3]u8 = .{ undefined, 0, 1 }; const E = @Enum(u8, .exhaustive, field_names[0..2], field_values[1..3]); diff --git a/test/behavior/type_info.zig b/test/behavior/type_info.zig index eeea34879b7a7cb8226a8c9107b381a79981a474..12950e4f0bba3973b3e898f6afab42683ede7ae2 100644 --- a/test/behavior/type_info.zig +++ b/test/behavior/type_info.zig @@ -310,8 +310,6 @@ const TestStruct = struct { }; test "type info: packed struct info" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - try testPackedStruct(); try comptime testPackedStruct(); } @@ -596,8 +594,6 @@ test "value from struct @typeInfo default_value_ptr can be loaded at comptime" { } test "type info of tuple of string literal default value" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const struct_info = @typeInfo(@TypeOf(.{"hi"})).@"struct"; const struct_field_attrs = struct_info.field_attrs[0]; const struct_field_type = struct_info.field_types[0]; diff --git a/test/behavior/union.zig b/test/behavior/union.zig index 083da4df4bf67ae8b6d36a4a5dd3d1394f1f8ff4..298b77f122410c1a94698f48c4eca16b1880ae9e 100644 --- a/test/behavior/union.zig +++ b/test/behavior/union.zig @@ -1109,8 +1109,6 @@ test "union with a large struct field" { } test "comptime equality of extern unions with same tag" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const S = struct { const U = extern union { a: i32, @@ -1845,8 +1843,6 @@ test "inner struct initializer uses packed union layout" { } test "extern union initialized via reintepreted struct field initializer" { - if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest; - const bytes = [_]u8{ 0xaa, 0xbb, 0xcc, 0xdd }; const U = extern union { -- 2.54.0