authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-08-10 19:45:16+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-08-10 19:45:16+02:00
log6bb6013d3a4da18ca79c5d4d882920cc147520bf
tree8ddc329028ab11506fc22fd9a9a673243e26628a
parent298328581534de4b7cb984f7d5e8a5c4080a881c
parent64563e2fffd0e304019c343a32f31c925be20ea2
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #24768 from alichraghi/spv5

spirv: remove prune_unused ISel

15 files changed, 154 insertions(+), 484 deletions(-)

src/codegen/spirv/CodeGen.zig+96-105
......@@ -274,6 +274,13 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
274274 .storage_class = storage_class,
275275 });
276276
277 if (nav.getAlignment() != ty.abiAlignment(zcu)) {
278 if (target.os.tag != .opencl) return cg.fail("cannot apply alignment to variables", .{});
279 try cg.module.decorate(result_id, .{
280 .alignment = .{ .alignment = @intCast(nav.getAlignment().toByteUnits().?) },
281 });
282 }
283
277284 switch (target.os.tag) {
278285 .vulkan, .opengl => {
279286 if (ty.zigTypeTag(zcu) == .@"struct") {
......@@ -348,7 +355,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
348355 .id_result_type = ptr_ty_id,
349356 .id_result = result_id,
350357 .set = try cg.module.importInstructionSet(.zig),
351 .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere...
358 .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) },
352359 .id_ref_4 = &.{initializer_id},
353360 });
354361 } else {
......@@ -356,7 +363,7 @@ pub fn genNav(cg: *CodeGen, do_codegen: bool) Error!void {
356363 .id_result_type = ptr_ty_id,
357364 .id_result = result_id,
358365 .set = try cg.module.importInstructionSet(.zig),
359 .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere...
366 .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) },
360367 .id_ref_4 = &.{},
361368 });
362369 }
......@@ -498,7 +505,7 @@ fn resolveUav(cg: *CodeGen, val: InternPool.Index) !Id {
498505 .id_result_type = fn_decl_ptr_ty_id,
499506 .id_result = result_id,
500507 .set = try cg.module.importInstructionSet(.zig),
501 .instruction = .{ .inst = 0 }, // TODO: Put this definition somewhere...
508 .instruction = .{ .inst = @intFromEnum(spec.Zig.InvocationGlobal) },
502509 .id_ref_4 = &.{initializer_id},
503510 });
504511 }
......@@ -1037,9 +1044,18 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id {
10371044 const gpa = cg.module.gpa;
10381045 const pt = cg.pt;
10391046 const zcu = cg.module.zcu;
1047 const target = zcu.getTarget();
10401048 switch (derivation) {
10411049 .comptime_alloc_ptr, .comptime_field_ptr => unreachable,
10421050 .int => |int| {
1051 if (target.os.tag != .opencl) {
1052 if (int.ptr_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
1053 return cg.fail(
1054 "cannot cast integer to pointer with address space '{s}'",
1055 .{@tagName(int.ptr_ty.ptrAddressSpace(zcu))},
1056 );
1057 }
1058 }
10431059 const result_ty_id = try cg.resolveType(int.ptr_ty, .direct);
10441060 // TODO: This can probably be an OpSpecConstantOp Bitcast, but
10451061 // that is not implemented by Mesa yet. Therefore, just generate it
......@@ -1137,7 +1153,7 @@ fn constantUavRef(
11371153 // Uav refs are always generic.
11381154 assert(ty.ptrAddressSpace(zcu) == .generic);
11391155 const uav_ty_id = try cg.resolveType(uav_ty, .indirect);
1140 const decl_ptr_ty_id = try cg.module.ptrType(uav_ty_id, .generic);
1156 const decl_ptr_ty_id = try cg.module.ptrType(uav_ty_id, .function);
11411157 const ptr_id = try cg.resolveUav(uav.val);
11421158
11431159 if (decl_ptr_ty_id != ty_id) {
......@@ -1327,7 +1343,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
13271343 },
13281344 .void => switch (repr) {
13291345 .direct => return try cg.module.voidType(),
1330 .indirect => return try cg.module.opaqueType("void"),
1346 .indirect => {
1347 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
1348 return try cg.module.opaqueType("void");
1349 },
13311350 },
13321351 .bool => switch (repr) {
13331352 .direct => return try cg.module.boolType(),
......@@ -1337,6 +1356,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
13371356 const int_info = ty.intInfo(zcu);
13381357 if (int_info.bits == 0) {
13391358 assert(repr == .indirect);
1359 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
13401360 return try cg.module.opaqueType("u0");
13411361 }
13421362 return try cg.module.intType(int_info.signedness, int_info.bits);
......@@ -1369,6 +1389,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
13691389
13701390 if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
13711391 assert(repr == .indirect);
1392 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
13721393 return try cg.module.opaqueType("zero-sized-array");
13731394 } else if (total_len == 0) {
13741395 // The size of the array would be 0, but that is not allowed in SPIR-V.
......@@ -1590,6 +1611,7 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
15901611 return try cg.module.structType(&member_types, &member_names, null, .none);
15911612 },
15921613 .@"opaque" => {
1614 if (target.os.tag != .opencl) return cg.fail("cannot generate opaque type", .{});
15931615 const type_name = try cg.resolveTypeName(ty);
15941616 defer gpa.free(type_name);
15951617 return try cg.module.opaqueType(type_name);
......@@ -2510,11 +2532,7 @@ fn generateTestEntryPoint(
25102532 try cg.module.declareEntryPoint(spv_decl_index, test_name, execution_mode, null);
25112533}
25122534
2513fn intFromBool(cg: *CodeGen, value: Temporary) !Temporary {
2514 return try cg.intFromBool2(value, Type.u1);
2515}
2516
2517fn intFromBool2(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary {
2535fn intFromBool(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary {
25182536 const zero_id = try cg.constInt(result_ty, 0);
25192537 const one_id = try cg.constInt(result_ty, 1);
25202538
......@@ -2558,7 +2576,7 @@ fn convertToIndirect(cg: *CodeGen, ty: Type, operand_id: Id) !Id {
25582576 const zcu = cg.module.zcu;
25592577 switch (ty.scalarType(zcu).zigTypeTag(zcu)) {
25602578 .bool => {
2561 const result = try cg.intFromBool(Temporary.init(ty, operand_id));
2579 const result = try cg.intFromBool(.init(ty, operand_id), .u1);
25622580 return try result.materialize(cg);
25632581 },
25642582 else => return operand_id,
......@@ -2958,7 +2976,7 @@ fn normalize(cg: *CodeGen, value: Temporary, info: ArithmeticTypeInfo) !Temporar
29582976 .composite_integer, .integer, .bool, .float => return value,
29592977 .strange_integer => switch (info.signedness) {
29602978 .unsigned => {
2961 const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
2979 const mask_value = @as(u64, std.math.maxInt(u64)) >> @as(u6, @intCast(64 - info.bits));
29622980 const mask_id = try cg.constInt(ty.scalarType(zcu), mask_value);
29632981 return try cg.buildBinary(.OpBitwiseAnd, value, Temporary.init(ty.scalarType(zcu), mask_id));
29642982 },
......@@ -2997,28 +3015,12 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
29973015
29983016 const div = try cg.buildBinary(.OpSDiv, lhs, rhs);
29993017 const rem = try cg.buildBinary(.OpSRem, lhs, rhs);
3000
30013018 const zero: Temporary = .init(lhs.ty, try cg.constInt(lhs.ty, 0));
3002
3003 const rem_is_not_zero = try cg.buildCmp(.OpINotEqual, rem, zero);
3004
3005 const result_negative = try cg.buildCmp(
3006 .OpLogicalNotEqual,
3007 try cg.buildCmp(.OpSLessThan, lhs, zero),
3008 try cg.buildCmp(.OpSLessThan, rhs, zero),
3009 );
3010 const rem_is_not_zero_and_result_is_negative = try cg.buildBinary(
3011 .OpLogicalAnd,
3012 rem_is_not_zero,
3013 result_negative,
3014 );
3015
3016 const result = try cg.buildBinary(
3017 .OpISub,
3018 div,
3019 try cg.intFromBool2(rem_is_not_zero_and_result_is_negative, div.ty),
3020 );
3021
3019 const rem_non_zero = try cg.buildCmp(.OpINotEqual, rem, zero);
3020 const lhs_rhs_xor = try cg.buildBinary(.OpBitwiseXor, lhs, rhs);
3021 const signs_differ = try cg.buildCmp(.OpSLessThan, lhs_rhs_xor, zero);
3022 const adjust = try cg.buildBinary(.OpLogicalAnd, rem_non_zero, signs_differ);
3023 const result = try cg.buildBinary(.OpISub, div, try cg.intFromBool(adjust, div.ty));
30223024 return try result.materialize(cg);
30233025 },
30243026 .float => {
......@@ -3032,10 +3034,8 @@ fn airDivFloor(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
30323034
30333035fn airDivTrunc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
30343036 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3035
30363037 const lhs = try cg.temporary(bin_op.lhs);
30373038 const rhs = try cg.temporary(bin_op.rhs);
3038
30393039 const info = cg.arithmeticTypeInfo(lhs.ty);
30403040 switch (info.class) {
30413041 .composite_integer => unreachable, // TODO
......@@ -3073,12 +3073,9 @@ fn airArithOp(
30733073 comptime uop: Opcode,
30743074) !?Id {
30753075 const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op;
3076
30773076 const lhs = try cg.temporary(bin_op.lhs);
30783077 const rhs = try cg.temporary(bin_op.rhs);
3079
30803078 const info = cg.arithmeticTypeInfo(lhs.ty);
3081
30823079 const result = switch (info.class) {
30833080 .composite_integer => unreachable, // TODO
30843081 .integer, .strange_integer => switch (info.signedness) {
......@@ -3088,7 +3085,6 @@ fn airArithOp(
30883085 .float => try cg.buildBinary(fop, lhs, rhs),
30893086 .bool => unreachable,
30903087 };
3091
30923088 return try result.materialize(cg);
30933089}
30943090
......@@ -3105,12 +3101,10 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary {
31053101 const zcu = cg.module.zcu;
31063102 const target = cg.module.zcu.getTarget();
31073103 const operand_info = cg.arithmeticTypeInfo(value.ty);
3108
31093104 switch (operand_info.class) {
31103105 .float => return try cg.buildUnary(.f_abs, value),
31113106 .integer, .strange_integer => {
31123107 const abs_value = try cg.buildUnary(.i_abs, value);
3113
31143108 switch (target.os.tag) {
31153109 .vulkan, .opengl => {
31163110 if (value.ty.intInfo(zcu).signedness == .signed) {
......@@ -3119,7 +3113,6 @@ fn abs(cg: *CodeGen, result_ty: Type, value: Temporary) !Temporary {
31193113 },
31203114 else => {},
31213115 }
3122
31233116 return try cg.normalize(abs_value, cg.arithmeticTypeInfo(result_ty));
31243117 },
31253118 .composite_integer => unreachable, // TODO
......@@ -3134,19 +3127,18 @@ fn airAddSubOverflow(
31343127 u_opcode: Opcode,
31353128 s_opcode: Opcode,
31363129) !?Id {
3137 _ = s_opcode;
31383130 // Note: OpIAddCarry and OpISubBorrow are not really useful here: For unsigned numbers,
31393131 // there is in both cases only one extra operation required. For signed operations,
31403132 // the overflow bit is set then going from 0x80.. to 0x00.., but this doesn't actually
31413133 // normally set a carry bit. So the SPIR-V overflow operations are not particularly
31423134 // useful here.
31433135
3136 _ = s_opcode;
3137
31443138 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
31453139 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
3146
31473140 const lhs = try cg.temporary(extra.lhs);
31483141 const rhs = try cg.temporary(extra.rhs);
3149
31503142 const result_ty = cg.typeOfIndex(inst);
31513143
31523144 const info = cg.arithmeticTypeInfo(lhs.ty);
......@@ -3158,7 +3150,6 @@ fn airAddSubOverflow(
31583150
31593151 const sum = try cg.buildBinary(add, lhs, rhs);
31603152 const result = try cg.normalize(sum, info);
3161
31623153 const overflowed = switch (info.signedness) {
31633154 // Overflow happened if the result is smaller than either of the operands. It doesn't matter which.
31643155 // For subtraction the conditions need to be swapped.
......@@ -3173,38 +3164,31 @@ fn airAddSubOverflow(
31733164 // and the result's sign is different from the minuend's (a's) sign.
31743165 // (sign(a) != sign(b)) && (sign(a) != sign(result))
31753166 const zero: Temporary = .init(rhs.ty, try cg.constInt(rhs.ty, 0));
3176
31773167 const lhs_is_neg = try cg.buildCmp(.OpSLessThan, lhs, zero);
31783168 const rhs_is_neg = try cg.buildCmp(.OpSLessThan, rhs, zero);
31793169 const result_is_neg = try cg.buildCmp(.OpSLessThan, result, zero);
3180
31813170 const signs_match = try cg.buildCmp(.OpLogicalEqual, lhs_is_neg, rhs_is_neg);
31823171 const result_sign_differs = try cg.buildCmp(.OpLogicalNotEqual, lhs_is_neg, result_is_neg);
3183
3184 const overflow_condition = if (add == .OpIAdd)
3185 signs_match
3186 else // .OpISub
3187 try cg.buildUnary(.l_not, signs_match);
3188
3172 const overflow_condition = switch (add) {
3173 .OpIAdd => signs_match,
3174 .OpISub => try cg.buildUnary(.l_not, signs_match),
3175 else => unreachable,
3176 };
31893177 break :blk try cg.buildCmp(.OpLogicalAnd, overflow_condition, result_sign_differs);
31903178 },
31913179 };
31923180
3193 const ov = try cg.intFromBool(overflowed);
3194
3181 const ov = try cg.intFromBool(overflowed, .u1);
31953182 const result_ty_id = try cg.resolveType(result_ty, .direct);
31963183 return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) });
31973184}
31983185
31993186fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
32003187 const pt = cg.pt;
3201
32023188 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
32033189 const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data;
3204
32053190 const lhs = try cg.temporary(extra.lhs);
32063191 const rhs = try cg.temporary(extra.rhs);
3207
32083192 const result_ty = cg.typeOfIndex(inst);
32093193
32103194 const info = cg.arithmeticTypeInfo(lhs.ty);
......@@ -3237,20 +3221,15 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
32373221 const op_ty = try pt.intType(.unsigned, op_ty_bits);
32383222 const casted_lhs = try cg.buildConvert(op_ty, lhs);
32393223 const casted_rhs = try cg.buildConvert(op_ty, rhs);
3240
32413224 const full_result = try cg.buildBinary(.OpIMul, casted_lhs, casted_rhs);
3242
32433225 const low_bits = try cg.buildConvert(lhs.ty, full_result);
32443226 const result = try cg.normalize(low_bits, info);
3245
32463227 // Shift the result bits away to get the overflow bits.
32473228 const shift: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, info.bits));
32483229 const overflow = try cg.buildBinary(.OpShiftRightLogical, full_result, shift);
3249
32503230 // Directly check if its zero in the op_ty without converting first.
32513231 const zero: Temporary = .init(full_result.ty, try cg.constInt(full_result.ty, 0));
32523232 const overflowed = try cg.buildCmp(.OpINotEqual, zero, overflow);
3253
32543233 break :blk .{ result, overflowed };
32553234 }
32563235
......@@ -3362,7 +3341,7 @@ fn airMulOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
33623341 },
33633342 };
33643343
3365 const ov = try cg.intFromBool(overflowed);
3344 const ov = try cg.intFromBool(overflowed, .u1);
33663345
33673346 const result_ty_id = try cg.resolveType(result_ty, .direct);
33683347 return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) });
......@@ -3403,7 +3382,7 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
34033382 };
34043383
34053384 const overflowed = try cg.buildCmp(.OpINotEqual, base, right);
3406 const ov = try cg.intFromBool(overflowed);
3385 const ov = try cg.intFromBool(overflowed, .u1);
34073386
34083387 const result_ty_id = try cg.resolveType(result_ty, .direct);
34093388 return try cg.constructComposite(result_ty_id, &.{ try result.materialize(cg), try ov.materialize(cg) });
......@@ -3931,6 +3910,7 @@ fn bitCast(
39313910) !Id {
39323911 const gpa = cg.module.gpa;
39333912 const zcu = cg.module.zcu;
3913 const target = zcu.getTarget();
39343914 const src_ty_id = try cg.resolveType(src_ty, .direct);
39353915 const dst_ty_id = try cg.resolveType(dst_ty, .direct);
39363916
......@@ -3941,6 +3921,15 @@ fn bitCast(
39413921 // See fn bitCast in llvm.zig
39423922
39433923 if (src_ty.zigTypeTag(zcu) == .int and dst_ty.isPtrAtRuntime(zcu)) {
3924 if (target.os.tag != .opencl) {
3925 if (dst_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
3926 return cg.fail(
3927 "cannot cast integer to pointer with address space '{s}'",
3928 .{@tagName(dst_ty.ptrAddressSpace(zcu))},
3929 );
3930 }
3931 }
3932
39443933 const result_id = cg.module.allocId();
39453934 try cg.body.emit(gpa, .OpConvertUToPtr, .{
39463935 .id_result_type = dst_ty_id,
......@@ -3967,7 +3956,8 @@ fn bitCast(
39673956
39683957 const dst_ptr_ty_id = try cg.module.ptrType(dst_ty_id, .function);
39693958
3970 const tmp_id = try cg.alloc(src_ty, .{ .storage_class = .function });
3959 const src_ty_indirect_id = try cg.resolveType(src_ty, .indirect);
3960 const tmp_id = try cg.alloc(src_ty_indirect_id, null);
39713961 try cg.store(src_ty, tmp_id, src_id, .{});
39723962 const casted_ptr_id = cg.module.allocId();
39733963 try cg.body.emit(gpa, .OpBitcast, .{
......@@ -3997,7 +3987,7 @@ fn airBitCast(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
39973987 const result_ty = cg.typeOfIndex(inst);
39983988 if (operand_ty.toIntern() == .bool_type) {
39993989 const operand = try cg.temporary(ty_op.operand);
4000 const result = try cg.intFromBool(operand);
3990 const result = try cg.intFromBool(operand, .u1);
40013991 return try result.materialize(cg);
40023992 }
40033993 const operand_id = try cg.resolve(ty_op.operand);
......@@ -4420,7 +4410,6 @@ fn airArrayElemVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
44204410 // TODO: This backend probably also should use isByRef from llvm...
44214411
44224412 const is_vector = array_ty.isVector(zcu);
4423
44244413 const elem_repr: Repr = if (is_vector) .direct else .indirect;
44254414 const array_ty_id = try cg.resolveType(array_ty, .direct);
44264415 const elem_ty_id = try cg.resolveType(elem_ty, elem_repr);
......@@ -4588,7 +4577,8 @@ fn unionInit(
45884577 return try cg.constInt(tag_ty, tag_int);
45894578 }
45904579
4591 const tmp_id = try cg.alloc(ty, .{ .storage_class = .function });
4580 const ty_id = try cg.resolveType(ty, .indirect);
4581 const tmp_id = try cg.alloc(ty_id, null);
45924582
45934583 if (layout.tag_size != 0) {
45944584 const tag_ty_id = try cg.resolveType(tag_ty, .indirect);
......@@ -4709,7 +4699,8 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
47094699 const layout = cg.unionLayout(object_ty);
47104700 assert(layout.has_payload);
47114701
4712 const tmp_id = try cg.alloc(object_ty, .{ .storage_class = .function });
4702 const object_ty_id = try cg.resolveType(object_ty, .indirect);
4703 const tmp_id = try cg.alloc(object_ty_id, null);
47134704 try cg.store(object_ty, tmp_id, object_id, .{});
47144705
47154706 const layout_payload_ty_id = try cg.resolveType(layout.payload_ty, .indirect);
......@@ -4733,13 +4724,16 @@ fn airStructFieldVal(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
47334724
47344725fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
47354726 const zcu = cg.module.zcu;
4727 const target = zcu.getTarget();
47364728 const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
47374729 const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
47384730
4739 const parent_ty = ty_pl.ty.toType().childType(zcu);
4740 const result_ty_id = try cg.resolveType(ty_pl.ty.toType(), .indirect);
4731 const parent_ptr_ty = ty_pl.ty.toType();
4732 const parent_ty = parent_ptr_ty.childType(zcu);
4733 const result_ty_id = try cg.resolveType(parent_ptr_ty, .indirect);
47414734
47424735 const field_ptr = try cg.resolve(extra.field_ptr);
4736 const field_ptr_ty = cg.typeOf(extra.field_ptr);
47434737 const field_ptr_int = try cg.intFromPtr(field_ptr);
47444738 const field_offset = parent_ty.structFieldOffset(extra.field_index, zcu);
47454739
......@@ -4753,6 +4747,15 @@ fn airFieldParentPtr(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
47534747 break :base_ptr_int try result.materialize(cg);
47544748 };
47554749
4750 if (target.os.tag != .opencl) {
4751 if (field_ptr_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) {
4752 return cg.fail(
4753 "cannot cast integer to pointer with address space '{s}'",
4754 .{@tagName(field_ptr_ty.ptrAddressSpace(zcu))},
4755 );
4756 }
4757 }
4758
47564759 const base_ptr = cg.module.allocId();
47574760 try cg.body.emit(cg.module.gpa, .OpConvertUToPtr, .{
47584761 .id_result_type = result_ty_id,
......@@ -4821,46 +4824,33 @@ fn airStructFieldPtrIndex(cg: *CodeGen, inst: Air.Inst.Index, field_index: u32)
48214824 return try cg.structFieldPtr(result_ptr_ty, struct_ptr_ty, struct_ptr, field_index);
48224825}
48234826
4824const AllocOptions = struct {
4825 initializer: ?Id = null,
4826 /// The final storage class of the pointer. This may be either `.Generic` or `.Function`.
4827 /// In either case, the local is allocated in the `.Function` storage class, and optionally
4828 /// cast back to `.Generic`.
4829 storage_class: StorageClass,
4830};
4831
4832// Allocate a function-local variable, with possible initializer.
4833// This function returns a pointer to a variable of type `ty`,
4834// which is in the Generic address space. The variable is actually
4835// placed in the Function address space.
4836fn alloc(
4837 cg: *CodeGen,
4838 ty: Type,
4839 options: AllocOptions,
4840) !Id {
4841 const ty_id = try cg.resolveType(ty, .indirect);
4842 const ptr_fn_ty_id = try cg.module.ptrType(ty_id, .function);
4843
4844 // SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
4845 // directly generate them into func.prologue instead of the body.
4846 const var_id = cg.module.allocId();
4827fn alloc(cg: *CodeGen, ty_id: Id, initializer: ?Id) !Id {
4828 const ptr_ty_id = try cg.module.ptrType(ty_id, .function);
4829 const result_id = cg.module.allocId();
48474830 try cg.prologue.emit(cg.module.gpa, .OpVariable, .{
4848 .id_result_type = ptr_fn_ty_id,
4849 .id_result = var_id,
4831 .id_result_type = ptr_ty_id,
4832 .id_result = result_id,
48504833 .storage_class = .function,
4851 .initializer = options.initializer,
4834 .initializer = initializer,
48524835 });
4853
4854 return var_id;
4836 return result_id;
48554837}
48564838
48574839fn airAlloc(cg: *CodeGen, inst: Air.Inst.Index) !?Id {
48584840 const zcu = cg.module.zcu;
4841 const target = zcu.getTarget();
48594842 const ptr_ty = cg.typeOfIndex(inst);
48604843 const child_ty = ptr_ty.childType(zcu);
4861 return try cg.alloc(child_ty, .{
4862 .storage_class = cg.module.storageClass(ptr_ty.ptrAddressSpace(zcu)),
4863 });
4844 const child_ty_id = try cg.resolveType(child_ty, .indirect);
4845 const ptr_align = ptr_ty.ptrAlignment(zcu);
4846 const result_id = try cg.alloc(child_ty_id, null);
4847 if (ptr_align != child_ty.abiAlignment(zcu)) {
4848 if (target.os.tag != .opencl) return cg.fail("cannot apply alignment to variables", .{});
4849 try cg.module.decorate(result_id, .{
4850 .alignment = .{ .alignment = @intCast(ptr_align.toByteUnits().?) },
4851 });
4852 }
4853 return result_id;
48644854}
48654855
48664856fn airArg(cg: *CodeGen) Id {
......@@ -5087,7 +5077,8 @@ fn lowerBlock(cg: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index)
50875077 };
50885078
50895079 const maybe_block_result_var_id = if (have_block_result) blk: {
5090 const block_result_var_id = try cg.alloc(ty, .{ .storage_class = .function });
5080 const ty_id = try cg.resolveType(ty, .indirect);
5081 const block_result_var_id = try cg.alloc(ty_id, null);
50915082 try cf.block_results.putNoClobber(gpa, inst, block_result_var_id);
50925083 break :blk block_result_var_id;
50935084 } else null;
src/codegen/spirv/Module.zig+6-5
......@@ -676,8 +676,9 @@ pub fn structType(
676676 ip_index: InternPool.Index,
677677) !Id {
678678 const target = module.zcu.getTarget();
679 const actual_ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index;
679680
680 if (module.cache.struct_types.get(.{ .fields = types, .ip_index = ip_index })) |id| return id;
681 if (module.cache.struct_types.get(.{ .fields = types, .ip_index = actual_ip_index })) |id| return id;
681682 const result_id = module.allocId();
682683 const types_dup = try module.arena.dupe(Id, types);
683684 try module.sections.globals.emit(module.gpa, .OpTypeStruct, .{
......@@ -710,10 +711,7 @@ pub fn structType(
710711
711712 try module.cache.struct_types.put(
712713 module.gpa,
713 .{
714 .fields = types_dup,
715 .ip_index = if (module.zcu.comp.config.root_strip) .none else ip_index,
716 },
714 .{ .fields = types_dup, .ip_index = actual_ip_index },
717715 result_id,
718716 );
719717 return result_id;
......@@ -874,6 +872,7 @@ pub fn declareEntryPoint(
874872}
875873
876874pub fn debugName(module: *Module, target: Id, name: []const u8) !void {
875 if (module.zcu.comp.config.root_strip) return;
877876 try module.sections.debug_names.emit(module.gpa, .OpName, .{
878877 .target = target,
879878 .name = name,
......@@ -881,12 +880,14 @@ pub fn debugName(module: *Module, target: Id, name: []const u8) !void {
881880}
882881
883882pub fn debugNameFmt(module: *Module, target: Id, comptime fmt: []const u8, args: anytype) !void {
883 if (module.zcu.comp.config.root_strip) return;
884884 const name = try std.fmt.allocPrint(module.gpa, fmt, args);
885885 defer module.gpa.free(name);
886886 try module.debugName(target, name);
887887}
888888
889889pub fn memberDebugName(module: *Module, target: Id, member: u32, name: []const u8) !void {
890 if (module.zcu.comp.config.root_strip) return;
890891 try module.sections.debug_names.emit(module.gpa, .OpMemberName, .{
891892 .type = target,
892893 .member = member,
src/link/SpirV.zig+4-11
......@@ -10,10 +10,11 @@ const Compilation = @import("../Compilation.zig");
1010const link = @import("../link.zig");
1111const Air = @import("../Air.zig");
1212const Type = @import("../Type.zig");
13const BinaryModule = @import("SpirV/BinaryModule.zig");
1413const CodeGen = @import("../codegen/spirv/CodeGen.zig");
1514const Module = @import("../codegen/spirv/Module.zig");
1615const trace = @import("../tracy.zig").trace;
16const BinaryModule = @import("SpirV/BinaryModule.zig");
17const lower_invocation_globals = @import("SpirV/lower_invocation_globals.zig");
1718
1819const spec = @import("../codegen/spirv/spec.zig");
1920const Id = spec.Id;
......@@ -279,7 +280,7 @@ pub fn flush(
279280 const module = try linker.module.finalize(arena);
280281 errdefer arena.free(module);
281282
282 const linked_module = linker.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
283 const linked_module = linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
283284 error.OutOfMemory => return error.OutOfMemory,
284285 else => |other| return diags.fail("error while linking: {s}", .{@errorName(other)}),
285286 };
......@@ -288,18 +289,10 @@ pub fn flush(
288289 return diags.fail("failed to write: {s}", .{@errorName(err)});
289290}
290291
291fn linkModule(linker: *Linker, arena: Allocator, module: []Word, progress: std.Progress.Node) ![]Word {
292 _ = linker;
293
294 const lower_invocation_globals = @import("SpirV/lower_invocation_globals.zig");
295 const prune_unused = @import("SpirV/prune_unused.zig");
296
292fn linkModule(arena: Allocator, module: []Word, progress: std.Progress.Node) ![]Word {
297293 var parser = try BinaryModule.Parser.init(arena);
298294 defer parser.deinit();
299295 var binary = try parser.parse(module);
300
301296 try lower_invocation_globals.run(&parser, &binary, progress);
302 try prune_unused.run(&parser, &binary, progress);
303
304297 return binary.finalize(arena);
305298}
src/link/SpirV/lower_invocation_globals.zig+10-1
......@@ -382,6 +382,15 @@ const ModuleBuilder = struct {
382382 var it = binary.iterateInstructions();
383383 while (it.next()) |inst| {
384384 switch (inst.opcode) {
385 .OpName => {
386 const id: ResultId = @enumFromInt(inst.operands[0]);
387 if (info.invocation_globals.contains(id)) continue;
388 },
389 .OpExtInstImport => {
390 const set_id: ResultId = @enumFromInt(inst.operands[0]);
391 const set = binary.ext_inst_map.get(set_id).?;
392 if (set == .zig) continue;
393 },
385394 .OpExtInst => {
386395 const set_id: ResultId = @enumFromInt(inst.operands[2]);
387396 const set_inst = inst.operands[3];
......@@ -482,7 +491,7 @@ const ModuleBuilder = struct {
482491 return entry.value_ptr.*;
483492 }
484493
485 /// Rewrite the modules' functions and emit them with the new parameter types.
494 /// Rewrite the modules functions and emit them with the new parameter types.
486495 fn rewriteFunctions(
487496 self: *ModuleBuilder,
488497 parser: *BinaryModule.Parser,
src/link/SpirV/prune_unused.zig deleted-362
......@@ -1,362 +0,0 @@
1//! This pass is used to simple pruning of unused things:
2//! - Instructions at global scope
3//! - Functions
4//! Debug info and nonsemantic instructions are not handled;
5//! this pass is mainly intended for cleaning up left over
6//! stuff from codegen and other passes that is generated
7//! but not actually used.
8
9const std = @import("std");
10const Allocator = std.mem.Allocator;
11const assert = std.debug.assert;
12const log = std.log.scoped(.spirv_link);
13
14const BinaryModule = @import("BinaryModule.zig");
15const Section = @import("../../codegen/spirv/Section.zig");
16const spec = @import("../../codegen/spirv/spec.zig");
17const Opcode = spec.Opcode;
18const ResultId = spec.Id;
19const Word = spec.Word;
20
21/// Return whether a particular opcode's instruction can be pruned.
22/// These are idempotent instructions at globals scope and instructions
23/// within functions that do not have any side effects.
24/// The opcodes that return true here do not necessarily need to
25/// have an .Id. If they don't, then they are regarded
26/// as 'decoration'-style instructions that don't keep their
27/// operands alive, but will be emitted if they are.
28fn canPrune(op: Opcode) bool {
29 // This list should be as worked out as possible, but just
30 // getting common instructions is a good effort/effect ratio.
31 // When adding items to this list, also check whether the
32 // instruction requires any special control flow rules (like
33 // with labels and control flow and stuff) and whether the
34 // instruction has any non-trivial side effects (like OpLoad
35 // with the Volatile memory semantics).
36 return switch (op.class()) {
37 .type_declaration,
38 .conversion,
39 .arithmetic,
40 .relational_and_logical,
41 .bit,
42 .annotation,
43 => true,
44 else => switch (op) {
45 .OpFunction,
46 .OpUndef,
47 .OpString,
48 .OpName,
49 .OpMemberName,
50 // Prune OpConstant* instructions but
51 // retain OpSpecConstant declaration instructions
52 .OpConstantTrue,
53 .OpConstantFalse,
54 .OpConstant,
55 .OpConstantComposite,
56 .OpConstantSampler,
57 .OpConstantNull,
58 .OpSpecConstantOp,
59 // Prune ext inst import instructions, but not
60 // ext inst instructions themselves, because
61 // we don't know if they might have side effects.
62 .OpExtInstImport,
63 => true,
64 else => false,
65 },
66 };
67}
68
69const ModuleInfo = struct {
70 const Fn = struct {
71 /// The index of the first callee in `callee_store`.
72 first_callee: usize,
73 };
74
75 /// Maps function result-id -> Fn information structure.
76 functions: std.AutoArrayHashMapUnmanaged(ResultId, Fn),
77 /// For each function, a list of function result-ids that it calls.
78 callee_store: []const ResultId,
79 /// For each instruction, the offset at which it appears in the source module.
80 result_id_to_code_offset: std.AutoArrayHashMapUnmanaged(ResultId, usize),
81
82 /// Fetch the list of callees per function. Guaranteed to contain only unique IDs.
83 fn callees(self: ModuleInfo, fn_id: ResultId) []const ResultId {
84 const fn_index = self.functions.getIndex(fn_id).?;
85 const values = self.functions.values();
86 const first_callee = values[fn_index].first_callee;
87 if (fn_index == values.len - 1) {
88 return self.callee_store[first_callee..];
89 } else {
90 const next_first_callee = values[fn_index + 1].first_callee;
91 return self.callee_store[first_callee..next_first_callee];
92 }
93 }
94
95 /// Extract the information required to run this pass from the binary.
96 // TODO: Should the contents of this function be merged with that of lower_invocation_globals.zig?
97 // Many of the contents are the same...
98 fn parse(
99 arena: Allocator,
100 parser: *BinaryModule.Parser,
101 binary: BinaryModule,
102 ) !ModuleInfo {
103 var functions = std.AutoArrayHashMap(ResultId, Fn).init(arena);
104 var calls = std.AutoArrayHashMap(ResultId, void).init(arena);
105 var callee_store = std.ArrayList(ResultId).init(arena);
106 var result_id_to_code_offset = std.AutoArrayHashMap(ResultId, usize).init(arena);
107 var maybe_current_function: ?ResultId = null;
108 var it = binary.iterateInstructions();
109 while (it.next()) |inst| {
110 const inst_spec = parser.getInstSpec(inst.opcode).?;
111
112 // Result-id can only be the first or second operand
113 const maybe_result_id: ?ResultId = for (0..2) |i| {
114 if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) {
115 break @enumFromInt(inst.operands[i]);
116 }
117 } else null;
118
119 // Only add result-ids of functions and anything outside a function.
120 // Result-ids declared inside functions cannot be reached outside anyway,
121 // and we don't care about the internals of functions anyway.
122 // Note that in the case of OpFunction, `maybe_current_function` is
123 // also `null`, because it is set below.
124 if (maybe_result_id) |result_id| {
125 try result_id_to_code_offset.put(result_id, inst.offset);
126 }
127
128 switch (inst.opcode) {
129 .OpFunction => {
130 if (maybe_current_function) |current_function| {
131 log.err("OpFunction {f} does not have an OpFunctionEnd", .{current_function});
132 return error.InvalidPhysicalFormat;
133 }
134
135 maybe_current_function = @enumFromInt(inst.operands[1]);
136 },
137 .OpFunctionCall => {
138 const callee: ResultId = @enumFromInt(inst.operands[2]);
139 try calls.put(callee, {});
140 },
141 .OpFunctionEnd => {
142 const current_function = maybe_current_function orelse {
143 log.err("encountered OpFunctionEnd without corresponding OpFunction", .{});
144 return error.InvalidPhysicalFormat;
145 };
146 const entry = try functions.getOrPut(current_function);
147 if (entry.found_existing) {
148 log.err("Function {f} has duplicate definition", .{current_function});
149 return error.DuplicateId;
150 }
151
152 const first_callee = callee_store.items.len;
153 try callee_store.appendSlice(calls.keys());
154
155 entry.value_ptr.* = .{
156 .first_callee = first_callee,
157 };
158 maybe_current_function = null;
159 calls.clearRetainingCapacity();
160 },
161 else => {},
162 }
163 }
164
165 if (maybe_current_function) |current_function| {
166 log.err("OpFunction {f} does not have an OpFunctionEnd", .{current_function});
167 return error.InvalidPhysicalFormat;
168 }
169
170 return .{
171 .functions = functions.unmanaged,
172 .callee_store = callee_store.items,
173 .result_id_to_code_offset = result_id_to_code_offset.unmanaged,
174 };
175 }
176};
177
178const AliveMarker = struct {
179 parser: *BinaryModule.Parser,
180 binary: BinaryModule,
181 info: ModuleInfo,
182 result_id_offsets: std.ArrayList(u16),
183 alive: std.DynamicBitSetUnmanaged,
184
185 fn markAlive(self: *AliveMarker, result_id: ResultId) BinaryModule.ParseError!void {
186 const index = self.info.result_id_to_code_offset.getIndex(result_id) orelse {
187 log.err("undefined result-id {f}", .{result_id});
188 return error.InvalidId;
189 };
190
191 if (self.alive.isSet(index)) {
192 return;
193 }
194 self.alive.set(index);
195
196 const offset = self.info.result_id_to_code_offset.values()[index];
197 const inst = self.binary.instructionAt(offset);
198
199 if (inst.opcode == .OpFunction) {
200 try self.markFunctionAlive(inst);
201 } else {
202 try self.markInstructionAlive(inst);
203 }
204 }
205
206 fn markFunctionAlive(
207 self: *AliveMarker,
208 func_inst: BinaryModule.Instruction,
209 ) !void {
210 // Go through the instruction and mark the
211 // operands of each instruction alive.
212 var it = self.binary.iterateInstructionsFrom(func_inst.offset);
213 try self.markInstructionAlive(it.next().?);
214 while (it.next()) |inst| {
215 if (inst.opcode == .OpFunctionEnd) {
216 break;
217 }
218
219 if (!canPrune(inst.opcode)) {
220 try self.markInstructionAlive(inst);
221 }
222 }
223 }
224
225 fn markInstructionAlive(
226 self: *AliveMarker,
227 inst: BinaryModule.Instruction,
228 ) !void {
229 const start_offset = self.result_id_offsets.items.len;
230 try self.parser.parseInstructionResultIds(self.binary, inst, &self.result_id_offsets);
231 const end_offset = self.result_id_offsets.items.len;
232
233 // Recursive calls to markInstructionAlive() might change the pointer in self.result_id_offsets,
234 // so we need to iterate it manually.
235 var i = start_offset;
236 while (i < end_offset) : (i += 1) {
237 const offset = self.result_id_offsets.items[i];
238 try self.markAlive(@enumFromInt(inst.operands[offset]));
239 }
240 }
241};
242
243fn removeIdsFromMap(a: Allocator, map: anytype, info: ModuleInfo, alive_marker: AliveMarker) !void {
244 var to_remove = std.ArrayList(ResultId).init(a);
245 var it = map.iterator();
246 while (it.next()) |entry| {
247 const id = entry.key_ptr.*;
248 const index = info.result_id_to_code_offset.getIndex(id).?;
249 if (!alive_marker.alive.isSet(index)) {
250 try to_remove.append(id);
251 }
252 }
253
254 for (to_remove.items) |id| {
255 assert(map.remove(id));
256 }
257}
258
259pub fn run(parser: *BinaryModule.Parser, binary: *BinaryModule, progress: std.Progress.Node) !void {
260 const sub_node = progress.start("Prune unused IDs", 0);
261 defer sub_node.end();
262
263 var arena = std.heap.ArenaAllocator.init(parser.a);
264 defer arena.deinit();
265 const a = arena.allocator();
266
267 const info = try ModuleInfo.parse(a, parser, binary.*);
268
269 var alive_marker = AliveMarker{
270 .parser = parser,
271 .binary = binary.*,
272 .info = info,
273 .result_id_offsets = std.ArrayList(u16).init(a),
274 .alive = try std.DynamicBitSetUnmanaged.initEmpty(a, info.result_id_to_code_offset.count()),
275 };
276
277 // Mark initial stuff as alive
278 {
279 var it = binary.iterateInstructions();
280 while (it.next()) |inst| {
281 if (inst.opcode == .OpFunction) {
282 // No need to process further.
283 break;
284 } else if (!canPrune(inst.opcode)) {
285 try alive_marker.markInstructionAlive(inst);
286 }
287 }
288 }
289
290 var section = Section{};
291
292 sub_node.setEstimatedTotalItems(binary.instructions.len);
293
294 var new_functions_section: ?usize = null;
295 var it = binary.iterateInstructions();
296 skip: while (it.next()) |inst| {
297 defer sub_node.setCompletedItems(inst.offset);
298
299 const inst_spec = parser.getInstSpec(inst.opcode).?;
300
301 reemit: {
302 if (!canPrune(inst.opcode)) {
303 break :reemit;
304 }
305
306 // Result-id can only be the first or second operand
307 const result_id: ResultId = for (0..2) |i| {
308 if (inst_spec.operands.len > i and inst_spec.operands[i].kind == .id_result) {
309 break @enumFromInt(inst.operands[i]);
310 }
311 } else {
312 // Instruction can be pruned but doesn't have a result id.
313 // Check all operands to see if they are alive, and emit it only if so.
314 alive_marker.result_id_offsets.items.len = 0;
315 try parser.parseInstructionResultIds(binary.*, inst, &alive_marker.result_id_offsets);
316 for (alive_marker.result_id_offsets.items) |offset| {
317 const id: ResultId = @enumFromInt(inst.operands[offset]);
318 const index = info.result_id_to_code_offset.getIndex(id).?;
319
320 if (!alive_marker.alive.isSet(index)) {
321 continue :skip;
322 }
323 }
324
325 break :reemit;
326 };
327
328 const index = info.result_id_to_code_offset.getIndex(result_id).?;
329 if (alive_marker.alive.isSet(index)) {
330 break :reemit;
331 }
332
333 if (inst.opcode != .OpFunction) {
334 // Instruction can be pruned and its not alive, so skip it.
335 continue :skip;
336 }
337
338 // We're at the start of a function that can be pruned, so skip everything until
339 // we encounter an OpFunctionEnd.
340 while (it.next()) |body_inst| {
341 if (body_inst.opcode == .OpFunctionEnd)
342 break;
343 }
344
345 continue :skip;
346 }
347
348 if (inst.opcode == .OpFunction and new_functions_section == null) {
349 new_functions_section = section.instructions.items.len;
350 }
351
352 try section.emitRawInstruction(a, inst.opcode, inst.operands);
353 }
354
355 // This pass might have pruned ext inst imports or arith types, update
356 // those maps to main consistency.
357 try removeIdsFromMap(a, &binary.ext_inst_map, info, alive_marker);
358 try removeIdsFromMap(a, &binary.arith_type_width, info, alive_marker);
359
360 binary.instructions = try parser.a.dupe(Word, section.toWords());
361 binary.sections.functions = new_functions_section orelse binary.instructions.len;
362}
test/behavior/align.zig+2
......@@ -561,6 +561,8 @@ test "function pointer @intFromPtr/@ptrFromInt roundtrip" {
561561}
562562
563563test "function pointer align mask" {
564 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
565
564566 const int = if (builtin.cpu.arch.isArm() or builtin.cpu.arch.isMIPS()) 0x20202021 else 0x20202020;
565567 const unaligned: *const fn () callconv(.c) void = @ptrFromInt(int);
566568 const aligned: *align(16) const fn () callconv(.c) void = @alignCast(unaligned);
test/behavior/array.zig+2
......@@ -1088,6 +1088,8 @@ test "pass pointer to empty array initializer to anytype parameter" {
10881088}
10891089
10901090test "initialize pointer to anyopaque with reference to empty array initializer" {
1091 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1092
10911093 const ptr: *const anyopaque = &.{};
10921094 // The above acts like an untyped initializer, since the `.{}` has no result type.
10931095 // So, `ptr` points in memory to an empty tuple (`@TypeOf(.{})`).
test/behavior/cast.zig+10
......@@ -9,6 +9,8 @@ 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
1214 const x = @as(usize, 13);
1315 const y = @as(*u8, @ptrFromInt(x));
1416 const z = @intFromPtr(y);
......@@ -16,6 +18,8 @@ test "int to ptr cast" {
1618}
1719
1820test "integer literal to pointer cast" {
21 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
22
1923 const vga_mem = @as(*u16, @ptrFromInt(0xB8000));
2024 try expect(@intFromPtr(vga_mem) == 0xB8000);
2125}
......@@ -269,6 +273,8 @@ test "implicit cast from *[N]T to [*c]T" {
269273}
270274
271275test "*usize to *void" {
276 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
277
272278 var i = @as(usize, 0);
273279 const v: *void = @ptrCast(&i);
274280 v.* = {};
......@@ -1481,6 +1487,8 @@ test "coerce between pointers of compatible differently-named floats" {
14811487}
14821488
14831489test "peer type resolution of const and non-const pointer to array" {
1490 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1491
14841492 const a = @as(*[1024]u8, @ptrFromInt(42));
14851493 const b = @as(*const [1024]u8, @ptrFromInt(42));
14861494 try std.testing.expect(@TypeOf(a, b) == *const [1024]u8);
......@@ -1543,6 +1551,8 @@ test "optional pointer coerced to optional allowzero pointer" {
15431551}
15441552
15451553test "optional slice coerced to allowzero many pointer" {
1554 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1555
15461556 const a: ?[]const u32 = null;
15471557 const b: [*]allowzero const u8 = @ptrCast(a);
15481558 const c = @intFromPtr(b);
test/behavior/comptime_memory.zig+6
......@@ -406,6 +406,8 @@ test "mutate entire slice at comptime" {
406406}
407407
408408test "dereference undefined pointer to zero-bit type" {
409 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
410
409411 const p0: *void = undefined;
410412 try testing.expectEqual({}, p0.*);
411413
......@@ -421,6 +423,8 @@ test "type pun extern struct" {
421423}
422424
423425test "type pun @ptrFromInt" {
426 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
427
424428 const p: *u8 = @ptrFromInt(42);
425429 // note that expectEqual hides the bug
426430 try testing.expect(@as(*const [*]u8, @ptrCast(&p)).* == @as([*]u8, @ptrFromInt(42)));
......@@ -511,6 +515,8 @@ fn fieldPtrTest() u32 {
511515 return a.value;
512516}
513517test "pointer in aggregate field can mutate comptime state" {
518 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
519
514520 try comptime std.testing.expect(fieldPtrTest() == 2);
515521}
516522
test/behavior/generics.zig+1
......@@ -169,6 +169,7 @@ test "generic fn keeps non-generic parameter types" {
169169 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
170170 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
171171 if (builtin.zig_backend == .stage2_riscv64) return error.SkipZigTest;
172 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
172173
173174 const A = 128;
174175
test/behavior/pointers.zig+8
......@@ -267,6 +267,8 @@ test "implicit cast error unions with non-optional to optional pointer" {
267267}
268268
269269test "compare equality of optional and non-optional pointer" {
270 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
271
270272 const a = @as(*const usize, @ptrFromInt(0x12345678));
271273 const b = @as(?*usize, @ptrFromInt(0x12345678));
272274 try expect(a == b);
......@@ -453,6 +455,8 @@ test "pointer sentinel with +inf" {
453455}
454456
455457test "pointer to array at fixed address" {
458 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
459
456460 const array = @as(*volatile [2]u32, @ptrFromInt(0x10));
457461 // Silly check just to reference `array`
458462 try expect(@intFromPtr(&array[0]) == 0x10);
......@@ -494,6 +498,8 @@ test "pointer-integer arithmetic affects the alignment" {
494498}
495499
496500test "@intFromPtr on null optional at comptime" {
501 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
502
497503 {
498504 const pointer = @as(?*u8, @ptrFromInt(0x000));
499505 const x = @intFromPtr(pointer);
......@@ -704,6 +710,8 @@ test "pointer-to-array constness for zero-size elements, const" {
704710}
705711
706712test "cast pointers with zero sized elements" {
713 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
714
707715 const a: *void = undefined;
708716 const b: *[1]void = a;
709717 _ = b;
test/behavior/ptrfromint.zig+1
......@@ -44,6 +44,7 @@ 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;
4748
4849 const ptr = @as(*allowzero u32, @ptrFromInt(0));
4950 try expectEqual(@as(usize, 0), @intFromPtr(ptr));
test/behavior/slice.zig+2
......@@ -238,6 +238,8 @@ test "slicing pointer by length" {
238238const x = @as([*]i32, @ptrFromInt(0x1000))[0..0x500];
239239const y = x[0x100..];
240240test "compile time slice of pointer to hard coded address" {
241 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
242
241243 try expect(@intFromPtr(x) == 0x1000);
242244 try expect(x.len == 0x500);
243245
test/behavior/struct.zig+4
......@@ -1347,6 +1347,8 @@ test "struct field has a pointer to an aligned version of itself" {
13471347}
13481348
13491349test "struct has only one reference" {
1350 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
1351
13501352 const S = struct {
13511353 fn optionalStructParam(_: ?struct { x: u8 }) void {}
13521354 fn errorUnionStructParam(_: error{}!struct { x: u8 }) void {}
......@@ -1553,6 +1555,7 @@ test "struct field pointer has correct alignment" {
15531555 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
15541556 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15551557 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1558 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
15561559
15571560 const S = struct {
15581561 fn doTheTest() !void {
......@@ -1582,6 +1585,7 @@ test "extern struct field pointer has correct alignment" {
15821585 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
15831586 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15841587 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1588 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
15851589
15861590 const S = struct {
15871591 fn doTheTest() !void {
test/behavior/union.zig+2
......@@ -1481,6 +1481,7 @@ test "defined-layout union field pointer has correct alignment" {
14811481 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
14821482 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
14831483 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1484 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
14841485
14851486 const S = struct {
14861487 fn doTheTest(comptime U: type) !void {
......@@ -1515,6 +1516,7 @@ test "undefined-layout union field pointer has correct alignment" {
15151516 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
15161517 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
15171518 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1519 if (builtin.zig_backend == .stage2_spirv) return error.SkipZigTest;
15181520
15191521 const S = struct {
15201522 fn doTheTest(comptime U: type) !void {