From afa779335186acf10f79848775afaf55698d8d88 Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Thu, 1 Feb 2024 15:48:51 +0330 Subject: spirv: basic shader support --- src/codegen/spirv/Module.zig | 53 +++++++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 13 deletions(-) (limited to 'src/codegen/spirv/Module.zig') diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index 056792ab9a..98a7c67bee 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -92,7 +92,7 @@ pub const Global = struct { /// The past-end offset into `self.flobals.section`. end_inst: u32, /// The result-id of the function that initializes this value. - initializer_id: IdRef, + initializer_id: ?IdRef, }; /// This models a kernel entry point. @@ -101,6 +101,8 @@ pub const EntryPoint = struct { decl_index: Decl.Index, /// The name of the kernel to be exported. name: CacheString, + /// Calling Convention + execution_model: spec.ExecutionModel, }; /// A general-purpose allocator which may be used to allocate resources for this module @@ -313,7 +315,7 @@ fn entryPoints(self: *Module) !Section { const entry_point_id = self.declPtr(entry_point.decl_index).result_id; try entry_points.emit(self.gpa, .OpEntryPoint, .{ - .execution_model = .Kernel, + .execution_model = entry_point.execution_model, .entry_point = entry_point_id, .name = self.cache.getString(entry_point.name).?, .interface = interface.items, @@ -362,11 +364,13 @@ fn initializer(self: *Module, entry_points: *Section) !Section { for (self.globals.globals.keys(), self.globals.globals.values()) |decl_index, global| { try self.addEntryPointDeps(decl_index, &seen, &interface); - try section.emit(self.gpa, .OpFunctionCall, .{ - .id_result_type = void_ty_id, - .id_result = self.allocId(), - .function = global.initializer_id, - }); + if (global.initializer_id) |initializer_id| { + try section.emit(self.gpa, .OpFunctionCall, .{ + .id_result_type = void_ty_id, + .id_result = self.allocId(), + .function = initializer_id, + }); + } } try section.emit(self.gpa, .OpReturn, {}); @@ -390,7 +394,7 @@ fn initializer(self: *Module, entry_points: *Section) !Section { } /// Emit this module as a spir-v binary. -pub fn flush(self: *Module, file: std.fs.File) !void { +pub fn flush(self: *Module, file: std.fs.File, target: std.Target) !void { // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction" // TODO: Perform topological sort on the globals. @@ -403,14 +407,25 @@ pub fn flush(self: *Module, file: std.fs.File) !void { var types_constants = try self.cache.materialize(self); defer types_constants.deinit(self.gpa); - var init_func = try self.initializer(&entry_points); + // TODO: Vulkan doesn't support initializer kernel + var init_func = if (target.os.tag != .vulkan) + try self.initializer(&entry_points) + else + Section{}; defer init_func.deinit(self.gpa); const header = [_]Word{ spec.magic_number, // TODO: From cpu features - // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. - (1 << 16) | (4 << 8), + spec.Version.toWord(.{ + .major = 1, + .minor = switch (target.os.tag) { + // Emit SPIR-V 1.3 for now. This is the highest version that Vulkan 1.1 supports. + .vulkan => 3, + // Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports. + else => 4, + }, + }), 0, // TODO: Register Zig compiler magic number. self.idBound(), 0, // Schema (currently reserved for future use) @@ -617,7 +632,13 @@ pub fn beginGlobal(self: *Module) u32 { return @as(u32, @intCast(self.globals.section.instructions.items.len)); } -pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32, result_id: IdRef, initializer_id: IdRef) void { +pub fn endGlobal( + self: *Module, + global_index: Decl.Index, + begin_inst: u32, + result_id: IdRef, + initializer_id: ?IdRef, +) void { const global = self.globalPtr(global_index).?; global.* = .{ .result_id = result_id, @@ -627,10 +648,16 @@ pub fn endGlobal(self: *Module, global_index: Decl.Index, begin_inst: u32, resul }; } -pub fn declareEntryPoint(self: *Module, decl_index: Decl.Index, name: []const u8) !void { +pub fn declareEntryPoint( + self: *Module, + decl_index: Decl.Index, + name: []const u8, + execution_model: spec.ExecutionModel, +) !void { try self.entry_points.append(self.gpa, .{ .decl_index = decl_index, .name = try self.resolveString(name), + .execution_model = execution_model, }); } -- cgit v1.2.3 From b41aad019364b2c99934339d70aa4f37d945f248 Mon Sep 17 00:00:00 2001 From: Ali Chraghi Date: Thu, 1 Feb 2024 19:38:23 +0330 Subject: spirv: emit vectors whenever we can --- src/codegen/spirv.zig | 59 ++++++++++++++++++++++++++++++++++++++------ src/codegen/spirv/Module.zig | 7 ++++++ 2 files changed, 59 insertions(+), 7 deletions(-) (limited to 'src/codegen/spirv/Module.zig') diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index e8446622f7..8bcc907436 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -744,6 +744,30 @@ const DeclGen = struct { return try self.load(ty, ptr_composite_id, .{}); } + /// Construct a vector at runtime. + /// ty must be an vector type. + /// Constituents should be in `indirect` representation (as the elements of an vector should be). + /// Result is in `direct` representation. + fn constructVector(self: *DeclGen, ty: Type, constituents: []const IdRef) !IdRef { + // The Khronos LLVM-SPIRV translator crashes because it cannot construct structs which' + // operands are not constant. + // See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349 + // For now, just initialize the struct by setting the fields manually... + // TODO: Make this OpCompositeConstruct when we can + const mod = self.module; + const ptr_composite_id = try self.alloc(ty, .{ .storage_class = .Function }); + const ptr_elem_ty_ref = try self.ptrType(ty.elemType2(mod), .Function); + for (constituents, 0..) |constitent_id, index| { + const ptr_id = try self.accessChain(ptr_elem_ty_ref, ptr_composite_id, &.{@as(u32, @intCast(index))}); + try self.func.body.emit(self.spv.gpa, .OpStore, .{ + .pointer = ptr_id, + .object = constitent_id, + }); + } + + return try self.load(ty, ptr_composite_id, .{}); + } + /// Construct an array at runtime. /// ty must be an array type. /// Constituents should be in `indirect` representation (as the elements of an array should be). @@ -963,13 +987,16 @@ const DeclGen = struct { } switch (tag) { - inline .array_type => if (array_type.sentinel != .none) { - constituents[constituents.len - 1] = try self.constant(elem_ty, Value.fromInterned(array_type.sentinel), .indirect); + inline .array_type => { + if (array_type.sentinel != .none) { + const sentinel = Value.fromInterned(array_type.sentinel); + constituents[constituents.len - 1] = try self.constant(elem_ty, sentinel, .indirect); + } + return self.constructArray(ty, constituents); }, - else => {}, + inline .vector_type => return self.constructVector(ty, constituents), + else => unreachable, } - - return try self.constructArray(ty, constituents); }, .struct_type => { const struct_type = mod.typeToStruct(ty).?; @@ -1492,8 +1519,14 @@ const DeclGen = struct { const elem_ty = ty.childType(mod); const elem_ty_ref = try self.resolveType(elem_ty, .indirect); + const len = ty.vectorLen(mod); + const is_scalar = elem_ty.isNumeric(mod) or elem_ty.toIntern() == .bool_type; + + const ty_ref = if (is_scalar and len > 1 and len <= 4) + try self.spv.vectorType(ty.vectorLen(mod), elem_ty_ref) + else + try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref); - const ty_ref = try self.spv.arrayType(ty.vectorLen(mod), elem_ty_ref); try self.type_map.put(self.gpa, ty.toIntern(), .{ .ty_ref = ty_ref }); return ty_ref; }, @@ -3688,7 +3721,19 @@ const DeclGen = struct { constituents[0..index], ); }, - .Vector, .Array => { + .Vector => { + const n_elems = result_ty.vectorLen(mod); + const elem_ids = try self.gpa.alloc(IdRef, n_elems); + defer self.gpa.free(elem_ids); + + for (elements, 0..) |element, i| { + const id = try self.resolve(element); + elem_ids[i] = try self.convertToIndirect(result_ty.childType(mod), id); + } + + return try self.constructVector(result_ty, elem_ids); + }, + .Array => { const array_info = result_ty.arrayInfo(mod); const n_elems: usize = @intCast(result_ty.arrayLenIncludingSentinel(mod)); const elem_ids = try self.gpa.alloc(IdRef, n_elems); diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index 98a7c67bee..2c411b4590 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -508,6 +508,13 @@ pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !Ca } }); } +pub fn vectorType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef { + return try self.resolve(.{ .vector_type = .{ + .component_type = elem_ty_ref, + .component_count = len, + } }); +} + pub fn arrayType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef { const len_ty_ref = try self.resolve(.{ .int_type = .{ .signedness = .unsigned, -- cgit v1.2.3