This commit is contained in:
DialecticalMaterialist 2025-11-25 08:08:18 -06:00 committed by GitHub
commit 397e3f43e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 4967 additions and 302 deletions

View file

@ -66,6 +66,23 @@ pub fn build(b: *std.Build) !void {
b.getInstallStep().dependOn(&install_std_docs.step);
}
const update_cpu_features = b.addExecutable(.{
.name = "update-cpu-features",
.root_module = b.createModule(.{
.root_source_file = b.path("tools/update_cpu_features.zig"),
.target = b.graph.host,
.imports = &.{.{
.name = "spirv_spec",
.module = b.createModule(.{
.root_source_file = b.path("src/codegen/spirv/spec.zig"),
.target = b.graph.host,
}),
}},
}),
});
const run_update_cpu_features = b.addRunArtifact(update_cpu_features);
if (b.args) |args| run_update_cpu_features.addArgs(args);
if (flat) {
b.installFile("LICENSE", "LICENSE");
b.installFile("README.md", "README.md");
@ -81,6 +98,9 @@ pub fn build(b: *std.Build) !void {
docs_step.dependOn(langref_step);
docs_step.dependOn(std_docs_step);
const update_cpu_features_step = b.step("update-cpu-features", "Update CPU Features");
update_cpu_features_step.dependOn(&run_update_cpu_features.step);
const no_matrix = b.option(bool, "no-matrix", "Limit test matrix to exactly one target configuration") orelse false;
const skip_debug = b.option(bool, "skip-debug", "Main test suite skips debug builds") orelse false;
const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse no_matrix;

View file

@ -1182,7 +1182,7 @@ pub const Cpu = struct {
pub const Set = struct {
ints: [usize_count]usize,
pub const needed_bit_count = 317;
pub const needed_bit_count = 421;
pub const byte_count = (needed_bit_count + 7) / 8;
pub const usize_count = (byte_count + (@sizeOf(usize) - 1)) / @sizeOf(usize);
pub const Index = std.math.Log2Int(std.meta.Int(.unsigned, usize_count * @bitSizeOf(usize)));
@ -1924,6 +1924,8 @@ pub const Cpu = struct {
.spirv_kernel,
.spirv_fragment,
.spirv_vertex,
.spirv_task,
.spirv_mesh,
=> &.{ .spirv32, .spirv64 },
};
}
@ -3291,6 +3293,12 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 {
.longdouble => return 128,
},
// OpenGL has no well defined C ABI, we do this to prevent panic when using floating point types.
.opengl => switch (c_type) {
.char, .short, .ushort, .int, .uint, .float, .long, .ulong, .longlong, .ulonglong => return 32,
.double, .longdouble => return 64,
},
.opencl, .vulkan => switch (c_type) {
.char => return 8,
.short, .ushort => return 16,
@ -3326,9 +3334,8 @@ pub fn cTypeBitSize(target: *const Target, c_type: CType) u16 {
},
.ps3,
.contiki,
.managarm,
.opengl,
.contiki,
=> @panic("specify the C integer and float type sizes for this OS"),
}
}

File diff suppressed because it is too large Load diff

View file

@ -140,7 +140,7 @@ pub const CallingConvention = union(enum(u8)) {
pub const kernel: CallingConvention = switch (builtin.target.cpu.arch) {
.amdgcn => .amdgcn_kernel,
.nvptx, .nvptx64 => .nvptx_kernel,
.spirv32, .spirv64 => .spirv_kernel,
.spirv32, .spirv64 => .{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } },
else => unreachable,
};
@ -335,11 +335,13 @@ pub const CallingConvention = union(enum(u8)) {
nvptx_device,
nvptx_kernel,
// Calling conventions for kernels and shaders on the `spirv`, `spirv32`, and `spirv64` architectures.
// Calling conventions for kernels and shaders on the `spirv32`, and `spirv64` architectures.
spirv_device,
spirv_kernel,
spirv_fragment,
spirv_kernel: SpirvKernelOptions,
spirv_fragment: SpirvFragmentOptions,
spirv_vertex,
spirv_task: SpirvKernelOptions,
spirv_mesh: SpirvMeshOptions,
/// Options shared across most calling conventions.
pub const CommonOptions = struct {
@ -448,6 +450,32 @@ pub const CallingConvention = union(enum(u8)) {
};
};
pub const SpirvKernelOptions = struct { x: u32, y: u32, z: u32 };
pub const SpirvFragmentOptions = struct {
const DepthAssumption = enum(u2) {
none = 0,
greater = 1,
less = 2,
unchanged = 3,
};
pixel_centered_integer: bool = false,
depth_assumption: DepthAssumption = .none,
};
pub const SpirvMeshOptions = struct {
const StageOutput = enum(u2) {
output_points = 0,
output_lines = 1,
output_triangles = 2,
};
stage_output: StageOutput = .output_triangles,
max_primitives: u32 = 1,
max_vertices: u32 = 3,
};
/// Options for the `sh_interrupt` calling convention.
pub const ShInterruptOptions = struct {
/// The boundary the stack is aligned to when the function is called.

View file

@ -22,7 +22,7 @@ pub extern const instance_index: u32 addrspace(.input);
/// Forms the main linkage for `input` and `output` address spaces.
/// `ptr` must be a reference to variable or struct field.
pub fn location(comptime ptr: anytype, comptime loc: u32) void {
pub inline fn location(comptime ptr: anytype, comptime loc: u32) void {
asm volatile (
\\OpDecorate %ptr Location $loc
:
@ -33,7 +33,7 @@ pub fn location(comptime ptr: anytype, comptime loc: u32) void {
/// Forms the main linkage for `input` and `output` address spaces.
/// `ptr` must be a reference to variable or struct field.
pub fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) void {
pub inline fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) void {
asm volatile (
\\OpDecorate %ptr DescriptorSet $set
\\OpDecorate %ptr Binding $bind
@ -43,86 +43,3 @@ pub fn binding(comptime ptr: anytype, comptime set: u32, comptime bind: u32) voi
[bind] "c" (bind),
);
}
pub const ExecutionMode = union(Tag) {
/// Sets origin of the framebuffer to the upper-left corner
origin_upper_left,
/// Sets origin of the framebuffer to the lower-left corner
origin_lower_left,
/// Indicates that the fragment shader writes to `frag_depth`,
/// replacing the fixed-function depth value.
depth_replacing,
/// Indicates that per-fragment tests may assume that
/// any `frag_depth` built in-decorated value written by the shader is
/// greater-than-or-equal to the fragments interpolated depth value
depth_greater,
/// Indicates that per-fragment tests may assume that
/// any `frag_depth` built in-decorated value written by the shader is
/// less-than-or-equal to the fragments interpolated depth value
depth_less,
/// Indicates that per-fragment tests may assume that
/// any `frag_depth` built in-decorated value written by the shader is
/// the same as the fragments interpolated depth value
depth_unchanged,
/// Indicates the workgroup size in the x, y, and z dimensions.
local_size: LocalSize,
pub const Tag = enum(u32) {
origin_upper_left = 7,
origin_lower_left = 8,
depth_replacing = 12,
depth_greater = 14,
depth_less = 15,
depth_unchanged = 16,
local_size = 17,
};
pub const LocalSize = struct { x: u32, y: u32, z: u32 };
};
/// Declare the mode entry point executes in.
pub fn executionMode(comptime entry_point: anytype, comptime mode: ExecutionMode) void {
const cc = @typeInfo(@TypeOf(entry_point)).@"fn".calling_convention;
switch (mode) {
.origin_upper_left,
.origin_lower_left,
.depth_replacing,
.depth_greater,
.depth_less,
.depth_unchanged,
=> {
if (cc != .spirv_fragment) {
@compileError(
\\invalid execution mode '
++ @tagName(mode) ++
\\' for function with '
++ @tagName(cc) ++
\\' calling convention
);
}
asm volatile (
\\OpExecutionMode %entry_point $mode
:
: [entry_point] "" (entry_point),
[mode] "c" (@intFromEnum(mode)),
);
},
.local_size => |size| {
if (cc != .spirv_kernel) {
@compileError(
\\invalid execution mode 'local_size' for function with '
++ @tagName(cc) ++
\\' calling convention
);
}
asm volatile (
\\OpExecutionMode %entry_point LocalSize $x $y $z
:
: [entry_point] "" (entry_point),
[x] "c" (size.x),
[y] "c" (size.y),
[z] "c" (size.z),
);
},
}
}

View file

@ -4880,12 +4880,19 @@ pub const Index = enum(u32) {
type_function: struct {
const @"data.flags.has_comptime_bits" = opaque {};
const @"data.flags.has_noalias_bits" = opaque {};
const @"data.flags.cc_extra_len" = opaque {};
const @"data.params_len" = opaque {};
data: *Tag.TypeFunction,
@"trailing.comptime_bits.len": *@"data.flags.has_comptime_bits",
@"trailing.noalias_bits.len": *@"data.flags.has_noalias_bits",
@"trailing.cc_bits.len": *@"data.flags.cc_extra_len",
@"trailing.param_types.len": *@"data.params_len",
trailing: struct { comptime_bits: []u32, noalias_bits: []u32, param_types: []Index },
trailing: struct {
comptime_bits: []u32,
noalias_bits: []u32,
cc_bits: []u32,
param_types: []Index,
},
},
undef: DataIsIndex,
@ -5861,6 +5868,7 @@ pub const Tag = enum(u8) {
.trailing = struct {
param_comptime_bits: ?[]u32,
param_noalias_bits: ?[]u32,
param_cc_bits: ?[]u32,
param_type: []Index,
},
.config = .{
@ -5868,6 +5876,8 @@ pub const Tag = enum(u8) {
.@"trailing.param_comptime_bits.?.len" = .@"(payload.params_len + 31) / 32",
.@"trailing.param_noalias_bits.?" = .@"payload.flags.has_noalias_bits",
.@"trailing.param_noalias_bits.?.len" = .@"(payload.params_len + 31) / 32",
.@"trailing.param_cc_bits.?" = .@"payload.flags.cc_extra_len != 0",
.@"trailing.param_cc_bits.?.len" = .@"payload.flags.cc_extra_len",
.@"trailing.param_type.len" = .@"payload.params_len",
},
},
@ -6074,7 +6084,11 @@ pub const Tag = enum(u8) {
/// Trailing:
/// 0. comptime_bits: u32, // if has_comptime_bits
/// 1. noalias_bits: u32, // if has_noalias_bits
/// 2. param_type: Index for each params_len
/// 2. if cc.tag == .spirv_mesh
/// cc_bits: [2]u32
/// if cc.tag == .spirv_kernel or cc.tag == .spirv_task
/// cc_bits: [3]u32
/// 3. param_type: Index for each params_len
pub const TypeFunction = struct {
params_len: u32,
return_type: Index,
@ -6086,8 +6100,9 @@ pub const Tag = enum(u8) {
is_generic: bool,
has_comptime_bits: bool,
has_noalias_bits: bool,
cc_extra_len: u2,
is_noinline: bool,
_: u9 = 0,
_: u7 = 0,
};
};
@ -7538,6 +7553,20 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke
trail_index += 1;
break :b x;
};
var cc = type_function.data.flags.cc.unpack();
switch (cc) {
.spirv_kernel, .spirv_task => |*kernel| {
kernel.x = extra.view().items(.@"0")[trail_index];
kernel.y = extra.view().items(.@"0")[trail_index + 1];
kernel.z = extra.view().items(.@"0")[trail_index + 2];
},
.spirv_mesh => |*mesh| {
mesh.max_primitives = extra.view().items(.@"0")[trail_index];
mesh.max_vertices = extra.view().items(.@"0")[trail_index + 1];
},
else => {},
}
trail_index += type_function.data.flags.cc_extra_len;
return .{
.param_types = .{
.tid = tid,
@ -7547,7 +7576,7 @@ fn extraFuncType(tid: Zcu.PerThread.Id, extra: Local.Extra, extra_index: u32) Ke
.return_type = type_function.data.return_type,
.comptime_bits = comptime_bits,
.noalias_bits = noalias_bits,
.cc = type_function.data.flags.cc.unpack(),
.cc = cc,
.is_var_args = type_function.data.flags.is_var_args,
.is_noinline = type_function.data.flags.is_noinline,
.is_generic = type_function.data.flags.is_generic,
@ -9182,11 +9211,17 @@ pub fn getFuncType(
// ask if it already exists, and if so, revert the lengths of the mutated
// arrays. This is similar to what `getOrPutTrailingString` does.
const prev_extra_len = extra.mutate.len;
const cc_extra_len: u2 = if (key.cc) |cc| switch (cc) {
.spirv_kernel, .spirv_task => 3,
.spirv_mesh => 2,
else => 0,
} else 0;
const params_len: u32 = @intCast(key.param_types.len);
try extra.ensureUnusedCapacity(@typeInfo(Tag.TypeFunction).@"struct".fields.len +
@intFromBool(key.comptime_bits != 0) +
@intFromBool(key.noalias_bits != 0) +
cc_extra_len +
params_len);
const func_type_extra_index = addExtraAssumeCapacity(extra, Tag.TypeFunction{
@ -9197,6 +9232,7 @@ pub fn getFuncType(
.is_var_args = key.is_var_args,
.has_comptime_bits = key.comptime_bits != 0,
.has_noalias_bits = key.noalias_bits != 0,
.cc_extra_len = cc_extra_len,
.is_generic = key.is_generic,
.is_noinline = key.is_noinline,
},
@ -9204,6 +9240,18 @@ pub fn getFuncType(
if (key.comptime_bits != 0) extra.appendAssumeCapacity(.{key.comptime_bits});
if (key.noalias_bits != 0) extra.appendAssumeCapacity(.{key.noalias_bits});
if (key.cc) |cc| switch (cc) {
.spirv_kernel, .spirv_task => |kernel| extra.appendSliceAssumeCapacity(.{&.{
kernel.x,
kernel.y,
kernel.z,
}}),
.spirv_mesh => |mesh| extra.appendSliceAssumeCapacity(.{&.{
mesh.max_primitives,
mesh.max_vertices,
}}),
else => {},
};
extra.appendSliceAssumeCapacity(.{@ptrCast(key.param_types)});
errdefer extra.mutate.len = prev_extra_len;
@ -9458,6 +9506,7 @@ pub fn getFuncDeclIes(
.is_var_args = key.is_var_args,
.has_comptime_bits = key.comptime_bits != 0,
.has_noalias_bits = key.noalias_bits != 0,
.cc_extra_len = 0,
.is_generic = key.is_generic,
.is_noinline = key.is_noinline,
},
@ -9754,6 +9803,7 @@ pub fn getFuncInstanceIes(
.is_var_args = false,
.has_comptime_bits = false,
.has_noalias_bits = arg.noalias_bits != 0,
.cc_extra_len = 0,
.is_generic = false,
.is_noinline = arg.is_noinline,
},
@ -11225,7 +11275,8 @@ fn dumpStatsFallible(ip: *const InternPool, arena: Allocator) anyerror!void {
break :b @sizeOf(Tag.TypeFunction) +
(@sizeOf(Index) * info.params_len) +
(@as(u32, 4) * @intFromBool(info.flags.has_comptime_bits)) +
(@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits));
(@as(u32, 4) * @intFromBool(info.flags.has_noalias_bits)) +
(@as(u32, 4) * info.flags.cc_extra_len);
},
.undef => 0,
@ -12983,6 +13034,21 @@ const PackedCallingConvention = packed struct(u18) {
.incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
.extra = @intFromEnum(pl.mode),
},
std.builtin.CallingConvention.SpirvKernelOptions => .{
.tag = tag,
.incoming_stack_alignment = .none, // unused
.extra = 0, // unused
},
std.builtin.CallingConvention.SpirvFragmentOptions => .{
.tag = tag,
.incoming_stack_alignment = .none, // unused
.extra = @as(u4, @intFromEnum(pl.depth_assumption)) << 1 | @intFromBool(pl.pixel_centered_integer),
},
std.builtin.CallingConvention.SpirvMeshOptions => .{
.tag = tag,
.incoming_stack_alignment = .none, // unused
.extra = @intFromEnum(pl.stage_output),
},
std.builtin.CallingConvention.ShInterruptOptions => .{
.tag = tag,
.incoming_stack_alignment = .fromByteUnits(pl.incoming_stack_alignment orelse 0),
@ -13027,6 +13093,20 @@ const PackedCallingConvention = packed struct(u18) {
.incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
.mode = @enumFromInt(cc.extra),
},
std.builtin.CallingConvention.SpirvKernelOptions => .{
.x = undefined, // Populated later
.y = undefined, // Populated later
.z = undefined, // Populated later
},
std.builtin.CallingConvention.SpirvFragmentOptions => .{
.pixel_centered_integer = @bitCast(@as(u1, @truncate(cc.extra))),
.depth_assumption = @enumFromInt(@as(u2, @truncate(cc.extra >> 1))),
},
std.builtin.CallingConvention.SpirvMeshOptions => .{
.stage_output = @enumFromInt(cc.extra),
.max_primitives = undefined, // Populated later
.max_vertices = undefined, // Populated later
},
std.builtin.CallingConvention.ShInterruptOptions => .{
.incoming_stack_alignment = cc.incoming_stack_alignment.toByteUnits(),
.save = @enumFromInt(cc.extra),

View file

@ -9379,6 +9379,8 @@ fn callConvIsCallable(cc: std.builtin.CallingConvention.Tag) bool {
.spirv_kernel,
.spirv_fragment,
.spirv_vertex,
.spirv_task,
.spirv_mesh,
=> false,
else => true,
@ -30024,14 +30026,23 @@ fn callconvCoerceAllowed(
switch (src_cc) {
inline else => |src_data, tag| {
const dest_data = @field(dest_cc, @tagName(tag));
if (@TypeOf(src_data) != void) {
if (@TypeOf(src_data) != void and
@TypeOf(src_data) != std.builtin.CallingConvention.SpirvKernelOptions and
@TypeOf(src_data) != std.builtin.CallingConvention.SpirvMeshOptions and
@TypeOf(src_data) != std.builtin.CallingConvention.SpirvFragmentOptions)
{
const default_stack_align = target.stackAlignment();
const src_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
const dest_stack_align = src_data.incoming_stack_alignment orelse default_stack_align;
if (dest_stack_align < src_stack_align) return false;
}
switch (@TypeOf(src_data)) {
void, std.builtin.CallingConvention.CommonOptions => {},
void,
std.builtin.CallingConvention.CommonOptions,
std.builtin.CallingConvention.SpirvKernelOptions,
std.builtin.CallingConvention.SpirvMeshOptions,
std.builtin.CallingConvention.SpirvFragmentOptions,
=> {},
std.builtin.CallingConvention.X86RegparmOptions => {
if (src_data.register_params != dest_data.register_params) return false;
},

View file

@ -4563,7 +4563,7 @@ pub fn callconvSupported(zcu: *Zcu, cc: std.builtin.CallingConvention) union(enu
},
.stage2_spirv => switch (cc) {
.spirv_device, .spirv_kernel => true,
.spirv_fragment, .spirv_vertex => target.os.tag == .vulkan or target.os.tag == .opengl,
.spirv_fragment, .spirv_vertex, .spirv_task, .spirv_mesh => target.os.tag == .vulkan or target.os.tag == .opengl,
else => false,
},
};

View file

@ -11768,7 +11768,11 @@ pub fn toLlvmCallConv(cc: std.builtin.CallingConvention, target: *const std.Targ
const llvm_cc = toLlvmCallConvTag(cc, target) orelse return null;
const incoming_stack_alignment: ?u64, const register_params: u2 = switch (cc) {
inline else => |pl| switch (@TypeOf(pl)) {
void => .{ null, 0 },
void,
std.builtin.CallingConvention.SpirvKernelOptions,
std.builtin.CallingConvention.SpirvMeshOptions,
std.builtin.CallingConvention.SpirvFragmentOptions,
=> .{ null, 0 },
std.builtin.CallingConvention.ArcInterruptOptions,
std.builtin.CallingConvention.ArmInterruptOptions,
std.builtin.CallingConvention.RiscvInterruptOptions,
@ -11917,6 +11921,8 @@ fn toLlvmCallConvTag(cc_tag: std.builtin.CallingConvention.Tag, target: *const s
.spirv_kernel,
.spirv_fragment,
.spirv_vertex,
.spirv_task,
.spirv_mesh,
=> null,
};
}

View file

@ -168,14 +168,10 @@ fn processInstruction(ass: *Assembler) !void {
return ass.fail(ass.currentToken().start, "cannot set execution mode in assembly", .{});
},
.OpCapability => {
try module.addCapability(@enumFromInt(ass.inst.operands.items[0].value));
return;
return ass.fail(ass.currentToken().start, "cannot set capability in assembly", .{});
},
.OpExtension => {
const ext_name_offset = ass.inst.operands.items[0].string;
const ext_name = std.mem.sliceTo(ass.inst.string_bytes.items[ext_name_offset..], 0);
try module.addExtension(ext_name);
return;
return ass.fail(ass.currentToken().start, "cannot set extension in assembly", .{});
},
.OpExtInstImport => blk: {
const set_name_offset = ass.inst.operands.items[1].string;

View file

@ -1425,6 +1425,8 @@ fn resolveType(cg: *CodeGen, ty: Type, repr: Repr) Error!Id {
.spirv_fragment,
.spirv_vertex,
.spirv_device,
.spirv_task,
.spirv_mesh,
=> {},
else => unreachable,
}
@ -2517,13 +2519,11 @@ fn generateTestEntryPoint(
// point name is the same as a different OpName.
const test_name = try std.fmt.allocPrint(cg.module.arena, "test {s}", .{name});
const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
.vulkan, .opengl => .gl_compute,
.opencl, .amdhsa => .kernel,
else => unreachable,
};
try cg.module.declareEntryPoint(spv_decl_index, test_name, execution_mode, null);
try cg.module.declareEntryPoint(
spv_decl_index,
test_name,
.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } },
);
}
fn intFromBool(cg: *CodeGen, value: Temporary, result_ty: Type) !Temporary {

View file

@ -61,8 +61,8 @@ cache: struct {
struct_types: std.ArrayHashMapUnmanaged(StructType, Id, StructType.HashContext, true) = .empty,
fn_types: std.ArrayHashMapUnmanaged(FnType, Id, FnType.HashContext, true) = .empty,
capabilities: std.AutoHashMapUnmanaged(spec.Capability, void) = .empty,
extensions: std.StringHashMapUnmanaged(void) = .empty,
capabilities: std.EnumSet(spec.Capability) = .initEmpty(),
extensions: std.EnumSet(spec.Extension) = .initEmpty(),
extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, Id) = .empty,
decorations: std.AutoHashMapUnmanaged(struct { Id, spec.Decoration }, void) = .empty,
builtins: std.AutoHashMapUnmanaged(struct { spec.BuiltIn, spec.StorageClass }, Decl.Index) = .empty,
@ -130,15 +130,10 @@ pub const Decl = struct {
end_dep: usize = 0,
};
/// This models a kernel entry point.
pub const EntryPoint = struct {
/// The declaration that should be exported.
decl_index: Decl.Index,
/// The name of the kernel to be exported.
name: []const u8,
/// Calling Convention
exec_model: spec.ExecutionModel,
exec_mode: ?spec.ExecutionMode = null,
cc: std.builtin.CallingConvention,
};
const StructType = struct {
@ -229,8 +224,6 @@ pub fn deinit(module: *Module) void {
module.cache.array_types.deinit(module.gpa);
module.cache.struct_types.deinit(module.gpa);
module.cache.fn_types.deinit(module.gpa);
module.cache.capabilities.deinit(module.gpa);
module.cache.extensions.deinit(module.gpa);
module.cache.extended_instruction_set.deinit(module.gpa);
module.cache.decorations.deinit(module.gpa);
module.cache.builtins.deinit(module.gpa);
@ -317,25 +310,108 @@ fn entryPoints(module: *Module) !Section {
interface.items.len = 0;
seen.setRangeValue(.{ .start = 0, .end = module.decls.items.len }, false);
const exec_model: spec.ExecutionModel = switch (target.os.tag) {
.vulkan, .opengl => switch (entry_point.cc) {
.spirv_vertex => .vertex,
.spirv_fragment => .fragment,
.spirv_kernel => .gl_compute,
.spirv_task => .task_ext,
.spirv_mesh => .mesh_ext,
// TODO: We should integrate with the Linkage capability and export this function
.spirv_device => continue,
else => unreachable,
},
.opencl => switch (entry_point.cc) {
.spirv_kernel => .kernel,
// TODO: We should integrate with the Linkage capability and export this function
.spirv_device => continue,
else => unreachable,
},
else => unreachable,
};
try module.addEntryPointDeps(entry_point.decl_index, &seen, &interface);
try entry_points.emit(module.gpa, .OpEntryPoint, .{
.execution_model = entry_point.exec_model,
.execution_model = exec_model,
.entry_point = entry_point_id,
.name = entry_point.name,
.interface = interface.items,
});
if (entry_point.exec_mode == null and entry_point.exec_model == .fragment) {
switch (target.os.tag) {
.vulkan, .opengl => |tag| {
switch (entry_point.cc) {
.spirv_kernel, .spirv_task => |kernel| {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .{ .local_size = .{
.x_size = kernel.x,
.y_size = kernel.y,
.z_size = kernel.z,
} },
});
},
.spirv_fragment => |fragment| {
if (fragment.pixel_centered_integer and target.os.tag != .vulkan) {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = if (tag == .vulkan) .origin_upper_left else .origin_lower_left,
.mode = .pixel_center_integer,
});
},
.opencl => {},
else => unreachable,
}
}
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = if (target.os.tag == .vulkan) .origin_upper_left else .origin_lower_left,
});
switch (fragment.depth_assumption) {
.none => {},
.greater => {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .depth_greater,
});
},
.less => {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .depth_less,
});
},
.unchanged => {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .depth_unchanged,
});
},
}
},
.spirv_mesh => |mesh| {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .{ .output_vertices = .{ .vertex_count = mesh.max_vertices } },
});
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .{ .output_primitives_ext = .{ .primitive_count = mesh.max_primitives } },
});
switch (mesh.stage_output) {
.output_points => {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .output_points,
});
},
.output_lines => {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .output_lines_ext,
});
},
.output_triangles => {
try module.sections.execution_modes.emit(module.gpa, .OpExecutionMode, .{
.entry_point = entry_point_id,
.mode = .output_triangles_ext,
});
},
}
},
else => {},
}
}
@ -355,7 +431,7 @@ pub fn finalize(module: *Module, gpa: Allocator) ![]Word {
try module.addCapability(.shader);
try module.addCapability(.matrix);
if (target.cpu.arch == .spirv64) {
try module.addExtension("SPV_KHR_physical_storage_buffer");
try module.addExtension(.SPV_KHR_physical_storage_buffer);
try module.addCapability(.physical_storage_buffer_addresses);
}
},
@ -366,27 +442,24 @@ pub fn finalize(module: *Module, gpa: Allocator) ![]Word {
else => unreachable,
}
if (target.cpu.arch == .spirv64) try module.addCapability(.int64);
if (target.cpu.has(.spirv, .int64)) try module.addCapability(.int64);
if (target.cpu.has(.spirv, .float16)) {
if (target.os.tag == .opencl) try module.addExtension("cl_khr_fp16");
try module.addCapability(.float16);
inline for (@typeInfo(spec.Capability).@"enum".fields) |field| {
if (target.cpu.has(.spirv, std.meta.stringToEnum(std.Target.spirv.Feature, field.name).?)) {
try module.addCapability(@enumFromInt(field.value));
}
}
if (target.cpu.has(.spirv, .float64)) try module.addCapability(.float64);
if (target.cpu.has(.spirv, .generic_pointer)) try module.addCapability(.generic_pointer);
if (target.cpu.has(.spirv, .vector16)) try module.addCapability(.vector16);
if (target.cpu.has(.spirv, .storage_push_constant16)) {
try module.addExtension("SPV_KHR_16bit_storage");
try module.addCapability(.storage_push_constant16);
}
if (target.cpu.has(.spirv, .arbitrary_precision_integers)) {
try module.addExtension("SPV_INTEL_arbitrary_precision_integers");
try module.addCapability(.arbitrary_precision_integers_intel);
}
if (target.cpu.has(.spirv, .variable_pointers)) {
try module.addExtension("SPV_KHR_variable_pointers");
try module.addCapability(.variable_pointers_storage_buffer);
try module.addCapability(.variable_pointers);
inline for (@typeInfo(spec.Extension).@"enum".fields) |field| {
const ext: spec.Extension = @enumFromInt(field.value);
switch (ext) {
.v1_0, .v1_1, .v1_2, .v1_3, .v1_4, .v1_5, .v1_6 => continue,
else => {
if (target.cpu.has(.spirv, std.meta.stringToEnum(std.Target.spirv.Feature, field.name).?)) {
try module.addExtension(ext);
}
},
}
}
// These are well supported
try module.addCapability(.int8);
try module.addCapability(.int16);
@ -490,15 +563,15 @@ pub fn finalize(module: *Module, gpa: Allocator) ![]Word {
}
pub fn addCapability(module: *Module, cap: spec.Capability) !void {
const entry = try module.cache.capabilities.getOrPut(module.gpa, cap);
if (entry.found_existing) return;
if (module.cache.capabilities.contains(cap)) return;
module.cache.capabilities.insert(cap);
try module.sections.capabilities.emit(module.gpa, .OpCapability, .{ .capability = cap });
}
pub fn addExtension(module: *Module, ext: []const u8) !void {
const entry = try module.cache.extensions.getOrPut(module.gpa, ext);
if (entry.found_existing) return;
try module.sections.extensions.emit(module.gpa, .OpExtension, .{ .name = ext });
pub fn addExtension(module: *Module, ext: spec.Extension) !void {
if (module.cache.extensions.contains(ext)) return;
module.cache.extensions.insert(ext);
try module.sections.extensions.emit(module.gpa, .OpExtension, .{ .name = @tagName(ext) });
}
/// Imports or returns the existing id of an extended instruction set
@ -558,7 +631,7 @@ pub fn backingIntBits(module: *Module, bits: u16) struct { u16, bool } {
assert(bits != 0);
const target = module.zcu.getTarget();
if (target.cpu.has(.spirv, .arbitrary_precision_integers) and bits <= 32) {
if (target.cpu.has(.spirv, .arbitrary_precision_integers_intel) and bits <= 32) {
return .{ bits, false };
}
@ -871,15 +944,12 @@ pub fn declareEntryPoint(
module: *Module,
decl_index: Decl.Index,
name: []const u8,
exec_model: spec.ExecutionModel,
exec_mode: ?spec.ExecutionMode,
cc: std.builtin.CallingConvention,
) !void {
const gop = try module.entry_points.getOrPut(module.gpa, module.declPtr(decl_index).result_id);
gop.value_ptr.decl_index = decl_index;
gop.value_ptr.name = 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;
gop.value_ptr.cc = cc;
}
pub fn debugName(module: *Module, target: Id, name: []const u8) !void {

File diff suppressed because it is too large Load diff

View file

@ -189,35 +189,15 @@ pub fn updateExports(
},
};
const nav_ty = ip.getNav(nav_index).typeOf(ip);
const target = zcu.getTarget();
if (ip.isFunctionType(nav_ty)) {
const spv_decl_index = try linker.module.resolveNav(ip, nav_index);
const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
const exec_model: spec.ExecutionModel = switch (target.os.tag) {
.vulkan, .opengl => switch (cc) {
.spirv_vertex => .vertex,
.spirv_fragment => .fragment,
.spirv_kernel => .gl_compute,
// 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,
};
for (export_indices) |export_idx| {
const exp = export_idx.ptr(zcu);
try linker.module.declareEntryPoint(
spv_decl_index,
exp.opts.name.toSlice(ip),
exec_model,
null,
cc,
);
}
}

View file

@ -29,6 +29,10 @@ pub fn build(b: *std.Build) void {
const tools_tests_step = b.step("standalone_test_cases.tools", "Test tools");
step.dependOn(tools_tests_step);
const tools_target = b.resolveTargetQuery(.{});
const spirv_spec = b.createModule(.{
.root_source_file = b.path("../../src/codegen/spirv/spec.zig"),
.target = tools_target,
});
for ([_][]const u8{
// Alphabetically sorted. No need to build `tools/spirv/grammar.zig`.
"../../tools/dump-cov.zig",
@ -60,6 +64,9 @@ pub fn build(b: *std.Build) void {
.target = tools_target,
}),
});
if (std.mem.endsWith(u8, tool_src_path, "update_cpu_features.zig")) {
tool.root_module.addImport("spirv_spec", spirv_spec);
}
tools_tests_step.dependOn(&tool.step);
}
for ([_][]const u8{

View file

@ -323,6 +323,38 @@ fn render(
try renderOperandKinds(writer, all_operand_kinds.values(), extended_structs);
try renderInstructionSet(writer, registry, extensions, all_operand_kinds);
try renderExtension(writer, all_operand_kinds.values());
}
fn renderExtension(writer: anytype, kinds: []const OperandKind) !void {
try writer.writeAll(
\\pub const Extension = enum {
\\v1_0,
\\v1_1,
\\v1_2,
\\v1_3,
\\v1_4,
\\v1_5,
\\v1_6,
\\
);
var seen_extensions = std.StringHashMap(void).init(allocator);
defer seen_extensions.deinit();
for (kinds) |kind| {
if (std.mem.eql(u8, "Capability", kind.kind)) {
for (kind.enumerants.?) |enumerant| {
for (enumerant.extensions) |ext| {
if (seen_extensions.contains(ext)) continue;
try seen_extensions.put(ext, {});
try writer.print("{s},\n", .{ext});
}
}
}
}
try writer.writeAll("};\n");
}
fn renderInstructionSet(
@ -704,6 +736,39 @@ fn renderValueEnum(
});
}
if (std.mem.eql(u8, "Capability", enumeration.kind)) {
try writer.writeAll(
\\
\\pub fn dependencies(self: Capability) []const Extension {
\\ return switch (self) {
);
for (enum_indices) |i| {
const enumerant = enumerants[i];
// Convert version to enum.
// None is for reserved
// Example: "None" -> .v1_0
// Example: "1.5" -> .v1_5
const enum_version = enumerant.version.?;
const version: [4]u8 = .{ 'v', '1', '_', if (enum_version[0] == 'N') '0' else enum_version[2] };
try writer.print("\n.{f} => &.{{.{s},", .{ formatId(enumerant.enumerant), version });
for (enumerant.extensions) |extension| {
try writer.print(".{s},", .{extension});
}
try writer.writeAll("},");
}
try writer.writeAll(
\\};
\\}
\\};
\\
);
return;
}
if (!extended_structs.contains(enumeration.kind)) {
try writer.writeAll("};\n");
return;

View file

@ -1,5 +1,6 @@
const std = @import("std");
const builtin = @import("builtin");
const spirv_spec = @import("spirv_spec");
const fs = std.fs;
const mem = std.mem;
const json = std.json;
@ -1367,83 +1368,7 @@ const targets = [_]ArchTarget{
.td_name = "SPIRV",
},
.branch_quota = 2000,
.extra_features = &.{
.{
.zig_name = "v1_0",
.desc = "Enable version 1.0",
.deps = &.{},
},
.{
.zig_name = "v1_1",
.desc = "Enable version 1.1",
.deps = &.{"v1_0"},
},
.{
.zig_name = "v1_2",
.desc = "Enable version 1.2",
.deps = &.{"v1_1"},
},
.{
.zig_name = "v1_3",
.desc = "Enable version 1.3",
.deps = &.{"v1_2"},
},
.{
.zig_name = "v1_4",
.desc = "Enable version 1.4",
.deps = &.{"v1_3"},
},
.{
.zig_name = "v1_5",
.desc = "Enable version 1.5",
.deps = &.{"v1_4"},
},
.{
.zig_name = "v1_6",
.desc = "Enable version 1.6",
.deps = &.{"v1_5"},
},
.{
.zig_name = "int64",
.desc = "Enable Int64 capability",
.deps = &.{"v1_0"},
},
.{
.zig_name = "float16",
.desc = "Enable Float16 capability",
.deps = &.{"v1_0"},
},
.{
.zig_name = "float64",
.desc = "Enable Float64 capability",
.deps = &.{"v1_0"},
},
.{
.zig_name = "storage_push_constant16",
.desc = "Enable SPV_KHR_16bit_storage extension and the StoragePushConstant16 capability",
.deps = &.{"v1_3"},
},
.{
.zig_name = "arbitrary_precision_integers",
.desc = "Enable SPV_INTEL_arbitrary_precision_integers extension and the ArbitraryPrecisionIntegersINTEL capability",
.deps = &.{"v1_5"},
},
.{
.zig_name = "generic_pointer",
.desc = "Enable GenericPointer capability",
.deps = &.{"v1_0"},
},
.{
.zig_name = "vector16",
.desc = "Enable Vector16 capability",
.deps = &.{"v1_0"},
},
.{
.zig_name = "variable_pointers",
.desc = "Enable SPV_KHR_physical_storage_buffer extension and the PhysicalStorageBufferAddresses capability",
.deps = &.{"v1_0"},
},
},
.extra_features = spirvFeatures(),
.extra_cpus = &.{
.{
.llvm_name = null,
@ -2439,6 +2364,41 @@ fn usageAndExit(arg0: []const u8, code: u8) noreturn {
std.process.exit(code);
}
fn spirvFeatures() []const Feature {
const cap_fields = @typeInfo(spirv_spec.Capability).@"enum".fields;
const ext_fields = @typeInfo(spirv_spec.Extension).@"enum".fields;
var out_feature: [cap_fields.len + ext_fields.len]Feature = undefined;
@setEvalBranchQuota(2000);
for (cap_fields, out_feature[0..cap_fields.len]) |field, *feature| {
feature.* = .{
.zig_name = field.name,
.desc = "Enable " ++ field.name ++ " Capability.",
.deps = comptime res: {
const extensions = spirv_spec.Capability.dependencies(@enumFromInt(field.value));
var out_extensions: [extensions.len][]const u8 = undefined;
for (extensions, 0..) |ext, i| {
out_extensions[i] = @tagName(ext);
}
const final_extensions = out_extensions;
break :res &final_extensions;
},
};
}
for (ext_fields, out_feature[cap_fields.len..]) |field, *feature| {
feature.* = .{
.zig_name = field.name,
.desc = "Enable " ++ field.name ++ " Extension.",
.deps = &.{},
};
}
const final = out_feature;
return &final;
}
fn featureLessThan(_: void, a: Feature, b: Feature) bool {
return std.ascii.lessThanIgnoreCase(a.zig_name, b.zig_name);
}