diff options
| author | Robin Voetter <robin@voetter.nl> | 2022-01-22 01:49:47 +0100 |
|---|---|---|
| committer | Robin Voetter <robin@voetter.nl> | 2022-01-28 14:45:23 +0100 |
| commit | 98ee39d1b0ed516428c611d8dc1e52d21c786f97 (patch) | |
| tree | a5667f3012952c1301b5d8631d86b753d0f71704 /src/codegen/spirv/Module.zig | |
| parent | 1b6ebce0da45169979b0f51a07274ff6fb5590bc (diff) | |
| download | zig-98ee39d1b0ed516428c611d8dc1e52d21c786f97.tar.gz zig-98ee39d1b0ed516428c611d8dc1e52d21c786f97.zip | |
spirv: spir-v dedicated type system
Diffstat (limited to 'src/codegen/spirv/Module.zig')
| -rw-r--r-- | src/codegen/spirv/Module.zig | 327 |
1 files changed, 301 insertions, 26 deletions
diff --git a/src/codegen/spirv/Module.zig b/src/codegen/spirv/Module.zig index fac9b92c70..59ed1b9b78 100644 --- a/src/codegen/spirv/Module.zig +++ b/src/codegen/spirv/Module.zig @@ -9,14 +9,20 @@ const Module = @This(); const std = @import("std"); const Allocator = std.mem.Allocator; +const assert = std.debug.assert; const ZigDecl = @import("../../Module.zig").Decl; const spec = @import("spec.zig"); const Word = spec.Word; const IdRef = spec.IdRef; +const IdResult = spec.IdResult; +const IdResultType = spec.IdResultType; const Section = @import("Section.zig"); +const Type = @import("type.zig").Type; + +const TypeCache = std.ArrayHashMapUnmanaged(Type, IdResultType, Type.ShallowHashContext32, true); /// A general-purpose allocator which may be used to allocate resources for this module gpa: Allocator, @@ -57,6 +63,12 @@ next_result_id: Word, /// just the ones for OpLine. Note that OpLine needs the result of OpString, and not that of OpSource. source_file_names: std.StringHashMapUnmanaged(IdRef) = .{}, +/// SPIR-V type cache. Note that according to SPIR-V spec section 2.8, Types and Variables, non-pointer +/// non-aggrerate types (which includes matrices and vectors) must have a _unique_ representation in +/// the final binary. +/// Note: Uses ArrayHashMap which is insertion ordered, so that we may refer to other types by index (Type.Ref). +type_cache: TypeCache = .{}, + pub fn init(gpa: Allocator, arena: Allocator) Module { return .{ .gpa = gpa, @@ -75,44 +87,20 @@ pub fn deinit(self: *Module) void { self.sections.functions.deinit(self.gpa); self.source_file_names.deinit(self.gpa); + self.type_cache.deinit(self.gpa); self.* = undefined; } pub fn allocId(self: *Module) spec.IdResult { defer self.next_result_id += 1; - return .{.id = self.next_result_id}; + return .{ .id = self.next_result_id }; } pub fn idBound(self: Module) Word { return self.next_result_id; } -/// Fetch the result-id of an OpString instruction that encodes the path of the source -/// file of the decl. This function may also emit an OpSource with source-level information regarding -/// the decl. -pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef { - const path = decl.getFileScope().sub_file_path; - const result = try self.source_file_names.getOrPut(self.gpa, path); - if (!result.found_existing) { - const file_result_id = self.allocId(); - result.value_ptr.* = file_result_id.toRef(); - try self.sections.debug_strings.emit(self.gpa, .OpString, .{ - .id_result = file_result_id, - .string = path, - }); - - try self.sections.debug_strings.emit(self.gpa, .OpSource, .{ - .source_language = .Unknown, // TODO: Register Zig source language. - .version = 0, // TODO: Zig version as u32? - .file = file_result_id.toRef(), - .source = null, // TODO: Store actual source also? - }); - } - - return result.value_ptr.*; -} - /// Emit this module as a spir-v binary. pub fn flush(self: Module, file: std.fs.File) !void { // See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction" @@ -151,3 +139,290 @@ pub fn flush(self: Module, file: std.fs.File) !void { try file.setEndPos(file_size); try file.pwritevAll(&iovc_buffers, 0); } + +/// Fetch the result-id of an OpString instruction that encodes the path of the source +/// file of the decl. This function may also emit an OpSource with source-level information regarding +/// the decl. +pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef { + const path = decl.getFileScope().sub_file_path; + const result = try self.source_file_names.getOrPut(self.gpa, path); + if (!result.found_existing) { + const file_result_id = self.allocId(); + result.value_ptr.* = file_result_id.toRef(); + try self.sections.debug_strings.emit(self.gpa, .OpString, .{ + .id_result = file_result_id, + .string = path, + }); + + try self.sections.debug_strings.emit(self.gpa, .OpSource, .{ + .source_language = .Unknown, // TODO: Register Zig source language. + .version = 0, // TODO: Zig version as u32? + .file = file_result_id.toRef(), + .source = null, // TODO: Store actual source also? + }); + } + + return result.value_ptr.*; +} + +/// Fetch a result-id for a spir-v type. This function deduplicates the type as appropriate, +/// and returns a cached version if that exists. +/// Note: This function does not attempt to perform any validation on the type. +/// The type is emitted in a shallow fashion; any child types should already +/// be emitted at this point. +pub fn resolveType(self: *Module, ty: Type) !Type.Ref { + const result = try self.type_cache.getOrPut(self.gpa, ty); + if (!result.found_existing) { + result.value_ptr.* = try self.emitType(ty); + } + return result.index; +} + +pub fn resolveTypeId(self: *Module, ty: Type) !IdRef { + return self.typeResultId(try self.resolveType(ty)); +} + +/// Get the result-id of a particular type, by reference. Asserts type_ref is valid. +pub fn typeResultId(self: Module, type_ref: Type.Ref) IdResultType { + return self.type_cache.values()[type_ref]; +} + +/// Get the result-id of a particular type as IdRef, by Type.Ref. Asserts type_ref is valid. +pub fn typeRefId(self: Module, type_ref: Type.Ref) IdRef { + return self.type_cache.values()[type_ref].toRef(); +} + +/// Unconditionally emit a spir-v type into the appropriate section. +/// Note: If this function is called with a type that is already generated, it may yield an invalid module +/// as non-pointer non-aggregrate types must me unique! +/// Note: This function does not attempt to perform any validation on the type. +/// The type is emitted in a shallow fashion; any child types should already +/// be emitted at this point. +pub fn emitType(self: *Module, ty: Type) !IdResultType { + const result_id = self.allocId(); + const ref_id = result_id.toRef(); + const types = &self.sections.types_globals_constants; + const annotations = &self.sections.annotations; + const result_id_operand = .{ .id_result = result_id }; + + switch (ty.tag()) { + .void => try types.emit(self.gpa, .OpTypeVoid, result_id_operand), + .bool => try types.emit(self.gpa, .OpTypeBool, result_id_operand), + .int => try types.emit(self.gpa, .OpTypeInt, .{ + .id_result = result_id, + .width = ty.payload(.int).width, + .signedness = switch (ty.payload(.int).signedness) { + .unsigned => @as(spec.LiteralInteger, 0), + .signed => 1, + }, + }), + .float => try types.emit(self.gpa, .OpTypeFloat, .{ + .id_result = result_id, + .width = ty.payload(.float).width, + }), + .vector => try types.emit(self.gpa, .OpTypeVector, .{ + .id_result = result_id, + .component_type = self.typeResultId(ty.childType()).toRef(), + .component_count = ty.payload(.vector).component_count, + }), + .matrix => try types.emit(self.gpa, .OpTypeMatrix, .{ + .id_result = result_id, + .column_type = self.typeResultId(ty.childType()).toRef(), + .column_count = ty.payload(.matrix).column_count, + }), + .image => { + const info = ty.payload(.image); + try types.emit(self.gpa, .OpTypeImage, .{ + .id_result = result_id, + .sampled_type = self.typeResultId(ty.childType()).toRef(), + .dim = info.dim, + .depth = @enumToInt(info.depth), + .arrayed = @boolToInt(info.arrayed), + .ms = @boolToInt(info.multisampled), + .sampled = @enumToInt(info.sampled), + .image_format = info.format, + .access_qualifier = info.access_qualifier, + }); + }, + .sampler => try types.emit(self.gpa, .OpTypeSampler, result_id_operand), + .sampled_image => try types.emit(self.gpa, .OpTypeSampledImage, .{ + .id_result = result_id, + .image_type = self.typeResultId(ty.childType()).toRef(), + }), + .array => { + const info = ty.payload(.array); + assert(info.length != 0); + try types.emit(self.gpa, .OpTypeArray, .{ + .id_result = result_id, + .element_type = self.typeResultId(ty.childType()).toRef(), + .length = .{ .id = 0 }, // TODO: info.length must be emitted as constant! + }); + if (info.array_stride != 0) { + try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); + } + }, + .runtime_array => { + const info = ty.payload(.runtime_array); + try types.emit(self.gpa, .OpTypeRuntimeArray, .{ + .id_result = result_id, + .element_type = self.typeResultId(ty.childType()).toRef(), + }); + if (info.array_stride != 0) { + try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); + } + }, + .@"struct" => { + const info = ty.payload(.@"struct"); + try types.emitRaw(self.gpa, .OpTypeStruct, 1 + info.members.len); + types.writeOperand(IdResult, result_id); + for (info.members) |member| { + types.writeOperand(IdRef, self.typeResultId(member.ty).toRef()); + } + try self.decorateStruct(ref_id, info); + }, + .@"opaque" => try types.emit(self.gpa, .OpTypeOpaque, .{ + .id_result = result_id, + .literal_string = ty.payload(.@"opaque").name, + }), + .pointer => { + const info = ty.payload(.pointer); + try types.emit(self.gpa, .OpTypePointer, .{ + .id_result = result_id, + .storage_class = info.storage_class, + .type = self.typeResultId(ty.childType()).toRef(), + }); + if (info.array_stride != 0) { + try annotations.decorate(self.gpa, ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } }); + } + if (info.alignment) |alignment| { + try annotations.decorate(self.gpa, ref_id, .{ .Alignment = .{ .alignment = alignment } }); + } + if (info.max_byte_offset) |max_byte_offset| { + try annotations.decorate(self.gpa, ref_id, .{ .MaxByteOffset = .{ .max_byte_offset = max_byte_offset } }); + } + }, + .function => { + const info = ty.payload(.function); + try types.emitRaw(self.gpa, .OpTypeFunction, 2 + info.parameters.len); + types.writeOperand(IdResult, result_id); + types.writeOperand(IdRef, self.typeResultId(info.return_type).toRef()); + for (info.parameters) |parameter_type| { + types.writeOperand(IdRef, self.typeResultId(parameter_type).toRef()); + } + }, + .event => try types.emit(self.gpa, .OpTypeEvent, result_id_operand), + .device_event => try types.emit(self.gpa, .OpTypeDeviceEvent, result_id_operand), + .reserve_id => try types.emit(self.gpa, .OpTypeReserveId, result_id_operand), + .queue => try types.emit(self.gpa, .OpTypeQueue, result_id_operand), + .pipe => try types.emit(self.gpa, .OpTypePipe, .{ + .id_result = result_id, + .qualifier = ty.payload(.pipe).qualifier, + }), + .pipe_storage => try types.emit(self.gpa, .OpTypePipeStorage, result_id_operand), + .named_barrier => try types.emit(self.gpa, .OpTypeNamedBarrier, result_id_operand), + } + + return result_id.toResultType(); +} + +fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void { + const annotations = &self.sections.annotations; + + // Decorations for the struct type itself. + if (info.decorations.block) + try annotations.decorate(self.gpa, target, .Block); + if (info.decorations.buffer_block) + try annotations.decorate(self.gpa, target, .BufferBlock); + if (info.decorations.glsl_shared) + try annotations.decorate(self.gpa, target, .GLSLShared); + if (info.decorations.glsl_packed) + try annotations.decorate(self.gpa, target, .GLSLPacked); + if (info.decorations.c_packed) + try annotations.decorate(self.gpa, target, .CPacked); + + // Decorations for the struct members. + const extra = info.member_decoration_extra; + var extra_i: u32 = 0; + for (info.members) |member, i| { + const d = member.decorations; + const index = @intCast(Word, i); + switch (d.matrix_layout) { + .row_major => try annotations.decorateMember(self.gpa, target, index, .RowMajor), + .col_major => try annotations.decorateMember(self.gpa, target, index, .ColMajor), + .none => {}, + } + if (d.matrix_layout != .none) { + try annotations.decorateMember(self.gpa, target, index, .{ + .MatrixStride = .{ .matrix_stride = extra[extra_i] }, + }); + extra_i += 1; + } + + if (d.no_perspective) + try annotations.decorateMember(self.gpa, target, index, .NoPerspective); + if (d.flat) + try annotations.decorateMember(self.gpa, target, index, .Flat); + if (d.patch) + try annotations.decorateMember(self.gpa, target, index, .Patch); + if (d.centroid) + try annotations.decorateMember(self.gpa, target, index, .Centroid); + if (d.sample) + try annotations.decorateMember(self.gpa, target, index, .Sample); + if (d.invariant) + try annotations.decorateMember(self.gpa, target, index, .Invariant); + if (d.@"volatile") + try annotations.decorateMember(self.gpa, target, index, .Volatile); + if (d.coherent) + try annotations.decorateMember(self.gpa, target, index, .Coherent); + if (d.non_writable) + try annotations.decorateMember(self.gpa, target, index, .NonWritable); + if (d.non_readable) + try annotations.decorateMember(self.gpa, target, index, .NonReadable); + + if (d.builtin) { + try annotations.decorateMember(self.gpa, target, index, .{ + .BuiltIn = .{ .built_in = @intToEnum(spec.BuiltIn, extra[extra_i]) }, + }); + extra_i += 1; + } + if (d.stream) { + try annotations.decorateMember(self.gpa, target, index, .{ + .Stream = .{ .stream_number = extra[extra_i] }, + }); + extra_i += 1; + } + if (d.location) { + try annotations.decorateMember(self.gpa, target, index, .{ + .Location = .{ .location = extra[extra_i] }, + }); + extra_i += 1; + } + if (d.component) { + try annotations.decorateMember(self.gpa, target, index, .{ + .Component = .{ .component = extra[extra_i] }, + }); + extra_i += 1; + } + if (d.xfb_buffer) { + try annotations.decorateMember(self.gpa, target, index, .{ + .XfbBuffer = .{ .xfb_buffer_number = extra[extra_i] }, + }); + extra_i += 1; + } + if (d.xfb_stride) { + try annotations.decorateMember(self.gpa, target, index, .{ + .XfbStride = .{ .xfb_stride = extra[extra_i] }, + }); + extra_i += 1; + } + if (d.user_semantic) { + const len = extra[extra_i]; + extra_i += 1; + const semantic = @ptrCast([*]const u8, &extra[extra_i])[0..len]; + try annotations.decorateMember(self.gpa, target, index, .{ + .UserSemantic = .{ .semantic = semantic }, + }); + extra_i += std.math.divCeil(u32, extra_i, @sizeOf(u32)) catch unreachable; + } + } +} |
