| ... | @@ -348,7 +348,7 @@ pub const DeclGen = struct { | ... | @@ -348,7 +348,7 @@ pub const DeclGen = struct { |
| 348 | const int_info = ty.intInfo(target); | 348 | const int_info = ty.intInfo(target); |
| 349 | const backing_bits = self.backingIntBits(int_info.bits) orelse { | 349 | const backing_bits = self.backingIntBits(int_info.bits) orelse { |
| 350 | // Integers too big for any native type are represented as "composite integers": An array of largestSupportedIntBits. | 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 | // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here. | 354 | // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here. |
| ... | @@ -487,12 +487,12 @@ pub const DeclGen = struct { | ... | @@ -487,12 +487,12 @@ pub const DeclGen = struct { |
| 487 | .bit_and => try self.genBinOp(inst.castTag(.bit_and).?), | 487 | .bit_and => try self.genBinOp(inst.castTag(.bit_and).?), |
| 488 | .bit_or => try self.genBinOp(inst.castTag(.bit_or).?), | 488 | .bit_or => try self.genBinOp(inst.castTag(.bit_or).?), |
| 489 | .xor => try self.genBinOp(inst.castTag(.xor).?), | 489 | .xor => try self.genBinOp(inst.castTag(.xor).?), |
| 490 | .cmp_eq => try self.genBinOp(inst.castTag(.cmp_eq).?), | 490 | .cmp_eq => try self.genCmp(inst.castTag(.cmp_eq).?), |
| 491 | .cmp_neq => try self.genBinOp(inst.castTag(.cmp_neq).?), | 491 | .cmp_neq => try self.genCmp(inst.castTag(.cmp_neq).?), |
| 492 | .cmp_gt => try self.genBinOp(inst.castTag(.cmp_gt).?), | 492 | .cmp_gt => try self.genCmp(inst.castTag(.cmp_gt).?), |
| 493 | .cmp_gte => try self.genBinOp(inst.castTag(.cmp_gte).?), | 493 | .cmp_gte => try self.genCmp(inst.castTag(.cmp_gte).?), |
| 494 | .cmp_lt => try self.genBinOp(inst.castTag(.cmp_lt).?), | 494 | .cmp_lt => try self.genCmp(inst.castTag(.cmp_lt).?), |
| 495 | .cmp_lte => try self.genBinOp(inst.castTag(.cmp_lte).?), | 495 | .cmp_lte => try self.genCmp(inst.castTag(.cmp_lte).?), |
| 496 | .bool_and => try self.genBinOp(inst.castTag(.bool_and).?), | 496 | .bool_and => try self.genBinOp(inst.castTag(.bool_and).?), |
| 497 | .bool_or => try self.genBinOp(inst.castTag(.bool_or).?), | 497 | .bool_or => try self.genBinOp(inst.castTag(.bool_or).?), |
| 498 | .not => try self.genUnOp(inst.castTag(.not).?), | 498 | .not => try self.genUnOp(inst.castTag(.not).?), |
| ... | @@ -504,7 +504,7 @@ pub const DeclGen = struct { | ... | @@ -504,7 +504,7 @@ pub const DeclGen = struct { |
| 504 | .ret => self.genRet(inst.castTag(.ret).?), | 504 | .ret => self.genRet(inst.castTag(.ret).?), |
| 505 | .retvoid => self.genRetVoid(), | 505 | .retvoid => self.genRetVoid(), |
| 506 | .unreach => self.genUnreach(), | 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,13 +528,14 @@ pub const DeclGen = struct { |
| 528 | const info = try self.arithmeticTypeInfo(inst.lhs.ty); | 528 | const info = try self.arithmeticTypeInfo(inst.lhs.ty); |
| 529 | | 529 | |
| 530 | if (info.class == .composite_integer) | 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 | const is_bool = info.class == .bool; | 535 | const is_bool = info.class == .bool; |
| 534 | const is_float = info.class == .float; | 536 | const is_float = info.class == .float; |
| 535 | const is_signed = info.signedness == .signed; | 537 | const is_signed = info.signedness == .signed; |
| 536 | // **Note**: All these operations must be valid for vectors of floats, integers and bools as well! | 538 | // **Note**: All these operations must be valid for vectors as well! |
| 537 | // For floating points, we generally want ordered operations (which return false if either operand is nan). | | |
| 538 | const opcode = switch (inst.base.tag) { | 539 | const opcode = switch (inst.base.tag) { |
| 539 | // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers, | 540 | // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers, |
| 540 | // we can just switch on both cases here. | 541 | // we can just switch on both cases here. |
| ... | @@ -551,16 +552,6 @@ pub const DeclGen = struct { | ... | @@ -551,16 +552,6 @@ pub const DeclGen = struct { |
| 551 | .bit_and => Opcode.OpBitwiseAnd, | 552 | .bit_and => Opcode.OpBitwiseAnd, |
| 552 | .bit_or => Opcode.OpBitwiseOr, | 553 | .bit_or => Opcode.OpBitwiseOr, |
| 553 | .xor => Opcode.OpBitwiseXor, | 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 | // Bool -> bool operations. | 555 | // Bool -> bool operations. |
| 565 | .bool_and => Opcode.OpLogicalAnd, | 556 | .bool_and => Opcode.OpLogicalAnd, |
| 566 | .bool_or => Opcode.OpLogicalOr, | 557 | .bool_or => Opcode.OpLogicalOr, |
| ... | @@ -575,7 +566,51 @@ pub const DeclGen = struct { | ... | @@ -575,7 +566,51 @@ pub const DeclGen = struct { |
| 575 | if (info.class != .strange_integer) | 566 | if (info.class != .strange_integer) |
| 576 | return result_id; | 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 | fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !ResultId { | 616 | fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !ResultId { |