| ... | ... | @@ -348,7 +348,7 @@ pub const DeclGen = struct { |
| 348 | 348 | const int_info = ty.intInfo(target); |
| 349 | 349 | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| 350 | 350 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. |
| 351 | | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement composite ints {}", .{ty}); |
| 351 | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement composite int {}", .{ty}); |
| 352 | 352 | }; |
| 353 | 353 | |
| 354 | 354 | // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here. |
| ... | ... | @@ -487,12 +487,12 @@ pub const DeclGen = struct { |
| 487 | 487 | .bit_and => try self.genBinOp(inst.castTag(.bit_and).?), |
| 488 | 488 | .bit_or => try self.genBinOp(inst.castTag(.bit_or).?), |
| 489 | 489 | .xor => try self.genBinOp(inst.castTag(.xor).?), |
| 490 | | .cmp_eq => try self.genBinOp(inst.castTag(.cmp_eq).?), |
| 491 | | .cmp_neq => try self.genBinOp(inst.castTag(.cmp_neq).?), |
| 492 | | .cmp_gt => try self.genBinOp(inst.castTag(.cmp_gt).?), |
| 493 | | .cmp_gte => try self.genBinOp(inst.castTag(.cmp_gte).?), |
| 494 | | .cmp_lt => try self.genBinOp(inst.castTag(.cmp_lt).?), |
| 495 | | .cmp_lte => try self.genBinOp(inst.castTag(.cmp_lte).?), |
| 490 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?), |
| 491 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?), |
| 492 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?), |
| 493 | .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?), |
| 494 | .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?), |
| 495 | .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?), |
| 496 | 496 | .bool_and => try self.genBinOp(inst.castTag(.bool_and).?), |
| 497 | 497 | .bool_or => try self.genBinOp(inst.castTag(.bool_or).?), |
| 498 | 498 | .not => try self.genUnOp(inst.castTag(.not).?), |
| ... | ... | @@ -504,7 +504,7 @@ pub const DeclGen = struct { |
| 504 | 504 | .ret => self.genRet(inst.castTag(.ret).?), |
| 505 | 505 | .retvoid => self.genRetVoid(), |
| 506 | 506 | .unreach => self.genUnreach(), |
| 507 | | else => self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: implement inst {}", .{inst.tag}), |
| 507 | else => self.fail(inst.src, "TODO: SPIR-V backend: implement inst {s}", .{@tagName(inst.tag)}), |
| 508 | 508 | }; |
| 509 | 509 | } |
| 510 | 510 | |
| ... | ... | @@ -528,13 +528,14 @@ pub const DeclGen = struct { |
| 528 | 528 | const info = try self.arithmeticTypeInfo(inst.lhs.ty); |
| 529 | 529 | |
| 530 | 530 | if (info.class == .composite_integer) |
| 531 | | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: binary operations for composite integers", .{}); |
| 531 | return self.fail(inst.base.src, "TODO: SPIR-V backend: binary operations for composite integers", .{}); |
| 532 | else if (info.class == .strange_integer) |
| 533 | return self.fail(inst.base.src, "TODO: SPIR-V backend: binary operations for strange integers", .{}); |
| 532 | 534 | |
| 533 | 535 | const is_bool = info.class == .bool; |
| 534 | 536 | const is_float = info.class == .float; |
| 535 | 537 | const is_signed = info.signedness == .signed; |
| 536 | | // **Note**: All these operations must be valid for vectors of floats, integers and bools as well! |
| 537 | | // For floating points, we generally want ordered operations (which return false if either operand is nan). |
| 538 | // **Note**: All these operations must be valid for vectors as well! |
| 538 | 539 | const opcode = switch (inst.base.tag) { |
| 539 | 540 | // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers, |
| 540 | 541 | // we can just switch on both cases here. |
| ... | ... | @@ -551,16 +552,6 @@ pub const DeclGen = struct { |
| 551 | 552 | .bit_and => Opcode.OpBitwiseAnd, |
| 552 | 553 | .bit_or => Opcode.OpBitwiseOr, |
| 553 | 554 | .xor => Opcode.OpBitwiseXor, |
| 554 | | // Int/bool/float -> bool operations. |
| 555 | | .cmp_eq => if (is_float) Opcode.OpFOrdEqual else if (is_bool) Opcode.OpLogicalEqual else Opcode.OpIEqual, |
| 556 | | .cmp_neq => if (is_float) Opcode.OpFOrdNotEqual else if (is_bool) Opcode.OpLogicalNotEqual else Opcode.OpINotEqual, |
| 557 | | // Int/float -> bool operations. |
| 558 | | // TODO: Verify that these OpFOrd type operations produce the right value. |
| 559 | | // TODO: Is there a more fundamental difference between OpU and OpS operations here than just the type? |
| 560 | | .cmp_gt => if (is_float) Opcode.OpFOrdGreaterThan else if (is_signed) Opcode.OpSGreaterThan else Opcode.OpUGreaterThan, |
| 561 | | .cmp_gte => if (is_float) Opcode.OpFOrdGreaterThanEqual else if (is_signed) Opcode.OpSGreaterThanEqual else Opcode.OpUGreaterThanEqual, |
| 562 | | .cmp_lt => if (is_float) Opcode.OpFOrdLessThan else if (is_signed) Opcode.OpSLessThan else Opcode.OpULessThan, |
| 563 | | .cmp_lte => if (is_float) Opcode.OpFOrdLessThanEqual else if (is_signed) Opcode.OpSLessThanEqual else Opcode.OpULessThanEqual, |
| 564 | 555 | // Bool -> bool operations. |
| 565 | 556 | .bool_and => Opcode.OpLogicalAnd, |
| 566 | 557 | .bool_or => Opcode.OpLogicalOr, |
| ... | ... | @@ -575,7 +566,51 @@ pub const DeclGen = struct { |
| 575 | 566 | if (info.class != .strange_integer) |
| 576 | 567 | return result_id; |
| 577 | 568 | |
| 578 | | return self.fail(.{ .node_offset = 0 }, "TODO: SPIR-V backend: strange integer operation mask", .{}); |
| 569 | return self.fail(inst.base.src, "TODO: SPIR-V backend: strange integer operation mask", .{}); |
| 570 | } |
| 571 | |
| 572 | fn genCmp(self: *DeclGen, inst: *Inst.BinOp) !ResultId { |
| 573 | const lhs_id = try self.resolve(inst.lhs); |
| 574 | const rhs_id = try self.resolve(inst.rhs); |
| 575 | |
| 576 | const result_id = self.spv.allocResultId(); |
| 577 | const result_type_id = try self.getOrGenType(inst.base.ty); |
| 578 | |
| 579 | // All of these operations should be 2 equal types -> bool |
| 580 | std.debug.assert(inst.rhs.ty.eql(inst.lhs.ty)); |
| 581 | std.debug.assert(inst.base.ty.tag() == .bool); |
| 582 | |
| 583 | // Comparisons are generally applicable to both scalar and vector operations in SPIR-V, but int and float |
| 584 | // versions of operations require different opcodes. |
| 585 | // Since inst.base.ty is always bool and so not very useful, and because both arguments must be the same, just get the info |
| 586 | // from either of the operands. |
| 587 | const info = try self.arithmeticTypeInfo(inst.lhs.ty); |
| 588 | |
| 589 | if (info.class == .composite_integer) |
| 590 | return self.fail(inst.base.src, "TODO: SPIR-V backend: binary operations for composite integers", .{}); |
| 591 | else if (info.class == .strange_integer) |
| 592 | return self.fail(inst.base.src, "TODO: SPIR-V backend: comparison for strange integers", .{}); |
| 593 | |
| 594 | const is_bool = info.class == .bool; |
| 595 | const is_float = info.class == .float; |
| 596 | const is_signed = info.signedness == .signed; |
| 597 | |
| 598 | // **Note**: All these operations must be valid for vectors as well! |
| 599 | // For floating points, we generally want ordered operations (which return false if either operand is nan). |
| 600 | const opcode = switch (inst.base.tag) { |
| 601 | .cmp_eq => if (is_float) Opcode.OpFOrdEqual else if (is_bool) Opcode.OpLogicalEqual else Opcode.OpIEqual, |
| 602 | .cmp_neq => if (is_float) Opcode.OpFOrdNotEqual else if (is_bool) Opcode.OpLogicalNotEqual else Opcode.OpINotEqual, |
| 603 | // TODO: Verify that these OpFOrd type operations produce the right value. |
| 604 | // TODO: Is there a more fundamental difference between OpU and OpS operations here than just the type? |
| 605 | .cmp_gt => if (is_float) Opcode.OpFOrdGreaterThan else if (is_signed) Opcode.OpSGreaterThan else Opcode.OpUGreaterThan, |
| 606 | .cmp_gte => if (is_float) Opcode.OpFOrdGreaterThanEqual else if (is_signed) Opcode.OpSGreaterThanEqual else Opcode.OpUGreaterThanEqual, |
| 607 | .cmp_lt => if (is_float) Opcode.OpFOrdLessThan else if (is_signed) Opcode.OpSLessThan else Opcode.OpULessThan, |
| 608 | .cmp_lte => if (is_float) Opcode.OpFOrdLessThanEqual else if (is_signed) Opcode.OpSLessThanEqual else Opcode.OpULessThanEqual, |
| 609 | else => unreachable, |
| 610 | }; |
| 611 | |
| 612 | try writeInstruction(&self.spv.binary.fn_decls, opcode, &[_]Word{ result_type_id, result_id, lhs_id, rhs_id }); |
| 613 | return result_id; |
| 579 | 614 | } |
| 580 | 615 | |
| 581 | 616 | fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !ResultId { |