aboutsummaryrefslogtreecommitdiff
path: root/src/codegen/spirv/Module.zig
diff options
context:
space:
mode:
authorRobin Voetter <robin@voetter.nl>2022-01-22 01:49:47 +0100
committerRobin Voetter <robin@voetter.nl>2022-01-28 14:45:23 +0100
commit98ee39d1b0ed516428c611d8dc1e52d21c786f97 (patch)
treea5667f3012952c1301b5d8631d86b753d0f71704 /src/codegen/spirv/Module.zig
parent1b6ebce0da45169979b0f51a07274ff6fb5590bc (diff)
downloadzig-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.zig327
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;
+ }
+ }
+}