| ... | ... | @@ -74,6 +74,44 @@ pub const DeclGen = struct { |
| 74 | 74 | OutOfMemory |
| 75 | 75 | }; |
| 76 | 76 | |
| 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 | |
| 77 | 115 | fn fail(self: *DeclGen, src: LazySrcLoc, comptime format: []const u8, args: anytype) Error { |
| 78 | 116 | @setCold(true); |
| 79 | 117 | const src_loc = src.toSrcLocWithDecl(self.decl); |
| ... | ... | @@ -96,16 +134,14 @@ pub const DeclGen = struct { |
| 96 | 134 | /// that size. In this case, multiple elements of the largest type should be used. |
| 97 | 135 | /// The backing type will be chosen as the smallest supported integer larger or equal to it in number of bits. |
| 98 | 136 | /// The result is valid to be used with OpTypeInt. |
| 99 | | /// asserts `ty` is an integer. |
| 100 | 137 | /// TODO: The extension SPV_INTEL_arbitrary_precision_integers allows any integer size (at least up to 32 bits). |
| 101 | 138 | /// TODO: This probably needs an ABI-version as well (especially in combination with SPV_INTEL_arbitrary_precision_integers). |
| 102 | 139 | /// 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 { |
| 104 | 141 | const target = self.module.getTarget(); |
| 105 | | const int_info = ty.intInfo(target); |
| 106 | 142 | |
| 107 | 143 | // TODO: Figure out what to do with u0/i0. |
| 108 | | std.debug.assert(int_info.bits != 0); |
| 144 | std.debug.assert(bits != 0); |
| 109 | 145 | |
| 110 | 146 | // 8, 16 and 64-bit integers require the Int8, Int16 and Inr64 capabilities respectively. |
| 111 | 147 | const ints = [_]struct{ bits: u32, feature: ?Target.spirv.Feature } { |
| ... | ... | @@ -121,7 +157,7 @@ pub const DeclGen = struct { |
| 121 | 157 | else |
| 122 | 158 | true; |
| 123 | 159 | |
| 124 | | if (int_info.bits <= int.bits and has_feature) { |
| 160 | if (bits <= int.bits and has_feature) { |
| 125 | 161 | return int.bits; |
| 126 | 162 | } |
| 127 | 163 | } |
| ... | ... | @@ -150,6 +186,34 @@ pub const DeclGen = struct { |
| 150 | 186 | return self.backingIntBits(ty) == null; |
| 151 | 187 | } |
| 152 | 188 | |
| 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 | |
| 153 | 217 | /// Generate a constant representing `val`. |
| 154 | 218 | /// TODO: Deduplication? |
| 155 | 219 | fn genConstant(self: *DeclGen, ty: Type, val: Value) Error!u32 { |
| ... | ... | @@ -218,7 +282,8 @@ pub const DeclGen = struct { |
| 218 | 282 | .Void => try writeInstruction(code, .OpTypeVoid, &[_]u32{ result_id }), |
| 219 | 283 | .Bool => try writeInstruction(code, .OpTypeBool, &[_]u32{ result_id }), |
| 220 | 284 | .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 { |
| 222 | 287 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. |
| 223 | 288 | return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: implement composite ints {}", .{ ty }); |
| 224 | 289 | }; |
| ... | ... | @@ -227,7 +292,10 @@ pub const DeclGen = struct { |
| 227 | 292 | try writeInstruction(code, .OpTypeInt, &[_]u32{ |
| 228 | 293 | result_id, |
| 229 | 294 | backing_bits, |
| 230 | | @boolToInt(ty.isSignedInt()), |
| 295 | switch (int_info.signedness) { |
| 296 | .unsigned => 0, |
| 297 | .signed => 1, |
| 298 | }, |
| 231 | 299 | }); |
| 232 | 300 | }, |
| 233 | 301 | .Float => { |
| ... | ... | @@ -346,6 +414,7 @@ pub const DeclGen = struct { |
| 346 | 414 | |
| 347 | 415 | fn genInst(self: *DeclGen, inst: *Inst) !?u32 { |
| 348 | 416 | return switch (inst.tag) { |
| 417 | .add => try self.genBinOp(inst.castTag(.add).?), |
| 349 | 418 | .arg => self.genArg(), |
| 350 | 419 | // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them |
| 351 | 420 | // throughout the IR. |
| ... | ... | @@ -358,6 +427,41 @@ pub const DeclGen = struct { |
| 358 | 427 | }; |
| 359 | 428 | } |
| 360 | 429 | |
| 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 | |
| 361 | 465 | fn genArg(self: *DeclGen) u32 { |
| 362 | 466 | defer self.next_arg_index += 1; |
| 363 | 467 | return self.args.items[self.next_arg_index]; |