authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 13:32:32+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 14:20:12+02:00
log4735e95d1699c90e821655bdbe0afbb1044738ea
tree8eb7dbbc764378fca05c8babc4d72742dda6495d
parent10678af8768a8d8cad7640837d0ec354dc8c07bc

SPIR-V: More binary operations


1 files changed, 39 insertions(+), 14 deletions(-)

src/codegen/spirv.zig+39-14
...@@ -4,6 +4,8 @@ const Target = std.Target;...@@ -4,6 +4,8 @@ const Target = std.Target;
4const log = std.log.scoped(.codegen);4const log = std.log.scoped(.codegen);
55
6const spec = @import("spirv/spec.zig");6const spec = @import("spirv/spec.zig");
7const Opcode = spec.Opcode;
8
7const Module = @import("../Module.zig");9const Module = @import("../Module.zig");
8const Decl = Module.Decl;10const Decl = Module.Decl;
9const Type = @import("../type.zig").Type;11const Type = @import("../type.zig").Type;
...@@ -15,12 +17,12 @@ const Inst = ir.Inst;...@@ -15,12 +17,12 @@ const Inst = ir.Inst;
15pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);17pub const TypeMap = std.HashMap(Type, u32, Type.hash, Type.eql, std.hash_map.default_max_load_percentage);
16pub const ValueMap = std.AutoHashMap(*Inst, u32);18pub const ValueMap = std.AutoHashMap(*Inst, u32);
1719
18pub fn writeOpcode(code: *std.ArrayList(u32), opcode: spec.Opcode, arg_count: u32) !void {20pub fn writeOpcode(code: *std.ArrayList(u32), opcode: Opcode, arg_count: u32) !void {
19 const word_count = arg_count + 1;21 const word_count = arg_count + 1;
20 try code.append((word_count << 16) | @enumToInt(opcode));22 try code.append((word_count << 16) | @enumToInt(opcode));
21}23}
2224
23pub fn writeInstruction(code: *std.ArrayList(u32), opcode: spec.Opcode, args: []const u32) !void {25pub fn writeInstruction(code: *std.ArrayList(u32), opcode: Opcode, args: []const u32) !void {
24 try writeOpcode(code, opcode, @intCast(u32, args.len));26 try writeOpcode(code, opcode, @intCast(u32, args.len));
25 try code.appendSlice(args);27 try code.appendSlice(args);
26}28}
...@@ -102,11 +104,14 @@ pub const DeclGen = struct {...@@ -102,11 +104,14 @@ pub const DeclGen = struct {
102104
103 /// The number of bits in the inner type.105 /// The number of bits in the inner type.
104 /// Note: this is the actual number of bits of the type, not the size of the backing integer.106 /// Note: this is the actual number of bits of the type, not the size of the backing integer.
105 bits: u32,107 bits: u16,
106108
107 /// Whether the type is a vector.109 /// Whether the type is a vector.
108 is_vector: bool,110 is_vector: bool,
109111
112 /// Whether the inner type is signed. Only relevant for integers.
113 signedness: std.builtin.Signedness,
114
110 /// A classification of the inner type. These four scenarios115 /// A classification of the inner type. These four scenarios
111 /// will all have to be handled slightly different.116 /// will all have to be handled slightly different.
112 class: Class,117 class: Class,
...@@ -137,14 +142,14 @@ pub const DeclGen = struct {...@@ -137,14 +142,14 @@ pub const DeclGen = struct {
137 /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).142 /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
138 /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).143 /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
139 /// TODO: Should the result of this function be cached?144 /// TODO: Should the result of this function be cached?
140 fn backingIntBits(self: *DeclGen, bits: u32) ?u32 {145 fn backingIntBits(self: *DeclGen, bits: u16) ?u16 {
141 const target = self.module.getTarget();146 const target = self.module.getTarget();
142147
143 // TODO: Figure out what to do with u0/i0.148 // TODO: Figure out what to do with u0/i0.
144 std.debug.assert(bits != 0);149 std.debug.assert(bits != 0);
145150
146 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.151 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
147 const ints = [_]struct{ bits: u32, feature: ?Target.spirv.Feature } {152 const ints = [_]struct{ bits: u16, feature: ?Target.spirv.Feature } {
148 .{ .bits = 8, .feature = .Int8 },153 .{ .bits = 8, .feature = .Int8 },
149 .{ .bits = 16, .feature = .Int16 },154 .{ .bits = 16, .feature = .Int16 },
150 .{ .bits = 32, .feature = null },155 .{ .bits = 32, .feature = null },
...@@ -171,7 +176,7 @@ pub const DeclGen = struct {...@@ -171,7 +176,7 @@ pub const DeclGen = struct {
171 /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there176 /// In theory that could also be used, but since the spec says that it only guarantees support up to 32-bit ints there
172 /// is no way of knowing whether those are actually supported.177 /// is no way of knowing whether those are actually supported.
173 /// TODO: Maybe this should be cached?178 /// TODO: Maybe this should be cached?
174 fn largestSupportedIntBits(self: *DeclGen) u32 {179 fn largestSupportedIntBits(self: *DeclGen) u16 {
175 const target = self.module.getTarget();180 const target = self.module.getTarget();
176 return if (Target.spirv.featureSetHas(target.cpu.features, .Int64))181 return if (Target.spirv.featureSetHas(target.cpu.features, .Int64))
177 64182 64
...@@ -190,7 +195,12 @@ pub const DeclGen = struct {...@@ -190,7 +195,12 @@ pub const DeclGen = struct {
190 const target = self.module.getTarget();195 const target = self.module.getTarget();
191196
192 return switch (ty.zigTypeTag()) {197 return switch (ty.zigTypeTag()) {
193 .Float => ArithmeticTypeInfo{ .bits = ty.floatBits(target), .is_vector = false, .class = .float },198 .Float => ArithmeticTypeInfo{
199 .bits = ty.floatBits(target),
200 .is_vector = false,
201 .signedness = .signed, // I guess technically it is.
202 .class = .float
203 },
194 .Int => blk: {204 .Int => blk: {
195 const int_info = ty.intInfo(target);205 const int_info = ty.intInfo(target);
196 // TODO: Maybe it's useful to also return this value.206 // TODO: Maybe it's useful to also return this value.
...@@ -198,6 +208,7 @@ pub const DeclGen = struct {...@@ -198,6 +208,7 @@ pub const DeclGen = struct {
198 break :blk ArithmeticTypeInfo{208 break :blk ArithmeticTypeInfo{
199 .bits = int_info.bits,209 .bits = int_info.bits,
200 .is_vector = false,210 .is_vector = false,
211 .signedness = int_info.signedness,
201 .class = if (maybe_backing_bits) |backing_bits|212 .class = if (maybe_backing_bits) |backing_bits|
202 if (backing_bits == int_info.bits)213 if (backing_bits == int_info.bits)
203 ArithmeticTypeInfo.Class.integer214 ArithmeticTypeInfo.Class.integer
...@@ -228,7 +239,7 @@ pub const DeclGen = struct {...@@ -228,7 +239,7 @@ pub const DeclGen = struct {
228239
229 switch (ty.zigTypeTag()) {240 switch (ty.zigTypeTag()) {
230 .Bool => {241 .Bool => {
231 const opcode: spec.Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;242 const opcode: Opcode = if (val.toBool()) .OpConstantTrue else .OpConstantFalse;
232 try writeInstruction(code, opcode, &[_]u32{ result_type_id, result_id });243 try writeInstruction(code, opcode, &[_]u32{ result_type_id, result_id });
233 },244 },
234 .Float => {245 .Float => {
...@@ -414,7 +425,10 @@ pub const DeclGen = struct {...@@ -414,7 +425,10 @@ pub const DeclGen = struct {
414425
415 fn genInst(self: *DeclGen, inst: *Inst) !?u32 {426 fn genInst(self: *DeclGen, inst: *Inst) !?u32 {
416 return switch (inst.tag) {427 return switch (inst.tag) {
417 .add => try self.genBinOp(inst.castTag(.add).?),428 .add, .addwrap => try self.genBinOp(inst.castTag(.add).?),
429 .sub, .subwrap => try self.genBinOp(inst.castTag(.sub).?),
430 .mul, .mulwrap => try self.genBinOp(inst.castTag(.mul).?),
431 .div => try self.genBinOp(inst.castTag(.div).?),
418 .arg => self.genArg(),432 .arg => self.genArg(),
419 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them433 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them
420 // throughout the IR.434 // throughout the IR.
...@@ -446,16 +460,27 @@ pub const DeclGen = struct {...@@ -446,16 +460,27 @@ pub const DeclGen = struct {
446 if (info.class == .composite_integer)460 if (info.class == .composite_integer)
447 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: binary operations for composite integers", .{});461 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: binary operations for composite integers", .{});
448462
449 // Fetch the integer and float opcodes for each operation.463 const is_float = info.class == .float;
450 // Doing it this way removes a bit of code clutter.464 const is_signed = info.signedness == .signed;
451 const opcodes: [2]spec.Opcode = switch (inst.base.tag) {465 // **Note**: All these operations must be valid for vectors of floats and integers as well!
452 .add => .{.OpIAdd, .OpFAdd},466 const opcode = switch (inst.base.tag) {
467 // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,
468 // we can just switch on both cases here.
469 .add, .addwrap => if (is_float) Opcode.OpFAdd else Opcode.OpIAdd,
470 .sub, .subwrap => if (is_float) Opcode.OpFSub else Opcode.OpISub,
471 .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul,
472 // TODO: Trap if divisor is 0?
473 // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful.
474 .div => if (is_float) Opcode.OpFDiv else if (is_signed) Opcode.OpSDiv else Opcode.OpUDiv,
475
453 else => unreachable,476 else => unreachable,
454 };477 };
455478
456 const opcode = if (info.class == .float) opcodes[1] else opcodes[0];
457 try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, binop_result_id, lhs_id, rhs_id });479 try writeInstruction(&self.spv.fn_decls, opcode, &[_]u32{ result_type_id, binop_result_id, lhs_id, rhs_id });
458480
481 // TODO: Trap on overflow? Probably going to be annoying.
482 // TODO: Look into NoSignedWrap/NoUnsignedWrap extensions.
483
459 if (info.class != .strange_integer)484 if (info.class != .strange_integer)
460 return binop_result_id;485 return binop_result_id;
461486