From 11d8a359d6447d29d8fda37ee99e0e22e67d352d Mon Sep 17 00:00:00 2001 From: Ali Cheraghi Date: Wed, 8 Jul 2026 18:42:58 +0330 Subject: [PATCH] spirv: move some compile errors into frontend --- src/Sema.zig | 43 ++++++++++++++++--- src/codegen/spirv/CodeGen.zig | 38 ++++------------ .../loading_spirv_runtime_array_value.zig | 4 +- .../spirv_c_constraint_errors.zig | 33 ++++++++++++++ .../spirv_cannot_call_function_pointer.zig | 13 ++++++ .../spirv_extern_var_addrspace.zig | 11 +++++ ...pirv_pointer_cast_requires_offset_zero.zig | 2 +- .../spirv_unsupported_float_width.zig | 16 +++++++ 8 files changed, 123 insertions(+), 37 deletions(-) create mode 100644 test/cases/compile_errors/spirv_c_constraint_errors.zig create mode 100644 test/cases/compile_errors/spirv_cannot_call_function_pointer.zig create mode 100644 test/cases/compile_errors/spirv_extern_var_addrspace.zig create mode 100644 test/cases/compile_errors/spirv_unsupported_float_width.zig diff --git a/src/Sema.zig b/src/Sema.zig index 48303df878684068303d127882203272cba3ea84..9158aeeecd936aa7ea22e87c565e27b25eae0bd0 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6916,6 +6916,9 @@ fn analyzeCall( const is_inline_call = block.isComptime() or inline_requested; if (!is_inline_call) { + if (func_val == null and !func_is_extern and !block.is_typeof and zcu.getTarget().cpu.arch.isSpirV()) { + return sema.fail(block, func_src, "SPIR-V does not support calling function pointers", .{}); + } if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); errdefer msg.destroy(gpa); @@ -15184,6 +15187,23 @@ fn zirAsm( } const constraint = sema.code.nullTerminatedString(input.data.constraint); + if (zcu.getTarget().cpu.arch.isSpirV() and std.mem.eql(u8, constraint, "c")) { + const val = sema.resolveValue(arg.*) orelse { + return sema.fail(block, input_src, "assembly input with 'c' constraint must be compile-time known", .{}); + }; + if (val.isUndef(zcu)) { + return sema.fail(block, input_src, "assembly input with 'c' constraint cannot be undefined", .{}); + } + const bad_type: bool = switch (uncasted_arg_ty.zigTypeTag(zcu)) { + .bool, .int, .float, .comptime_int, .comptime_float, .enum_literal => false, + .vector => switch (uncasted_arg_ty.childType(zcu).zigTypeTag(zcu)) { + .bool, .int, .float => false, + else => true, + }, + else => true, + }; + if (bad_type) return sema.fail(block, input_src, "unsupported type '{f}' for 'c' constraint", .{uncasted_arg_ty.fmt(pt)}); + } needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; inputs[arg_i] = .{ .c = constraint, .n = name }; } @@ -34415,11 +34435,24 @@ pub fn resolveNavPtrModifiers( }, }; const target = zcu.getTarget(); - const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) { - .function => target_util.defaultAddressSpace(target, .function), - .variable => target_util.defaultAddressSpace(target, .global_mutable), - .constant => target_util.defaultAddressSpace(target, .global_constant), - else => unreachable, + const addrspace_body = zir_decl.addrspace_body orelse { + if (zir_decl.linkage == .@"extern" and + target.cpu.arch.isSpirV() and + nav_ty.zigTypeTag(zcu) != .@"fn") + { + return sema.fail( + block, + block.src(.{ .node_offset_var_decl_ty = .zero }), + "SPIR-V extern variables require an explicit address space", + .{}, + ); + } + break :as switch (addrspace_ctx) { + .function => target_util.defaultAddressSpace(target, .function), + .variable => target_util.defaultAddressSpace(target, .global_mutable), + .constant => target_util.defaultAddressSpace(target, .global_constant), + else => unreachable, + }; }; const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst); break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx); diff --git a/src/codegen/spirv/CodeGen.zig b/src/codegen/spirv/CodeGen.zig index b90be9c093e0e63d3ad32aaec53c4b695bf0d527..edf9a3628d9540abacdbaa713843bace8431e248 100644 --- a/src/codegen/spirv/CodeGen.zig +++ b/src/codegen/spirv/CodeGen.zig @@ -2222,14 +2222,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { 64 => target.cpu.has(.spirv, .float64), else => false, }; - - if (!supported) { - return cg.fail( - "floating point width of {} bits is not supported for the current SPIR-V feature set", - .{bits}, - ); - } - + if (!supported) return cg.fail( + "'{f}' is not supported on the current SPIR-V feature set", + .{ty.fmt(cg.pt)}, + ); return try cg.floatType(bits); }, .array => { @@ -4591,10 +4587,6 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode const zcu = cg.zcu; const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; - if (cg.typeOf(bin_op.lhs).isVector(zcu) and !cg.typeOf(bin_op.rhs).isVector(zcu)) { - return cg.fail("vector shift with scalar rhs", .{}); - } - const base = try cg.temporary(bin_op.lhs); const shift = try cg.temporary(bin_op.rhs); @@ -5425,10 +5417,6 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; - if (cg.typeOf(extra.lhs).isVector(zcu) and !cg.typeOf(extra.rhs).isVector(zcu)) { - return cg.fail("vector shift with scalar rhs", .{}); - } - const base = try cg.temporary(extra.lhs); const shift = try cg.temporary(extra.rhs); @@ -8685,16 +8673,9 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { const input_ty = cg.typeOf(in.operand); if (std.mem.eql(u8, in.constraint, "c")) { - // constant - const val: Value = .fromInterned(in.operand.toInterned() orelse { - return cg.fail("assembly inputs with 'c' constraint have to be compile-time known", .{}); - }); - + const val: Value = .fromInterned(in.operand.toInterned().?); const ip = &zcu.intern_pool; const target = cg.pt.zcu.getTarget(); - if (ip.indexToKey(val.toIntern()) == .undef) { - return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}); - } switch (input_ty.zigTypeTag(zcu)) { .int => { const bits: u64 = switch (input_ty.intInfo(zcu).signedness) { @@ -8708,7 +8689,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))), 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))), 64 => @bitCast(val.toFloat(f64, zcu)), - else => return cg.fail("unsupported float width for 'c' constraint", .{}), + else => unreachable, // Sema rejects unsupported float widths. }; try ass.value_map.put(gpa, in.name, .{ .constant = bits }); }, @@ -8719,7 +8700,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { .bool => 0, .int => @intCast(child_ty.intInfo(zcu).bits), .float => child_ty.floatBits(target), - else => return cg.fail("'c' constraint vector element must be bool, int, or float", .{}), + else => unreachable, // Sema rejects unsupported vector element types. }; const vec_len: usize = @intCast(input_ty.vectorLen(zcu)); const values = try gpa.alloc(u64, vec_len); @@ -8753,7 +8734,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }), else => unreachable, }, - else => return cg.fail("unsupported type for 'c' constraint", .{}), + else => unreachable, // Sema rejects unsupported types. } } else if (std.mem.eql(u8, in.constraint, "t")) { // type @@ -8840,8 +8821,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) const callee_ty = cg.typeOf(air_call.callee); const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { .@"fn" => callee_ty, - .pointer => return cg.fail("cannot call function pointers", .{}), - else => unreachable, + else => unreachable, // rejected by Sema for SPIR-V }; const fn_info = zcu.typeToFunc(zig_fn_ty).?; const return_type = fn_info.return_type; diff --git a/test/cases/compile_errors/loading_spirv_runtime_array_value.zig b/test/cases/compile_errors/loading_spirv_runtime_array_value.zig index b3473fa406eb8883f7fbf372c22e075386b10f71..39cd38b36f0e3bce36884911de9b9e2f4f03c55f 100644 --- a/test/cases/compile_errors/loading_spirv_runtime_array_value.zig +++ b/test/cases/compile_errors/loading_spirv_runtime_array_value.zig @@ -6,11 +6,11 @@ const buf = @extern(*addrspace(.storage_buffer) Buffer, .{ .name = "buf", .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, }); -export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { +export fn main() callconv(.kernel) void { const a = buf.data; _ = a; } -export fn main2() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { +export fn main2() callconv(.kernel) void { const p: *addrspace(.storage_buffer) const RuntimeArray = &buf.data; _ = p.*; } diff --git a/test/cases/compile_errors/spirv_c_constraint_errors.zig b/test/cases/compile_errors/spirv_c_constraint_errors.zig new file mode 100644 index 0000000000000000000000000000000000000000..e7d2a5601b47337c860e27b350e25b0444a2c8ed --- /dev/null +++ b/test/cases/compile_errors/spirv_c_constraint_errors.zig @@ -0,0 +1,33 @@ +export fn not_comptime() callconv(.kernel) void { + var runtime: u32 = 42; + _ = &runtime; + _ = asm ("%ret = OpSpecConstant %ty $default" + : [ret] "" (-> u32), + : [ty] "t" (u32), + [default] "c" (runtime), + ); +} + +export fn undef_input() callconv(.kernel) void { + const x: u32 = undefined; + _ = asm ("%ret = OpDummy $x" + : [ret] "" (-> u32), + : [x] "c" (x), + ); +} + +export fn unsupported_type() callconv(.kernel) void { + const s = "hi"; + _ = asm ("%ret = OpDummy $x" + : [ret] "" (-> u32), + : [x] "c" (s), + ); +} + +// error +// backend=selfhosted +// target=spirv32-vulkan +// +// :7:26: error: assembly input with 'c' constraint must be compile-time known +// :15:20: error: assembly input with 'c' constraint cannot be undefined +// :23:20: error: unsupported type '*const [2:0]u8' for 'c' constraint diff --git a/test/cases/compile_errors/spirv_cannot_call_function_pointer.zig b/test/cases/compile_errors/spirv_cannot_call_function_pointer.zig new file mode 100644 index 0000000000000000000000000000000000000000..1eadba5473c6182163faa8537c3d2dda366e4305 --- /dev/null +++ b/test/cases/compile_errors/spirv_cannot_call_function_pointer.zig @@ -0,0 +1,13 @@ +fn foo() void {} + +export fn main() callconv(.kernel) void { + var fp = &foo; + fp = &foo; + fp(); +} + +// error +// backend=selfhosted +// target=spirv32-vulkan +// +// :6:5: error: SPIR-V does not support calling function pointers diff --git a/test/cases/compile_errors/spirv_extern_var_addrspace.zig b/test/cases/compile_errors/spirv_extern_var_addrspace.zig new file mode 100644 index 0000000000000000000000000000000000000000..b750eda17fda0f48874a05697ca37b0570d66995 --- /dev/null +++ b/test/cases/compile_errors/spirv_extern_var_addrspace.zig @@ -0,0 +1,11 @@ +extern var x: u32; + +export fn main() callconv(.kernel) void { + _ = x; +} + +// error +// backend=selfhosted +// target=spirv64-vulkan +// +// :1:15: error: SPIR-V extern variables require an explicit address space diff --git a/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig b/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig index da77d7cd169be3f6bb7274f7424bec87101e01dc..8eda14440d21815df25b8e9e509a46f9b1eb1db6 100644 --- a/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig +++ b/test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig @@ -6,7 +6,7 @@ const a = @extern(*addrspace(.uniform) const A, .{ .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, }); -export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { +export fn main() callconv(.kernel) void { const b: *addrspace(.uniform) const B = @ptrCast(a); _ = &b; } diff --git a/test/cases/compile_errors/spirv_unsupported_float_width.zig b/test/cases/compile_errors/spirv_unsupported_float_width.zig new file mode 100644 index 0000000000000000000000000000000000000000..364e9f8385f607af4f766bdd742c261a782635a8 --- /dev/null +++ b/test/cases/compile_errors/spirv_unsupported_float_width.zig @@ -0,0 +1,16 @@ +export fn use_f80() callconv(.kernel) void { + var x: f80 = 1.5; + _ = &x; +} + +export fn use_f16() callconv(.kernel) void { + var x: f16 = 1.5; + _ = &x; +} + +// error +// backend=selfhosted +// target=spirv32-vulkan +// +// :2:5: error: 'f80' is not supported on the current SPIR-V feature set +// :7:5: error: 'f16' is not supported on the current SPIR-V feature set -- 2.54.0