authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2024-06-16 08:42:35+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2024-06-16 08:42:35+02:00
log8f27a43701118f4c713629dfc61b3e63a805851f
tree1cb7fbe31e4070be75e682a85618a5f8a4c06207
parent1b728e1834672848b12d10dd992b34141a38fc05
parent50a771a11ed1604d642e94219ac1e939c249f6da
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #20309 from Snektron/spirv-gpu-builtins

spirv: add support for GPU builtins

3 files changed, 80 insertions(+), 2 deletions(-)

src/Sema.zig+1-1
......@@ -26488,7 +26488,7 @@ fn zirWorkItem(
2648826488
2648926489 switch (target.cpu.arch) {
2649026490 // TODO: Allow for other GPU targets.
26491 .amdgcn => {},
26491 .amdgcn, .spirv64, .spirv32 => {},
2649226492 else => {
2649326493 return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)});
2649426494 },
src/codegen/spirv.zig+55
......@@ -3376,6 +3376,11 @@ const DeclGen = struct {
33763376 .call_always_tail => try self.airCall(inst, .always_tail),
33773377 .call_never_tail => try self.airCall(inst, .never_tail),
33783378 .call_never_inline => try self.airCall(inst, .never_inline),
3379
3380 .work_item_id => try self.airWorkItemId(inst),
3381 .work_group_size => try self.airWorkGroupSize(inst),
3382 .work_group_id => try self.airWorkGroupId(inst),
3383
33793384 // zig fmt: on
33803385
33813386 else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}),
......@@ -6533,6 +6538,56 @@ const DeclGen = struct {
65336538 return result_id;
65346539 }
65356540
6541 fn builtin3D(self: *DeclGen, result_ty: Type, builtin: spec.BuiltIn, dimension: u32, out_of_range_value: anytype) !IdRef {
6542 const mod = self.module;
6543 if (dimension >= 3) {
6544 return try self.constInt(result_ty, out_of_range_value, .direct);
6545 }
6546 const vec_ty = try mod.vectorType(.{
6547 .len = 3,
6548 .child = result_ty.toIntern(),
6549 });
6550 const ptr_ty_id = try self.ptrType(vec_ty, .Input);
6551 const spv_decl_index = try self.spv.builtin(ptr_ty_id, builtin);
6552 try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
6553 const ptr = self.spv.declPtr(spv_decl_index).result_id;
6554 const vec = try self.load(vec_ty, ptr, .{});
6555 return try self.extractVectorComponent(result_ty, vec, dimension);
6556 }
6557
6558 fn airWorkItemId(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
6559 if (self.liveness.isUnused(inst)) return null;
6560 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6561 const dimension = pl_op.payload;
6562 // TODO: Should we make these builtins return usize?
6563 const result_id = try self.builtin3D(Type.u64, .LocalInvocationId, dimension, 0);
6564 const tmp = Temporary.init(Type.u64, result_id);
6565 const result = try self.buildIntConvert(Type.u32, tmp);
6566 return try result.materialize(self);
6567 }
6568
6569 fn airWorkGroupSize(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
6570 if (self.liveness.isUnused(inst)) return null;
6571 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6572 const dimension = pl_op.payload;
6573 // TODO: Should we make these builtins return usize?
6574 const result_id = try self.builtin3D(Type.u64, .WorkgroupSize, dimension, 0);
6575 const tmp = Temporary.init(Type.u64, result_id);
6576 const result = try self.buildIntConvert(Type.u32, tmp);
6577 return try result.materialize(self);
6578 }
6579
6580 fn airWorkGroupId(self: *DeclGen, inst: Air.Inst.Index) !?IdRef {
6581 if (self.liveness.isUnused(inst)) return null;
6582 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op;
6583 const dimension = pl_op.payload;
6584 // TODO: Should we make these builtins return usize?
6585 const result_id = try self.builtin3D(Type.u64, .WorkgroupId, dimension, 0);
6586 const tmp = Temporary.init(Type.u64, result_id);
6587 const result = try self.buildIntConvert(Type.u32, tmp);
6588 return try result.materialize(self);
6589 }
6590
65366591 fn typeOf(self: *DeclGen, inst: Air.Inst.Ref) Type {
65376592 const mod = self.module;
65386593 return self.air.typeOf(inst, &mod.intern_pool);
src/codegen/spirv/Module.zig+24-1
......@@ -8,7 +8,6 @@
88const Module = @This();
99
1010const std = @import("std");
11const builtin = @import("builtin");
1211const Allocator = std.mem.Allocator;
1312const assert = std.debug.assert;
1413
......@@ -150,6 +149,8 @@ strings: std.StringArrayHashMapUnmanaged(IdRef) = .{},
150149/// this is an ad-hoc structure to cache types where required.
151150/// According to the SPIR-V specification, section 2.8, this includes all non-aggregate
152151/// non-pointer types.
152/// Additionally, this is used for other values which can be cached, for example,
153/// built-in variables.
153154cache: struct {
154155 bool_type: ?IdRef = null,
155156 void_type: ?IdRef = null,
......@@ -158,6 +159,8 @@ cache: struct {
158159 // This cache is required so that @Vector(X, u1) in direct representation has the
159160 // same ID as @Vector(X, bool) in indirect representation.
160161 vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .{},
162
163 builtins: std.AutoHashMapUnmanaged(struct { IdRef, spec.BuiltIn }, Decl.Index) = .{},
161164} = .{},
162165
163166/// Set of Decls, referred to by Decl.Index.
......@@ -198,6 +201,7 @@ pub fn deinit(self: *Module) void {
198201 self.cache.int_types.deinit(self.gpa);
199202 self.cache.float_types.deinit(self.gpa);
200203 self.cache.vector_types.deinit(self.gpa);
204 self.cache.builtins.deinit(self.gpa);
201205
202206 self.decls.deinit(self.gpa);
203207 self.decl_deps.deinit(self.gpa);
......@@ -491,6 +495,25 @@ pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef {
491495 return entry.value_ptr.*;
492496}
493497
498/// Return a pointer to a builtin variable. `result_ty_id` must be a **pointer**
499/// with storage class `.Input`.
500pub fn builtin(self: *Module, result_ty_id: IdRef, spirv_builtin: spec.BuiltIn) !Decl.Index {
501 const entry = try self.cache.builtins.getOrPut(self.gpa, .{ result_ty_id, spirv_builtin });
502 if (!entry.found_existing) {
503 const decl_index = try self.allocDecl(.global);
504 const result_id = self.declPtr(decl_index).result_id;
505 entry.value_ptr.* = decl_index;
506 try self.sections.types_globals_constants.emit(self.gpa, .OpVariable, .{
507 .id_result_type = result_ty_id,
508 .id_result = result_id,
509 .storage_class = .Input,
510 });
511 try self.decorate(result_id, .{ .BuiltIn = .{ .built_in = spirv_builtin } });
512 try self.declareDeclDeps(decl_index, &.{});
513 }
514 return entry.value_ptr.*;
515}
516
494517pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef {
495518 const result_id = self.allocId();
496519 try self.sections.types_globals_constants.emit(self.gpa, .OpUndef, .{