Merge pull request #21826 from Snektron/spirv-vulkan
spirv: vulkan setup
This commit is contained in:
@@ -19,8 +19,7 @@ pub const simplified_logic =
|
||||
builtin.zig_backend == .stage2_aarch64 or
|
||||
builtin.zig_backend == .stage2_arm or
|
||||
builtin.zig_backend == .stage2_sparc64 or
|
||||
builtin.cpu.arch == .spirv32 or
|
||||
builtin.cpu.arch == .spirv64;
|
||||
builtin.zig_backend == .stage2_spirv64;
|
||||
|
||||
comptime {
|
||||
// No matter what, we import the root file, so that any export, test, comptime
|
||||
@@ -37,7 +36,7 @@ comptime {
|
||||
if (!@hasDecl(root, "wWinMainCRTStartup") and !@hasDecl(root, "mainCRTStartup")) {
|
||||
@export(&wWinMainCRTStartup2, .{ .name = "wWinMainCRTStartup" });
|
||||
}
|
||||
} else if (builtin.os.tag == .opencl) {
|
||||
} else if (builtin.os.tag == .opencl or builtin.os.tag == .vulkan) {
|
||||
if (@hasDecl(root, "main"))
|
||||
@export(&spirvMain2, .{ .name = "main" });
|
||||
} else {
|
||||
|
||||
36
src/Sema.zig
36
src/Sema.zig
@@ -6253,6 +6253,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 => {
|
||||
@@ -9688,6 +9691,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,
|
||||
|
||||
@@ -3639,11 +3639,8 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
|
||||
else => false,
|
||||
},
|
||||
.stage2_spirv64 => switch (cc) {
|
||||
.spirv_device,
|
||||
.spirv_kernel,
|
||||
.spirv_fragment,
|
||||
.spirv_vertex,
|
||||
=> true,
|
||||
.spirv_device, .spirv_kernel => true,
|
||||
.spirv_fragment, .spirv_vertex => target.os.tag == .vulkan,
|
||||
else => false,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -169,6 +169,13 @@ pub const Object = struct {
|
||||
/// via the usual `intern_map` mechanism.
|
||||
ptr_types: PtrTypeMap = .{},
|
||||
|
||||
/// For test declarations for Vulkan, we have to add a push constant with a pointer to a
|
||||
/// buffer that we can use. We only need to generate this once, this holds the link information
|
||||
/// related to that.
|
||||
error_push_constant: ?struct {
|
||||
push_constant_ptr: SpvModule.Decl.Index,
|
||||
} = null,
|
||||
|
||||
pub fn init(gpa: Allocator) Object {
|
||||
return .{
|
||||
.gpa = gpa,
|
||||
@@ -1640,13 +1647,18 @@ const NavGen = struct {
|
||||
|
||||
comptime assert(zig_call_abi_ver == 3);
|
||||
switch (fn_info.cc) {
|
||||
.auto, .spirv_kernel, .spirv_fragment, .spirv_vertex => {},
|
||||
else => @panic("TODO"),
|
||||
.auto,
|
||||
.spirv_kernel,
|
||||
.spirv_fragment,
|
||||
.spirv_vertex,
|
||||
.spirv_device,
|
||||
=> {},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
// TODO: Put this somewhere in Sema.zig
|
||||
if (fn_info.is_var_args)
|
||||
return self.fail("VarArgs functions are unsupported for SPIR-V", .{});
|
||||
// Guaranteed by callConvSupportsVarArgs, there are nog SPIR-V CCs which support
|
||||
// varargs.
|
||||
assert(!fn_info.is_var_args);
|
||||
|
||||
// Note: Logic is different from functionType().
|
||||
const param_ty_ids = try self.gpa.alloc(IdRef, fn_info.param_types.len);
|
||||
@@ -1838,11 +1850,16 @@ const NavGen = struct {
|
||||
return switch (as) {
|
||||
.generic => switch (target.os.tag) {
|
||||
.vulkan => .Private,
|
||||
else => .Generic,
|
||||
.opencl => .Generic,
|
||||
else => unreachable,
|
||||
},
|
||||
.shared => .Workgroup,
|
||||
.local => .Private,
|
||||
.global => .CrossWorkgroup,
|
||||
.global => switch (target.os.tag) {
|
||||
.opencl => .CrossWorkgroup,
|
||||
.vulkan => .PhysicalStorageBuffer,
|
||||
else => unreachable,
|
||||
},
|
||||
.constant => .UniformConstant,
|
||||
.input => .Input,
|
||||
.output => .Output,
|
||||
@@ -2898,30 +2915,118 @@ const NavGen = struct {
|
||||
.flags = .{ .address_space = .global },
|
||||
});
|
||||
const ptr_anyerror_ty_id = try self.resolveType(ptr_anyerror_ty, .direct);
|
||||
const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty});
|
||||
|
||||
const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
|
||||
|
||||
const spv_decl_index = try self.spv.allocDecl(.func);
|
||||
const kernel_id = self.spv.declPtr(spv_decl_index).result_id;
|
||||
|
||||
const error_id = self.spv.allocId();
|
||||
const p_error_id = self.spv.allocId();
|
||||
// for some reason we don't need to decorate the push constant here...
|
||||
try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
|
||||
|
||||
const section = &self.spv.sections.functions;
|
||||
try section.emit(self.spv.gpa, .OpFunction, .{
|
||||
.id_result_type = try self.resolveType(Type.void, .direct),
|
||||
.id_result = kernel_id,
|
||||
.function_control = .{},
|
||||
.function_type = kernel_proto_ty_id,
|
||||
});
|
||||
try section.emit(self.spv.gpa, .OpFunctionParameter, .{
|
||||
.id_result_type = ptr_anyerror_ty_id,
|
||||
.id_result = p_error_id,
|
||||
});
|
||||
try section.emit(self.spv.gpa, .OpLabel, .{
|
||||
.id_result = self.spv.allocId(),
|
||||
});
|
||||
|
||||
const target = self.getTarget();
|
||||
|
||||
const p_error_id = self.spv.allocId();
|
||||
switch (target.os.tag) {
|
||||
.opencl => {
|
||||
const kernel_proto_ty_id = try self.functionType(Type.void, &.{ptr_anyerror_ty});
|
||||
|
||||
try section.emit(self.spv.gpa, .OpFunction, .{
|
||||
.id_result_type = try self.resolveType(Type.void, .direct),
|
||||
.id_result = kernel_id,
|
||||
.function_control = .{},
|
||||
.function_type = kernel_proto_ty_id,
|
||||
});
|
||||
|
||||
try section.emit(self.spv.gpa, .OpFunctionParameter, .{
|
||||
.id_result_type = ptr_anyerror_ty_id,
|
||||
.id_result = p_error_id,
|
||||
});
|
||||
|
||||
try section.emit(self.spv.gpa, .OpLabel, .{
|
||||
.id_result = self.spv.allocId(),
|
||||
});
|
||||
},
|
||||
.vulkan => {
|
||||
const ptr_ptr_anyerror_ty_id = self.spv.allocId();
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
|
||||
.id_result = ptr_ptr_anyerror_ty_id,
|
||||
.storage_class = .PushConstant,
|
||||
.type = ptr_anyerror_ty_id,
|
||||
});
|
||||
|
||||
if (self.object.error_push_constant == null) {
|
||||
const spv_err_decl_index = try self.spv.allocDecl(.global);
|
||||
try self.spv.declareDeclDeps(spv_err_decl_index, &.{});
|
||||
|
||||
const push_constant_struct_ty_id = try self.spv.structType(
|
||||
&.{ptr_anyerror_ty_id},
|
||||
&.{"error_out_ptr"},
|
||||
);
|
||||
try self.spv.decorate(push_constant_struct_ty_id, .Block);
|
||||
try self.spv.decorateMember(push_constant_struct_ty_id, 0, .{ .Offset = .{ .byte_offset = 0 } });
|
||||
|
||||
const ptr_push_constant_struct_ty_id = self.spv.allocId();
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
|
||||
.id_result = ptr_push_constant_struct_ty_id,
|
||||
.storage_class = .PushConstant,
|
||||
.type = push_constant_struct_ty_id,
|
||||
});
|
||||
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
|
||||
.id_result_type = ptr_push_constant_struct_ty_id,
|
||||
.id_result = self.spv.declPtr(spv_err_decl_index).result_id,
|
||||
.storage_class = .PushConstant,
|
||||
});
|
||||
|
||||
self.object.error_push_constant = .{
|
||||
.push_constant_ptr = spv_err_decl_index,
|
||||
};
|
||||
}
|
||||
|
||||
try self.spv.sections.execution_modes.emit(self.spv.gpa, .OpExecutionMode, .{
|
||||
.entry_point = kernel_id,
|
||||
.mode = .{ .LocalSize = .{
|
||||
.x_size = 1,
|
||||
.y_size = 1,
|
||||
.z_size = 1,
|
||||
} },
|
||||
});
|
||||
|
||||
const kernel_proto_ty_id = try self.functionType(Type.void, &.{});
|
||||
try section.emit(self.spv.gpa, .OpFunction, .{
|
||||
.id_result_type = try self.resolveType(Type.void, .direct),
|
||||
.id_result = kernel_id,
|
||||
.function_control = .{},
|
||||
.function_type = kernel_proto_ty_id,
|
||||
});
|
||||
try section.emit(self.spv.gpa, .OpLabel, .{
|
||||
.id_result = self.spv.allocId(),
|
||||
});
|
||||
|
||||
const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
|
||||
const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
|
||||
|
||||
const zero_id = try self.constInt(Type.u32, 0, .direct);
|
||||
// We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
|
||||
// a load.
|
||||
const tmp = self.spv.allocId();
|
||||
try section.emit(self.spv.gpa, .OpInBoundsAccessChain, .{
|
||||
.id_result_type = ptr_ptr_anyerror_ty_id,
|
||||
.id_result = tmp,
|
||||
.base = push_constant_id,
|
||||
.indexes = &.{zero_id},
|
||||
});
|
||||
try section.emit(self.spv.gpa, .OpLoad, .{
|
||||
.id_result_type = ptr_anyerror_ty_id,
|
||||
.id_result = p_error_id,
|
||||
.pointer = tmp,
|
||||
});
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
const test_id = self.spv.declPtr(spv_test_decl_index).result_id;
|
||||
const error_id = self.spv.allocId();
|
||||
try section.emit(self.spv.gpa, .OpFunctionCall, .{
|
||||
.id_result_type = anyerror_ty_id,
|
||||
.id_result = error_id,
|
||||
@@ -2931,17 +3036,25 @@ const NavGen = struct {
|
||||
try section.emit(self.spv.gpa, .OpStore, .{
|
||||
.pointer = p_error_id,
|
||||
.object = error_id,
|
||||
.memory_access = .{
|
||||
.Aligned = .{ .literal_integer = @sizeOf(u16) },
|
||||
},
|
||||
});
|
||||
try section.emit(self.spv.gpa, .OpReturn, {});
|
||||
try section.emit(self.spv.gpa, .OpFunctionEnd, {});
|
||||
|
||||
try self.spv.declareDeclDeps(spv_decl_index, &.{spv_test_decl_index});
|
||||
|
||||
// Just generate a quick other name because the intel runtime crashes when the entry-
|
||||
// point name is the same as a different OpName.
|
||||
const test_name = try std.fmt.allocPrint(self.gpa, "test {s}", .{name});
|
||||
defer self.gpa.free(test_name);
|
||||
try self.spv.declareEntryPoint(spv_decl_index, test_name, .Kernel);
|
||||
|
||||
const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
|
||||
.vulkan => .GLCompute,
|
||||
.opencl => .Kernel,
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode);
|
||||
}
|
||||
|
||||
fn genNav(self: *NavGen, do_codegen: bool) !void {
|
||||
@@ -2969,11 +3082,10 @@ const NavGen = struct {
|
||||
try self.func.prologue.emit(self.spv.gpa, .OpFunction, .{
|
||||
.id_result_type = return_ty_id,
|
||||
.id_result = result_id,
|
||||
.function_control = switch (fn_info.cc) {
|
||||
.@"inline" => .{ .Inline = true },
|
||||
else => .{},
|
||||
},
|
||||
.function_type = prototype_ty_id,
|
||||
// Note: the backend will never be asked to generate an inline function
|
||||
// (this is handled in sema), so we don't need to set function_control here.
|
||||
.function_control = .{},
|
||||
});
|
||||
|
||||
comptime assert(zig_call_abi_ver == 3);
|
||||
|
||||
@@ -3845,6 +3845,7 @@ pub fn flushModule(dwarf: *Dwarf, pt: Zcu.PerThread) FlushError!void {
|
||||
}
|
||||
if (global_error_set_names.len > 0) try uleb128(diw, @intFromEnum(AbbrevCode.null));
|
||||
try dwarf.debug_info.section.replaceEntry(wip_nav.unit, wip_nav.entry, dwarf, wip_nav.debug_info.items);
|
||||
try wip_nav.flush(.unneeded);
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -161,28 +161,35 @@ pub fn updateExports(
|
||||
},
|
||||
};
|
||||
const nav_ty = ip.getNav(nav_index).typeOf(ip);
|
||||
const target = zcu.getTarget();
|
||||
if (ip.isFunctionType(nav_ty)) {
|
||||
const target = zcu.getTarget();
|
||||
const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
|
||||
const execution_model = switch (Type.fromInterned(nav_ty).fnCallingConvention(zcu)) {
|
||||
.spirv_vertex => spec.ExecutionModel.Vertex,
|
||||
.spirv_fragment => spec.ExecutionModel.Fragment,
|
||||
.spirv_kernel => spec.ExecutionModel.Kernel,
|
||||
const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
|
||||
const execution_model: spec.ExecutionModel = switch (target.os.tag) {
|
||||
.vulkan => switch (cc) {
|
||||
.spirv_vertex => .Vertex,
|
||||
.spirv_fragment => .Fragment,
|
||||
.spirv_kernel => .GLCompute,
|
||||
// TODO: We should integrate with the Linkage capability and export this function
|
||||
.spirv_device => return,
|
||||
else => unreachable,
|
||||
},
|
||||
.opencl => switch (cc) {
|
||||
.spirv_kernel => .Kernel,
|
||||
// TODO: We should integrate with the Linkage capability and export this function
|
||||
.spirv_device => return,
|
||||
else => unreachable,
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
const is_vulkan = target.os.tag == .vulkan;
|
||||
|
||||
if ((!is_vulkan and execution_model == .Kernel) or
|
||||
(is_vulkan and (execution_model == .Fragment or execution_model == .Vertex)))
|
||||
{
|
||||
for (export_indices) |export_idx| {
|
||||
const exp = zcu.all_exports.items[export_idx];
|
||||
try self.object.spv.declareEntryPoint(
|
||||
spv_decl_index,
|
||||
exp.opts.name.toSlice(ip),
|
||||
execution_model,
|
||||
);
|
||||
}
|
||||
for (export_indices) |export_idx| {
|
||||
const exp = zcu.all_exports.items[export_idx];
|
||||
try self.object.spv.declareEntryPoint(
|
||||
spv_decl_index,
|
||||
exp.opts.name.toSlice(ip),
|
||||
execution_model,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +265,7 @@ pub fn flushModule(self: *SpirV, arena: Allocator, tid: Zcu.PerThread.Id, prog_n
|
||||
const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => |other| {
|
||||
log.err("error while linking: {s}\n", .{@errorName(other)});
|
||||
log.err("error while linking: {s}", .{@errorName(other)});
|
||||
return error.FlushFailure;
|
||||
},
|
||||
};
|
||||
@@ -289,9 +296,8 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
|
||||
// TODO: Integrate with a hypothetical feature system
|
||||
const caps: []const spec.Capability = switch (target.os.tag) {
|
||||
.opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .Vector16, .GenericPointer },
|
||||
.opengl => &.{.Shader},
|
||||
.vulkan => &.{ .Shader, .VariablePointersStorageBuffer, .Int8, .Int16, .Int64, .Float64, .Float16 },
|
||||
else => unreachable, // TODO
|
||||
.vulkan => &.{ .Shader, .PhysicalStorageBufferAddresses, .StoragePushConstant16, .Int8, .Int16, .Int64, .Float64, .Float16 },
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
for (caps) |cap| {
|
||||
@@ -299,19 +305,32 @@ fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
|
||||
.capability = cap,
|
||||
});
|
||||
}
|
||||
|
||||
switch (target.os.tag) {
|
||||
.vulkan => {
|
||||
try spv.sections.extensions.emit(gpa, .OpExtension, .{
|
||||
.name = "SPV_KHR_physical_storage_buffer",
|
||||
});
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
}
|
||||
|
||||
fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {
|
||||
const gpa = spv.gpa;
|
||||
|
||||
const addressing_model = switch (target.os.tag) {
|
||||
const addressing_model: spec.AddressingModel = switch (target.os.tag) {
|
||||
.opencl => switch (target.cpu.arch) {
|
||||
.spirv32 => spec.AddressingModel.Physical32,
|
||||
.spirv64 => spec.AddressingModel.Physical64,
|
||||
else => unreachable, // TODO
|
||||
.spirv32 => .Physical32,
|
||||
.spirv64 => .Physical64,
|
||||
else => unreachable,
|
||||
},
|
||||
.opengl, .vulkan => spec.AddressingModel.Logical,
|
||||
else => unreachable, // TODO
|
||||
.opengl, .vulkan => switch (target.cpu.arch) {
|
||||
.spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
|
||||
.spirv64 => .PhysicalStorageBuffer64,
|
||||
else => unreachable,
|
||||
},
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const memory_model: spec.MemoryModel = switch (target.os.tag) {
|
||||
|
||||
@@ -400,6 +400,15 @@ const ModuleBuilder = struct {
|
||||
self.section.writeWords(inst.operands[2..]);
|
||||
continue;
|
||||
},
|
||||
.OpExecutionMode, .OpExecutionModeId => {
|
||||
const original_id: ResultId = @enumFromInt(inst.operands[0]);
|
||||
const new_id_index = info.entry_points.getIndex(original_id).?;
|
||||
const new_id: ResultId = @enumFromInt(self.entry_point_new_id_base + new_id_index);
|
||||
try self.section.emitRaw(self.arena, inst.opcode, inst.operands.len);
|
||||
self.section.writeOperand(ResultId, new_id);
|
||||
self.section.writeWords(inst.operands[1..]);
|
||||
continue;
|
||||
},
|
||||
.OpTypeFunction => {
|
||||
// Re-emitted in `emitFunctionTypes()`. We can do this because
|
||||
// OpTypeFunction's may not currently be used anywhere that is not
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -10,4 +10,4 @@ comptime {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :5:21: error: 'error.B' not a member of error set 'error{C,A}'
|
||||
// :5:21: error: 'error.B' not a member of error set 'error{A,C}'
|
||||
|
||||
@@ -12,5 +12,5 @@ fn foo(set1: Set1) void {
|
||||
// backend=stage2
|
||||
// target=native
|
||||
//
|
||||
// :7:21: error: expected type 'error{C,A}', found 'error{A,B}'
|
||||
// :7:21: error: expected type 'error{A,C}', found 'error{A,B}'
|
||||
// :7:21: note: 'error.B' not a member of destination error set
|
||||
|
||||
@@ -16,4 +16,4 @@ comptime {
|
||||
// backend=llvm
|
||||
// target=native
|
||||
//
|
||||
// :11:21: error: 'error.B' not a member of error set 'error{C,A}'
|
||||
// :11:21: error: 'error.B' not a member of error set 'error{A,C}'
|
||||
|
||||
19
test/cases/compile_errors/spirv_merge_logical_pointers.zig
Normal file
19
test/cases/compile_errors/spirv_merge_logical_pointers.zig
Normal file
@@ -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
|
||||
16
test/cases/spirv_mergable_pointers.zig
Normal file
16
test/cases/spirv_mergable_pointers.zig
Normal file
@@ -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
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user