mirror of
https://github.com/ziglang/zig.git
synced 2025-12-29 01:23:17 +00:00
spirv: remove indirect constant lowering
It is stupid and I hate it.
This commit is contained in:
parent
b30cd67987
commit
66036e6000
@ -520,576 +520,6 @@ pub const DeclGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
const IndirectConstantLowering = struct {
|
||||
const undef = 0xAA;
|
||||
|
||||
dg: *DeclGen,
|
||||
/// Cached reference of the u32 type.
|
||||
u32_ty_ref: CacheRef,
|
||||
/// The members of the resulting structure type
|
||||
members: std.ArrayList(CacheRef),
|
||||
/// The initializers of each of the members.
|
||||
initializers: std.ArrayList(IdRef),
|
||||
/// The current size of the structure. Includes
|
||||
/// the bytes in partial_word.
|
||||
size: u32 = 0,
|
||||
/// The partially filled last constant.
|
||||
/// If full, its flushed.
|
||||
partial_word: std.BoundedArray(u8, @sizeOf(Word)) = .{},
|
||||
/// The declaration dependencies of the constant we are lowering.
|
||||
decl_deps: std.AutoArrayHashMap(SpvModule.Decl.Index, void),
|
||||
|
||||
/// Utility function to get the section that instructions should be lowered to.
|
||||
fn section(self: *@This()) *SpvSection {
|
||||
return &self.dg.spv.globals.section;
|
||||
}
|
||||
|
||||
/// Flush the partial_word to the members. If the partial_word is not
|
||||
/// filled, this adds padding bytes (which are undefined).
|
||||
fn flush(self: *@This()) !void {
|
||||
if (self.partial_word.len == 0) {
|
||||
// No need to add it there.
|
||||
return;
|
||||
}
|
||||
|
||||
for (self.partial_word.unusedCapacitySlice()) |*unused| {
|
||||
// TODO: Perhaps we should generate OpUndef for these bytes?
|
||||
unused.* = undef;
|
||||
}
|
||||
|
||||
const word = @as(Word, @bitCast(self.partial_word.buffer));
|
||||
const result_id = try self.dg.spv.constInt(self.u32_ty_ref, word);
|
||||
try self.members.append(self.u32_ty_ref);
|
||||
try self.initializers.append(result_id);
|
||||
|
||||
self.partial_word.len = 0;
|
||||
self.size = std.mem.alignForward(u32, self.size, @sizeOf(Word));
|
||||
}
|
||||
|
||||
/// Fill the buffer with undefined values until the size is aligned to `align`.
|
||||
fn fillToAlign(self: *@This(), alignment: u32) !void {
|
||||
const target_size = std.mem.alignForward(u32, self.size, alignment);
|
||||
try self.addUndef(target_size - self.size);
|
||||
}
|
||||
|
||||
fn addUndef(self: *@This(), amt: u64) !void {
|
||||
for (0..@as(usize, @intCast(amt))) |_| {
|
||||
try self.addByte(undef);
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a single byte of data to the constant.
|
||||
fn addByte(self: *@This(), data: u8) !void {
|
||||
self.partial_word.append(data) catch {
|
||||
try self.flush();
|
||||
self.partial_word.append(data) catch unreachable;
|
||||
};
|
||||
self.size += 1;
|
||||
}
|
||||
|
||||
/// Add many bytes of data to the constnat.
|
||||
fn addBytes(self: *@This(), data: []const u8) !void {
|
||||
// TODO: Improve performance by adding in bulk, or something?
|
||||
for (data) |byte| {
|
||||
try self.addByte(byte);
|
||||
}
|
||||
}
|
||||
|
||||
fn addPtr(self: *@This(), ptr_ty_ref: CacheRef, ptr_id: IdRef) !void {
|
||||
// TODO: Double check pointer sizes here.
|
||||
// shared pointers might be u32...
|
||||
const target = self.dg.getTarget();
|
||||
const width = @divExact(target.ptrBitWidth(), 8);
|
||||
if (self.size % width != 0) {
|
||||
return self.dg.todo("misaligned pointer constants", .{});
|
||||
}
|
||||
try self.members.append(ptr_ty_ref);
|
||||
try self.initializers.append(ptr_id);
|
||||
self.size += width;
|
||||
}
|
||||
|
||||
fn addNullPtr(self: *@This(), ptr_ty_ref: CacheRef) !void {
|
||||
const result_id = try self.dg.spv.constNull(ptr_ty_ref);
|
||||
try self.addPtr(ptr_ty_ref, result_id);
|
||||
}
|
||||
|
||||
fn addConstInt(self: *@This(), comptime T: type, value: T) !void {
|
||||
if (@bitSizeOf(T) % 8 != 0) {
|
||||
@compileError("todo: non byte aligned int constants");
|
||||
}
|
||||
|
||||
// TODO: Swap endianness if the compiler is big endian.
|
||||
try self.addBytes(std.mem.asBytes(&value));
|
||||
}
|
||||
|
||||
fn addConstBool(self: *@This(), value: bool) !void {
|
||||
try self.addByte(@intFromBool(value)); // TODO: Keep in sync with something?
|
||||
}
|
||||
|
||||
fn addInt(self: *@This(), ty: Type, val: Value) !void {
|
||||
const mod = self.dg.module;
|
||||
const len = ty.abiSize(mod);
|
||||
if (val.isUndef(mod)) {
|
||||
try self.addUndef(len);
|
||||
return;
|
||||
}
|
||||
|
||||
const int_info = ty.intInfo(mod);
|
||||
const int_bits = switch (int_info.signedness) {
|
||||
.signed => @as(u64, @bitCast(val.toSignedInt(mod))),
|
||||
.unsigned => val.toUnsignedInt(mod),
|
||||
};
|
||||
|
||||
// TODO: Swap endianess if the compiler is big endian.
|
||||
try self.addBytes(std.mem.asBytes(&int_bits)[0..@as(usize, @intCast(len))]);
|
||||
}
|
||||
|
||||
fn addFloat(self: *@This(), ty: Type, val: Value) !void {
|
||||
const mod = self.dg.module;
|
||||
const target = self.dg.getTarget();
|
||||
const len = ty.abiSize(mod);
|
||||
|
||||
// TODO: Swap endianess if the compiler is big endian.
|
||||
switch (ty.floatBits(target)) {
|
||||
16 => {
|
||||
const float_bits = val.toFloat(f16, mod);
|
||||
try self.addBytes(std.mem.asBytes(&float_bits)[0..@as(usize, @intCast(len))]);
|
||||
},
|
||||
32 => {
|
||||
const float_bits = val.toFloat(f32, mod);
|
||||
try self.addBytes(std.mem.asBytes(&float_bits)[0..@as(usize, @intCast(len))]);
|
||||
},
|
||||
64 => {
|
||||
const float_bits = val.toFloat(f64, mod);
|
||||
try self.addBytes(std.mem.asBytes(&float_bits)[0..@as(usize, @intCast(len))]);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
fn addDeclRef(self: *@This(), ty: Type, decl_index: Decl.Index) !void {
|
||||
const dg = self.dg;
|
||||
const mod = dg.module;
|
||||
|
||||
const ty_ref = try self.dg.resolveType(ty, .indirect);
|
||||
const ty_id = dg.typeId(ty_ref);
|
||||
|
||||
const decl = dg.module.declPtr(decl_index);
|
||||
const spv_decl_index = try dg.resolveDecl(decl_index);
|
||||
|
||||
switch (mod.intern_pool.indexToKey(decl.val.ip_index)) {
|
||||
.func => {
|
||||
// TODO: Properly lower function pointers. For now we are going to hack around it and
|
||||
// just generate an empty pointer. Function pointers are represented by usize for now,
|
||||
// though.
|
||||
try self.addInt(Type.usize, Value.zero_usize);
|
||||
// TODO: Add dependency
|
||||
return;
|
||||
},
|
||||
.extern_func => unreachable, // TODO
|
||||
else => {
|
||||
const result_id = dg.spv.allocId();
|
||||
|
||||
try self.decl_deps.put(spv_decl_index, {});
|
||||
|
||||
const decl_id = dg.spv.declPtr(spv_decl_index).result_id;
|
||||
// TODO: Do we need a storage class cast here?
|
||||
// TODO: We can probably eliminate these casts
|
||||
try dg.spv.globals.section.emitSpecConstantOp(dg.spv.gpa, .OpBitcast, .{
|
||||
.id_result_type = ty_id,
|
||||
.id_result = result_id,
|
||||
.operand = decl_id,
|
||||
});
|
||||
|
||||
try self.addPtr(ty_ref, result_id);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn lower(self: *@This(), ty: Type, arg_val: Value) !void {
|
||||
const dg = self.dg;
|
||||
const mod = dg.module;
|
||||
const ip = &mod.intern_pool;
|
||||
|
||||
var val = arg_val;
|
||||
switch (ip.indexToKey(val.toIntern())) {
|
||||
.runtime_value => |rt| val = rt.val.toValue(),
|
||||
else => {},
|
||||
}
|
||||
|
||||
if (val.isUndefDeep(mod)) {
|
||||
const size = ty.abiSize(mod);
|
||||
return try self.addUndef(size);
|
||||
}
|
||||
|
||||
switch (ip.indexToKey(val.toIntern())) {
|
||||
.int_type,
|
||||
.ptr_type,
|
||||
.array_type,
|
||||
.vector_type,
|
||||
.opt_type,
|
||||
.anyframe_type,
|
||||
.error_union_type,
|
||||
.simple_type,
|
||||
.struct_type,
|
||||
.anon_struct_type,
|
||||
.union_type,
|
||||
.opaque_type,
|
||||
.enum_type,
|
||||
.func_type,
|
||||
.error_set_type,
|
||||
.inferred_error_set_type,
|
||||
=> unreachable, // types, not values
|
||||
|
||||
.undef, .runtime_value => unreachable, // handled above
|
||||
.simple_value => |simple_value| switch (simple_value) {
|
||||
.undefined,
|
||||
.void,
|
||||
.null,
|
||||
.empty_struct,
|
||||
.@"unreachable",
|
||||
.generic_poison,
|
||||
=> unreachable, // non-runtime values
|
||||
.false, .true => try self.addConstBool(val.toBool()),
|
||||
},
|
||||
.variable,
|
||||
.extern_func,
|
||||
.func,
|
||||
.enum_literal,
|
||||
.empty_enum_value,
|
||||
=> unreachable, // non-runtime values
|
||||
.int => try self.addInt(ty, val),
|
||||
.err => |err| {
|
||||
const int = try mod.getErrorValue(err.name);
|
||||
try self.addConstInt(u16, @as(u16, @intCast(int)));
|
||||
},
|
||||
.error_union => |error_union| {
|
||||
const err_ty = switch (error_union.val) {
|
||||
.err_name => ty.errorUnionSet(mod),
|
||||
.payload => Type.err_int,
|
||||
};
|
||||
const err_val = switch (error_union.val) {
|
||||
.err_name => |err_name| (try mod.intern(.{ .err = .{
|
||||
.ty = ty.errorUnionSet(mod).toIntern(),
|
||||
.name = err_name,
|
||||
} })).toValue(),
|
||||
.payload => try mod.intValue(Type.err_int, 0),
|
||||
};
|
||||
const payload_ty = ty.errorUnionPayload(mod);
|
||||
const eu_layout = dg.errorUnionLayout(payload_ty);
|
||||
if (!eu_layout.payload_has_bits) {
|
||||
// We use the error type directly as the type.
|
||||
try self.lower(err_ty, err_val);
|
||||
return;
|
||||
}
|
||||
|
||||
const payload_size = payload_ty.abiSize(mod);
|
||||
const error_size = err_ty.abiSize(mod);
|
||||
const ty_size = ty.abiSize(mod);
|
||||
const padding = ty_size - payload_size - error_size;
|
||||
|
||||
const payload_val = switch (error_union.val) {
|
||||
.err_name => try mod.intern(.{ .undef = payload_ty.toIntern() }),
|
||||
.payload => |payload| payload,
|
||||
}.toValue();
|
||||
|
||||
if (eu_layout.error_first) {
|
||||
try self.lower(err_ty, err_val);
|
||||
try self.lower(payload_ty, payload_val);
|
||||
} else {
|
||||
try self.lower(payload_ty, payload_val);
|
||||
try self.lower(err_ty, err_val);
|
||||
}
|
||||
|
||||
try self.addUndef(padding);
|
||||
},
|
||||
.enum_tag => {
|
||||
const int_val = try val.intFromEnum(ty, mod);
|
||||
|
||||
const int_ty = ty.intTagType(mod);
|
||||
|
||||
try self.lower(int_ty, int_val);
|
||||
},
|
||||
.float => try self.addFloat(ty, val),
|
||||
.ptr => |ptr| {
|
||||
const ptr_ty = switch (ptr.len) {
|
||||
.none => ty,
|
||||
else => ty.slicePtrFieldType(mod),
|
||||
};
|
||||
switch (ptr.addr) {
|
||||
.decl => |decl| try self.addDeclRef(ptr_ty, decl),
|
||||
.mut_decl => |mut_decl| try self.addDeclRef(ptr_ty, mut_decl.decl),
|
||||
.int => |int| try self.addInt(Type.usize, int.toValue()),
|
||||
else => |tag| return dg.todo("pointer value of type {s}", .{@tagName(tag)}),
|
||||
}
|
||||
if (ptr.len != .none) {
|
||||
try self.addInt(Type.usize, ptr.len.toValue());
|
||||
}
|
||||
},
|
||||
.opt => {
|
||||
const payload_ty = ty.optionalChild(mod);
|
||||
const payload_val = val.optionalValue(mod);
|
||||
const abi_size = ty.abiSize(mod);
|
||||
|
||||
if (!payload_ty.hasRuntimeBits(mod)) {
|
||||
try self.addConstBool(payload_val != null);
|
||||
return;
|
||||
} else if (ty.optionalReprIsPayload(mod)) {
|
||||
// Optional representation is a nullable pointer or slice.
|
||||
if (payload_val) |pl_val| {
|
||||
try self.lower(payload_ty, pl_val);
|
||||
} else {
|
||||
const ptr_ty_ref = try dg.resolveType(ty, .indirect);
|
||||
try self.addNullPtr(ptr_ty_ref);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Optional representation is a structure.
|
||||
// { Payload, Bool }
|
||||
|
||||
// Subtract 1 for @sizeOf(bool).
|
||||
// TODO: Make this not hardcoded.
|
||||
const payload_size = payload_ty.abiSize(mod);
|
||||
const padding = abi_size - payload_size - 1;
|
||||
|
||||
if (payload_val) |pl_val| {
|
||||
try self.lower(payload_ty, pl_val);
|
||||
} else {
|
||||
try self.addUndef(payload_size);
|
||||
}
|
||||
try self.addConstBool(payload_val != null);
|
||||
try self.addUndef(padding);
|
||||
},
|
||||
.aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
|
||||
.array_type => |array_type| {
|
||||
const elem_ty = array_type.child.toType();
|
||||
switch (aggregate.storage) {
|
||||
.bytes => |bytes| try self.addBytes(bytes),
|
||||
.elems, .repeated_elem => {
|
||||
for (0..@as(usize, @intCast(array_type.len))) |i| {
|
||||
try self.lower(elem_ty, switch (aggregate.storage) {
|
||||
.bytes => unreachable,
|
||||
.elems => |elem_vals| elem_vals[@as(usize, @intCast(i))].toValue(),
|
||||
.repeated_elem => |elem_val| elem_val.toValue(),
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
if (array_type.sentinel != .none) {
|
||||
try self.lower(elem_ty, array_type.sentinel.toValue());
|
||||
}
|
||||
},
|
||||
.vector_type => return dg.todo("indirect constant of type {}", .{ty.fmt(mod)}),
|
||||
.struct_type => {
|
||||
const struct_type = mod.typeToStruct(ty).?;
|
||||
if (struct_type.layout == .Packed) {
|
||||
return dg.todo("packed struct constants", .{});
|
||||
}
|
||||
|
||||
// TODO iterate with runtime order instead so that struct field
|
||||
// reordering can be enabled for this backend.
|
||||
const struct_begin = self.size;
|
||||
for (struct_type.field_types.get(ip), 0..) |field_ty, i_usize| {
|
||||
const i: u32 = @intCast(i_usize);
|
||||
if (struct_type.fieldIsComptime(ip, i)) continue;
|
||||
if (!field_ty.toType().hasRuntimeBits(mod)) continue;
|
||||
|
||||
const field_val = switch (aggregate.storage) {
|
||||
.bytes => |bytes| try ip.get(mod.gpa, .{ .int = .{
|
||||
.ty = field_ty,
|
||||
.storage = .{ .u64 = bytes[i] },
|
||||
} }),
|
||||
.elems => |elems| elems[i],
|
||||
.repeated_elem => |elem| elem,
|
||||
};
|
||||
try self.lower(field_ty.toType(), field_val.toValue());
|
||||
|
||||
// Add padding if required.
|
||||
// TODO: Add to type generation as well?
|
||||
const unpadded_field_end = self.size - struct_begin;
|
||||
const padded_field_end = ty.structFieldOffset(i + 1, mod);
|
||||
const padding = padded_field_end - unpadded_field_end;
|
||||
try self.addUndef(padding);
|
||||
}
|
||||
},
|
||||
.anon_struct_type => unreachable, // TODO
|
||||
else => unreachable,
|
||||
},
|
||||
.un => |un| {
|
||||
const layout = ty.unionGetLayout(mod);
|
||||
|
||||
if (layout.payload_size == 0) {
|
||||
return try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue());
|
||||
}
|
||||
|
||||
const union_obj = mod.typeToUnion(ty).?;
|
||||
if (union_obj.getLayout(ip) == .Packed) {
|
||||
return dg.todo("packed union constants", .{});
|
||||
}
|
||||
|
||||
const active_field = ty.unionTagFieldIndex(un.tag.toValue(), dg.module).?;
|
||||
const active_field_ty = union_obj.field_types.get(ip)[active_field].toType();
|
||||
|
||||
const has_tag = layout.tag_size != 0;
|
||||
const tag_first = layout.tag_align.compare(.gte, layout.payload_align);
|
||||
|
||||
if (has_tag and tag_first) {
|
||||
try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue());
|
||||
}
|
||||
|
||||
const active_field_size = if (active_field_ty.hasRuntimeBitsIgnoreComptime(mod)) blk: {
|
||||
try self.lower(active_field_ty, un.val.toValue());
|
||||
break :blk active_field_ty.abiSize(mod);
|
||||
} else 0;
|
||||
|
||||
const payload_padding_len = layout.payload_size - active_field_size;
|
||||
try self.addUndef(payload_padding_len);
|
||||
|
||||
if (has_tag and !tag_first) {
|
||||
try self.lower(ty.unionTagTypeSafety(mod).?, un.tag.toValue());
|
||||
}
|
||||
|
||||
try self.addUndef(layout.padding);
|
||||
},
|
||||
.memoized_call => unreachable,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns a pointer to `val`. The value is placed directly
|
||||
/// into the storage class `storage_class`, and this is also where the resulting
|
||||
/// pointer points to. Note: result is not necessarily an OpVariable instruction!
|
||||
fn lowerIndirectConstant(
|
||||
self: *DeclGen,
|
||||
spv_decl_index: SpvModule.Decl.Index,
|
||||
ty: Type,
|
||||
val: Value,
|
||||
storage_class: StorageClass,
|
||||
cast_to_generic: bool,
|
||||
alignment: u32,
|
||||
) Error!void {
|
||||
// To simplify constant generation, we're going to generate constants as a word-array, and
|
||||
// pointer cast the result to the right type.
|
||||
// This means that the final constant will be generated as follows:
|
||||
// %T = OpTypeStruct %members...
|
||||
// %P = OpTypePointer %T
|
||||
// %U = OpTypePointer %ty
|
||||
// %1 = OpConstantComposite %T %initializers...
|
||||
// %2 = OpVariable %P %1
|
||||
// %result_id = OpSpecConstantOp OpBitcast %U %2
|
||||
//
|
||||
// The members consist of two options:
|
||||
// - Literal values: ints, strings, etc. These are generated as u32 words.
|
||||
// - Relocations, such as pointers: These are generated by embedding the pointer into the
|
||||
// to-be-generated structure. There are two options here, depending on the alignment of the
|
||||
// pointer value itself (not the alignment of the pointee).
|
||||
// - Natively or over-aligned values. These can just be generated directly.
|
||||
// - Underaligned pointers. These need to be packed into the word array by using a mixture of
|
||||
// OpSpecConstantOp instructions such as OpConvertPtrToU, OpBitcast, OpShift, etc.
|
||||
|
||||
// TODO: Implement alignment here.
|
||||
// This is hoing to require some hacks because there is no real way to
|
||||
// set an OpVariable's alignment.
|
||||
_ = alignment;
|
||||
|
||||
assert(storage_class != .Generic and storage_class != .Function);
|
||||
|
||||
const var_id = self.spv.allocId();
|
||||
log.debug("lowerIndirectConstant: id = {}, index = {}, ty = {}, val = {}", .{ var_id.id, @intFromEnum(spv_decl_index), ty.fmt(self.module), val.fmtDebug() });
|
||||
|
||||
const section = &self.spv.globals.section;
|
||||
|
||||
const ty_ref = try self.resolveType(ty, .indirect);
|
||||
const ptr_ty_ref = try self.spv.ptrType(ty_ref, storage_class);
|
||||
|
||||
// const target = self.getTarget();
|
||||
|
||||
// TODO: Fix the resulting global linking for these paths.
|
||||
// if (val.isUndef(mod)) {
|
||||
// // Special case: the entire value is undefined. In this case, we can just
|
||||
// // generate an OpVariable with no initializer.
|
||||
// return try section.emit(self.spv.gpa, .OpVariable, .{
|
||||
// .id_result_type = self.typeId(ptr_ty_ref),
|
||||
// .id_result = result_id,
|
||||
// .storage_class = storage_class,
|
||||
// });
|
||||
// } else if (ty.abiSize(mod) == 0) {
|
||||
// // Special case: if the type has no size, then return an undefined pointer.
|
||||
// return try section.emit(self.spv.gpa, .OpUndef, .{
|
||||
// .id_result_type = self.typeId(ptr_ty_ref),
|
||||
// .id_result = result_id,
|
||||
// });
|
||||
// }
|
||||
|
||||
// TODO: Capture the above stuff in here as well...
|
||||
const begin_inst = self.spv.beginGlobal();
|
||||
|
||||
const u32_ty_ref = try self.intType(.unsigned, 32);
|
||||
var icl = IndirectConstantLowering{
|
||||
.dg = self,
|
||||
.u32_ty_ref = u32_ty_ref,
|
||||
.members = std.ArrayList(CacheRef).init(self.gpa),
|
||||
.initializers = std.ArrayList(IdRef).init(self.gpa),
|
||||
.decl_deps = std.AutoArrayHashMap(SpvModule.Decl.Index, void).init(self.gpa),
|
||||
};
|
||||
|
||||
defer icl.members.deinit();
|
||||
defer icl.initializers.deinit();
|
||||
defer icl.decl_deps.deinit();
|
||||
|
||||
try icl.lower(ty, val);
|
||||
try icl.flush();
|
||||
|
||||
const constant_struct_ty_ref = try self.spv.resolve(.{ .struct_type = .{
|
||||
.member_types = icl.members.items,
|
||||
} });
|
||||
const ptr_constant_struct_ty_ref = try self.spv.ptrType(constant_struct_ty_ref, storage_class);
|
||||
|
||||
const constant_struct_id = self.spv.allocId();
|
||||
try section.emit(self.spv.gpa, .OpSpecConstantComposite, .{
|
||||
.id_result_type = self.typeId(constant_struct_ty_ref),
|
||||
.id_result = constant_struct_id,
|
||||
.constituents = icl.initializers.items,
|
||||
});
|
||||
|
||||
self.spv.globalPtr(spv_decl_index).?.result_id = var_id;
|
||||
try section.emit(self.spv.gpa, .OpVariable, .{
|
||||
.id_result_type = self.typeId(ptr_constant_struct_ty_ref),
|
||||
.id_result = var_id,
|
||||
.storage_class = storage_class,
|
||||
.initializer = constant_struct_id,
|
||||
});
|
||||
// TODO: Set alignment of OpVariable.
|
||||
// TODO: We may be able to eliminate these casts.
|
||||
|
||||
const const_ptr_id = try self.makePointerConstant(section, ptr_constant_struct_ty_ref, var_id);
|
||||
const result_id = self.spv.declPtr(spv_decl_index).result_id;
|
||||
|
||||
const bitcast_result_id = if (cast_to_generic)
|
||||
self.spv.allocId()
|
||||
else
|
||||
result_id;
|
||||
|
||||
try section.emitSpecConstantOp(self.spv.gpa, .OpBitcast, .{
|
||||
.id_result_type = self.typeId(ptr_ty_ref),
|
||||
.id_result = bitcast_result_id,
|
||||
.operand = const_ptr_id,
|
||||
});
|
||||
|
||||
if (cast_to_generic) {
|
||||
const generic_ptr_ty_ref = try self.spv.ptrType(ty_ref, .Generic);
|
||||
try section.emitSpecConstantOp(self.spv.gpa, .OpPtrCastToGeneric, .{
|
||||
.id_result_type = self.typeId(generic_ptr_ty_ref),
|
||||
.id_result = result_id,
|
||||
.pointer = bitcast_result_id,
|
||||
});
|
||||
}
|
||||
|
||||
try self.spv.declareDeclDeps(spv_decl_index, icl.decl_deps.keys());
|
||||
self.spv.endGlobal(spv_decl_index, begin_inst);
|
||||
}
|
||||
|
||||
/// This function generates a load for a constant in direct (ie, non-memory) representation.
|
||||
/// When the constant is simple, it can be generated directly using OpConstant instructions.
|
||||
/// When the constant is more complicated however, it needs to be constructed using multiple values. This
|
||||
@ -2104,8 +1534,6 @@ pub const DeclGen = struct {
|
||||
.id_result = decl_id,
|
||||
.storage_class = actual_storage_class,
|
||||
});
|
||||
// TODO: We should be able to get rid of this by now...
|
||||
self.spv.endGlobal(spv_decl_index, begin);
|
||||
|
||||
// Now emit the instructions that initialize the variable.
|
||||
const initializer_id = self.spv.allocId();
|
||||
@ -2127,6 +1555,9 @@ pub const DeclGen = struct {
|
||||
.object = val_id,
|
||||
});
|
||||
|
||||
// TODO: We should be able to get rid of this by now...
|
||||
self.spv.endGlobal(spv_decl_index, begin);
|
||||
|
||||
try self.func.body.emit(self.spv.gpa, .OpReturn, {});
|
||||
try self.func.body.emit(self.spv.gpa, .OpFunctionEnd, {});
|
||||
try self.spv.addFunction(spv_decl_index, self.func);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user