Commit 4735e95d16
Changed files (1)
src
codegen
src/codegen/spirv.zig
@@ -4,6 +4,8 @@ const Target = std.Target;
const log = std.log.scoped(.codegen);
const spec = @import("spirv/spec.zig");
+const Opcode = spec.Opcode;
+
const Module = @import("../Module.zig");
const Decl = Module.Decl;
const Type = @import("../type.zig").Type;
@@ -15,12 +17,12 @@ const Inst = ir.Inst;
pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
pub const ValueMap = std.AutoHashMap(*Inst, u32);
-pub fn writeOpcode(code: *std.ArrayList(u32), opcode: spec.Opcode, arg_count: u32) !void {
+pub fn writeOpcode(code: *std.ArrayList(u32), opcode: Opcode, arg_count: u32) !void {
const word_count = arg_count + 1;
try code.append((word_count << 16) | @enumToInt(opcode));
}
-pub fn writeInstruction(code: *std.ArrayList(u32), opcode: spec.Opcode, args: []const u32) !void {
+pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const u32) !void {
try writeOpcode(code, opcode, @intCast(u32, args.len));
try code.appendSlice(args);
}
@@ -102,11 +104,14 @@ pub const DeclGen = struct {
/// The number of bits in the inner type.
/// Note: this is the actual number of bits of the type, not the size of the backing integer.
- bits: u32,
+ bits: u16,
/// Whether the type is a vector.
is_vector: bool,
+ /// Whether the inner type is signed. Only relevant for integers.
+ signedness: std.builtin.Signedness,
+
/// A classification of the inner type. These four scenarios
/// will all have to be handled slightly different.
class: Class,
@@ -137,14 +142,14 @@ pub const DeclGen = struct {
/// 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).
/// TODO: Should the result of this function be cached?
- fn backingIntBits(self: *DeclGen, bits: u32) ?u32 {
+ fn backingIntBits(self: *DeclGen, bits: u16) ?u16 {
const target = self.module.getTarget();
// TODO: Figure out what to do with u0/i0.
std.debug.assert(bits != 0);
// 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
- const ints = [_]struct{ bits: u32, feature: ?Target.spirv.Feature } {
+ const ints = [_]struct{ bits: u16, feature: ?Target.spirv.Feature } {
.{ .bits = 8, .feature = .Int8 },
.{ .bits = 16, .feature = .Int16 },
.{ .bits = 32, .feature = null },
@@ -171,7 +176,7 @@ pub const DeclGen = struct {
/// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there
/// is no way of knowing whether those are actually supported.
/// TODO: Maybe this should be cached?
- fn largestSupportedIntBits(self: *DeclGen) u32 {
+ fn largestSupportedIntBits(self: *DeclGen) u16 {
const target = self.module.getTarget();
return if (Target.spirv.featureSetHas(target.cpu.features, .Int64))
64
@@ -190,7 +195,12 @@ pub const DeclGen = struct {
const target = self.module.getTarget();
return switch (ty.zigTypeTag()) {
- .Float => ArithmeticTypeInfo{ .bits = ty.floatBits(target), .is_vector = false, .class = .float },
+ .Float => ArithmeticTypeInfo{
+ .bits = ty.floatBits(target),
+ .is_vector = false,
+ .signedness = .signed, // I guess technically it is.
+ .class = .float
+ },
.Int => blk: {
const int_info = ty.intInfo(target);
// TODO: Maybe it's useful to also return this value.
@@ -198,6 +208,7 @@ pub const DeclGen = struct {
break :blk ArithmeticTypeInfo{
.bits = int_info.bits,
.is_vector = false,
+ .signedness = int_info.signedness,
.class = if (maybe_backing_bits) |backing_bits|
if (backing_bits == int_info.bits)
ArithmeticTypeInfo.Class.integer
@@ -228,7 +239,7 @@ pub const DeclGen = struct {
switch (ty.zigTypeTag()) {
.Bool => {
- const opcode: spec.Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;
+ const opcode: Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;
try writeInstruction(code, opcode, &[_]u32{ result_type_id, result_id });
},
.Float => {
@@ -414,7 +425,10 @@ pub const DeclGen = struct {
fn genInst(self: *DeclGen, inst: *Inst) !?u32 {
return switch (inst.tag) {
- .add => try self.genBinOp(inst.castTag(.add).?),
+ .add, .addwrap => try self.genBinOp(inst.castTag(.add).?),
+ .sub, .subwrap => try self.genBinOp(inst.castTag(.sub).?),
+ .mul, .mulwrap => try self.genBinOp(inst.castTag(.mul).?),
+ .div => try self.genBinOp(inst.castTag(.div).?),
.arg => self.genArg(),
// TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them
// throughout the IR.
@@ -446,16 +460,27 @@ pub const DeclGen = struct {
if (info.class == .composite_integer)
return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: binary operations for composite integers", .{});
- // Fetch the integer and float opcodes for each operation.
- // Doing it this way removes a bit of code clutter.
- const opcodes: [2]spec.Opcode = switch (inst.base.tag) {
- .add => .{.OpIAdd, .OpFAdd},
+ const is_float = info.class == .float;
+ const is_signed = info.signedness == .signed;
+ // **Note**: All these operations must be valid for vectors of floats and integers as well!
+ const opcode = switch (inst.base.tag) {
+ // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,
+ // we can just switch on both cases here.
+ .add, .addwrap => if (is_float) Opcode.OpFAdd else Opcode.OpIAdd,
+ .sub, .subwrap => if (is_float) Opcode.OpFSub else Opcode.OpISub,
+ .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul,
+ // TODO: Trap if divisor is 0?
+ // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful.
+ .div => if (is_float) Opcode.OpFDiv else if (is_signed) Opcode.OpSDiv else Opcode.OpUDiv,
+
else => unreachable,
};
- const opcode = if (info.class == .float) opcodes[1] else opcodes[0];
try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, binop_result_id, lhs_id, rhs_id });
+ // TODO: Trap on overflow? Probably going to be annoying.
+ // TODO: Look into NoSignedWrap/NoUnsignedWrap extensions.
+
if (info.class != .strange_integer)
return binop_result_id;