| author | |
| committer | |
| log | 8f27a43701118f4c713629dfc61b3e63a805851f |
| tree | 1cb7fbe31e4070be75e682a85618a5f8a4c06207 |
| parent | 1b728e1834672848b12d10dd992b34141a38fc05 |
| parent | 50a771a11ed1604d642e94219ac1e939c249f6da |
| signature |
spirv: add support for GPU builtins3 files changed, 80 insertions(+), 2 deletions(-)
src/Sema.zig+1-1| ... | @@ -26488,7 +26488,7 @@ fn zirWorkItem( | ... | @@ -26488,7 +26488,7 @@ fn zirWorkItem( |
| 26488 | 26488 | ||
| 26489 | switch (target.cpu.arch) { | 26489 | switch (target.cpu.arch) { |
| 26490 | // TODO: Allow for other GPU targets. | 26490 | // TODO: Allow for other GPU targets. |
| 26491 | .amdgcn => {}, | 26491 | .amdgcn, .spirv64, .spirv32 => {}, |
| 26492 | else => { | 26492 | else => { |
| 26493 | return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); | 26493 | return sema.fail(block, builtin_src, "builtin only available on GPU targets; targeted architecture is {s}", .{@tagName(target.cpu.arch)}); |
| 26494 | }, | 26494 | }, |
src/codegen/spirv.zig+55| ... | @@ -3376,6 +3376,11 @@ const DeclGen = struct { | ... | @@ -3376,6 +3376,11 @@ const DeclGen = struct { |
| 3376 | .call_always_tail => try self.airCall(inst, .always_tail), | 3376 | .call_always_tail => try self.airCall(inst, .always_tail), |
| 3377 | .call_never_tail => try self.airCall(inst, .never_tail), | 3377 | .call_never_tail => try self.airCall(inst, .never_tail), |
| 3378 | .call_never_inline => try self.airCall(inst, .never_inline), | 3378 | .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 | |||
| 3379 | // zig fmt: on | 3384 | // zig fmt: on |
| 3380 | 3385 | ||
| 3381 | else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}), | 3386 | else => |tag| return self.todo("implement AIR tag {s}", .{@tagName(tag)}), |
| ... | @@ -6533,6 +6538,56 @@ const DeclGen = struct { | ... | @@ -6533,6 +6538,56 @@ const DeclGen = struct { |
| 6533 | return result_id; | 6538 | return result_id; |
| 6534 | } | 6539 | } |
| 6535 | 6540 | ||
| 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 | |||
| 6536 | fn typeOf(self: *DeclGen, inst: Air.Inst.Ref) Type { | 6591 | fn typeOf(self: *DeclGen, inst: Air.Inst.Ref) Type { |
| 6537 | const mod = self.module; | 6592 | const mod = self.module; |
| 6538 | return self.air.typeOf(inst, &mod.intern_pool); | 6593 | return self.air.typeOf(inst, &mod.intern_pool); |
src/codegen/spirv/Module.zig+24-1| ... | @@ -8,7 +8,6 @@ | ... | @@ -8,7 +8,6 @@ |
| 8 | const Module = @This(); | 8 | const Module = @This(); |
| 9 | 9 | ||
| 10 | const std = @import("std"); | 10 | const std = @import("std"); |
| 11 | const builtin = @import("builtin"); | ||
| 12 | const Allocator = std.mem.Allocator; | 11 | const Allocator = std.mem.Allocator; |
| 13 | const assert = std.debug.assert; | 12 | const assert = std.debug.assert; |
| 14 | 13 | ||
| ... | @@ -150,6 +149,8 @@ strings: std.StringArrayHashMapUnmanaged(IdRef) = .{}, | ... | @@ -150,6 +149,8 @@ strings: std.StringArrayHashMapUnmanaged(IdRef) = .{}, |
| 150 | /// this is an ad-hoc structure to cache types where required. | 149 | /// this is an ad-hoc structure to cache types where required. |
| 151 | /// According to the SPIR-V specification, section 2.8, this includes all non-aggregate | 150 | /// According to the SPIR-V specification, section 2.8, this includes all non-aggregate |
| 152 | /// non-pointer types. | 151 | /// non-pointer types. |
| 152 | /// Additionally, this is used for other values which can be cached, for example, | ||
| 153 | /// built-in variables. | ||
| 153 | cache: struct { | 154 | cache: struct { |
| 154 | bool_type: ?IdRef = null, | 155 | bool_type: ?IdRef = null, |
| 155 | void_type: ?IdRef = null, | 156 | void_type: ?IdRef = null, |
| ... | @@ -158,6 +159,8 @@ cache: struct { | ... | @@ -158,6 +159,8 @@ cache: struct { |
| 158 | // This cache is required so that @Vector(X, u1) in direct representation has the | 159 | // This cache is required so that @Vector(X, u1) in direct representation has the |
| 159 | // same ID as @Vector(X, bool) in indirect representation. | 160 | // same ID as @Vector(X, bool) in indirect representation. |
| 160 | vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .{}, | 161 | vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .{}, |
| 162 | |||
| 163 | builtins: std.AutoHashMapUnmanaged(struct { IdRef, spec.BuiltIn }, Decl.Index) = .{}, | ||
| 161 | } = .{}, | 164 | } = .{}, |
| 162 | 165 | ||
| 163 | /// Set of Decls, referred to by Decl.Index. | 166 | /// Set of Decls, referred to by Decl.Index. |
| ... | @@ -198,6 +201,7 @@ pub fn deinit(self: *Module) void { | ... | @@ -198,6 +201,7 @@ pub fn deinit(self: *Module) void { |
| 198 | self.cache.int_types.deinit(self.gpa); | 201 | self.cache.int_types.deinit(self.gpa); |
| 199 | self.cache.float_types.deinit(self.gpa); | 202 | self.cache.float_types.deinit(self.gpa); |
| 200 | self.cache.vector_types.deinit(self.gpa); | 203 | self.cache.vector_types.deinit(self.gpa); |
| 204 | self.cache.builtins.deinit(self.gpa); | ||
| 201 | 205 | ||
| 202 | self.decls.deinit(self.gpa); | 206 | self.decls.deinit(self.gpa); |
| 203 | self.decl_deps.deinit(self.gpa); | 207 | self.decl_deps.deinit(self.gpa); |
| ... | @@ -491,6 +495,25 @@ pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef { | ... | @@ -491,6 +495,25 @@ pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef { |
| 491 | return entry.value_ptr.*; | 495 | return entry.value_ptr.*; |
| 492 | } | 496 | } |
| 493 | 497 | ||
| 498 | /// Return a pointer to a builtin variable. `result_ty_id` must be a **pointer** | ||
| 499 | /// with storage class `.Input`. | ||
| 500 | pub 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 | |||
| 494 | pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef { | 517 | pub fn constUndef(self: *Module, ty_id: IdRef) !IdRef { |
| 495 | const result_id = self.allocId(); | 518 | const result_id = self.allocId(); |
| 496 | try self.sections.types_globals_constants.emit(self.gpa, .OpUndef, .{ | 519 | try self.sections.types_globals_constants.emit(self.gpa, .OpUndef, .{ |