SPIR-V: Compute backing integer bits

This commit is contained in:
Robin Voetter 2021-05-15 01:38:39 +02:00
parent de6df2bc12
commit 458c338aeb

View File

@ -2,6 +2,8 @@ const std = @import("std");
const Allocator = std.mem.Allocator;
const log = std.log.scoped(.codegen);
const Target = std.Target;
const spec = @import("spirv/spec.zig");
const Module = @import("../Module.zig");
const Decl = Module.Decl;
@ -63,6 +65,43 @@ pub const DeclGen = struct {
return error.AnalysisFail;
}
/// SPIR-V requires enabling specific integer sizes through capabilities, and so if they are not enabled, we need
/// to emulate them in other instructions/types. This function returns, given an integer bit width (signed or unsigned, sign
/// included), the width of the underlying type which represents it, given the enabled features for the current target.
/// If the result is `null`, the largest type the target platform supports natively is not able to perform computations using
/// that size. In this case, multiple elements of the largest type should be used.
/// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.
/// The result is valid to be used with OpTypeInt.
/// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
/// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
fn backingIntBits(self: *DeclGen, bits: u32) ?u32 {
// TODO: Figure out what to do with u0/i0.
std.debug.assert(bits != 0);
const target = self.module.getTarget();
// 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
const ints = [_]struct{ bits: u32, feature: ?Target.spirv.Feature } {
.{ .bits = 8, .feature = .Int8 },
.{ .bits = 16, .feature = .Int16 },
.{ .bits = 32, .feature = null },
.{ .bits = 64, .feature = .Int64 },
};
for (ints) |int| {
const has_feature = if (int.feature) |feature|
Target.spirv.featureSetHas(target.cpu.features, feature)
else
true;
if (bits <= int.bits and has_feature) {
return int.bits;
}
}
return null;
}
pub fn getOrGenType(self: *DeclGen, t: Type) !u32 {
// We can't use getOrPut here so we can recursively generate types.
if (self.types.get(t)) |already_generated| {
@ -76,10 +115,12 @@ pub const DeclGen = struct {
.Bool => try writeInstruction(&self.spv.types_and_globals, .OpTypeBool, &[_]u32{ result }),
.Int => {
const int_info = t.intInfo(self.module.getTarget());
// TODO: Capabilities.
const backing_bits = self.backingIntBits(int_info.bits) orelse
return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement fallback for integer of {} bits", .{ int_info.bits });
try writeInstruction(&self.spv.types_and_globals, .OpTypeInt, &[_]u32{
result,
int_info.bits,
backing_bits,
switch (int_info.signedness) {
.unsigned => 0,
.signed => 1,