spirv: error when execution mode is set more than once

This commit is contained in:
Ali Cheraghi
2025-05-11 15:45:44 +03:30
parent 9209f4b16a
commit 8fa54eb798
5 changed files with 144 additions and 167 deletions

View File

@@ -2870,7 +2870,7 @@ const NavGen = struct {
};
try self.spv.declareDeclDeps(spv_decl_index, decl_deps.items);
try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode);
try self.spv.declareEntryPoint(spv_decl_index, test_name, execution_mode, null);
}
fn genNav(self: *NavGen, do_codegen: bool) !void {
@@ -2976,10 +2976,6 @@ const NavGen = struct {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .Position } });
} else if (nav.fqn.eqlSlice("point_size", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .PointSize } });
} else if (nav.fqn.eqlSlice("vertex_id", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .VertexId } });
} else if (nav.fqn.eqlSlice("instance_id", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .InstanceId } });
} else if (nav.fqn.eqlSlice("invocation_id", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .InvocationId } });
} else if (nav.fqn.eqlSlice("frag_coord", ip)) {
@@ -2990,8 +2986,6 @@ const NavGen = struct {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FrontFacing } });
} else if (nav.fqn.eqlSlice("sample_mask", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .SampleMask } });
} else if (nav.fqn.eqlSlice("sample_mask", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .SampleMask } });
} else if (nav.fqn.eqlSlice("frag_depth", ip)) {
try self.spv.decorate(result_id, .{ .BuiltIn = .{ .built_in = .FragDepth } });
} else if (nav.fqn.eqlSlice("num_workgroups", ip)) {

View File

@@ -296,12 +296,26 @@ fn processInstruction(self: *Assembler) !void {
};
break :blk .{ .value = try self.spv.importInstructionSet(set_tag) };
},
.OpExecutionMode, .OpExecutionModeId => {
assert(try self.processGenericInstruction() == null);
const entry_point_id = try self.resolveRefId(self.inst.operands.items[0].ref_id);
const exec_mode: spec.ExecutionMode = @enumFromInt(self.inst.operands.items[1].value);
const gop = try self.spv.entry_points.getOrPut(self.gpa, entry_point_id);
if (!gop.found_existing) {
gop.value_ptr.* = .{};
} else if (gop.value_ptr.exec_mode != null) {
return self.fail(
self.currentToken().start,
"cannot set execution mode more than once to any entry point",
.{},
);
}
gop.value_ptr.exec_mode = exec_mode;
return;
},
else => switch (self.inst.opcode.class()) {
.TypeDeclaration => try self.processTypeInstruction(),
else => if (try self.processGenericInstruction()) |result|
result
else
return,
else => (try self.processGenericInstruction()) orelse return,
},
};

View File

@@ -92,11 +92,12 @@ pub const Decl = struct {
/// This models a kernel entry point.
pub const EntryPoint = struct {
/// The declaration that should be exported.
decl_index: Decl.Index,
decl_index: ?Decl.Index = null,
/// The name of the kernel to be exported.
name: []const u8,
name: ?[]const u8 = null,
/// Calling Convention
execution_model: spec.ExecutionModel,
exec_model: ?spec.ExecutionModel = null,
exec_mode: ?spec.ExecutionMode = null,
};
/// A general-purpose allocator which may be used to allocate resources for this module
@@ -184,7 +185,7 @@ decls: std.ArrayListUnmanaged(Decl) = .empty,
decl_deps: std.ArrayListUnmanaged(Decl.Index) = .empty,
/// The list of entry points that should be exported from this module.
entry_points: std.ArrayListUnmanaged(EntryPoint) = .empty,
entry_points: std.AutoArrayHashMapUnmanaged(IdRef, EntryPoint) = .empty,
pub fn init(gpa: Allocator, target: std.Target) Module {
const version_minor: u8 = blk: {
@@ -304,19 +305,30 @@ fn entryPoints(self: *Module) !Section {
var seen = try std.DynamicBitSetUnmanaged.initEmpty(self.gpa, self.decls.items.len);
defer seen.deinit(self.gpa);
for (self.entry_points.items) |entry_point| {
for (self.entry_points.keys(), self.entry_points.values()) |entry_point_id, entry_point| {
interface.items.len = 0;
seen.setRangeValue(.{ .start = 0, .end = self.decls.items.len }, false);
try self.addEntryPointDeps(entry_point.decl_index, &seen, &interface);
const entry_point_id = self.declPtr(entry_point.decl_index).result_id;
try self.addEntryPointDeps(entry_point.decl_index.?, &seen, &interface);
try entry_points.emit(self.gpa, .OpEntryPoint, .{
.execution_model = entry_point.execution_model,
.execution_model = entry_point.exec_model.?,
.entry_point = entry_point_id,
.name = entry_point.name,
.name = entry_point.name.?,
.interface = interface.items,
});
if (entry_point.exec_mode == null and entry_point.exec_model == .Fragment) {
switch (self.target.os.tag) {
.vulkan, .opengl => |tag| {
try self.sections.execution_modes.emit(self.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = if (tag == .vulkan) .OriginUpperLeft else .OriginLowerLeft,
});
},
.opencl => {},
else => unreachable,
}
}
}
return entry_points;
@@ -749,13 +761,15 @@ pub fn declareEntryPoint(
self: *Module,
decl_index: Decl.Index,
name: []const u8,
execution_model: spec.ExecutionModel,
exec_model: spec.ExecutionModel,
exec_mode: ?spec.ExecutionMode,
) !void {
try self.entry_points.append(self.gpa, .{
.decl_index = decl_index,
.name = try self.arena.allocator().dupe(u8, name),
.execution_model = execution_model,
});
const gop = try self.entry_points.getOrPut(self.gpa, self.declPtr(decl_index).result_id);
gop.value_ptr.decl_index = decl_index;
gop.value_ptr.name = try self.arena.allocator().dupe(u8, name);
gop.value_ptr.exec_model = exec_model;
// Might've been set by assembler
if (!gop.found_existing) gop.value_ptr.exec_mode = exec_mode;
}
pub fn debugName(self: *Module, target: IdResult, name: []const u8) !void {