authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 13:09:32+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 14:13:23+02:00
log10678af8768a8d8cad7640837d0ec354dc8c07bc
tree5540ab75ba4b703e76183ec87f087e4119e890b8
parentae2e21639a958b0bc8c085c8eac6733aaa933457

SPIR-V: genBinOp setup


1 files changed, 111 insertions(+), 7 deletions(-)

src/codegen/spirv.zig+111-7
......@@ -74,6 +74,44 @@ pub const DeclGen = struct {
7474 OutOfMemory
7575 };
7676
77 /// This structure is used to return information about a type typically used for arithmetic operations.
78 /// These types may either be integers, floats, or a vector of these. Most scalar operations also work on vectors,
79 /// so we can easily represent those as arithmetic types.
80 /// If the type is a scalar, 'inner type' refers to the scalar type. Otherwise, if its a vector, it refers
81 /// to the vector's element type.
82 const ArithmeticTypeInfo = struct {
83 /// A classification of the inner type.
84 const Class = enum {
85 /// A regular, **native**, integer operation.
86 /// This is only returned when the backend supports this int as a native type (when
87 /// the relevant capability is enabled).
88 integer,
89
90 /// A regular float. These are all required to be natively supported. Floating points for
91 /// which the relevant capability is not enabled are not emulated.
92 float,
93
94 /// An integer of a 'strange' size (which' bit size is not the same as its backing type. **Note**: this
95 /// may **also** include power-of-2 integers for which the relevant capability is not enabled), but still
96 /// within the limits of the largest natively supported integer type.
97 strange_integer,
98
99 /// An integer with more bits than the largest natively supported integer type.
100 composite_integer,
101 };
102
103 /// 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.
105 bits: u32,
106
107 /// Whether the type is a vector.
108 is_vector: bool,
109
110 /// A classification of the inner type. These four scenarios
111 /// will all have to be handled slightly different.
112 class: Class,
113 };
114
77115 fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) Error {
78116 @setCold(true);
79117 const src_loc = src.toSrcLocWithDecl(self.decl);
......@@ -96,16 +134,14 @@ pub const DeclGen = struct {
96134 /// that size. In this case, multiple elements of the largest type should be used.
97135 /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits.
98136 /// The result is valid to be used with OpTypeInt.
99 /// asserts `ty` is an integer.
100137 /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits).
101138 /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers).
102139 /// TODO: Should the result of this function be cached?
103 fn backingIntBits(self: *DeclGen, ty: Type) ?u32 {
140 fn backingIntBits(self: *DeclGen, bits: u32) ?u32 {
104141 const target = self.module.getTarget();
105 const int_info = ty.intInfo(target);
106142
107143 // TODO: Figure out what to do with u0/i0.
108 std.debug.assert(int_info.bits != 0);
144 std.debug.assert(bits != 0);
109145
110146 // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively.
111147 const ints = [_]struct{ bits: u32, feature: ?Target.spirv.Feature } {
......@@ -121,7 +157,7 @@ pub const DeclGen = struct {
121157 else
122158 true;
123159
124 if (int_info.bits <= int.bits and has_feature) {
160 if (bits <= int.bits and has_feature) {
125161 return int.bits;
126162 }
127163 }
......@@ -150,6 +186,34 @@ pub const DeclGen = struct {
150186 return self.backingIntBits(ty) == null;
151187 }
152188
189 fn arithmeticTypeInfo(self: *DeclGen, ty: Type) !ArithmeticTypeInfo {
190 const target = self.module.getTarget();
191
192 return switch (ty.zigTypeTag()) {
193 .Float => ArithmeticTypeInfo{ .bits = ty.floatBits(target), .is_vector = false, .class = .float },
194 .Int => blk: {
195 const int_info = ty.intInfo(target);
196 // TODO: Maybe it's useful to also return this value.
197 const maybe_backing_bits = self.backingIntBits(int_info.bits);
198 break :blk ArithmeticTypeInfo{
199 .bits = int_info.bits,
200 .is_vector = false,
201 .class = if (maybe_backing_bits) |backing_bits|
202 if (backing_bits == int_info.bits)
203 ArithmeticTypeInfo.Class.integer
204 else
205 ArithmeticTypeInfo.Class.strange_integer
206 else
207 .composite_integer
208 };
209 },
210 // As of yet, there is no vector support in the self-hosted compiler.
211 .Vector => self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement arithmeticTypeInfo for Vector", .{}),
212 // TODO: For which types is this the case?
213 else => self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement arithmeticTypeInfo for {}", .{ty}),
214 };
215 }
216
153217 /// Generate a constant representing `val`.
154218 /// TODO: Deduplication?
155219 fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 {
......@@ -218,7 +282,8 @@ pub const DeclGen = struct {
218282 .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{ result_id }),
219283 .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{ result_id }),
220284 .Int => {
221 const backing_bits = self.backingIntBits(ty) orelse {
285 const int_info = ty.intInfo(target);
286 const backing_bits = self.backingIntBits(int_info.bits) orelse {
222287 // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits.
223288 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement composite ints {}", .{ ty });
224289 };
......@@ -227,7 +292,10 @@ pub const DeclGen = struct {
227292 try writeInstruction(code, .OpTypeInt, &[_]u32{
228293 result_id,
229294 backing_bits,
230 @boolToInt(ty.isSignedInt()),
295 switch (int_info.signedness) {
296 .unsigned => 0,
297 .signed => 1,
298 },
231299 });
232300 },
233301 .Float => {
......@@ -346,6 +414,7 @@ pub const DeclGen = struct {
346414
347415 fn genInst(self: *DeclGen, inst: *Inst) !?u32 {
348416 return switch (inst.tag) {
417 .add => try self.genBinOp(inst.castTag(.add).?),
349418 .arg => self.genArg(),
350419 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them
351420 // throughout the IR.
......@@ -358,6 +427,41 @@ pub const DeclGen = struct {
358427 };
359428 }
360429
430 fn genBinOp(self: *DeclGen, inst: *Inst.BinOp) !u32 {
431 // TODO: Will lhs and rhs have the same type?
432 const lhs_id = try self.resolve(inst.lhs);
433 const rhs_id = try self.resolve(inst.rhs);
434
435 const binop_result_id = self.spv.allocResultId();
436 const result_type_id = try self.getOrGenType(inst.base.ty);
437
438 // TODO: Is the result the same as the argument types?
439 // This is supposed to be the case for SPIR-V.
440 std.debug.assert(inst.base.ty.eql(inst.lhs.ty) and inst.base.ty.eql(inst.rhs.ty));
441
442 // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float
443 // versions of operations require different opcodes.
444 const info = try self.arithmeticTypeInfo(inst.base.ty);
445
446 if (info.class == .composite_integer)
447 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: binary operations for composite integers", .{});
448
449 // Fetch the integer and float opcodes for each operation.
450 // Doing it this way removes a bit of code clutter.
451 const opcodes: [2]spec.Opcode = switch (inst.base.tag) {
452 .add => .{.OpIAdd, .OpFAdd},
453 else => unreachable,
454 };
455
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 });
458
459 if (info.class != .strange_integer)
460 return binop_result_id;
461
462 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: strange integer operation mask", .{});
463 }
464
361465 fn genArg(self: *DeclGen) u32 {
362466 defer self.next_arg_index += 1;
363467 return self.args.items[self.next_arg_index];