diff options
| -rw-r--r-- | src/Sema.zig | 36 | ||||
| -rw-r--r-- | src/target.zig | 25 | ||||
| -rw-r--r-- | test/cases/compile_errors/spirv_merge_logical_pointers.zig | 19 | ||||
| -rw-r--r-- | test/cases/spirv_mergable_pointers.zig | 16 | ||||
| -rw-r--r-- | test/src/Cases.zig | 2 |
5 files changed, 97 insertions, 1 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index b0ce7729cf..b0ce90d277 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -6319,6 +6319,9 @@ fn resolveAnalyzedBlock( for (merges.results.items, merges.src_locs.items) |merge_inst, merge_src| { try sema.validateRuntimeValue(child_block, merge_src orelse src, merge_inst); } + + try sema.checkMergeAllowed(child_block, type_src, resolved_ty); + const ty_inst = Air.internedToRef(resolved_ty.toIntern()); switch (block_tag) { .block => { @@ -9754,6 +9757,39 @@ fn checkCallConvSupportsVarArgs(sema: *Sema, block: *Block, src: LazySrcLoc, cc: } } +fn checkMergeAllowed(sema: *Sema, block: *Block, src: LazySrcLoc, peer_ty: Type) !void { + const pt = sema.pt; + const zcu = pt.zcu; + const target = zcu.getTarget(); + + if (!peer_ty.isPtrAtRuntime(zcu)) { + return; + } + + const as = peer_ty.ptrAddressSpace(zcu); + if (!target_util.arePointersLogical(target, as)) { + return; + } + + return sema.failWithOwnedErrorMsg(block, msg: { + const msg = try sema.errMsg(src, "value with non-mergable pointer type '{}' depends on runtime control flow", .{peer_ty.fmt(pt)}); + errdefer msg.destroy(sema.gpa); + + const runtime_src = block.runtime_cond orelse block.runtime_loop.?; + try sema.errNote(runtime_src, msg, "runtime control flow here", .{}); + + const backend = target_util.zigBackend(target, zcu.comp.config.use_llvm); + try sema.errNote(src, msg, "pointers with address space '{s}' cannot be returned from a branch on target {s}-{s} by compiler backend {s}", .{ + @tagName(as), + target.cpu.arch.genericName(), + @tagName(target.os.tag), + @tagName(backend), + }); + + break :msg msg; + }); +} + const Section = union(enum) { generic, default, diff --git a/src/target.zig b/src/target.zig index 1f8a567f03..b0d2bdfe74 100644 --- a/src/target.zig +++ b/src/target.zig @@ -398,6 +398,31 @@ pub fn addrSpaceCastIsValid( } } +/// Under SPIR-V with Vulkan, pointers are not 'real' (physical), but rather 'logical'. Effectively, +/// this means that all such pointers have to be resolvable to a location at compile time, and places +/// a number of restrictions on usage of such pointers. For example, a logical pointer may not be +/// part of a merge (result of a branch) and may not be stored in memory at all. This function returns +/// for a particular architecture and address space wether such pointers are logical. +pub fn arePointersLogical(target: std.Target, as: AddressSpace) bool { + if (target.os.tag != .vulkan) { + return false; + } + + return switch (as) { + // TODO: Vulkan doesn't support pointers in the generic address space, we + // should remove this case but this requires a change in defaultAddressSpace(). + // For now, at least disable them from being regarded as physical. + .generic => true, + // For now, all global pointers are represented using PhysicalStorageBuffer, so these are real + // pointers. + .global => false, + // TODO: Allowed with VK_KHR_variable_pointers. + .shared => true, + .constant, .local, .input, .output, .uniform => true, + else => unreachable, + }; +} + pub fn llvmMachineAbi(target: std.Target) ?[:0]const u8 { // LLD does not support ELFv1. Rather than having LLVM produce ELFv1 code and then linking it // into a broken ELFv2 binary, just force LLVM to use ELFv2 as well. This will break when glibc diff --git a/test/cases/compile_errors/spirv_merge_logical_pointers.zig b/test/cases/compile_errors/spirv_merge_logical_pointers.zig new file mode 100644 index 0000000000..ea81ef903c --- /dev/null +++ b/test/cases/compile_errors/spirv_merge_logical_pointers.zig @@ -0,0 +1,19 @@ +export fn a() void { + var x: *i32 = undefined; + _ = &x; + var y: *i32 = undefined; + _ = &y; + var rt_cond = false; + _ = &rt_cond; + + var z = if (rt_cond) x else y; + _ = &z; +} + +// error +// backend=stage2 +// target=spirv64-vulkan +// +// :9:13: error: value with non-mergable pointer type '*i32' depends on runtime control flow +// :9:17: note: runtime control flow here +// :9:13: note: pointers with address space 'generic' cannot be returned from a branch on target spirv-vulkan by compiler backend stage2_spirv64 diff --git a/test/cases/spirv_mergable_pointers.zig b/test/cases/spirv_mergable_pointers.zig new file mode 100644 index 0000000000..240c485f9d --- /dev/null +++ b/test/cases/spirv_mergable_pointers.zig @@ -0,0 +1,16 @@ +export fn a() void { + var x: *addrspace(.global) i32 = undefined; + _ = &x; + var y: *addrspace(.global) i32 = undefined; + _ = &y; + var rt_cond = false; + _ = &rt_cond; + + var z = if (rt_cond) x else y; + _ = &z; +} + +// compile +// output_mode=Obj +// backend=stage2 +// target=spirv64-vulkan diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 0e88bdd133..9314b5677b 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -467,7 +467,7 @@ fn addFromDirInner( const target = resolved_target.result; for (backends) |backend| { if (backend == .stage2 and - target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64) + target.cpu.arch != .wasm32 and target.cpu.arch != .x86_64 and target.cpu.arch != .spirv64) { // Other backends don't support new liveness format continue; |
