mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 14:23:09 +00:00
spirv: respect cpu features
This commit is contained in:
parent
1b0c7f51ef
commit
85169bbba2
@ -176,10 +176,10 @@ pub const Object = struct {
|
|||||||
push_constant_ptr: SpvModule.Decl.Index,
|
push_constant_ptr: SpvModule.Decl.Index,
|
||||||
} = null,
|
} = null,
|
||||||
|
|
||||||
pub fn init(gpa: Allocator) Object {
|
pub fn init(gpa: Allocator, target: std.Target) Object {
|
||||||
return .{
|
return .{
|
||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.spv = SpvModule.init(gpa),
|
.spv = SpvModule.init(gpa, target),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,11 +412,6 @@ const NavGen = struct {
|
|||||||
self.func.deinit(self.gpa);
|
self.func.deinit(self.gpa);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the target which we are currently compiling for.
|
|
||||||
pub fn getTarget(self: *NavGen) std.Target {
|
|
||||||
return self.pt.zcu.getTarget();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn fail(self: *NavGen, comptime format: []const u8, args: anytype) Error {
|
pub fn fail(self: *NavGen, comptime format: []const u8, args: anytype) Error {
|
||||||
@branchHint(.cold);
|
@branchHint(.cold);
|
||||||
const zcu = self.pt.zcu;
|
const zcu = self.pt.zcu;
|
||||||
@ -431,12 +426,12 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// This imports the "default" extended instruction set for the target
|
/// This imports the "default" extended instruction set for the target
|
||||||
/// For OpenCL, OpenCL.std.100. For Vulkan, GLSL.std.450.
|
/// For OpenCL, OpenCL.std.100. For Vulkan and OpenGL, GLSL.std.450.
|
||||||
fn importExtendedSet(self: *NavGen) !IdResult {
|
fn importExtendedSet(self: *NavGen) !IdResult {
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
return switch (target.os.tag) {
|
return switch (target.os.tag) {
|
||||||
.opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
|
.opencl => try self.spv.importInstructionSet(.@"OpenCL.std"),
|
||||||
.vulkan => try self.spv.importInstructionSet(.@"GLSL.std.450"),
|
.vulkan, .opengl => try self.spv.importInstructionSet(.@"GLSL.std.450"),
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -546,14 +541,10 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn addFunctionDep(self: *NavGen, decl_index: SpvModule.Decl.Index, storage_class: StorageClass) !void {
|
fn addFunctionDep(self: *NavGen, decl_index: SpvModule.Decl.Index, storage_class: StorageClass) !void {
|
||||||
const target = self.getTarget();
|
if (self.spv.version.minor < 4) {
|
||||||
if (target.os.tag == .vulkan) {
|
// Before version 1.4, the interface’s storage classes are limited to the Input and Output
|
||||||
// Shader entry point dependencies must be variables with Input or Output storage class
|
if (storage_class == .Input or storage_class == .Output) {
|
||||||
switch (storage_class) {
|
|
||||||
.Input, .Output => {
|
|
||||||
try self.func.decl_deps.put(self.spv.gpa, decl_index, {});
|
try self.func.decl_deps.put(self.spv.gpa, decl_index, {});
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
try self.func.decl_deps.put(self.spv.gpa, decl_index, {});
|
try self.func.decl_deps.put(self.spv.gpa, decl_index, {});
|
||||||
@ -561,11 +552,7 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn castToGeneric(self: *NavGen, type_id: IdRef, ptr_id: IdRef) !IdRef {
|
fn castToGeneric(self: *NavGen, type_id: IdRef, ptr_id: IdRef) !IdRef {
|
||||||
const target = self.getTarget();
|
if (self.spv.hasFeature(.Kernel)) {
|
||||||
|
|
||||||
if (target.os.tag == .vulkan) {
|
|
||||||
return ptr_id;
|
|
||||||
} else {
|
|
||||||
const result_id = self.spv.allocId();
|
const result_id = self.spv.allocId();
|
||||||
try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
|
try self.func.body.emit(self.spv.gpa, .OpPtrCastToGeneric, .{
|
||||||
.id_result_type = type_id,
|
.id_result_type = type_id,
|
||||||
@ -574,6 +561,8 @@ const NavGen = struct {
|
|||||||
});
|
});
|
||||||
return result_id;
|
return result_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ptr_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a new SPIR-V block, Emits the label of the new block, and stores which
|
/// Start a new SPIR-V block, Emits the label of the new block, and stores which
|
||||||
@ -596,8 +585,6 @@ const NavGen = struct {
|
|||||||
/// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
|
/// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
|
||||||
/// TODO: Should the result of this function be cached?
|
/// TODO: Should the result of this function be cached?
|
||||||
fn backingIntBits(self: *NavGen, bits: u16) ?u16 {
|
fn backingIntBits(self: *NavGen, bits: u16) ?u16 {
|
||||||
const target = self.getTarget();
|
|
||||||
|
|
||||||
// The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
|
// The backend will never be asked to compiler a 0-bit integer, so we won't have to handle those in this function.
|
||||||
assert(bits != 0);
|
assert(bits != 0);
|
||||||
|
|
||||||
@ -611,14 +598,8 @@ const NavGen = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
for (ints) |int| {
|
for (ints) |int| {
|
||||||
const has_feature = if (int.feature) |feature|
|
const has_feature = if (int.feature) |feature| self.spv.hasFeature(feature) else true;
|
||||||
Target.spirv.featureSetHas(target.cpu.features, feature)
|
if (bits <= int.bits and has_feature) return int.bits;
|
||||||
else
|
|
||||||
true;
|
|
||||||
|
|
||||||
if (bits <= int.bits and has_feature) {
|
|
||||||
return int.bits;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -631,11 +612,7 @@ const NavGen = struct {
|
|||||||
/// is no way of knowing whether those are actually supported.
|
/// is no way of knowing whether those are actually supported.
|
||||||
/// TODO: Maybe this should be cached?
|
/// TODO: Maybe this should be cached?
|
||||||
fn largestSupportedIntBits(self: *NavGen) u16 {
|
fn largestSupportedIntBits(self: *NavGen) u16 {
|
||||||
const target = self.getTarget();
|
return if (self.spv.hasFeature(.Int64)) 64 else 32;
|
||||||
return if (Target.spirv.featureSetHas(target.cpu.features, .Int64))
|
|
||||||
64
|
|
||||||
else
|
|
||||||
32;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by
|
/// Checks whether the type is "composite int", an integer consisting of multiple native integers. These are represented by
|
||||||
@ -648,7 +625,6 @@ const NavGen = struct {
|
|||||||
/// Checks whether the type can be directly translated to SPIR-V vectors
|
/// Checks whether the type can be directly translated to SPIR-V vectors
|
||||||
fn isSpvVector(self: *NavGen, ty: Type) bool {
|
fn isSpvVector(self: *NavGen, ty: Type) bool {
|
||||||
const zcu = self.pt.zcu;
|
const zcu = self.pt.zcu;
|
||||||
const target = self.getTarget();
|
|
||||||
if (ty.zigTypeTag(zcu) != .vector) return false;
|
if (ty.zigTypeTag(zcu) != .vector) return false;
|
||||||
|
|
||||||
// TODO: This check must be expanded for types that can be represented
|
// TODO: This check must be expanded for types that can be represented
|
||||||
@ -664,17 +640,19 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const elem_ty = ty.childType(zcu);
|
const elem_ty = ty.childType(zcu);
|
||||||
|
|
||||||
const len = ty.vectorLen(zcu);
|
const len = ty.vectorLen(zcu);
|
||||||
const is_scalar = elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type;
|
|
||||||
const spirv_len = len > 1 and len <= 4;
|
if (elem_ty.isNumeric(zcu) or elem_ty.toIntern() == .bool_type) {
|
||||||
const opencl_len = if (target.os.tag == .opencl) (len == 8 or len == 16) else false;
|
if (len > 1 and len <= 4) return true;
|
||||||
return is_scalar and (spirv_len or opencl_len);
|
if (self.spv.hasFeature(.Vector16)) return (len == 8 or len == 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo {
|
fn arithmeticTypeInfo(self: *NavGen, ty: Type) ArithmeticTypeInfo {
|
||||||
const zcu = self.pt.zcu;
|
const zcu = self.pt.zcu;
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
var scalar_ty = ty.scalarType(zcu);
|
var scalar_ty = ty.scalarType(zcu);
|
||||||
if (scalar_ty.zigTypeTag(zcu) == .@"enum") {
|
if (scalar_ty.zigTypeTag(zcu) == .@"enum") {
|
||||||
scalar_ty = scalar_ty.intTagType(zcu);
|
scalar_ty = scalar_ty.intTagType(zcu);
|
||||||
@ -791,7 +769,7 @@ const NavGen = struct {
|
|||||||
/// ty must be an aggregate type.
|
/// ty must be an aggregate type.
|
||||||
fn constructCompositeSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef {
|
fn constructCompositeSplat(self: *NavGen, ty: Type, constituent: IdRef) !IdRef {
|
||||||
const zcu = self.pt.zcu;
|
const zcu = self.pt.zcu;
|
||||||
const n = ty.arrayLen(zcu);
|
const n: usize = @intCast(ty.arrayLen(zcu));
|
||||||
|
|
||||||
const constituents = try self.gpa.alloc(IdRef, n);
|
const constituents = try self.gpa.alloc(IdRef, n);
|
||||||
defer self.gpa.free(constituents);
|
defer self.gpa.free(constituents);
|
||||||
@ -817,7 +795,7 @@ const NavGen = struct {
|
|||||||
|
|
||||||
const pt = self.pt;
|
const pt = self.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
const result_ty_id = try self.resolveType(ty, repr);
|
const result_ty_id = try self.resolveType(ty, repr);
|
||||||
const ip = &zcu.intern_pool;
|
const ip = &zcu.intern_pool;
|
||||||
|
|
||||||
@ -1263,11 +1241,11 @@ const NavGen = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Kernel only supports unsigned ints.
|
// Kernel only supports unsigned ints.
|
||||||
if (self.getTarget().os.tag == .vulkan) {
|
if (self.spv.hasFeature(.Kernel)) {
|
||||||
return self.spv.intType(signedness, backing_bits);
|
return self.spv.intType(.unsigned, backing_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.spv.intType(.unsigned, backing_bits);
|
return self.spv.intType(signedness, backing_bits);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arrayType(self: *NavGen, len: u32, child_ty: IdRef) !IdRef {
|
fn arrayType(self: *NavGen, len: u32, child_ty: IdRef) !IdRef {
|
||||||
@ -1436,7 +1414,7 @@ const NavGen = struct {
|
|||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
const ip = &zcu.intern_pool;
|
const ip = &zcu.intern_pool;
|
||||||
log.debug("resolveType: ty = {}", .{ty.fmt(pt)});
|
log.debug("resolveType: ty = {}", .{ty.fmt(pt)});
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
|
|
||||||
const section = &self.spv.sections.types_globals_constants;
|
const section = &self.spv.sections.types_globals_constants;
|
||||||
|
|
||||||
@ -1533,7 +1511,7 @@ const NavGen = struct {
|
|||||||
return try self.arrayType(1, elem_ty_id);
|
return try self.arrayType(1, elem_ty_id);
|
||||||
} else {
|
} else {
|
||||||
const result_id = try self.arrayType(total_len, elem_ty_id);
|
const result_id = try self.arrayType(total_len, elem_ty_id);
|
||||||
if (target.os.tag == .vulkan) {
|
if (self.spv.hasFeature(.Shader)) {
|
||||||
try self.spv.decorate(result_id, .{ .ArrayStride = .{
|
try self.spv.decorate(result_id, .{ .ArrayStride = .{
|
||||||
.array_stride = @intCast(elem_ty.abiSize(zcu)),
|
.array_stride = @intCast(elem_ty.abiSize(zcu)),
|
||||||
} });
|
} });
|
||||||
@ -1667,7 +1645,7 @@ const NavGen = struct {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.os.tag == .vulkan) {
|
if (self.spv.hasFeature(.Shader)) {
|
||||||
try self.spv.decorateMember(result_id, index, .{ .Offset = .{
|
try self.spv.decorateMember(result_id, index, .{ .Offset = .{
|
||||||
.byte_offset = @intCast(ty.structFieldOffset(field_index, zcu)),
|
.byte_offset = @intCast(ty.structFieldOffset(field_index, zcu)),
|
||||||
} });
|
} });
|
||||||
@ -1769,20 +1747,11 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {
|
fn spvStorageClass(self: *NavGen, as: std.builtin.AddressSpace) StorageClass {
|
||||||
const target = self.getTarget();
|
|
||||||
return switch (as) {
|
return switch (as) {
|
||||||
.generic => switch (target.os.tag) {
|
.generic => if (self.spv.hasFeature(.GenericPointer)) .Generic else .Function,
|
||||||
.vulkan => .Function,
|
|
||||||
.opencl => .Generic,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
.shared => .Workgroup,
|
.shared => .Workgroup,
|
||||||
.local => .Function,
|
.local => .Function,
|
||||||
.global => switch (target.os.tag) {
|
.global => if (self.spv.hasFeature(.Shader)) .PhysicalStorageBuffer else .CrossWorkgroup,
|
||||||
.opencl => .CrossWorkgroup,
|
|
||||||
.vulkan => .PhysicalStorageBuffer,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
.constant => .UniformConstant,
|
.constant => .UniformConstant,
|
||||||
.push_constant => .PushConstant,
|
.push_constant => .PushConstant,
|
||||||
.input => .Input,
|
.input => .Input,
|
||||||
@ -2326,7 +2295,7 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {
|
fn buildFma(self: *NavGen, a: Temporary, b: Temporary, c: Temporary) !Temporary {
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
|
|
||||||
const v = self.vectorization(.{ a, b, c });
|
const v = self.vectorization(.{ a, b, c });
|
||||||
const ops = v.operations();
|
const ops = v.operations();
|
||||||
@ -2348,7 +2317,7 @@ const NavGen = struct {
|
|||||||
// NOTE: Vulkan's FMA instruction does *NOT* produce the right values!
|
// NOTE: Vulkan's FMA instruction does *NOT* produce the right values!
|
||||||
// its precision guarantees do NOT match zigs and it does NOT match OpenCLs!
|
// its precision guarantees do NOT match zigs and it does NOT match OpenCLs!
|
||||||
// it needs to be emulated!
|
// it needs to be emulated!
|
||||||
.vulkan => unreachable, // TODO: See above
|
.vulkan, .opengl => unreachable, // TODO: See above
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2485,14 +2454,14 @@ const NavGen = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary {
|
fn buildUnary(self: *NavGen, op: UnaryOp, operand: Temporary) !Temporary {
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
const v = blk: {
|
const v = blk: {
|
||||||
const v = self.vectorization(.{operand});
|
const v = self.vectorization(.{operand});
|
||||||
break :blk switch (op) {
|
break :blk switch (op) {
|
||||||
// TODO: These instructions don't seem to be working
|
// TODO: These instructions don't seem to be working
|
||||||
// properly for LLVM-based backends on OpenCL for 8- and
|
// properly for LLVM-based backends on OpenCL for 8- and
|
||||||
// 16-component vectors.
|
// 16-component vectors.
|
||||||
.i_abs => if (target.os.tag == .opencl and v.components() >= 8) v.unroll() else v,
|
.i_abs => if (self.spv.hasFeature(.Vector16) and v.components() >= 8) v.unroll() else v,
|
||||||
else => v,
|
else => v,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@ -2545,7 +2514,7 @@ const NavGen = struct {
|
|||||||
// Note: We'll need to check these for floating point accuracy
|
// Note: We'll need to check these for floating point accuracy
|
||||||
// Vulkan does not put tight requirements on these, for correction
|
// Vulkan does not put tight requirements on these, for correction
|
||||||
// we might want to emulate them at some point.
|
// we might want to emulate them at some point.
|
||||||
.vulkan => switch (op) {
|
.vulkan, .opengl => switch (op) {
|
||||||
.i_abs => 5, // SAbs
|
.i_abs => 5, // SAbs
|
||||||
.f_abs => 4, // FAbs
|
.f_abs => 4, // FAbs
|
||||||
.clz => unreachable, // TODO
|
.clz => unreachable, // TODO
|
||||||
@ -2615,7 +2584,7 @@ const NavGen = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
|
fn buildBinary(self: *NavGen, op: BinaryOp, lhs: Temporary, rhs: Temporary) !Temporary {
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
|
|
||||||
const v = self.vectorization(.{ lhs, rhs });
|
const v = self.vectorization(.{ lhs, rhs });
|
||||||
const ops = v.operations();
|
const ops = v.operations();
|
||||||
@ -2674,7 +2643,7 @@ const NavGen = struct {
|
|||||||
.u_min => 159, // u_min
|
.u_min => 159, // u_min
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
},
|
},
|
||||||
.vulkan => switch (op) {
|
.vulkan, .opengl => switch (op) {
|
||||||
.f_max => 40, // FMax
|
.f_max => 40, // FMax
|
||||||
.s_max => 42, // SMax
|
.s_max => 42, // SMax
|
||||||
.u_max => 41, // UMax
|
.u_max => 41, // UMax
|
||||||
@ -2713,7 +2682,7 @@ const NavGen = struct {
|
|||||||
) !struct { Temporary, Temporary } {
|
) !struct { Temporary, Temporary } {
|
||||||
const pt = self.pt;
|
const pt = self.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
const ip = &zcu.intern_pool;
|
const ip = &zcu.intern_pool;
|
||||||
|
|
||||||
const v = lhs.vectorization(self).unify(rhs.vectorization(self));
|
const v = lhs.vectorization(self).unify(rhs.vectorization(self));
|
||||||
@ -2756,7 +2725,7 @@ const NavGen = struct {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.vulkan => {
|
.vulkan, .opengl => {
|
||||||
// Operations return a struct{T, T}
|
// Operations return a struct{T, T}
|
||||||
// where T is maybe vectorized.
|
// where T is maybe vectorized.
|
||||||
const op_result_ty: Type = .fromInterned(try ip.getTupleType(zcu.gpa, pt.tid, .{
|
const op_result_ty: Type = .fromInterned(try ip.getTupleType(zcu.gpa, pt.tid, .{
|
||||||
@ -2843,7 +2812,7 @@ const NavGen = struct {
|
|||||||
|
|
||||||
const section = &self.spv.sections.functions;
|
const section = &self.spv.sections.functions;
|
||||||
|
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
|
|
||||||
const p_error_id = self.spv.allocId();
|
const p_error_id = self.spv.allocId();
|
||||||
switch (target.os.tag) {
|
switch (target.os.tag) {
|
||||||
@ -2866,7 +2835,7 @@ const NavGen = struct {
|
|||||||
.id_result = self.spv.allocId(),
|
.id_result = self.spv.allocId(),
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
.vulkan => {
|
.vulkan, .opengl => {
|
||||||
const ptr_ptr_anyerror_ty_id = self.spv.allocId();
|
const ptr_ptr_anyerror_ty_id = self.spv.allocId();
|
||||||
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
|
try self.spv.sections.types_globals_constants.emit(self.spv.gpa, .OpTypePointer, .{
|
||||||
.id_result = ptr_ptr_anyerror_ty_id,
|
.id_result = ptr_ptr_anyerror_ty_id,
|
||||||
@ -2967,7 +2936,7 @@ const NavGen = struct {
|
|||||||
defer self.gpa.free(test_name);
|
defer self.gpa.free(test_name);
|
||||||
|
|
||||||
const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
|
const execution_mode: spec.ExecutionModel = switch (target.os.tag) {
|
||||||
.vulkan => .GLCompute,
|
.vulkan, .opengl => .GLCompute,
|
||||||
.opencl => .Kernel,
|
.opencl => .Kernel,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
@ -3670,7 +3639,6 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn abs(self: *NavGen, result_ty: Type, value: Temporary) !Temporary {
|
fn abs(self: *NavGen, result_ty: Type, value: Temporary) !Temporary {
|
||||||
const target = self.getTarget();
|
|
||||||
const operand_info = self.arithmeticTypeInfo(value.ty);
|
const operand_info = self.arithmeticTypeInfo(value.ty);
|
||||||
|
|
||||||
switch (operand_info.class) {
|
switch (operand_info.class) {
|
||||||
@ -3682,7 +3650,7 @@ const NavGen = struct {
|
|||||||
// depending on the result type. Do that when
|
// depending on the result type. Do that when
|
||||||
// bitCast is implemented for vectors.
|
// bitCast is implemented for vectors.
|
||||||
// This is only relevant for Vulkan
|
// This is only relevant for Vulkan
|
||||||
assert(target.os.tag != .vulkan); // TODO
|
assert(self.spv.hasFeature(.Kernel)); // TODO
|
||||||
|
|
||||||
return try self.normalize(abs_value, self.arithmeticTypeInfo(result_ty));
|
return try self.normalize(abs_value, self.arithmeticTypeInfo(result_ty));
|
||||||
},
|
},
|
||||||
@ -3756,7 +3724,6 @@ const NavGen = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn airMulOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
fn airMulOverflow(self: *NavGen, inst: Air.Inst.Index) !?IdRef {
|
||||||
const target = self.getTarget();
|
|
||||||
const pt = self.pt;
|
const pt = self.pt;
|
||||||
|
|
||||||
const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl;
|
||||||
@ -3780,7 +3747,7 @@ const NavGen = struct {
|
|||||||
// - Additionally, if info.bits != 32, we'll have to check the high bits
|
// - Additionally, if info.bits != 32, we'll have to check the high bits
|
||||||
// of the result too.
|
// of the result too.
|
||||||
|
|
||||||
const largest_int_bits: u16 = if (Target.spirv.featureSetHas(target.cpu.features, .Int64)) 64 else 32;
|
const largest_int_bits = self.largestSupportedIntBits();
|
||||||
// If non-null, the number of bits that the multiplication should be performed in. If
|
// If non-null, the number of bits that the multiplication should be performed in. If
|
||||||
// null, we have to use wide multiplication.
|
// null, we have to use wide multiplication.
|
||||||
const maybe_op_ty_bits: ?u16 = switch (info.bits) {
|
const maybe_op_ty_bits: ?u16 = switch (info.bits) {
|
||||||
@ -3989,7 +3956,6 @@ const NavGen = struct {
|
|||||||
if (self.liveness.isUnused(inst)) return null;
|
if (self.liveness.isUnused(inst)) return null;
|
||||||
|
|
||||||
const zcu = self.pt.zcu;
|
const zcu = self.pt.zcu;
|
||||||
const target = self.getTarget();
|
|
||||||
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
|
const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op;
|
||||||
const operand = try self.temporary(ty_op.operand);
|
const operand = try self.temporary(ty_op.operand);
|
||||||
|
|
||||||
@ -4002,10 +3968,7 @@ const NavGen = struct {
|
|||||||
.float, .bool => unreachable,
|
.float, .bool => unreachable,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (target.os.tag) {
|
assert(self.spv.hasFeature(.Kernel)); // TODO
|
||||||
.vulkan => unreachable, // TODO
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
|
|
||||||
const count = try self.buildUnary(op, operand);
|
const count = try self.buildUnary(op, operand);
|
||||||
|
|
||||||
@ -4241,23 +4204,22 @@ const NavGen = struct {
|
|||||||
defer self.gpa.free(ids);
|
defer self.gpa.free(ids);
|
||||||
|
|
||||||
const result_id = self.spv.allocId();
|
const result_id = self.spv.allocId();
|
||||||
const target = self.getTarget();
|
if (self.spv.hasFeature(.Kernel)) {
|
||||||
switch (target.os.tag) {
|
try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{
|
||||||
.opencl => try self.func.body.emit(self.spv.gpa, .OpInBoundsPtrAccessChain, .{
|
|
||||||
.id_result_type = result_ty_id,
|
.id_result_type = result_ty_id,
|
||||||
.id_result = result_id,
|
.id_result = result_id,
|
||||||
.base = base,
|
.base = base,
|
||||||
.element = element,
|
.element = element,
|
||||||
.indexes = ids,
|
.indexes = ids,
|
||||||
}),
|
});
|
||||||
.vulkan => try self.func.body.emit(self.spv.gpa, .OpPtrAccessChain, .{
|
} else {
|
||||||
|
try self.func.body.emit(self.spv.gpa, .OpPtrAccessChain, .{
|
||||||
.id_result_type = result_ty_id,
|
.id_result_type = result_ty_id,
|
||||||
.id_result = result_id,
|
.id_result = result_id,
|
||||||
.base = base,
|
.base = base,
|
||||||
.element = element,
|
.element = element,
|
||||||
.indexes = ids,
|
.indexes = ids,
|
||||||
}),
|
});
|
||||||
else => unreachable,
|
|
||||||
}
|
}
|
||||||
return result_id;
|
return result_id;
|
||||||
}
|
}
|
||||||
@ -5328,10 +5290,7 @@ const NavGen = struct {
|
|||||||
.initializer = options.initializer,
|
.initializer = options.initializer,
|
||||||
});
|
});
|
||||||
|
|
||||||
const target = self.getTarget();
|
if (self.spv.hasFeature(.Shader)) return var_id;
|
||||||
if (target.os.tag == .vulkan) {
|
|
||||||
return var_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (options.storage_class) {
|
switch (options.storage_class) {
|
||||||
.Generic => {
|
.Generic => {
|
||||||
@ -6204,7 +6163,7 @@ const NavGen = struct {
|
|||||||
fn airSwitchBr(self: *NavGen, inst: Air.Inst.Index) !void {
|
fn airSwitchBr(self: *NavGen, inst: Air.Inst.Index) !void {
|
||||||
const pt = self.pt;
|
const pt = self.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
const target = self.getTarget();
|
const target = self.spv.target;
|
||||||
const switch_br = self.air.unwrapSwitch(inst);
|
const switch_br = self.air.unwrapSwitch(inst);
|
||||||
const cond_ty = self.typeOf(switch_br.operand);
|
const cond_ty = self.typeOf(switch_br.operand);
|
||||||
const cond = try self.resolve(switch_br.operand);
|
const cond = try self.resolve(switch_br.operand);
|
||||||
|
|||||||
@ -118,6 +118,12 @@ gpa: Allocator,
|
|||||||
/// Arena for things that need to live for the length of this program.
|
/// Arena for things that need to live for the length of this program.
|
||||||
arena: std.heap.ArenaAllocator,
|
arena: std.heap.ArenaAllocator,
|
||||||
|
|
||||||
|
/// Target info
|
||||||
|
target: std.Target,
|
||||||
|
|
||||||
|
/// The target SPIR-V version
|
||||||
|
version: spec.Version,
|
||||||
|
|
||||||
/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".
|
/// Module layout, according to SPIR-V Spec section 2.4, "Logical Layout of a Module".
|
||||||
sections: struct {
|
sections: struct {
|
||||||
/// Capability instructions
|
/// Capability instructions
|
||||||
@ -196,10 +202,23 @@ entry_points: std.ArrayListUnmanaged(EntryPoint) = .empty,
|
|||||||
/// The list of extended instruction sets that should be imported.
|
/// The list of extended instruction sets that should be imported.
|
||||||
extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, IdRef) = .empty,
|
extended_instruction_set: std.AutoHashMapUnmanaged(spec.InstructionSet, IdRef) = .empty,
|
||||||
|
|
||||||
pub fn init(gpa: Allocator) Module {
|
pub fn init(gpa: Allocator, target: std.Target) Module {
|
||||||
|
const version_minor: u8 = blk: {
|
||||||
|
// Prefer higher versions
|
||||||
|
if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_6)) break :blk 6;
|
||||||
|
if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_5)) break :blk 5;
|
||||||
|
if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_4)) break :blk 4;
|
||||||
|
if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_3)) break :blk 3;
|
||||||
|
if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_2)) break :blk 2;
|
||||||
|
if (std.Target.spirv.featureSetHas(target.cpu.features, .v1_1)) break :blk 1;
|
||||||
|
break :blk 0;
|
||||||
|
};
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.gpa = gpa,
|
.gpa = gpa,
|
||||||
.arena = std.heap.ArenaAllocator.init(gpa),
|
.arena = std.heap.ArenaAllocator.init(gpa),
|
||||||
|
.target = target,
|
||||||
|
.version = .{ .major = 1, .minor = version_minor },
|
||||||
.next_result_id = 1, // 0 is an invalid SPIR-V result id, so start counting at 1.
|
.next_result_id = 1, // 0 is an invalid SPIR-V result id, so start counting at 1.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -263,6 +282,10 @@ pub fn idBound(self: Module) Word {
|
|||||||
return self.next_result_id;
|
return self.next_result_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn hasFeature(self: *Module, feature: std.Target.spirv.Feature) bool {
|
||||||
|
return std.Target.spirv.featureSetHas(self.target.cpu.features, feature);
|
||||||
|
}
|
||||||
|
|
||||||
fn addEntryPointDeps(
|
fn addEntryPointDeps(
|
||||||
self: *Module,
|
self: *Module,
|
||||||
decl_index: Decl.Index,
|
decl_index: Decl.Index,
|
||||||
@ -315,7 +338,7 @@ fn entryPoints(self: *Module) !Section {
|
|||||||
return entry_points;
|
return entry_points;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word {
|
pub fn finalize(self: *Module, a: Allocator) ![]Word {
|
||||||
// See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction"
|
// See SPIR-V Spec section 2.3, "Physical Layout of a SPIR-V Module and Instruction"
|
||||||
// TODO: Audit calls to allocId() in this function to make it idempotent.
|
// TODO: Audit calls to allocId() in this function to make it idempotent.
|
||||||
|
|
||||||
@ -324,16 +347,7 @@ pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word {
|
|||||||
|
|
||||||
const header = [_]Word{
|
const header = [_]Word{
|
||||||
spec.magic_number,
|
spec.magic_number,
|
||||||
// TODO: From cpu features
|
self.version.toWord(),
|
||||||
spec.Version.toWord(.{
|
|
||||||
.major = 1,
|
|
||||||
.minor = switch (target.os.tag) {
|
|
||||||
// Emit SPIR-V 1.3 for now. This is the highest version that Vulkan 1.1 supports.
|
|
||||||
.vulkan => 3,
|
|
||||||
// Emit SPIR-V 1.4 for now. This is the highest version that Intel's CPU OpenCL supports.
|
|
||||||
else => 4,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
spec.zig_generator_id,
|
spec.zig_generator_id,
|
||||||
self.idBound(),
|
self.idBound(),
|
||||||
0, // Schema (currently reserved for future use)
|
0, // Schema (currently reserved for future use)
|
||||||
@ -342,7 +356,7 @@ pub fn finalize(self: *Module, a: Allocator, target: std.Target) ![]Word {
|
|||||||
var source = Section{};
|
var source = Section{};
|
||||||
defer source.deinit(self.gpa);
|
defer source.deinit(self.gpa);
|
||||||
try self.sections.debug_strings.emit(self.gpa, .OpSource, .{
|
try self.sections.debug_strings.emit(self.gpa, .OpSource, .{
|
||||||
.source_language = .Unknown,
|
.source_language = .Zig,
|
||||||
.version = 0,
|
.version = 0,
|
||||||
// We cannot emit these because the Khronos translator does not parse this instruction
|
// We cannot emit these because the Khronos translator does not parse this instruction
|
||||||
// correctly.
|
// correctly.
|
||||||
|
|||||||
@ -75,7 +75,7 @@ pub fn createEmpty(
|
|||||||
.disable_lld_caching = options.disable_lld_caching,
|
.disable_lld_caching = options.disable_lld_caching,
|
||||||
.build_id = options.build_id,
|
.build_id = options.build_id,
|
||||||
},
|
},
|
||||||
.object = codegen.Object.init(gpa),
|
.object = codegen.Object.init(gpa, comp.getTarget()),
|
||||||
};
|
};
|
||||||
errdefer self.deinit();
|
errdefer self.deinit();
|
||||||
|
|
||||||
@ -172,7 +172,7 @@ pub fn updateExports(
|
|||||||
const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
|
const spv_decl_index = try self.object.resolveNav(zcu, nav_index);
|
||||||
const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
|
const cc = Type.fromInterned(nav_ty).fnCallingConvention(zcu);
|
||||||
const execution_model: spec.ExecutionModel = switch (target.os.tag) {
|
const execution_model: spec.ExecutionModel = switch (target.os.tag) {
|
||||||
.vulkan => switch (cc) {
|
.vulkan, .opengl => switch (cc) {
|
||||||
.spirv_vertex => .Vertex,
|
.spirv_vertex => .Vertex,
|
||||||
.spirv_fragment => .Fragment,
|
.spirv_fragment => .Fragment,
|
||||||
.spirv_kernel => .GLCompute,
|
.spirv_kernel => .GLCompute,
|
||||||
@ -231,10 +231,9 @@ pub fn flushModule(
|
|||||||
const spv = &self.object.spv;
|
const spv = &self.object.spv;
|
||||||
const diags = &comp.link_diags;
|
const diags = &comp.link_diags;
|
||||||
const gpa = comp.gpa;
|
const gpa = comp.gpa;
|
||||||
const target = comp.getTarget();
|
|
||||||
|
|
||||||
try writeCapabilities(spv, target);
|
try writeCapabilities(spv);
|
||||||
try writeMemoryModel(spv, target);
|
try writeMemoryModel(spv);
|
||||||
|
|
||||||
// We need to export the list of error names somewhere so that we can pretty-print them in the
|
// We need to export the list of error names somewhere so that we can pretty-print them in the
|
||||||
// executor. This is not really an important thing though, so we can just dump it in any old
|
// executor. This is not really an important thing though, so we can just dump it in any old
|
||||||
@ -269,7 +268,7 @@ pub fn flushModule(
|
|||||||
.extension = error_info.items,
|
.extension = error_info.items,
|
||||||
});
|
});
|
||||||
|
|
||||||
const module = try spv.finalize(arena, target);
|
const module = try spv.finalize(arena);
|
||||||
errdefer arena.free(module);
|
errdefer arena.free(module);
|
||||||
|
|
||||||
const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
|
const linked_module = self.linkModule(arena, module, sub_prog_node) catch |err| switch (err) {
|
||||||
@ -299,56 +298,63 @@ fn linkModule(self: *SpirV, a: Allocator, module: []Word, progress: std.Progress
|
|||||||
return binary.finalize(a);
|
return binary.finalize(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeCapabilities(spv: *SpvModule, target: std.Target) !void {
|
fn writeCapabilities(spv: *SpvModule) !void {
|
||||||
const gpa = spv.gpa;
|
var caps: std.ArrayList(spec.Capability) = .init(spv.gpa);
|
||||||
// TODO: Integrate with a hypothetical feature system
|
var extensions: std.ArrayList([]const u8) = .init(spv.gpa);
|
||||||
const caps: []const spec.Capability = switch (target.os.tag) {
|
defer {
|
||||||
.opencl => &.{ .Kernel, .Addresses, .Int8, .Int16, .Int64, .Float64, .Float16, .Vector16, .GenericPointer },
|
caps.deinit();
|
||||||
.vulkan => &.{ .Shader, .PhysicalStorageBufferAddresses, .Int8, .Int16, .Int64, .Float64, .Float16, .VariablePointers, .VariablePointersStorageBuffer },
|
extensions.deinit();
|
||||||
else => unreachable,
|
}
|
||||||
};
|
|
||||||
|
|
||||||
for (caps) |cap| {
|
// Currently all spirv target features name are mapped to a Capability or an Extension.
|
||||||
try spv.sections.capabilities.emit(gpa, .OpCapability, .{
|
// Except for versions which we ignore.
|
||||||
|
for (std.Target.spirv.all_features, 0..) |_, i| {
|
||||||
|
if (spv.target.cpu.features.isEnabled(@intCast(i))) {
|
||||||
|
const feature: std.Target.spirv.Feature = @enumFromInt(i);
|
||||||
|
const name = @tagName(feature);
|
||||||
|
if (std.meta.stringToEnum(spec.Capability, name)) |cap| {
|
||||||
|
try caps.append(cap);
|
||||||
|
} else if (std.mem.startsWith(u8, name, "SPV_")) {
|
||||||
|
try extensions.append(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (caps.items) |cap| {
|
||||||
|
try spv.sections.capabilities.emit(spv.gpa, .OpCapability, .{
|
||||||
.capability = cap,
|
.capability = cap,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (target.os.tag) {
|
for (extensions.items) |ext| {
|
||||||
.vulkan => {
|
try spv.sections.extensions.emit(spv.gpa, .OpExtension, .{ .name = ext });
|
||||||
try spv.sections.extensions.emit(gpa, .OpExtension, .{
|
|
||||||
.name = "SPV_KHR_physical_storage_buffer",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
else => {},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn writeMemoryModel(spv: *SpvModule, target: std.Target) !void {
|
fn writeMemoryModel(spv: *SpvModule) !void {
|
||||||
const gpa = spv.gpa;
|
const addressing_model: spec.AddressingModel = blk: {
|
||||||
|
if (spv.hasFeature(.Shader)) {
|
||||||
const addressing_model: spec.AddressingModel = switch (target.os.tag) {
|
break :blk switch (spv.target.cpu.arch) {
|
||||||
.opencl => switch (target.cpu.arch) {
|
|
||||||
.spirv32 => .Physical32,
|
|
||||||
.spirv64 => .Physical64,
|
|
||||||
else => unreachable,
|
|
||||||
},
|
|
||||||
.opengl, .vulkan => switch (target.cpu.arch) {
|
|
||||||
.spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
|
.spirv32 => .Logical, // TODO: I don't think this will ever be implemented.
|
||||||
.spirv64 => .PhysicalStorageBuffer64,
|
.spirv64 => .PhysicalStorageBuffer64,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
},
|
};
|
||||||
|
} else if (spv.hasFeature(.Kernel)) {
|
||||||
|
break :blk switch (spv.target.cpu.arch) {
|
||||||
|
.spirv32 => .Physical32,
|
||||||
|
.spirv64 => .Physical64,
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const memory_model: spec.MemoryModel = switch (target.os.tag) {
|
unreachable;
|
||||||
|
};
|
||||||
|
const memory_model: spec.MemoryModel = switch (spv.target.os.tag) {
|
||||||
.opencl => .OpenCL,
|
.opencl => .OpenCL,
|
||||||
.opengl => .GLSL450,
|
.vulkan, .opengl => .GLSL450,
|
||||||
.vulkan => .GLSL450,
|
|
||||||
else => unreachable,
|
else => unreachable,
|
||||||
};
|
};
|
||||||
|
try spv.sections.memory_model.emit(spv.gpa, .OpMemoryModel, .{
|
||||||
try spv.sections.memory_model.emit(gpa, .OpMemoryModel, .{
|
|
||||||
.addressing_model = addressing_model,
|
.addressing_model = addressing_model,
|
||||||
.memory_model = memory_model,
|
.memory_model = memory_model,
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user