authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-20 13:05:32+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-22 16:11:56+02:00
log6a121d9ccde34556ffb1baf3b8543defdf6136e0
treeb257848e5d00b142ab75966ffbca896bffd12908
parentb8444d2c51f334e3afec74ff80b051ed797ab480

SPIR-V: Split out genCmp from genBinOp


2 files changed, 58 insertions(+), 23 deletions(-)

src/codegen/spirv.zig+57-22
......@@ -348,7 +348,7 @@ pub const DeclGen = struct {
348348 const int_info = ty.intInfo(target);
349349 const backing_bits = self.backingIntBits(int_info.bits) orelse {
350350 // 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});
352352 };
353353
354354 // TODO: If backing_bits != int_info.bits, a duplicate type might be generated here.
......@@ -487,12 +487,12 @@ pub const DeclGen = struct {
487487 .bit_and => try self.genBinOp(inst.castTag(.bit_and).?),
488488 .bit_or => try self.genBinOp(inst.castTag(.bit_or).?),
489489 .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).?),
496496 .bool_and => try self.genBinOp(inst.castTag(.bool_and).?),
497497 .bool_or => try self.genBinOp(inst.castTag(.bool_or).?),
498498 .not => try self.genUnOp(inst.castTag(.not).?),
......@@ -504,7 +504,7 @@ pub const DeclGen = struct {
504504 .ret => self.genRet(inst.castTag(.ret).?),
505505 .retvoid => self.genRetVoid(),
506506 .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)}),
508508 };
509509 }
510510
......@@ -528,13 +528,14 @@ pub const DeclGen = struct {
528528 const info = try self.arithmeticTypeInfo(inst.lhs.ty);
529529
530530 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", .{});
532534
533535 const is_bool = info.class == .bool;
534536 const is_float = info.class == .float;
535537 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!
538539 const opcode = switch (inst.base.tag) {
539540 // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,
540541 // we can just switch on both cases here.
......@@ -551,16 +552,6 @@ pub const DeclGen = struct {
551552 .bit_and => Opcode.OpBitwiseAnd,
552553 .bit_or => Opcode.OpBitwiseOr,
553554 .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,
564555 // Bool -> bool operations.
565556 .bool_and => Opcode.OpLogicalAnd,
566557 .bool_or => Opcode.OpLogicalOr,
......@@ -575,7 +566,51 @@ pub const DeclGen = struct {
575566 if (info.class != .strange_integer)
576567 return result_id;
577568
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;
579614 }
580615
581616 fn genUnOp(self: *DeclGen, inst: *Inst.UnOp) !ResultId {
src/link/SpirV.zig+1-1
......@@ -41,7 +41,7 @@ const spec = @import("../codegen/spirv/spec.zig");
4141pub const FnData = struct {
4242 // We're going to fill these in flushModule, and we're going to fill them unconditionally,
4343 // so just set it to undefined.
44 id: ResultId = undefined
44 id: ResultId = undefined,
4545};
4646
4747base: link.File,