spirv: translate remaining types
This commit is contained in:
@@ -20,12 +20,13 @@ const IdResult = spec.IdResult;
|
||||
const IdResultType = spec.IdResultType;
|
||||
|
||||
const Section = @import("Section.zig");
|
||||
const Type = @import("type.zig").Type;
|
||||
pub const TypeConstantCache = @import("TypeConstantCache.zig");
|
||||
|
||||
const TypeCache = std.ArrayHashMapUnmanaged(Type, IdResultType, Type.ShallowHashContext32, true);
|
||||
const Cache = @import("TypeConstantCache.zig");
|
||||
pub const CacheKey = Cache.Key;
|
||||
pub const CacheRef = Cache.Ref;
|
||||
pub const CacheString = Cache.String;
|
||||
|
||||
/// This structure represents a function that is in-progress of being emitted.
|
||||
/// This structure represents a function that isc in-progress of being emitted.
|
||||
/// Commonly, the contents of this structure will be merged with the appropriate
|
||||
/// sections of the module and re-used. Note that the SPIR-V module system makes
|
||||
/// no attempt of compacting result-id's, so any Fn instance should ultimately
|
||||
@@ -130,7 +131,7 @@ sections: struct {
|
||||
/// From this section, OpLine and OpNoLine is allowed.
|
||||
/// According to the SPIR-V documentation, this section normally
|
||||
/// also holds type and constant instructions. These are managed
|
||||
/// via the tc_cache instead, which is the sole structure that
|
||||
/// via the cache instead, which is the sole structure that
|
||||
/// manages that section. These will be inserted between this and
|
||||
/// the previous section when emitting the final binary.
|
||||
/// TODO: Do we need this section? Globals are also managed with another mechanism.
|
||||
@@ -152,10 +153,9 @@ 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) = .{},
|
||||
|
||||
type_cache: TypeCache = .{},
|
||||
/// SPIR-V type- and constant cache. This structure is used to store information about these in a more
|
||||
/// efficient manner.
|
||||
tc_cache: TypeConstantCache = .{},
|
||||
cache: Cache = .{},
|
||||
|
||||
/// Set of Decls, referred to by Decl.Index.
|
||||
decls: std.ArrayListUnmanaged(Decl) = .{},
|
||||
@@ -196,7 +196,7 @@ pub fn deinit(self: *Module) void {
|
||||
self.sections.functions.deinit(self.gpa);
|
||||
|
||||
self.source_file_names.deinit(self.gpa);
|
||||
self.tc_cache.deinit(self);
|
||||
self.cache.deinit(self);
|
||||
|
||||
self.decls.deinit(self.gpa);
|
||||
self.decl_deps.deinit(self.gpa);
|
||||
@@ -223,20 +223,20 @@ pub fn idBound(self: Module) Word {
|
||||
return self.next_result_id;
|
||||
}
|
||||
|
||||
pub fn resolve(self: *Module, key: TypeConstantCache.Key) !TypeConstantCache.Ref {
|
||||
return self.tc_cache.resolve(self, key);
|
||||
pub fn resolve(self: *Module, key: CacheKey) !CacheRef {
|
||||
return self.cache.resolve(self, key);
|
||||
}
|
||||
|
||||
pub fn resultId(self: *Module, ref: TypeConstantCache.Ref) IdResult {
|
||||
return self.tc_cache.resultId(ref);
|
||||
pub fn resultId(self: *const Module, ref: CacheRef) IdResult {
|
||||
return self.cache.resultId(ref);
|
||||
}
|
||||
|
||||
pub fn resolveId(self: *Module, key: TypeConstantCache.Key) !IdResult {
|
||||
pub fn resolveId(self: *Module, key: CacheKey) !IdResult {
|
||||
return self.resultId(try self.resolve(key));
|
||||
}
|
||||
|
||||
pub fn resolveString(self: *Module, str: []const u8) !TypeConstantCache.String {
|
||||
return try self.tc_cache.addString(self, str);
|
||||
pub fn resolveString(self: *Module, str: []const u8) !CacheString {
|
||||
return try self.cache.addString(self, str);
|
||||
}
|
||||
|
||||
fn orderGlobalsInto(
|
||||
@@ -350,7 +350,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
|
||||
var entry_points = try self.entryPoints();
|
||||
defer entry_points.deinit(self.gpa);
|
||||
|
||||
var types_constants = try self.tc_cache.materialize(self);
|
||||
var types_constants = try self.cache.materialize(self);
|
||||
defer types_constants.deinit(self.gpa);
|
||||
|
||||
// Note: needs to be kept in order according to section 2.3!
|
||||
@@ -364,6 +364,7 @@ pub fn flush(self: *Module, file: std.fs.File) !void {
|
||||
self.sections.debug_names.toWords(),
|
||||
self.sections.annotations.toWords(),
|
||||
types_constants.toWords(),
|
||||
self.sections.types_globals_constants.toWords(),
|
||||
self.sections.globals.toWords(),
|
||||
globals.toWords(),
|
||||
self.sections.functions.toWords(),
|
||||
@@ -416,364 +417,14 @@ pub fn resolveSourceFileName(self: *Module, decl: *ZigDecl) !IdRef {
|
||||
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);
|
||||
const index = @intToEnum(Type.Ref, result.index);
|
||||
|
||||
if (!result.found_existing) {
|
||||
const ref = try self.emitType(ty);
|
||||
self.type_cache.values()[result.index] = ref;
|
||||
}
|
||||
|
||||
return index;
|
||||
pub fn intType(self: *Module, signedness: std.builtin.Signedness, bits: u16) !CacheRef {
|
||||
return try self.resolve(.{ .int_type = .{
|
||||
.signedness = signedness,
|
||||
.bits = bits,
|
||||
} });
|
||||
}
|
||||
|
||||
pub fn resolveTypeId(self: *Module, ty: Type) !IdResultType {
|
||||
const ty_ref = try self.resolveType(ty);
|
||||
return self.typeId(ty_ref);
|
||||
}
|
||||
|
||||
pub fn typeRefType(self: Module, ty_ref: Type.Ref) Type {
|
||||
return self.type_cache.keys()[@enumToInt(ty_ref)];
|
||||
}
|
||||
|
||||
/// Get the result-id of a particular type, by reference. Asserts type_ref is valid.
|
||||
pub fn typeId(self: Module, ty_ref: Type.Ref) IdResultType {
|
||||
return self.type_cache.values()[@enumToInt(ty_ref)];
|
||||
}
|
||||
|
||||
/// 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) error{OutOfMemory}!IdResultType {
|
||||
const result_id = self.allocId();
|
||||
const ref_id = result_id;
|
||||
const types = &self.sections.types_globals_constants;
|
||||
const debug_names = &self.sections.debug_names;
|
||||
const result_id_operand = .{ .id_result = result_id };
|
||||
|
||||
switch (ty.tag()) {
|
||||
.void => {
|
||||
try types.emit(self.gpa, .OpTypeVoid, result_id_operand);
|
||||
try debug_names.emit(self.gpa, .OpName, .{
|
||||
.target = result_id,
|
||||
.name = "void",
|
||||
});
|
||||
},
|
||||
.bool => {
|
||||
try types.emit(self.gpa, .OpTypeBool, result_id_operand);
|
||||
try debug_names.emit(self.gpa, .OpName, .{
|
||||
.target = result_id,
|
||||
.name = "bool",
|
||||
});
|
||||
},
|
||||
.u8,
|
||||
.u16,
|
||||
.u32,
|
||||
.u64,
|
||||
.i8,
|
||||
.i16,
|
||||
.i32,
|
||||
.i64,
|
||||
.int,
|
||||
=> {
|
||||
// TODO: Kernels do not support OpTypeInt that is signed. We can probably
|
||||
// can get rid of the signedness all together, in Shaders also.
|
||||
const bits = ty.intFloatBits();
|
||||
const signedness: spec.LiteralInteger = switch (ty.intSignedness()) {
|
||||
.unsigned => 0,
|
||||
.signed => 1,
|
||||
};
|
||||
|
||||
try types.emit(self.gpa, .OpTypeInt, .{
|
||||
.id_result = result_id,
|
||||
.width = bits,
|
||||
.signedness = signedness,
|
||||
});
|
||||
|
||||
const ui: []const u8 = switch (signedness) {
|
||||
0 => "u",
|
||||
1 => "i",
|
||||
else => unreachable,
|
||||
};
|
||||
const name = try std.fmt.allocPrint(self.gpa, "{s}{}", .{ ui, bits });
|
||||
defer self.gpa.free(name);
|
||||
|
||||
try debug_names.emit(self.gpa, .OpName, .{
|
||||
.target = result_id,
|
||||
.name = name,
|
||||
});
|
||||
},
|
||||
.f16, .f32, .f64 => {
|
||||
const bits = ty.intFloatBits();
|
||||
try types.emit(self.gpa, .OpTypeFloat, .{
|
||||
.id_result = result_id,
|
||||
.width = bits,
|
||||
});
|
||||
|
||||
const name = try std.fmt.allocPrint(self.gpa, "f{}", .{bits});
|
||||
defer self.gpa.free(name);
|
||||
try debug_names.emit(self.gpa, .OpName, .{
|
||||
.target = result_id,
|
||||
.name = name,
|
||||
});
|
||||
},
|
||||
.vector => try types.emit(self.gpa, .OpTypeVector, .{
|
||||
.id_result = result_id,
|
||||
.component_type = self.typeId(ty.childType()),
|
||||
.component_count = ty.payload(.vector).component_count,
|
||||
}),
|
||||
.matrix => try types.emit(self.gpa, .OpTypeMatrix, .{
|
||||
.id_result = result_id,
|
||||
.column_type = self.typeId(ty.childType()),
|
||||
.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.typeId(ty.childType()),
|
||||
.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.typeId(ty.childType()),
|
||||
}),
|
||||
.array => {
|
||||
const info = ty.payload(.array);
|
||||
assert(info.length != 0);
|
||||
|
||||
const size_type = Type.initTag(.u32);
|
||||
const size_type_id = try self.resolveTypeId(size_type);
|
||||
const length_id = self.allocId();
|
||||
try self.emitConstant(size_type_id, length_id, .{ .uint32 = info.length });
|
||||
|
||||
try types.emit(self.gpa, .OpTypeArray, .{
|
||||
.id_result = result_id,
|
||||
.element_type = self.typeId(ty.childType()),
|
||||
.length = length_id,
|
||||
});
|
||||
if (info.array_stride != 0) {
|
||||
try self.decorate(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.typeId(ty.childType()),
|
||||
});
|
||||
if (info.array_stride != 0) {
|
||||
try self.decorate(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.typeId(member.ty));
|
||||
}
|
||||
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.typeId(ty.childType()),
|
||||
});
|
||||
if (info.array_stride != 0) {
|
||||
try self.decorate(ref_id, .{ .ArrayStride = .{ .array_stride = info.array_stride } });
|
||||
}
|
||||
if (info.alignment != 0) {
|
||||
try self.decorate(ref_id, .{ .Alignment = .{ .alignment = info.alignment } });
|
||||
}
|
||||
if (info.max_byte_offset) |max_byte_offset| {
|
||||
try self.decorate(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.typeId(info.return_type));
|
||||
for (info.parameters) |parameter_type| {
|
||||
types.writeOperand(IdRef, self.typeId(parameter_type));
|
||||
}
|
||||
},
|
||||
.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;
|
||||
}
|
||||
|
||||
fn decorateStruct(self: *Module, target: IdRef, info: *const Type.Payload.Struct) !void {
|
||||
const debug_names = &self.sections.debug_names;
|
||||
|
||||
if (info.name.len != 0) {
|
||||
try debug_names.emit(self.gpa, .OpName, .{
|
||||
.target = target,
|
||||
.name = info.name,
|
||||
});
|
||||
}
|
||||
|
||||
// Decorations for the struct type itself.
|
||||
if (info.decorations.block)
|
||||
try self.decorate(target, .Block);
|
||||
if (info.decorations.buffer_block)
|
||||
try self.decorate(target, .BufferBlock);
|
||||
if (info.decorations.glsl_shared)
|
||||
try self.decorate(target, .GLSLShared);
|
||||
if (info.decorations.glsl_packed)
|
||||
try self.decorate(target, .GLSLPacked);
|
||||
if (info.decorations.c_packed)
|
||||
try self.decorate(target, .CPacked);
|
||||
|
||||
// Decorations for the struct members.
|
||||
const extra = info.member_decoration_extra;
|
||||
var extra_i: u32 = 0;
|
||||
for (info.members, 0..) |member, i| {
|
||||
const d = member.decorations;
|
||||
const index = @intCast(Word, i);
|
||||
|
||||
if (member.name.len != 0) {
|
||||
try debug_names.emit(self.gpa, .OpMemberName, .{
|
||||
.type = target,
|
||||
.member = index,
|
||||
.name = member.name,
|
||||
});
|
||||
}
|
||||
|
||||
switch (member.offset) {
|
||||
.none => {},
|
||||
else => try self.decorateMember(
|
||||
target,
|
||||
index,
|
||||
.{ .Offset = .{ .byte_offset = @enumToInt(member.offset) } },
|
||||
),
|
||||
}
|
||||
|
||||
switch (d.matrix_layout) {
|
||||
.row_major => try self.decorateMember(target, index, .RowMajor),
|
||||
.col_major => try self.decorateMember(target, index, .ColMajor),
|
||||
.none => {},
|
||||
}
|
||||
if (d.matrix_layout != .none) {
|
||||
try self.decorateMember(target, index, .{
|
||||
.MatrixStride = .{ .matrix_stride = extra[extra_i] },
|
||||
});
|
||||
extra_i += 1;
|
||||
}
|
||||
|
||||
if (d.no_perspective)
|
||||
try self.decorateMember(target, index, .NoPerspective);
|
||||
if (d.flat)
|
||||
try self.decorateMember(target, index, .Flat);
|
||||
if (d.patch)
|
||||
try self.decorateMember(target, index, .Patch);
|
||||
if (d.centroid)
|
||||
try self.decorateMember(target, index, .Centroid);
|
||||
if (d.sample)
|
||||
try self.decorateMember(target, index, .Sample);
|
||||
if (d.invariant)
|
||||
try self.decorateMember(target, index, .Invariant);
|
||||
if (d.@"volatile")
|
||||
try self.decorateMember(target, index, .Volatile);
|
||||
if (d.coherent)
|
||||
try self.decorateMember(target, index, .Coherent);
|
||||
if (d.non_writable)
|
||||
try self.decorateMember(target, index, .NonWritable);
|
||||
if (d.non_readable)
|
||||
try self.decorateMember(target, index, .NonReadable);
|
||||
|
||||
if (d.builtin) {
|
||||
try self.decorateMember(target, index, .{
|
||||
.BuiltIn = .{ .built_in = @intToEnum(spec.BuiltIn, extra[extra_i]) },
|
||||
});
|
||||
extra_i += 1;
|
||||
}
|
||||
if (d.stream) {
|
||||
try self.decorateMember(target, index, .{
|
||||
.Stream = .{ .stream_number = extra[extra_i] },
|
||||
});
|
||||
extra_i += 1;
|
||||
}
|
||||
if (d.location) {
|
||||
try self.decorateMember(target, index, .{
|
||||
.Location = .{ .location = extra[extra_i] },
|
||||
});
|
||||
extra_i += 1;
|
||||
}
|
||||
if (d.component) {
|
||||
try self.decorateMember(target, index, .{
|
||||
.Component = .{ .component = extra[extra_i] },
|
||||
});
|
||||
extra_i += 1;
|
||||
}
|
||||
if (d.xfb_buffer) {
|
||||
try self.decorateMember(target, index, .{
|
||||
.XfbBuffer = .{ .xfb_buffer_number = extra[extra_i] },
|
||||
});
|
||||
extra_i += 1;
|
||||
}
|
||||
if (d.xfb_stride) {
|
||||
try self.decorateMember(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 self.decorateMember(target, index, .{
|
||||
.UserSemantic = .{ .semantic = semantic },
|
||||
});
|
||||
extra_i += std.math.divCeil(u32, extra_i, @sizeOf(u32)) catch unreachable;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn simpleStructType(self: *Module, members: []const Type.Payload.Struct.Member) !Type.Ref {
|
||||
const payload = try self.arena.create(Type.Payload.Struct);
|
||||
payload.* = .{
|
||||
.members = try self.arena.dupe(Type.Payload.Struct.Member, members),
|
||||
.decorations = .{},
|
||||
};
|
||||
return try self.resolveType(Type.initPayload(&payload.base));
|
||||
}
|
||||
|
||||
pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !TypeConstantCache.Ref {
|
||||
pub fn arrayType(self: *Module, len: u32, elem_ty_ref: CacheRef) !CacheRef {
|
||||
const len_ty_ref = try self.resolve(.{ .int_type = .{
|
||||
.signedness = .unsigned,
|
||||
.bits = 32,
|
||||
@@ -788,41 +439,45 @@ pub fn arrayType2(self: *Module, len: u32, elem_ty_ref: TypeConstantCache.Ref) !
|
||||
} });
|
||||
}
|
||||
|
||||
pub fn arrayType(self: *Module, len: u32, ty: Type.Ref) !Type.Ref {
|
||||
const payload = try self.arena.create(Type.Payload.Array);
|
||||
payload.* = .{
|
||||
.element_type = ty,
|
||||
.length = len,
|
||||
};
|
||||
return try self.resolveType(Type.initPayload(&payload.base));
|
||||
}
|
||||
|
||||
pub fn ptrType(
|
||||
self: *Module,
|
||||
child: Type.Ref,
|
||||
child: CacheRef,
|
||||
storage_class: spec.StorageClass,
|
||||
alignment: u32,
|
||||
) !Type.Ref {
|
||||
const ptr_payload = try self.arena.create(Type.Payload.Pointer);
|
||||
ptr_payload.* = .{
|
||||
) !CacheRef {
|
||||
return try self.resolve(.{ .ptr_type = .{
|
||||
.storage_class = storage_class,
|
||||
.child_type = child,
|
||||
.alignment = alignment,
|
||||
};
|
||||
return try self.resolveType(Type.initPayload(&ptr_payload.base));
|
||||
} });
|
||||
}
|
||||
|
||||
pub fn changePtrStorageClass(self: *Module, ptr_ty_ref: Type.Ref, new_storage_class: spec.StorageClass) !Type.Ref {
|
||||
const payload = try self.arena.create(Type.Payload.Pointer);
|
||||
payload.* = self.typeRefType(ptr_ty_ref).payload(.pointer).*;
|
||||
payload.storage_class = new_storage_class;
|
||||
return try self.resolveType(Type.initPayload(&payload.base));
|
||||
pub fn constInt(self: *Module, ty_ref: CacheRef, value: anytype) !IdRef {
|
||||
const ty = self.cache.lookup(ty_ref).int_type;
|
||||
const Value = Cache.Key.Int.Value;
|
||||
return try self.resolveId(.{ .int = .{
|
||||
.ty = ty_ref,
|
||||
.value = switch (ty.signedness) {
|
||||
.signed => Value{ .int64 = @intCast(i64, value) },
|
||||
.unsigned => Value{ .uint64 = @intCast(u64, value) },
|
||||
},
|
||||
} });
|
||||
}
|
||||
|
||||
pub fn constComposite(self: *Module, ty_ref: Type.Ref, members: []const IdRef) !IdRef {
|
||||
pub fn constUndef(self: *Module, ty_ref: CacheRef) !IdRef {
|
||||
return try self.resolveId(.{ .undef = .{ .ty = ty_ref } });
|
||||
}
|
||||
|
||||
pub fn constNull(self: *Module, ty_ref: CacheRef) !IdRef {
|
||||
return try self.resolveId(.{ .null = .{ .ty = ty_ref } });
|
||||
}
|
||||
|
||||
pub fn constBool(self: *Module, ty_ref: CacheRef, value: bool) !IdRef {
|
||||
return try self.resolveId(.{ .bool = .{ .ty = ty_ref, .value = value } });
|
||||
}
|
||||
|
||||
pub fn constComposite(self: *Module, ty_ref: CacheRef, members: []const IdRef) !IdRef {
|
||||
const result_id = self.allocId();
|
||||
try self.sections.types_globals_constants.emit(self.gpa, .OpSpecConstantComposite, .{
|
||||
.id_result_type = self.typeId(ty_ref),
|
||||
.id_result_type = self.resultId(ty_ref),
|
||||
.id_result = result_id,
|
||||
.constituents = members,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user