| author | |
| committer | |
| log | 11d8a359d6447d29d8fda37ee99e0e22e67d352d |
| tree | 8a60dcae11d6c64735eb70f69ae7012378e6c613 |
| parent | 48821ea886e29c5568cd2036cd46ca435550aaa3 |
8 files changed, 123 insertions(+), 37 deletions(-)
src/Sema.zig+38-5| ... | @@ -6916,6 +6916,9 @@ fn analyzeCall( | ... | @@ -6916,6 +6916,9 @@ fn analyzeCall( |
| 6916 | const is_inline_call = block.isComptime() or inline_requested; | 6916 | const is_inline_call = block.isComptime() or inline_requested; |
| 6917 | 6917 | ||
| 6918 | if (!is_inline_call) { | 6918 | if (!is_inline_call) { |
| 6919 | if (func_val == null and !func_is_extern and !block.is_typeof and zcu.getTarget().cpu.arch.isSpirV()) { | ||
| 6920 | return sema.fail(block, func_src, "SPIR-V does not support calling function pointers", .{}); | ||
| 6921 | } | ||
| 6919 | if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { | 6922 | if (sema.func_is_naked) return sema.failWithOwnedErrorMsg(block, msg: { |
| 6920 | const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); | 6923 | const msg = try sema.errMsg(call_src, "runtime {s} not allowed in naked function", .{@tagName(operation)}); |
| 6921 | errdefer msg.destroy(gpa); | 6924 | errdefer msg.destroy(gpa); |
| ... | @@ -15184,6 +15187,23 @@ fn zirAsm( | ... | @@ -15184,6 +15187,23 @@ fn zirAsm( |
| 15184 | } | 15187 | } |
| 15185 | 15188 | ||
| 15186 | const constraint = sema.code.nullTerminatedString(input.data.constraint); | 15189 | const constraint = sema.code.nullTerminatedString(input.data.constraint); |
| 15190 | if (zcu.getTarget().cpu.arch.isSpirV() and std.mem.eql(u8, constraint, "c")) { | ||
| 15191 | const val = sema.resolveValue(arg.*) orelse { | ||
| 15192 | return sema.fail(block, input_src, "assembly input with 'c' constraint must be compile-time known", .{}); | ||
| 15193 | }; | ||
| 15194 | if (val.isUndef(zcu)) { | ||
| 15195 | return sema.fail(block, input_src, "assembly input with 'c' constraint cannot be undefined", .{}); | ||
| 15196 | } | ||
| 15197 | const bad_type: bool = switch (uncasted_arg_ty.zigTypeTag(zcu)) { | ||
| 15198 | .bool, .int, .float, .comptime_int, .comptime_float, .enum_literal => false, | ||
| 15199 | .vector => switch (uncasted_arg_ty.childType(zcu).zigTypeTag(zcu)) { | ||
| 15200 | .bool, .int, .float => false, | ||
| 15201 | else => true, | ||
| 15202 | }, | ||
| 15203 | else => true, | ||
| 15204 | }; | ||
| 15205 | if (bad_type) return sema.fail(block, input_src, "unsupported type '{f}' for 'c' constraint", .{uncasted_arg_ty.fmt(pt)}); | ||
| 15206 | } | ||
| 15187 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; | 15207 | needed_capacity += (constraint.len + name.len + (2 + 3)) / 4; |
| 15188 | inputs[arg_i] = .{ .c = constraint, .n = name }; | 15208 | inputs[arg_i] = .{ .c = constraint, .n = name }; |
| 15189 | } | 15209 | } |
| ... | @@ -34415,11 +34435,24 @@ pub fn resolveNavPtrModifiers( | ... | @@ -34415,11 +34435,24 @@ pub fn resolveNavPtrModifiers( |
| 34415 | }, | 34435 | }, |
| 34416 | }; | 34436 | }; |
| 34417 | const target = zcu.getTarget(); | 34437 | const target = zcu.getTarget(); |
| 34418 | const addrspace_body = zir_decl.addrspace_body orelse break :as switch (addrspace_ctx) { | 34438 | const addrspace_body = zir_decl.addrspace_body orelse { |
| 34419 | .function => target_util.defaultAddressSpace(target, .function), | 34439 | if (zir_decl.linkage == .@"extern" and |
| 34420 | .variable => target_util.defaultAddressSpace(target, .global_mutable), | 34440 | target.cpu.arch.isSpirV() and |
| 34421 | .constant => target_util.defaultAddressSpace(target, .global_constant), | 34441 | nav_ty.zigTypeTag(zcu) != .@"fn") |
| 34422 | else => unreachable, | 34442 | { |
| 34443 | return sema.fail( | ||
| 34444 | block, | ||
| 34445 | block.src(.{ .node_offset_var_decl_ty = .zero }), | ||
| 34446 | "SPIR-V extern variables require an explicit address space", | ||
| 34447 | .{}, | ||
| 34448 | ); | ||
| 34449 | } | ||
| 34450 | break :as switch (addrspace_ctx) { | ||
| 34451 | .function => target_util.defaultAddressSpace(target, .function), | ||
| 34452 | .variable => target_util.defaultAddressSpace(target, .global_mutable), | ||
| 34453 | .constant => target_util.defaultAddressSpace(target, .global_constant), | ||
| 34454 | else => unreachable, | ||
| 34455 | }; | ||
| 34423 | }; | 34456 | }; |
| 34424 | const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst); | 34457 | const addrspace_ref = try sema.resolveInlineBody(block, addrspace_body, decl_inst); |
| 34425 | break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx); | 34458 | break :as try sema.analyzeAsAddressSpace(block, addrspace_src, addrspace_ref, addrspace_ctx); |
src/codegen/spirv/CodeGen.zig+9-29| ... | @@ -2222,14 +2222,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { | ... | @@ -2222,14 +2222,10 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id { |
| 2222 | 64 => target.cpu.has(.spirv, .float64), | 2222 | 64 => target.cpu.has(.spirv, .float64), |
| 2223 | else => false, | 2223 | else => false, |
| 2224 | }; | 2224 | }; |
| 2225 | 2225 | if (!supported) return cg.fail( | |
| 2226 | if (!supported) { | 2226 | "'{f}' is not supported on the current SPIR-V feature set", |
| 2227 | return cg.fail( | 2227 | .{ty.fmt(cg.pt)}, |
| 2228 | "floating point width of {} bits is not supported for the current SPIR-V feature set", | 2228 | ); |
| 2229 | .{bits}, | ||
| 2230 | ); | ||
| 2231 | } | ||
| 2232 | |||
| 2233 | return try cg.floatType(bits); | 2229 | return try cg.floatType(bits); |
| 2234 | }, | 2230 | }, |
| 2235 | .array => { | 2231 | .array => { |
| ... | @@ -4591,10 +4587,6 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode | ... | @@ -4591,10 +4587,6 @@ fn airShift(cg: *CodeGen, inst: Air.Inst.Index, unsigned: Opcode, signed: Opcode |
| 4591 | const zcu = cg.zcu; | 4587 | const zcu = cg.zcu; |
| 4592 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; | 4588 | const bin_op = cg.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; |
| 4593 | 4589 | ||
| 4594 | if (cg.typeOf(bin_op.lhs).isVector(zcu) and !cg.typeOf(bin_op.rhs).isVector(zcu)) { | ||
| 4595 | return cg.fail("vector shift with scalar rhs", .{}); | ||
| 4596 | } | ||
| 4597 | |||
| 4598 | const base = try cg.temporary(bin_op.lhs); | 4590 | const base = try cg.temporary(bin_op.lhs); |
| 4599 | const shift = try cg.temporary(bin_op.rhs); | 4591 | const shift = try cg.temporary(bin_op.rhs); |
| 4600 | 4592 | ||
| ... | @@ -5425,10 +5417,6 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -5425,10 +5417,6 @@ fn airShlOverflow(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 5425 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; | 5417 | const ty_pl = cg.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; |
| 5426 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; | 5418 | const extra = cg.air.extraData(Air.Bin, ty_pl.payload).data; |
| 5427 | 5419 | ||
| 5428 | if (cg.typeOf(extra.lhs).isVector(zcu) and !cg.typeOf(extra.rhs).isVector(zcu)) { | ||
| 5429 | return cg.fail("vector shift with scalar rhs", .{}); | ||
| 5430 | } | ||
| 5431 | |||
| 5432 | const base = try cg.temporary(extra.lhs); | 5420 | const base = try cg.temporary(extra.lhs); |
| 5433 | const shift = try cg.temporary(extra.rhs); | 5421 | const shift = try cg.temporary(extra.rhs); |
| 5434 | 5422 | ||
| ... | @@ -8685,16 +8673,9 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -8685,16 +8673,9 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 8685 | const input_ty = cg.typeOf(in.operand); | 8673 | const input_ty = cg.typeOf(in.operand); |
| 8686 | 8674 | ||
| 8687 | if (std.mem.eql(u8, in.constraint, "c")) { | 8675 | if (std.mem.eql(u8, in.constraint, "c")) { |
| 8688 | // constant | 8676 | const val: Value = .fromInterned(in.operand.toInterned().?); |
| 8689 | const val: Value = .fromInterned(in.operand.toInterned() orelse { | ||
| 8690 | return cg.fail("assembly inputs with 'c' constraint have to be compile-time known", .{}); | ||
| 8691 | }); | ||
| 8692 | |||
| 8693 | const ip = &zcu.intern_pool; | 8677 | const ip = &zcu.intern_pool; |
| 8694 | const target = cg.pt.zcu.getTarget(); | 8678 | const target = cg.pt.zcu.getTarget(); |
| 8695 | if (ip.indexToKey(val.toIntern()) == .undef) { | ||
| 8696 | return cg.fail("assembly input with 'c' constraint cannot be undefined", .{}); | ||
| 8697 | } | ||
| 8698 | switch (input_ty.zigTypeTag(zcu)) { | 8679 | switch (input_ty.zigTypeTag(zcu)) { |
| 8699 | .int => { | 8680 | .int => { |
| 8700 | const bits: u64 = switch (input_ty.intInfo(zcu).signedness) { | 8681 | const bits: u64 = switch (input_ty.intInfo(zcu).signedness) { |
| ... | @@ -8708,7 +8689,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -8708,7 +8689,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 8708 | 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))), | 8689 | 16 => @as(u16, @bitCast(val.toFloat(f16, zcu))), |
| 8709 | 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))), | 8690 | 32 => @as(u32, @bitCast(val.toFloat(f32, zcu))), |
| 8710 | 64 => @bitCast(val.toFloat(f64, zcu)), | 8691 | 64 => @bitCast(val.toFloat(f64, zcu)), |
| 8711 | else => return cg.fail("unsupported float width for 'c' constraint", .{}), | 8692 | else => unreachable, // Sema rejects unsupported float widths. |
| 8712 | }; | 8693 | }; |
| 8713 | try ass.value_map.put(gpa, in.name, .{ .constant = bits }); | 8694 | try ass.value_map.put(gpa, in.name, .{ .constant = bits }); |
| 8714 | }, | 8695 | }, |
| ... | @@ -8719,7 +8700,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -8719,7 +8700,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 8719 | .bool => 0, | 8700 | .bool => 0, |
| 8720 | .int => @intCast(child_ty.intInfo(zcu).bits), | 8701 | .int => @intCast(child_ty.intInfo(zcu).bits), |
| 8721 | .float => child_ty.floatBits(target), | 8702 | .float => child_ty.floatBits(target), |
| 8722 | else => return cg.fail("'c' constraint vector element must be bool, int, or float", .{}), | 8703 | else => unreachable, // Sema rejects unsupported vector element types. |
| 8723 | }; | 8704 | }; |
| 8724 | const vec_len: usize = @intCast(input_ty.vectorLen(zcu)); | 8705 | const vec_len: usize = @intCast(input_ty.vectorLen(zcu)); |
| 8725 | const values = try gpa.alloc(u64, vec_len); | 8706 | const values = try gpa.alloc(u64, vec_len); |
| ... | @@ -8753,7 +8734,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { | ... | @@ -8753,7 +8734,7 @@ fn airAssembly(cg: *CodeGen, inst: Air.Inst.Index) !?Id { |
| 8753 | .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }), | 8734 | .enum_literal => |str| try ass.value_map.put(gpa, in.name, .{ .string = str.toSlice(ip) }), |
| 8754 | else => unreachable, | 8735 | else => unreachable, |
| 8755 | }, | 8736 | }, |
| 8756 | else => return cg.fail("unsupported type for 'c' constraint", .{}), | 8737 | else => unreachable, // Sema rejects unsupported types. |
| 8757 | } | 8738 | } |
| 8758 | } else if (std.mem.eql(u8, in.constraint, "t")) { | 8739 | } else if (std.mem.eql(u8, in.constraint, "t")) { |
| 8759 | // type | 8740 | // type |
| ... | @@ -8840,8 +8821,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) | ... | @@ -8840,8 +8821,7 @@ fn airCall(cg: *CodeGen, inst: Air.Inst.Index, modifier: std.lang.CallModifier) |
| 8840 | const callee_ty = cg.typeOf(air_call.callee); | 8821 | const callee_ty = cg.typeOf(air_call.callee); |
| 8841 | const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { | 8822 | const zig_fn_ty = switch (callee_ty.zigTypeTag(zcu)) { |
| 8842 | .@"fn" => callee_ty, | 8823 | .@"fn" => callee_ty, |
| 8843 | .pointer => return cg.fail("cannot call function pointers", .{}), | 8824 | else => unreachable, // rejected by Sema for SPIR-V |
| 8844 | else => unreachable, | ||
| 8845 | }; | 8825 | }; |
| 8846 | const fn_info = zcu.typeToFunc(zig_fn_ty).?; | 8826 | const fn_info = zcu.typeToFunc(zig_fn_ty).?; |
| 8847 | const return_type = fn_info.return_type; | 8827 | const return_type = fn_info.return_type; |
test/cases/compile_errors/loading_spirv_runtime_array_value.zig+2-2| ... | @@ -6,11 +6,11 @@ const buf = @extern(*addrspace(.storage_buffer) Buffer, .{ | ... | @@ -6,11 +6,11 @@ const buf = @extern(*addrspace(.storage_buffer) Buffer, .{ |
| 6 | .name = "buf", | 6 | .name = "buf", |
| 7 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, | 7 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, |
| 8 | }); | 8 | }); |
| 9 | export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { | 9 | export fn main() callconv(.kernel) void { |
| 10 | const a = buf.data; | 10 | const a = buf.data; |
| 11 | _ = a; | 11 | _ = a; |
| 12 | } | 12 | } |
| 13 | export fn main2() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { | 13 | export fn main2() callconv(.kernel) void { |
| 14 | const p: *addrspace(.storage_buffer) const RuntimeArray = &buf.data; | 14 | const p: *addrspace(.storage_buffer) const RuntimeArray = &buf.data; |
| 15 | _ = p.*; | 15 | _ = p.*; |
| 16 | } | 16 | } |
test/cases/compile_errors/spirv_c_constraint_errors.zig created+33| ... | @@ -0,0 +1,33 @@ | ||
| 1 | export fn not_comptime() callconv(.kernel) void { | ||
| 2 | var runtime: u32 = 42; | ||
| 3 | _ = &runtime; | ||
| 4 | _ = asm ("%ret = OpSpecConstant %ty $default" | ||
| 5 | : [ret] "" (-> u32), | ||
| 6 | : [ty] "t" (u32), | ||
| 7 | [default] "c" (runtime), | ||
| 8 | ); | ||
| 9 | } | ||
| 10 | |||
| 11 | export fn undef_input() callconv(.kernel) void { | ||
| 12 | const x: u32 = undefined; | ||
| 13 | _ = asm ("%ret = OpDummy $x" | ||
| 14 | : [ret] "" (-> u32), | ||
| 15 | : [x] "c" (x), | ||
| 16 | ); | ||
| 17 | } | ||
| 18 | |||
| 19 | export fn unsupported_type() callconv(.kernel) void { | ||
| 20 | const s = "hi"; | ||
| 21 | _ = asm ("%ret = OpDummy $x" | ||
| 22 | : [ret] "" (-> u32), | ||
| 23 | : [x] "c" (s), | ||
| 24 | ); | ||
| 25 | } | ||
| 26 | |||
| 27 | // error | ||
| 28 | // backend=selfhosted | ||
| 29 | // target=spirv32-vulkan | ||
| 30 | // | ||
| 31 | // :7:26: error: assembly input with 'c' constraint must be compile-time known | ||
| 32 | // :15:20: error: assembly input with 'c' constraint cannot be undefined | ||
| 33 | // :23:20: error: unsupported type '*const [2:0]u8' for 'c' constraint | ||
test/cases/compile_errors/spirv_cannot_call_function_pointer.zig created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | fn foo() void {} | ||
| 2 | |||
| 3 | export fn main() callconv(.kernel) void { | ||
| 4 | var fp = &foo; | ||
| 5 | fp = &foo; | ||
| 6 | fp(); | ||
| 7 | } | ||
| 8 | |||
| 9 | // error | ||
| 10 | // backend=selfhosted | ||
| 11 | // target=spirv32-vulkan | ||
| 12 | // | ||
| 13 | // :6:5: error: SPIR-V does not support calling function pointers | ||
test/cases/compile_errors/spirv_extern_var_addrspace.zig created+11| ... | @@ -0,0 +1,11 @@ | ||
| 1 | extern var x: u32; | ||
| 2 | |||
| 3 | export fn main() callconv(.kernel) void { | ||
| 4 | _ = x; | ||
| 5 | } | ||
| 6 | |||
| 7 | // error | ||
| 8 | // backend=selfhosted | ||
| 9 | // target=spirv64-vulkan | ||
| 10 | // | ||
| 11 | // :1:15: error: SPIR-V extern variables require an explicit address space | ||
test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig+1-1| ... | @@ -6,7 +6,7 @@ const a = @extern(*addrspace(.uniform) const A, .{ | ... | @@ -6,7 +6,7 @@ const a = @extern(*addrspace(.uniform) const A, .{ |
| 6 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, | 6 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, |
| 7 | }); | 7 | }); |
| 8 | 8 | ||
| 9 | export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { | 9 | export fn main() callconv(.kernel) void { |
| 10 | const b: *addrspace(.uniform) const B = @ptrCast(a); | 10 | const b: *addrspace(.uniform) const B = @ptrCast(a); |
| 11 | _ = &b; | 11 | _ = &b; |
| 12 | } | 12 | } |
test/cases/compile_errors/spirv_unsupported_float_width.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | export fn use_f80() callconv(.kernel) void { | ||
| 2 | var x: f80 = 1.5; | ||
| 3 | _ = &x; | ||
| 4 | } | ||
| 5 | |||
| 6 | export fn use_f16() callconv(.kernel) void { | ||
| 7 | var x: f16 = 1.5; | ||
| 8 | _ = &x; | ||
| 9 | } | ||
| 10 | |||
| 11 | // error | ||
| 12 | // backend=selfhosted | ||
| 13 | // target=spirv32-vulkan | ||
| 14 | // | ||
| 15 | // :2:5: error: 'f80' is not supported on the current SPIR-V feature set | ||
| 16 | // :7:5: error: 'f16' is not supported on the current SPIR-V feature set | ||