From 458c338aeb0ce72e772c19efcfc633f908ee91b3 Mon Sep 17 00:00:00 2001 From: Robin Voetter Date: Sat, 15 May 2021 01:38:39 +0200 Subject: [PATCH] SPIR-V: Compute backing integer bits --- src/codegen/spirv.zig | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/src/codegen/spirv.zig b/src/codegen/spirv.zig index 9ceaf107d8..fd153533c8 100644 --- a/src/codegen/spirv.zig +++ b/src/codegen/spirv.zig @@ -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,