mirror of
https://github.com/ziglang/zig.git
synced 2026-02-20 08:14:48 +00:00
spirv: cache more types & merge constructX functions
This commit is contained in:
parent
7bbeac7f17
commit
29e46633ce
@ -159,7 +159,7 @@ pub const Object = struct {
|
||||
uav_link: std.AutoHashMapUnmanaged(struct { InternPool.Index, StorageClass }, SpvModule.Decl.Index) = .empty,
|
||||
|
||||
/// A map that maps AIR intern pool indices to SPIR-V result-ids.
|
||||
intern_map: InternMap = .{},
|
||||
intern_map: InternMap = .empty,
|
||||
|
||||
/// This map serves a dual purpose:
|
||||
/// - It keeps track of pointers that are currently being emitted, so that we can tell
|
||||
@ -314,7 +314,7 @@ const NavGen = struct {
|
||||
next_arg_index: u32 = 0,
|
||||
|
||||
/// A map keeping track of which instruction generated which result-id.
|
||||
inst_results: InstMap = .{},
|
||||
inst_results: InstMap = .empty,
|
||||
|
||||
/// A map that maps AIR intern pool indices to SPIR-V result-ids.
|
||||
/// See `Object.intern_map`.
|
||||
@ -469,7 +469,7 @@ const NavGen = struct {
|
||||
|
||||
const zcu = self.pt.zcu;
|
||||
const ty = Type.fromInterned(zcu.intern_pool.typeOf(val));
|
||||
const decl_ptr_ty_id = try self.ptrType(ty, .Generic);
|
||||
const decl_ptr_ty_id = try self.ptrType(ty, .Generic, .indirect);
|
||||
|
||||
const spv_decl_index = blk: {
|
||||
const entry = try self.object.uav_link.getOrPut(self.object.gpa, .{ val, .Function });
|
||||
@ -532,7 +532,7 @@ const NavGen = struct {
|
||||
|
||||
try self.spv.debugNameFmt(initializer_id, "initializer of __anon_{d}", .{@intFromEnum(val)});
|
||||
|
||||
const fn_decl_ptr_ty_id = try self.ptrType(ty, .Function);
|
||||
const fn_decl_ptr_ty_id = try self.ptrType(ty, .Function, .indirect);
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpExtInst, .{
|
||||
.id_result_type = fn_decl_ptr_ty_id,
|
||||
.id_result = result_id,
|
||||
@ -721,36 +721,16 @@ const NavGen = struct {
|
||||
|
||||
/// Emits a bool constant in a particular representation.
|
||||
fn constBool(self: *NavGen, value: bool, repr: Repr) !IdRef {
|
||||
// TODO: Cache?
|
||||
|
||||
const section = &self.spv.sections.types_globals_constants;
|
||||
switch (repr) {
|
||||
.indirect => {
|
||||
return try self.constInt(Type.u1, @intFromBool(value), .indirect);
|
||||
},
|
||||
.direct => {
|
||||
const result_ty_id = try self.resolveType(Type.bool, .direct);
|
||||
const result_id = self.spv.allocId();
|
||||
switch (value) {
|
||||
inline else => |val_ct| try section.emit(
|
||||
self.spv.gpa,
|
||||
if (val_ct) .OpConstantTrue else .OpConstantFalse,
|
||||
.{
|
||||
.id_result_type = result_ty_id,
|
||||
.id_result = result_id,
|
||||
},
|
||||
),
|
||||
}
|
||||
return result_id;
|
||||
},
|
||||
}
|
||||
return switch (repr) {
|
||||
.indirect => self.constInt(Type.u1, @intFromBool(value)),
|
||||
.direct => self.spv.constBool(value),
|
||||
};
|
||||
}
|
||||
|
||||
/// Emits an integer constant.
|
||||
/// This function, unlike SpvModule.constInt, takes care to bitcast
|
||||
/// the value to an unsigned int first for Kernels.
|
||||
fn constInt(self: *NavGen, ty: Type, value: anytype, repr: Repr) !IdRef {
|
||||
// TODO: Cache?
|
||||
fn constInt(self: *NavGen, ty: Type, value: anytype) !IdRef {
|
||||
const zcu = self.pt.zcu;
|
||||
const scalar_ty = ty.scalarType(zcu);
|
||||
const int_info = scalar_ty.intInfo(zcu);
|
||||
@ -763,18 +743,18 @@ const NavGen = struct {
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
const bits: u64 = switch (signedness) {
|
||||
const value64: u64 = switch (signedness) {
|
||||
.signed => @bitCast(@as(i64, @intCast(value))),
|
||||
.unsigned => @as(u64, @intCast(value)),
|
||||
};
|
||||
|
||||
// Manually truncate the value to the right amount of bits.
|
||||
const truncated_bits = if (backing_bits == 64)
|
||||
bits
|
||||
const truncated_value = if (backing_bits == 64)
|
||||
value64
|
||||
else
|
||||
bits & (@as(u64, 1) << @intCast(backing_bits)) - 1;
|
||||
value64 & (@as(u64, 1) << @intCast(backing_bits)) - 1;
|
||||
|
||||
const result_ty_id = try self.resolveType(scalar_ty, repr);
|
||||
const result_ty_id = try self.resolveType(scalar_ty, .indirect);
|
||||
const result_id = self.spv.allocId();
|
||||
|
||||
const section = &self.spv.sections.types_globals_constants;
|
||||
@ -783,100 +763,42 @@ const NavGen = struct {
|
||||
1...32 => try section.emit(self.spv.gpa, .OpConstant, .{
|
||||
.id_result_type = result_ty_id,
|
||||
.id_result = result_id,
|
||||
.value = .{ .uint32 = @truncate(truncated_bits) },
|
||||
.value = .{ .uint32 = @truncate(truncated_value) },
|
||||
}),
|
||||
33...64 => try section.emit(self.spv.gpa, .OpConstant, .{
|
||||
.id_result_type = result_ty_id,
|
||||
.id_result = result_id,
|
||||
.value = .{ .uint64 = truncated_bits },
|
||||
.value = .{ .uint64 = truncated_value },
|
||||
}),
|
||||
else => unreachable, // TODO: Large integer constants
|
||||
}
|
||||
|
||||
if (!ty.isVector(zcu)) {
|
||||
return result_id;
|
||||
}
|
||||
|
||||
const n = ty.vectorLen(zcu);
|
||||
const ids = try self.gpa.alloc(IdRef, n);
|
||||
defer self.gpa.free(ids);
|
||||
@memset(ids, result_id);
|
||||
|
||||
const vec_ty_id = try self.resolveType(ty, repr);
|
||||
const vec_result_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
|
||||
.id_result_type = vec_ty_id,
|
||||
.id_result = vec_result_id,
|
||||
.constituents = ids,
|
||||
});
|
||||
return vec_result_id;
|
||||
if (!ty.isVector(zcu)) return result_id;
|
||||
return self.constructCompositeSplat(ty, result_id);
|
||||
}
|
||||
|
||||
/// Construct a struct at runtime.
|
||||
/// ty must be a struct type.
|
||||
/// Constituents should be in `indirect` representation (as the elements of a struct should be).
|
||||
/// Result is in `direct` representation.
|
||||
fn constructStruct(self: *NavGen, ty: Type, types: []const Type, constituents: []const IdRef) !IdRef {
|
||||
assert(types.len == constituents.len);
|
||||
|
||||
pub fn constructComposite(self: *NavGen, result_ty_id: IdRef, constituents: []const IdRef) !IdRef {
|
||||
const result_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
|
||||
.id_result_type = try self.resolveType(ty, .direct),
|
||||
try self.func.body.emit(self.gpa, .OpCompositeConstruct, .{
|
||||
.id_result_type = result_ty_id,
|
||||
.id_result = result_id,
|
||||
.constituents = constituents,
|
||||
});
|
||||
return result_id;
|
||||
}
|
||||
|
||||
/// Construct a vector at runtime.
|
||||
/// ty must be an vector type.
|
||||
fn constructVector(self: *NavGen, ty: Type, constituents: []const IdRef) !IdRef {
|
||||
/// Construct a composite at runtime with all lanes set to the same value.
|
||||
/// ty must be an aggregate type.
|
||||
fn constructCompositeSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef {
|
||||
const zcu = self.pt.zcu;
|
||||
assert(ty.vectorLen(zcu) == constituents.len);
|
||||
|
||||
// Note: older versions of the Khronos SPRIV-LLVM translator crash on this instruction
|
||||
// because it cannot construct structs which' operands are not constant.
|
||||
// See https://github.com/KhronosGroup/SPIRV-LLVM-Translator/issues/1349
|
||||
// Currently this is the case for Intel OpenCL CPU runtime (2023-WW46), but the
|
||||
// alternatives dont work properly:
|
||||
// - using temporaries/pointers doesn't work properly with vectors of bool, causes
|
||||
// backends that use llvm to crash
|
||||
// - using OpVectorInsertDynamic doesn't work for non-spirv-vectors of bool.
|
||||
|
||||
const result_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
|
||||
.id_result_type = try self.resolveType(ty, .direct),
|
||||
.id_result = result_id,
|
||||
.constituents = constituents,
|
||||
});
|
||||
return result_id;
|
||||
}
|
||||
|
||||
/// Construct a vector at runtime with all lanes set to the same value.
|
||||
/// ty must be an vector type.
|
||||
fn constructVectorSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef {
|
||||
const zcu = self.pt.zcu;
|
||||
const n = ty.vectorLen(zcu);
|
||||
const n = ty.arrayLen(zcu);
|
||||
|
||||
const constituents = try self.gpa.alloc(IdRef, n);
|
||||
defer self.gpa.free(constituents);
|
||||
@memset(constituents, constituent);
|
||||
|
||||
return try self.constructVector(ty, constituents);
|
||||
}
|
||||
|
||||
/// 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).
|
||||
/// Result is in `direct` representation.
|
||||
fn constructArray(self: *NavGen, ty: Type, constituents: []const IdRef) !IdRef {
|
||||
const result_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpCompositeConstruct, .{
|
||||
.id_result_type = try self.resolveType(ty, .direct),
|
||||
.id_result = result_id,
|
||||
.constituents = constituents,
|
||||
});
|
||||
return result_id;
|
||||
const result_ty_id = try self.resolveType(ty, .direct);
|
||||
return self.constructComposite(result_ty_id, constituents);
|
||||
}
|
||||
|
||||
/// This function generates a load for a constant in direct (ie, non-memory) representation.
|
||||
@ -947,9 +869,9 @@ const NavGen = struct {
|
||||
},
|
||||
.int => {
|
||||
if (ty.isSignedInt(zcu)) {
|
||||
break :cache try self.constInt(ty, val.toSignedInt(zcu), repr);
|
||||
break :cache try self.constInt(ty, val.toSignedInt(zcu));
|
||||
} else {
|
||||
break :cache try self.constInt(ty, val.toUnsignedInt(zcu), repr);
|
||||
break :cache try self.constInt(ty, val.toUnsignedInt(zcu));
|
||||
}
|
||||
},
|
||||
.float => {
|
||||
@ -970,7 +892,7 @@ const NavGen = struct {
|
||||
},
|
||||
.err => |err| {
|
||||
const value = try pt.getErrorValue(err.name);
|
||||
break :cache try self.constInt(ty, value, repr);
|
||||
break :cache try self.constInt(ty, value);
|
||||
},
|
||||
.error_union => |error_union| {
|
||||
// TODO: Error unions may be constructed with constant instructions if the payload type
|
||||
@ -1011,7 +933,8 @@ const NavGen = struct {
|
||||
types = .{ payload_ty, err_ty };
|
||||
}
|
||||
|
||||
return try self.constructStruct(ty, &types, &constituents);
|
||||
const comp_ty_id = try self.resolveType(ty, .direct);
|
||||
return try self.constructComposite(comp_ty_id, &constituents);
|
||||
},
|
||||
.enum_tag => {
|
||||
const int_val = try val.intFromEnum(ty, pt);
|
||||
@ -1020,14 +943,10 @@ const NavGen = struct {
|
||||
},
|
||||
.ptr => return self.constantPtr(val),
|
||||
.slice => |slice| {
|
||||
const ptr_ty = ty.slicePtrFieldType(zcu);
|
||||
const ptr_id = try self.constantPtr(Value.fromInterned(slice.ptr));
|
||||
const len_id = try self.constant(Type.usize, Value.fromInterned(slice.len), .indirect);
|
||||
return self.constructStruct(
|
||||
ty,
|
||||
&.{ ptr_ty, Type.usize },
|
||||
&.{ ptr_id, len_id },
|
||||
);
|
||||
const comp_ty_id = try self.resolveType(ty, .direct);
|
||||
return try self.constructComposite(comp_ty_id, &.{ ptr_id, len_id });
|
||||
},
|
||||
.opt => {
|
||||
const payload_ty = ty.optionalChild(zcu);
|
||||
@ -1053,11 +972,8 @@ const NavGen = struct {
|
||||
else
|
||||
try self.spv.constUndef(try self.resolveType(payload_ty, .indirect));
|
||||
|
||||
return try self.constructStruct(
|
||||
ty,
|
||||
&.{ payload_ty, Type.bool },
|
||||
&.{ payload_id, has_pl_id },
|
||||
);
|
||||
const comp_ty_id = try self.resolveType(ty, .direct);
|
||||
return try self.constructComposite(comp_ty_id, &.{ payload_id, has_pl_id });
|
||||
},
|
||||
.aggregate => |aggregate| switch (ip.indexToKey(ty.ip_index)) {
|
||||
inline .array_type, .vector_type => |array_type, tag| {
|
||||
@ -1077,7 +993,7 @@ const NavGen = struct {
|
||||
// TODO: This is really space inefficient, perhaps there is a better
|
||||
// way to do it?
|
||||
for (constituents, bytes.toSlice(constituents.len, ip)) |*constituent, byte| {
|
||||
constituent.* = try self.constInt(elem_ty, byte, child_repr);
|
||||
constituent.* = try self.constInt(elem_ty, byte);
|
||||
}
|
||||
},
|
||||
.elems => |elems| {
|
||||
@ -1090,11 +1006,8 @@ const NavGen = struct {
|
||||
},
|
||||
}
|
||||
|
||||
switch (tag) {
|
||||
.array_type => return self.constructArray(ty, constituents),
|
||||
.vector_type => return self.constructVector(ty, constituents),
|
||||
else => unreachable,
|
||||
}
|
||||
const comp_ty_id = try self.resolveType(ty, .direct);
|
||||
return self.constructComposite(comp_ty_id, constituents);
|
||||
},
|
||||
.struct_type => {
|
||||
const struct_type = zcu.typeToStruct(ty).?;
|
||||
@ -1124,7 +1037,8 @@ const NavGen = struct {
|
||||
try constituents.append(field_id);
|
||||
}
|
||||
|
||||
return try self.constructStruct(ty, types.items, constituents.items);
|
||||
const comp_ty_id = try self.resolveType(ty, .direct);
|
||||
return try self.constructComposite(comp_ty_id, constituents.items);
|
||||
},
|
||||
.tuple_type => unreachable, // TODO
|
||||
else => unreachable,
|
||||
@ -1149,8 +1063,6 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
fn constantPtr(self: *NavGen, ptr_val: Value) Error!IdRef {
|
||||
// TODO: Caching??
|
||||
|
||||
const pt = self.pt;
|
||||
|
||||
if (ptr_val.isUndef(pt.zcu)) {
|
||||
@ -1201,7 +1113,7 @@ const NavGen = struct {
|
||||
.elem_ptr => |elem| {
|
||||
const parent_ptr_id = try self.derivePtr(elem.parent.*);
|
||||
const parent_ptr_ty = try elem.parent.ptrType(pt);
|
||||
const index_id = try self.constInt(Type.usize, elem.elem_idx, .direct);
|
||||
const index_id = try self.constInt(Type.usize, elem.elem_idx);
|
||||
return self.ptrElemPtr(parent_ptr_ty, parent_ptr_id, index_id);
|
||||
},
|
||||
.offset_and_cast => |oac| {
|
||||
@ -1255,7 +1167,7 @@ const NavGen = struct {
|
||||
|
||||
// Uav refs are always generic.
|
||||
assert(ty.ptrAddressSpace(zcu) == .generic);
|
||||
const decl_ptr_ty_id = try self.ptrType(uav_ty, .Generic);
|
||||
const decl_ptr_ty_id = try self.ptrType(uav_ty, .Generic, .indirect);
|
||||
const ptr_id = try self.resolveUav(uav.val);
|
||||
|
||||
if (decl_ptr_ty_id != ty_id) {
|
||||
@ -1310,7 +1222,7 @@ const NavGen = struct {
|
||||
const storage_class = self.spvStorageClass(nav.getAddrspace());
|
||||
try self.addFunctionDep(spv_decl_index, storage_class);
|
||||
|
||||
const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class);
|
||||
const decl_ptr_ty_id = try self.ptrType(nav_ty, storage_class, .indirect);
|
||||
|
||||
const ptr_id = switch (storage_class) {
|
||||
.Generic => try self.castToGeneric(decl_ptr_ty_id, decl_id),
|
||||
@ -1359,23 +1271,11 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
fn arrayType(self: *NavGen, len: u32, child_ty: IdRef) !IdRef {
|
||||
// TODO: Cache??
|
||||
const len_id = try self.constInt(Type.u32, len, .direct);
|
||||
const result_id = self.spv.allocId();
|
||||
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeArray, .{
|
||||
.id_result = result_id,
|
||||
.element_type = child_ty,
|
||||
.length = len_id,
|
||||
});
|
||||
return result_id;
|
||||
const len_id = try self.constInt(Type.u32, len);
|
||||
return self.spv.arrayType(len_id, child_ty);
|
||||
}
|
||||
|
||||
fn ptrType(self: *NavGen, child_ty: Type, storage_class: StorageClass) !IdRef {
|
||||
return try self.ptrType2(child_ty, storage_class, .indirect);
|
||||
}
|
||||
|
||||
fn ptrType2(self: *NavGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef {
|
||||
fn ptrType(self: *NavGen, child_ty: Type, storage_class: StorageClass, child_repr: Repr) !IdRef {
|
||||
const key = .{ child_ty.toIntern(), storage_class, child_repr };
|
||||
const entry = try self.ptr_types.getOrPut(self.gpa, key);
|
||||
if (entry.found_existing) {
|
||||
@ -1408,8 +1308,7 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
fn functionType(self: *NavGen, return_ty: Type, param_types: []const Type) !IdRef {
|
||||
// TODO: Cache??
|
||||
|
||||
const return_ty_id = try self.resolveFnReturnType(return_ty);
|
||||
const param_ids = try self.gpa.alloc(IdRef, param_types.len);
|
||||
defer self.gpa.free(param_ids);
|
||||
|
||||
@ -1417,14 +1316,7 @@ const NavGen = struct {
|
||||
param_id.* = try self.resolveType(param_ty, .direct);
|
||||
}
|
||||
|
||||
const ty_id = self.spv.allocId();
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypeFunction, .{
|
||||
.id_result = ty_id,
|
||||
.return_type = try self.resolveFnReturnType(return_ty),
|
||||
.id_ref_2 = param_ids,
|
||||
});
|
||||
|
||||
return ty_id;
|
||||
return self.spv.functionType(return_ty_id, param_ids);
|
||||
}
|
||||
|
||||
fn zigScalarOrVectorTypeLike(self: *NavGen, new_ty: Type, base_ty: Type) !Type {
|
||||
@ -1702,13 +1594,7 @@ const NavGen = struct {
|
||||
|
||||
const child_ty = Type.fromInterned(ptr_info.child);
|
||||
const storage_class = self.spvStorageClass(ptr_info.flags.address_space);
|
||||
const ptr_ty_id = try self.ptrType(child_ty, storage_class);
|
||||
|
||||
if (target.os.tag == .vulkan and ptr_info.flags.size == .many) {
|
||||
try self.spv.decorate(ptr_ty_id, .{ .ArrayStride = .{
|
||||
.array_stride = @intCast(child_ty.abiSize(zcu)),
|
||||
} });
|
||||
}
|
||||
const ptr_ty_id = try self.ptrType(child_ty, storage_class, .indirect);
|
||||
|
||||
if (ptr_info.flags.size != .slice) {
|
||||
return ptr_ty_id;
|
||||
@ -1755,10 +1641,6 @@ const NavGen = struct {
|
||||
defer self.gpa.free(type_name);
|
||||
try self.spv.debugName(result_id, type_name);
|
||||
|
||||
if (target.os.tag == .vulkan) {
|
||||
try self.spv.decorate(result_id, .Block); // Decorate all structs as block for now...
|
||||
}
|
||||
|
||||
return result_id;
|
||||
},
|
||||
.struct_type => ip.loadStructType(ty.toIntern()),
|
||||
@ -1804,10 +1686,6 @@ const NavGen = struct {
|
||||
defer self.gpa.free(type_name);
|
||||
try self.spv.debugName(result_id, type_name);
|
||||
|
||||
if (target.os.tag == .vulkan) {
|
||||
try self.spv.decorate(result_id, .Block); // Decorate all structs as block for now...
|
||||
}
|
||||
|
||||
return result_id;
|
||||
},
|
||||
.optional => {
|
||||
@ -2073,12 +1951,13 @@ const NavGen = struct {
|
||||
.exploded_vector => |range| {
|
||||
assert(self.ty.isVector(zcu));
|
||||
assert(self.ty.vectorLen(zcu) == range.len);
|
||||
const consituents = try ng.gpa.alloc(IdRef, range.len);
|
||||
defer ng.gpa.free(consituents);
|
||||
for (consituents, 0..range.len) |*id, i| {
|
||||
const constituents = try ng.gpa.alloc(IdRef, range.len);
|
||||
defer ng.gpa.free(constituents);
|
||||
for (constituents, 0..range.len) |*id, i| {
|
||||
id.* = range.at(i);
|
||||
}
|
||||
return ng.constructVector(self.ty, consituents);
|
||||
const result_ty_id = try ng.resolveType(self.ty, .direct);
|
||||
return ng.constructComposite(result_ty_id, constituents);
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -2282,7 +2161,7 @@ const NavGen = struct {
|
||||
.child = tmp.ty.toIntern(),
|
||||
});
|
||||
|
||||
const vector = try ng.constructVectorSplat(vector_ty, id);
|
||||
const vector = try ng.constructCompositeSplat(vector_ty, id);
|
||||
return .{
|
||||
.ty = vector_ty,
|
||||
.value = .{ .spv_vectorwise = vector },
|
||||
@ -3045,7 +2924,7 @@ const NavGen = struct {
|
||||
const spv_err_decl_index = self.object.error_push_constant.?.push_constant_ptr;
|
||||
const push_constant_id = self.spv.declPtr(spv_err_decl_index).result_id;
|
||||
|
||||
const zero_id = try self.constInt(Type.u32, 0, .direct);
|
||||
const zero_id = try self.constInt(Type.u32, 0);
|
||||
// We cannot use OpInBoundsAccessChain to dereference cross-storage class, so we have to use
|
||||
// a load.
|
||||
const tmp = self.spv.allocId();
|
||||
@ -3187,7 +3066,7 @@ const NavGen = struct {
|
||||
const storage_class = self.spvStorageClass(nav.getAddrspace());
|
||||
assert(storage_class != .Generic); // These should be instance globals
|
||||
|
||||
const ptr_ty_id = try self.ptrType(ty, storage_class);
|
||||
const ptr_ty_id = try self.ptrType(ty, storage_class, .indirect);
|
||||
|
||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpVariable, .{
|
||||
.id_result_type = ptr_ty_id,
|
||||
@ -3208,7 +3087,7 @@ const NavGen = struct {
|
||||
|
||||
try self.spv.declareDeclDeps(spv_decl_index, &.{});
|
||||
|
||||
const ptr_ty_id = try self.ptrType(ty, .Function);
|
||||
const ptr_ty_id = try self.ptrType(ty, .Function, .indirect);
|
||||
|
||||
if (maybe_init_val) |init_val| {
|
||||
// TODO: Combine with resolveAnonDecl?
|
||||
@ -3265,8 +3144,8 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
fn intFromBool2(self: *NavGen, value: Temporary, result_ty: Type) !Temporary {
|
||||
const zero_id = try self.constInt(result_ty, 0, .direct);
|
||||
const one_id = try self.constInt(result_ty, 1, .direct);
|
||||
const zero_id = try self.constInt(result_ty, 0);
|
||||
const one_id = try self.constInt(result_ty, 1);
|
||||
|
||||
return try self.buildSelect(
|
||||
value,
|
||||
@ -3648,12 +3527,12 @@ const NavGen = struct {
|
||||
.strange_integer => switch (info.signedness) {
|
||||
.unsigned => {
|
||||
const mask_value = if (info.bits == 64) 0xFFFF_FFFF_FFFF_FFFF else (@as(u64, 1) << @as(u6, @intCast(info.bits))) - 1;
|
||||
const mask_id = try self.constInt(ty.scalarType(zcu), mask_value, .direct);
|
||||
const mask_id = try self.constInt(ty.scalarType(zcu), mask_value);
|
||||
return try self.buildBinary(.bit_and, value, Temporary.init(ty.scalarType(zcu), mask_id));
|
||||
},
|
||||
.signed => {
|
||||
// Shift left and right so that we can copy the sight bit that way.
|
||||
const shift_amt_id = try self.constInt(ty.scalarType(zcu), info.backing_bits - info.bits, .direct);
|
||||
const shift_amt_id = try self.constInt(ty.scalarType(zcu), info.backing_bits - info.bits);
|
||||
const shift_amt = Temporary.init(ty.scalarType(zcu), shift_amt_id);
|
||||
const left = try self.buildBinary(.sll, value, shift_amt);
|
||||
return try self.buildBinary(.sra, left, shift_amt);
|
||||
@ -3687,7 +3566,7 @@ const NavGen = struct {
|
||||
const div = try self.buildBinary(.s_div, lhs, rhs);
|
||||
const rem = try self.buildBinary(.s_rem, lhs, rhs);
|
||||
|
||||
const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
|
||||
const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0));
|
||||
|
||||
const rem_is_not_zero = try self.buildCmp(.i_ne, rem, zero);
|
||||
|
||||
@ -3863,7 +3742,7 @@ const NavGen = struct {
|
||||
// = (rhs < 0) == (value < lhs)
|
||||
// = (rhs < 0) == (lhs > value)
|
||||
.signed => blk: {
|
||||
const zero = Temporary.init(rhs.ty, try self.constInt(rhs.ty, 0, .direct));
|
||||
const zero = Temporary.init(rhs.ty, try self.constInt(rhs.ty, 0));
|
||||
const rhs_lt_zero = try self.buildCmp(.s_lt, rhs, zero);
|
||||
const result_gt_lhs = try self.buildCmp(scmp, lhs, result);
|
||||
break :blk try self.buildCmp(.l_eq, rhs_lt_zero, result_gt_lhs);
|
||||
@ -3872,11 +3751,8 @@ const NavGen = struct {
|
||||
|
||||
const ov = try self.intFromBool(overflowed);
|
||||
|
||||
return try self.constructStruct(
|
||||
result_ty,
|
||||
&.{ result.ty, ov.ty },
|
||||
&.{ try result.materialize(self), try ov.materialize(self) },
|
||||
);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, &.{ try result.materialize(self), try ov.materialize(self) });
|
||||
}
|
||||
|
||||
fn airMulOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -3928,11 +3804,11 @@ const NavGen = struct {
|
||||
const result = try self.normalize(low_bits, info);
|
||||
|
||||
// Shift the result bits away to get the overflow bits.
|
||||
const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits, .direct));
|
||||
const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits));
|
||||
const overflow = try self.buildBinary(.srl, full_result, shift);
|
||||
|
||||
// Directly check if its zero in the op_ty without converting first.
|
||||
const zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0, .direct));
|
||||
const zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0));
|
||||
const overflowed = try self.buildCmp(.i_ne, zero, overflow);
|
||||
|
||||
break :blk .{ result, overflowed };
|
||||
@ -3946,7 +3822,7 @@ const NavGen = struct {
|
||||
// Overflow happened if the high-bits of the result are non-zero OR if the
|
||||
// high bits of the low word of the result (those outside the range of the
|
||||
// int) are nonzero.
|
||||
const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
|
||||
const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0));
|
||||
const high_overflowed = try self.buildCmp(.i_ne, zero, high_bits);
|
||||
|
||||
// If no overflow bits in low_bits, no extra work needs to be done.
|
||||
@ -3955,7 +3831,7 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
// Shift the result bits away to get the overflow bits.
|
||||
const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits, .direct));
|
||||
const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits));
|
||||
const low_overflow = try self.buildBinary(.srl, low_bits, shift);
|
||||
const low_overflowed = try self.buildCmp(.i_ne, zero, low_overflow);
|
||||
|
||||
@ -3974,7 +3850,7 @@ const NavGen = struct {
|
||||
// overflow should be -1 when
|
||||
// (lhs > 0 && rhs < 0) || (lhs < 0 && rhs > 0)
|
||||
|
||||
const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0, .direct));
|
||||
const zero = Temporary.init(lhs.ty, try self.constInt(lhs.ty, 0));
|
||||
const lhs_negative = try self.buildCmp(.s_lt, lhs, zero);
|
||||
const rhs_negative = try self.buildCmp(.s_lt, rhs, zero);
|
||||
const lhs_positive = try self.buildCmp(.s_gt, lhs, zero);
|
||||
@ -4003,13 +3879,13 @@ const NavGen = struct {
|
||||
// bit for the expected overflow bits.
|
||||
// To do that, shift out everything bit the sign bit and
|
||||
// then check what remains.
|
||||
const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits - 1, .direct));
|
||||
const shift = Temporary.init(full_result.ty, try self.constInt(full_result.ty, info.bits - 1));
|
||||
// Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
|
||||
// for negative cases.
|
||||
const overflow = try self.buildBinary(.sra, full_result, shift);
|
||||
|
||||
const long_all_set = Temporary.init(full_result.ty, try self.constInt(full_result.ty, -1, .direct));
|
||||
const long_zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0, .direct));
|
||||
const long_all_set = Temporary.init(full_result.ty, try self.constInt(full_result.ty, -1));
|
||||
const long_zero = Temporary.init(full_result.ty, try self.constInt(full_result.ty, 0));
|
||||
const mask = try self.buildSelect(expected_overflow_bit, long_all_set, long_zero);
|
||||
|
||||
const overflowed = try self.buildCmp(.i_ne, mask, overflow);
|
||||
@ -4022,7 +3898,7 @@ const NavGen = struct {
|
||||
// Truncate result if required.
|
||||
const result = try self.normalize(low_bits, info);
|
||||
|
||||
const all_set = Temporary.init(lhs.ty, try self.constInt(lhs.ty, -1, .direct));
|
||||
const all_set = Temporary.init(lhs.ty, try self.constInt(lhs.ty, -1));
|
||||
const mask = try self.buildSelect(expected_overflow_bit, all_set, zero);
|
||||
|
||||
// Like with unsigned, overflow happened if high_bits are not the ones we expect,
|
||||
@ -4038,7 +3914,7 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
// Shift the result bits away to get the overflow bits.
|
||||
const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1, .direct));
|
||||
const shift = Temporary.init(lhs.ty, try self.constInt(lhs.ty, info.bits - 1));
|
||||
// Use SRA so that any sign bits are duplicated. Now we can just check if ALL bits are set
|
||||
// for negative cases.
|
||||
const low_overflow = try self.buildBinary(.sra, low_bits, shift);
|
||||
@ -4052,11 +3928,8 @@ const NavGen = struct {
|
||||
|
||||
const ov = try self.intFromBool(overflowed);
|
||||
|
||||
return try self.constructStruct(
|
||||
result_ty,
|
||||
&.{ result.ty, ov.ty },
|
||||
&.{ try result.materialize(self), try ov.materialize(self) },
|
||||
);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, &.{ try result.materialize(self), try ov.materialize(self) });
|
||||
}
|
||||
|
||||
fn airShlOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -4092,11 +3965,8 @@ const NavGen = struct {
|
||||
const overflowed = try self.buildCmp(.i_ne, base, right);
|
||||
const ov = try self.intFromBool(overflowed);
|
||||
|
||||
return try self.constructStruct(
|
||||
result_ty,
|
||||
&.{ result.ty, ov.ty },
|
||||
&.{ try result.materialize(self), try ov.materialize(self) },
|
||||
);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, &.{ try result.materialize(self), try ov.materialize(self) });
|
||||
}
|
||||
|
||||
fn airMulAdd(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -4163,7 +4033,7 @@ const NavGen = struct {
|
||||
const operand_id = try self.resolve(ty_op.operand);
|
||||
const result_ty = self.typeOfIndex(inst);
|
||||
|
||||
return try self.constructVectorSplat(result_ty, operand_id);
|
||||
return try self.constructCompositeSplat(result_ty, operand_id);
|
||||
}
|
||||
|
||||
fn airReduce(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -4297,10 +4167,10 @@ const NavGen = struct {
|
||||
|
||||
// Fall back to manually extracting and inserting components.
|
||||
|
||||
const components = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));
|
||||
defer self.gpa.free(components);
|
||||
const constituents = try self.gpa.alloc(IdRef, result_ty.vectorLen(zcu));
|
||||
defer self.gpa.free(constituents);
|
||||
|
||||
for (components, 0..) |*id, i| {
|
||||
for (constituents, 0..) |*id, i| {
|
||||
const elem = try mask.elemValue(pt, i);
|
||||
if (elem.isUndef(zcu)) {
|
||||
id.* = try self.spv.constUndef(scalar_ty_id);
|
||||
@ -4315,14 +4185,15 @@ const NavGen = struct {
|
||||
}
|
||||
}
|
||||
|
||||
return try self.constructVector(result_ty, components);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, constituents);
|
||||
}
|
||||
|
||||
fn indicesToIds(self: *NavGen, indices: []const u32) ![]IdRef {
|
||||
const ids = try self.gpa.alloc(IdRef, indices.len);
|
||||
errdefer self.gpa.free(ids);
|
||||
for (indices, ids) |index, *id| {
|
||||
id.* = try self.constInt(Type.u32, index, .direct);
|
||||
id.* = try self.constInt(Type.u32, index);
|
||||
}
|
||||
|
||||
return ids;
|
||||
@ -4676,7 +4547,7 @@ const NavGen = struct {
|
||||
break :blk result_id;
|
||||
}
|
||||
|
||||
const dst_ptr_ty_id = try self.ptrType(dst_ty, .Function);
|
||||
const dst_ptr_ty_id = try self.ptrType(dst_ty, .Function, .indirect);
|
||||
|
||||
const tmp_id = try self.alloc(src_ty, .{ .storage_class = .Function });
|
||||
try self.store(src_ty, tmp_id, src_id, .{});
|
||||
@ -4851,7 +4722,7 @@ const NavGen = struct {
|
||||
const elem_ptr_ty_id = try self.resolveType(elem_ptr_ty, .direct);
|
||||
|
||||
const array_ptr_id = try self.resolve(ty_op.operand);
|
||||
const len_id = try self.constInt(Type.usize, array_ty.arrayLen(zcu), .direct);
|
||||
const len_id = try self.constInt(Type.usize, array_ty.arrayLen(zcu));
|
||||
|
||||
const elem_ptr_id = if (!array_ty.hasRuntimeBitsIgnoreComptime(zcu))
|
||||
// Note: The pointer is something like *opaque{}, so we need to bitcast it to the element type.
|
||||
@ -4860,11 +4731,8 @@ const NavGen = struct {
|
||||
// Convert the pointer-to-array to a pointer to the first element.
|
||||
try self.accessChain(elem_ptr_ty_id, array_ptr_id, &.{0});
|
||||
|
||||
return try self.constructStruct(
|
||||
slice_ty,
|
||||
&.{ elem_ptr_ty, Type.usize },
|
||||
&.{ elem_ptr_id, len_id },
|
||||
);
|
||||
const slice_ty_id = try self.resolveType(slice_ty, .direct);
|
||||
return try self.constructComposite(slice_ty_id, &.{ elem_ptr_id, len_id });
|
||||
}
|
||||
|
||||
fn airSlice(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -4872,16 +4740,9 @@ const NavGen = struct {
|
||||
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
|
||||
const ptr_id = try self.resolve(bin_op.lhs);
|
||||
const len_id = try self.resolve(bin_op.rhs);
|
||||
const ptr_ty = self.typeOf(bin_op.lhs);
|
||||
const slice_ty = self.typeOfIndex(inst);
|
||||
|
||||
// Note: Types should not need to be converted to direct, these types
|
||||
// dont need to be converted.
|
||||
return try self.constructStruct(
|
||||
slice_ty,
|
||||
&.{ ptr_ty, Type.usize },
|
||||
&.{ ptr_id, len_id },
|
||||
);
|
||||
const slice_ty_id = try self.resolveType(slice_ty, .direct);
|
||||
return try self.constructComposite(slice_ty_id, &.{ ptr_id, len_id });
|
||||
}
|
||||
|
||||
fn airAggregateInit(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -4936,11 +4797,8 @@ const NavGen = struct {
|
||||
else => unreachable,
|
||||
}
|
||||
|
||||
return try self.constructStruct(
|
||||
result_ty,
|
||||
types[0..index],
|
||||
constituents[0..index],
|
||||
);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, constituents[0..index]);
|
||||
},
|
||||
.vector => {
|
||||
const n_elems = result_ty.vectorLen(zcu);
|
||||
@ -4951,7 +4809,8 @@ const NavGen = struct {
|
||||
elem_ids[i] = try self.resolve(element);
|
||||
}
|
||||
|
||||
return try self.constructVector(result_ty, elem_ids);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, elem_ids);
|
||||
},
|
||||
.array => {
|
||||
const array_info = result_ty.arrayInfo(zcu);
|
||||
@ -4968,7 +4827,8 @@ const NavGen = struct {
|
||||
elem_ids[n_elems - 1] = try self.constant(array_info.elem_type, sentinel_val, .indirect);
|
||||
}
|
||||
|
||||
return try self.constructArray(result_ty, elem_ids);
|
||||
const result_ty_id = try self.resolveType(result_ty, .direct);
|
||||
return try self.constructComposite(result_ty_id, elem_ids);
|
||||
},
|
||||
else => unreachable,
|
||||
}
|
||||
@ -4984,7 +4844,7 @@ const NavGen = struct {
|
||||
const elem_ty = array_ty.childType(zcu);
|
||||
const abi_size = elem_ty.abiSize(zcu);
|
||||
const size = array_ty.arrayLenIncludingSentinel(zcu) * abi_size;
|
||||
return try self.constInt(Type.usize, size, .direct);
|
||||
return try self.constInt(Type.usize, size);
|
||||
},
|
||||
.many, .c => unreachable,
|
||||
}
|
||||
@ -5060,7 +4920,7 @@ const NavGen = struct {
|
||||
const zcu = self.pt.zcu;
|
||||
// Construct new pointer type for the resulting pointer
|
||||
const elem_ty = ptr_ty.elemType2(zcu); // use elemType() so that we get T for *[N]T.
|
||||
const elem_ptr_ty_id = try self.ptrType(elem_ty, self.spvStorageClass(ptr_ty.ptrAddressSpace(zcu)));
|
||||
const elem_ptr_ty_id = try self.ptrType(elem_ty, self.spvStorageClass(ptr_ty.ptrAddressSpace(zcu)), .indirect);
|
||||
if (ptr_ty.isSinglePointer(zcu)) {
|
||||
// Pointer-to-array. In this case, the resulting pointer is not of the same type
|
||||
// as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain.
|
||||
@ -5115,8 +4975,8 @@ const NavGen = struct {
|
||||
const is_vector = array_ty.isVector(zcu);
|
||||
|
||||
const elem_repr: Repr = if (is_vector) .direct else .indirect;
|
||||
const ptr_array_ty_id = try self.ptrType2(array_ty, .Function, .direct);
|
||||
const ptr_elem_ty_id = try self.ptrType2(elem_ty, .Function, elem_repr);
|
||||
const ptr_array_ty_id = try self.ptrType(array_ty, .Function, .direct);
|
||||
const ptr_elem_ty_id = try self.ptrType(elem_ty, .Function, elem_repr);
|
||||
|
||||
const tmp_id = self.spv.allocId();
|
||||
try self.func.prologue.emit(self.spv.gpa, .OpVariable, .{
|
||||
@ -5171,7 +5031,7 @@ const NavGen = struct {
|
||||
const scalar_ty = vector_ty.scalarType(zcu);
|
||||
|
||||
const storage_class = self.spvStorageClass(vector_ptr_ty.ptrAddressSpace(zcu));
|
||||
const scalar_ptr_ty_id = try self.ptrType(scalar_ty, storage_class);
|
||||
const scalar_ptr_ty_id = try self.ptrType(scalar_ty, storage_class, .indirect);
|
||||
|
||||
const vector_ptr = try self.resolve(data.vector_ptr);
|
||||
const index = try self.resolve(extra.lhs);
|
||||
@ -5193,7 +5053,7 @@ const NavGen = struct {
|
||||
if (layout.tag_size == 0) return;
|
||||
|
||||
const tag_ty = un_ty.unionTagTypeSafety(zcu).?;
|
||||
const tag_ptr_ty_id = try self.ptrType(tag_ty, self.spvStorageClass(un_ptr_ty.ptrAddressSpace(zcu)));
|
||||
const tag_ptr_ty_id = try self.ptrType(tag_ty, self.spvStorageClass(un_ptr_ty.ptrAddressSpace(zcu)), .indirect);
|
||||
|
||||
const union_ptr_id = try self.resolve(bin_op.lhs);
|
||||
const new_tag_id = try self.resolve(bin_op.rhs);
|
||||
@ -5252,23 +5112,23 @@ const NavGen = struct {
|
||||
} else 0;
|
||||
|
||||
if (!layout.has_payload) {
|
||||
return try self.constInt(tag_ty, tag_int, .direct);
|
||||
return try self.constInt(tag_ty, tag_int);
|
||||
}
|
||||
|
||||
const tmp_id = try self.alloc(ty, .{ .storage_class = .Function });
|
||||
|
||||
if (layout.tag_size != 0) {
|
||||
const tag_ptr_ty_id = try self.ptrType(tag_ty, .Function);
|
||||
const tag_ptr_ty_id = try self.ptrType(tag_ty, .Function, .indirect);
|
||||
const ptr_id = try self.accessChain(tag_ptr_ty_id, tmp_id, &.{@as(u32, @intCast(layout.tag_index))});
|
||||
const tag_id = try self.constInt(tag_ty, tag_int, .direct);
|
||||
const tag_id = try self.constInt(tag_ty, tag_int);
|
||||
try self.store(tag_ty, ptr_id, tag_id, .{});
|
||||
}
|
||||
|
||||
const payload_ty = Type.fromInterned(union_ty.field_types.get(ip)[active_field]);
|
||||
if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
|
||||
const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function);
|
||||
const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
|
||||
const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
|
||||
const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function);
|
||||
const active_pl_ptr_ty_id = try self.ptrType(payload_ty, .Function, .indirect);
|
||||
const active_pl_ptr_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
|
||||
.id_result_type = active_pl_ptr_ty_id,
|
||||
@ -5332,10 +5192,10 @@ const NavGen = struct {
|
||||
const tmp_id = try self.alloc(object_ty, .{ .storage_class = .Function });
|
||||
try self.store(object_ty, tmp_id, object_id, .{});
|
||||
|
||||
const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function);
|
||||
const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, .Function, .indirect);
|
||||
const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, tmp_id, &.{layout.payload_index});
|
||||
|
||||
const active_pl_ptr_ty_id = try self.ptrType(field_ty, .Function);
|
||||
const active_pl_ptr_ty_id = try self.ptrType(field_ty, .Function, .indirect);
|
||||
const active_pl_ptr_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpBitcast, .{
|
||||
.id_result_type = active_pl_ptr_ty_id,
|
||||
@ -5365,7 +5225,7 @@ const NavGen = struct {
|
||||
const base_ptr_int = base_ptr_int: {
|
||||
if (field_offset == 0) break :base_ptr_int field_ptr_int;
|
||||
|
||||
const field_offset_id = try self.constInt(Type.usize, field_offset, .direct);
|
||||
const field_offset_id = try self.constInt(Type.usize, field_offset);
|
||||
const field_ptr_tmp = Temporary.init(Type.usize, field_ptr_int);
|
||||
const field_offset_tmp = Temporary.init(Type.usize, field_offset_id);
|
||||
const result = try self.buildBinary(.i_sub, field_ptr_tmp, field_offset_tmp);
|
||||
@ -5415,7 +5275,7 @@ const NavGen = struct {
|
||||
}
|
||||
|
||||
const storage_class = self.spvStorageClass(object_ptr_ty.ptrAddressSpace(zcu));
|
||||
const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, storage_class);
|
||||
const pl_ptr_ty_id = try self.ptrType(layout.payload_ty, storage_class, .indirect);
|
||||
const pl_ptr_id = try self.accessChain(pl_ptr_ty_id, object_ptr, &.{layout.payload_index});
|
||||
|
||||
const active_pl_ptr_id = self.spv.allocId();
|
||||
@ -5456,7 +5316,7 @@ const NavGen = struct {
|
||||
ty: Type,
|
||||
options: AllocOptions,
|
||||
) !IdRef {
|
||||
const ptr_fn_ty_id = try self.ptrType(ty, .Function);
|
||||
const ptr_fn_ty_id = try self.ptrType(ty, .Function, .indirect);
|
||||
|
||||
// SPIR-V requires that OpVariable declarations for locals go into the first block, so we are just going to
|
||||
// directly generate them into func.prologue instead of the body.
|
||||
@ -5475,7 +5335,7 @@ const NavGen = struct {
|
||||
|
||||
switch (options.storage_class) {
|
||||
.Generic => {
|
||||
const ptr_gn_ty_id = try self.ptrType(ty, .Generic);
|
||||
const ptr_gn_ty_id = try self.ptrType(ty, .Generic, .indirect);
|
||||
// Convert to a generic pointer
|
||||
return self.castToGeneric(ptr_gn_ty_id, var_id);
|
||||
},
|
||||
@ -5724,7 +5584,7 @@ const NavGen = struct {
|
||||
assert(cf.block_stack.items.len > 0);
|
||||
|
||||
// Check if the target of the branch was this current block.
|
||||
const this_block = try self.constInt(Type.u32, @intFromEnum(inst), .direct);
|
||||
const this_block = try self.constInt(Type.u32, @intFromEnum(inst));
|
||||
const jump_to_this_block_id = self.spv.allocId();
|
||||
const bool_ty_id = try self.resolveType(Type.bool, .direct);
|
||||
try self.func.body.emit(self.spv.gpa, .OpIEqual, .{
|
||||
@ -5804,7 +5664,7 @@ const NavGen = struct {
|
||||
try self.store(operand_ty, block_result_var_id, operand_id, .{});
|
||||
}
|
||||
|
||||
const next_block = try self.constInt(Type.u32, @intFromEnum(br.block_inst), .direct);
|
||||
const next_block = try self.constInt(Type.u32, @intFromEnum(br.block_inst));
|
||||
try self.structuredBreak(next_block);
|
||||
},
|
||||
.unstructured => |cf| {
|
||||
@ -5968,7 +5828,7 @@ const NavGen = struct {
|
||||
// Functions with an empty error set are emitted with an error code
|
||||
// return type and return zero so they can be function pointers coerced
|
||||
// to functions that return anyerror.
|
||||
const no_err_id = try self.constInt(Type.anyerror, 0, .direct);
|
||||
const no_err_id = try self.constInt(Type.anyerror, 0);
|
||||
return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
|
||||
} else {
|
||||
return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
|
||||
@ -5992,7 +5852,7 @@ const NavGen = struct {
|
||||
// Functions with an empty error set are emitted with an error code
|
||||
// return type and return zero so they can be function pointers coerced
|
||||
// to functions that return anyerror.
|
||||
const no_err_id = try self.constInt(Type.anyerror, 0, .direct);
|
||||
const no_err_id = try self.constInt(Type.anyerror, 0);
|
||||
return try self.func.body.emit(self.spv.gpa, .OpReturnValue, .{ .value = no_err_id });
|
||||
} else {
|
||||
return try self.func.body.emit(self.spv.gpa, .OpReturn, {});
|
||||
@ -6026,7 +5886,7 @@ const NavGen = struct {
|
||||
else
|
||||
err_union_id;
|
||||
|
||||
const zero_id = try self.constInt(Type.anyerror, 0, .direct);
|
||||
const zero_id = try self.constInt(Type.anyerror, 0);
|
||||
const is_err_id = self.spv.allocId();
|
||||
try self.func.body.emit(self.spv.gpa, .OpINotEqual, .{
|
||||
.id_result_type = bool_ty_id,
|
||||
@ -6134,7 +5994,8 @@ const NavGen = struct {
|
||||
types[eu_layout.errorFieldIndex()] = Type.anyerror;
|
||||
types[eu_layout.payloadFieldIndex()] = payload_ty;
|
||||
|
||||
return try self.constructStruct(err_union_ty, &types, &members);
|
||||
const err_union_ty_id = try self.resolveType(err_union_ty, .direct);
|
||||
return try self.constructComposite(err_union_ty_id, &members);
|
||||
}
|
||||
|
||||
fn airWrapErrUnionPayload(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||
@ -6145,18 +6006,19 @@ const NavGen = struct {
|
||||
const eu_layout = self.errorUnionLayout(payload_ty);
|
||||
|
||||
if (!eu_layout.payload_has_bits) {
|
||||
return try self.constInt(Type.anyerror, 0, .direct);
|
||||
return try self.constInt(Type.anyerror, 0);
|
||||
}
|
||||
|
||||
var members: [2]IdRef = undefined;
|
||||
members[eu_layout.errorFieldIndex()] = try self.constInt(Type.anyerror, 0, .direct);
|
||||
members[eu_layout.errorFieldIndex()] = try self.constInt(Type.anyerror, 0);
|
||||
members[eu_layout.payloadFieldIndex()] = try self.convertToIndirect(payload_ty, operand_id);
|
||||
|
||||
var types: [2]Type = undefined;
|
||||
types[eu_layout.errorFieldIndex()] = Type.anyerror;
|
||||
types[eu_layout.payloadFieldIndex()] = payload_ty;
|
||||
|
||||
return try self.constructStruct(err_union_ty, &types, &members);
|
||||
const err_union_ty_id = try self.resolveType(err_union_ty, .direct);
|
||||
return try self.constructComposite(err_union_ty_id, &members);
|
||||
}
|
||||
|
||||
fn airIsNull(self: *NavGen, inst: Air.Inst.Index, is_pointer: bool, pred: enum { is_null, is_non_null }) !?IdRef {
|
||||
@ -6204,7 +6066,7 @@ const NavGen = struct {
|
||||
if (is_pointer) {
|
||||
if (payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) {
|
||||
const storage_class = self.spvStorageClass(operand_ty.ptrAddressSpace(zcu));
|
||||
const bool_ptr_ty_id = try self.ptrType(Type.bool, storage_class);
|
||||
const bool_ptr_ty_id = try self.ptrType(Type.bool, storage_class, .indirect);
|
||||
const tag_ptr_id = try self.accessChain(bool_ptr_ty_id, operand_id, &.{1});
|
||||
break :blk try self.load(Type.bool, tag_ptr_id, .{});
|
||||
}
|
||||
@ -6267,7 +6129,7 @@ const NavGen = struct {
|
||||
.id_result_type = bool_ty_id,
|
||||
.id_result = result_id,
|
||||
.operand_1 = error_id,
|
||||
.operand_2 = try self.constInt(Type.anyerror, 0, .direct),
|
||||
.operand_2 = try self.constInt(Type.anyerror, 0),
|
||||
},
|
||||
),
|
||||
}
|
||||
@ -6335,8 +6197,8 @@ const NavGen = struct {
|
||||
|
||||
const payload_id = try self.convertToIndirect(payload_ty, operand_id);
|
||||
const members = [_]IdRef{ payload_id, try self.constBool(true, .indirect) };
|
||||
const types = [_]Type{ payload_ty, Type.bool };
|
||||
return try self.constructStruct(optional_ty, &types, &members);
|
||||
const optional_ty_id = try self.resolveType(optional_ty, .direct);
|
||||
return try self.constructComposite(optional_ty_id, &members);
|
||||
}
|
||||
|
||||
fn airSwitchBr(self: *NavGen, inst: Air.Inst.Index) !void {
|
||||
@ -6752,13 +6614,13 @@ const NavGen = struct {
|
||||
|
||||
fn builtin3D(self: *NavGen, result_ty: Type, builtin: spec.BuiltIn, dimension: u32, out_of_range_value: anytype) !IdRef {
|
||||
if (dimension >= 3) {
|
||||
return try self.constInt(result_ty, out_of_range_value, .direct);
|
||||
return try self.constInt(result_ty, out_of_range_value);
|
||||
}
|
||||
const vec_ty = try self.pt.vectorType(.{
|
||||
.len = 3,
|
||||
.child = result_ty.toIntern(),
|
||||
});
|
||||
const ptr_ty_id = try self.ptrType(vec_ty, .Input);
|
||||
const ptr_ty_id = try self.ptrType(vec_ty, .Input, .indirect);
|
||||
const spv_decl_index = try self.spv.builtin(ptr_ty_id, builtin);
|
||||
try self.func.decl_deps.put(self.spv.gpa, spv_decl_index, {});
|
||||
const ptr = self.spv.declPtr(spv_decl_index).result_id;
|
||||
|
||||
@ -10,6 +10,8 @@ const Module = @This();
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const autoHashStrat = std.hash.autoHashStrat;
|
||||
const Wyhash = std.hash.Wyhash;
|
||||
|
||||
const spec = @import("spec.zig");
|
||||
const Word = spec.Word;
|
||||
@ -19,6 +21,19 @@ const IdResultType = spec.IdResultType;
|
||||
|
||||
const Section = @import("Section.zig");
|
||||
|
||||
/// Helper HashMap type to hash deeply
|
||||
fn DeepHashMap(K: type, V: type) type {
|
||||
return std.HashMapUnmanaged(K, V, struct {
|
||||
pub fn hash(ctx: @This(), key: K) u64 {
|
||||
_ = ctx;
|
||||
var hasher = Wyhash.init(0);
|
||||
autoHashStrat(&hasher, key, .Deep);
|
||||
return hasher.final();
|
||||
}
|
||||
pub const eql = std.hash_map.getAutoEqlFn(K, @This());
|
||||
}, std.hash_map.default_max_load_percentage);
|
||||
}
|
||||
|
||||
/// 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
|
||||
@ -159,8 +174,13 @@ cache: struct {
|
||||
// This cache is required so that @Vector(X, u1) in direct representation has the
|
||||
// same ID as @Vector(X, bool) in indirect representation.
|
||||
vector_types: std.AutoHashMapUnmanaged(struct { IdRef, u32 }, IdRef) = .empty,
|
||||
array_types: std.AutoHashMapUnmanaged(struct { IdRef, IdRef }, IdRef) = .empty,
|
||||
function_types: DeepHashMap(struct { IdRef, []const IdRef }, IdRef) = .empty,
|
||||
|
||||
builtins: std.AutoHashMapUnmanaged(struct { IdRef, spec.BuiltIn }, Decl.Index) = .empty,
|
||||
decorations: std.AutoHashMapUnmanaged(struct { IdRef, spec.Decoration }, void) = .empty,
|
||||
|
||||
bool_const: [2]?IdRef = .{ null, null },
|
||||
} = .{},
|
||||
|
||||
/// Set of Decls, referred to by Decl.Index.
|
||||
@ -201,7 +221,10 @@ pub fn deinit(self: *Module) void {
|
||||
self.cache.int_types.deinit(self.gpa);
|
||||
self.cache.float_types.deinit(self.gpa);
|
||||
self.cache.vector_types.deinit(self.gpa);
|
||||
self.cache.array_types.deinit(self.gpa);
|
||||
self.cache.function_types.deinit(self.gpa);
|
||||
self.cache.builtins.deinit(self.gpa);
|
||||
self.cache.decorations.deinit(self.gpa);
|
||||
|
||||
self.decls.deinit(self.gpa);
|
||||
self.decl_deps.deinit(self.gpa);
|
||||
@ -477,20 +500,69 @@ pub fn floatType(self: *Module, bits: u16) !IdRef {
|
||||
return entry.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn vectorType(self: *Module, len: u32, child_id: IdRef) !IdRef {
|
||||
const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_id, len });
|
||||
pub fn vectorType(self: *Module, len: u32, child_ty_id: IdRef) !IdRef {
|
||||
const entry = try self.cache.vector_types.getOrPut(self.gpa, .{ child_ty_id, len });
|
||||
if (!entry.found_existing) {
|
||||
const result_id = self.allocId();
|
||||
entry.value_ptr.* = result_id;
|
||||
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeVector, .{
|
||||
.id_result = result_id,
|
||||
.component_type = child_id,
|
||||
.component_type = child_ty_id,
|
||||
.component_count = len,
|
||||
});
|
||||
}
|
||||
return entry.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn arrayType(self: *Module, len_id: IdRef, child_ty_id: IdRef) !IdRef {
|
||||
const entry = try self.cache.array_types.getOrPut(self.gpa, .{ child_ty_id, len_id });
|
||||
if (!entry.found_existing) {
|
||||
const result_id = self.allocId();
|
||||
entry.value_ptr.* = result_id;
|
||||
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeArray, .{
|
||||
.id_result = result_id,
|
||||
.element_type = child_ty_id,
|
||||
.length = len_id,
|
||||
});
|
||||
}
|
||||
return entry.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn functionType(self: *Module, return_ty_id: IdRef, param_type_ids: []const IdRef) !IdRef {
|
||||
const entry = try self.cache.function_types.getOrPut(self.gpa, .{ return_ty_id, param_type_ids });
|
||||
if (!entry.found_existing) {
|
||||
const result_id = self.allocId();
|
||||
entry.value_ptr.* = result_id;
|
||||
try self.sections.types_globals_constants.emit(self.gpa, .OpTypeFunction, .{
|
||||
.id_result = result_id,
|
||||
.return_type = return_ty_id,
|
||||
.id_ref_2 = param_type_ids,
|
||||
});
|
||||
}
|
||||
return entry.value_ptr.*;
|
||||
}
|
||||
|
||||
pub fn constBool(self: *Module, value: bool) !IdRef {
|
||||
if (self.cache.bool_const[@intFromBool(value)]) |b| return b;
|
||||
|
||||
const result_ty_id = try self.boolType();
|
||||
const result_id = self.allocId();
|
||||
self.cache.bool_const[@intFromBool(value)] = result_id;
|
||||
|
||||
switch (value) {
|
||||
inline else => |value_ct| try self.sections.types_globals_constants.emit(
|
||||
self.gpa,
|
||||
if (value_ct) .OpConstantTrue else .OpConstantFalse,
|
||||
.{
|
||||
.id_result_type = result_ty_id,
|
||||
.id_result = result_id,
|
||||
},
|
||||
),
|
||||
}
|
||||
|
||||
return result_id;
|
||||
}
|
||||
|
||||
/// Return a pointer to a builtin variable. `result_ty_id` must be a **pointer**
|
||||
/// with storage class `.Input`.
|
||||
pub fn builtin(self: *Module, result_ty_id: IdRef, spirv_builtin: spec.BuiltIn) !Decl.Index {
|
||||
@ -534,13 +606,17 @@ pub fn decorate(
|
||||
target: IdRef,
|
||||
decoration: spec.Decoration.Extended,
|
||||
) !void {
|
||||
try self.sections.annotations.emit(self.gpa, .OpDecorate, .{
|
||||
.target = target,
|
||||
.decoration = decoration,
|
||||
});
|
||||
const entry = try self.cache.decorations.getOrPut(self.gpa, .{ target, decoration });
|
||||
if (!entry.found_existing) {
|
||||
try self.sections.annotations.emit(self.gpa, .OpDecorate, .{
|
||||
.target = target,
|
||||
.decoration = decoration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Decorate a result-id which is a member of some struct.
|
||||
/// We really don't have to and shouldn't need to cache this.
|
||||
pub fn decorateMember(
|
||||
self: *Module,
|
||||
structure_type: IdRef,
|
||||
|
||||
@ -155,7 +155,7 @@ const ModuleInfo = struct {
|
||||
}
|
||||
}
|
||||
|
||||
return ModuleInfo{
|
||||
return .{
|
||||
.entities = entities.unmanaged,
|
||||
.operand_is_id = operand_is_id,
|
||||
// There may be unrelated decorations at the end, so make sure to
|
||||
|
||||
@ -166,7 +166,7 @@ const ModuleInfo = struct {
|
||||
return error.InvalidPhysicalFormat;
|
||||
}
|
||||
|
||||
return ModuleInfo{
|
||||
return .{
|
||||
.functions = functions.unmanaged,
|
||||
.callee_store = callee_store.items,
|
||||
.result_id_to_code_offset = result_id_to_code_offset.unmanaged,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user