authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 14:46:58+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-05-16 14:46:58+02:00
log585122b1ac51f9ac23bae537dfc40bbae1d7cb3c
tree8cebec3bde9cff4e52058a322c1a0b20bb2e801a
parentf14000c7e1bd9032185b3c0a2a28a73eec6c48f7

SPIR-V: comparison and equality operations


1 files changed, 36 insertions(+), 3 deletions(-)

src/codegen/spirv.zig+36-3
...@@ -84,6 +84,9 @@ pub const DeclGen = struct {...@@ -84,6 +84,9 @@ pub const DeclGen = struct {
84 const ArithmeticTypeInfo = struct {84 const ArithmeticTypeInfo = struct {
85 /// A classification of the inner type.85 /// A classification of the inner type.
86 const Class = enum {86 const Class = enum {
87 /// A boolean.
88 bool,
89
87 /// A regular, **native**, integer.90 /// A regular, **native**, integer.
88 /// This is only returned when the backend supports this int as a native type (when91 /// This is only returned when the backend supports this int as a native type (when
89 /// the relevant capability is enabled).92 /// the relevant capability is enabled).
...@@ -196,6 +199,12 @@ pub const DeclGen = struct {...@@ -196,6 +199,12 @@ pub const DeclGen = struct {
196 const target = self.module.getTarget();199 const target = self.module.getTarget();
197200
198 return switch (ty.zigTypeTag()) {201 return switch (ty.zigTypeTag()) {
202 .Bool => ArithmeticTypeInfo{
203 .bits = 1, // Doesn't matter for this class.
204 .is_vector = false,
205 .signedness = .unsigned, // Technically, but doesn't matter for this class.
206 .class = .bool,
207 },
199 .Float => ArithmeticTypeInfo{208 .Float => ArithmeticTypeInfo{
200 .bits = ty.floatBits(target),209 .bits = ty.floatBits(target),
201 .is_vector = false,210 .is_vector = false,
...@@ -436,6 +445,12 @@ pub const DeclGen = struct {...@@ -436,6 +445,12 @@ pub const DeclGen = struct {
436 .bit_and => try self.genBinOp(inst.castTag(.bit_and).?),445 .bit_and => try self.genBinOp(inst.castTag(.bit_and).?),
437 .bit_or => try self.genBinOp(inst.castTag(.bit_or).?),446 .bit_or => try self.genBinOp(inst.castTag(.bit_or).?),
438 .xor => try self.genBinOp(inst.castTag(.xor).?),447 .xor => try self.genBinOp(inst.castTag(.xor).?),
448 .cmp_eq => try self.genBinOp(inst.castTag(.cmp_eq).?),
449 .cmp_neq => try self.genBinOp(inst.castTag(.cmp_neq).?),
450 .cmp_gt => try self.genBinOp(inst.castTag(.cmp_gt).?),
451 .cmp_gte => try self.genBinOp(inst.castTag(.cmp_gte).?),
452 .cmp_lt => try self.genBinOp(inst.castTag(.cmp_lt).?),
453 .cmp_lte => try self.genBinOp(inst.castTag(.cmp_lte).?),
439 .arg => self.genArg(),454 .arg => self.genArg(),
440 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them455 // TODO: Breakpoints won't be supported in SPIR-V, but the compiler seems to insert them
441 // throughout the IR.456 // throughout the IR.
...@@ -458,18 +473,23 @@ pub const DeclGen = struct {...@@ -458,18 +473,23 @@ pub const DeclGen = struct {
458473
459 // TODO: Is the result the same as the argument types?474 // TODO: Is the result the same as the argument types?
460 // This is supposed to be the case for SPIR-V.475 // This is supposed to be the case for SPIR-V.
461 std.debug.assert(inst.base.ty.eql(inst.lhs.ty) and inst.base.ty.eql(inst.rhs.ty));476 std.debug.assert(inst.rhs.ty.eql(inst.lhs.ty));
477 std.debug.assert(inst.base.ty.tag() == .bool or inst.base.ty.eql(inst.lhs.ty));
462478
463 // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float479 // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float
464 // versions of operations require different opcodes.480 // versions of operations require different opcodes.
465 const info = try self.arithmeticTypeInfo(inst.base.ty);481 // For operations which produce bools, the information of inst.base.ty is not useful, so just pick either operand
482 // instead.
483 const info = try self.arithmeticTypeInfo(inst.lhs.ty);
466484
467 if (info.class == .composite_integer)485 if (info.class == .composite_integer)
468 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: binary operations for composite integers", .{});486 return self.fail(.{.node_offset = 0}, "TODO: SPIR-V backend: binary operations for composite integers", .{});
469487
488 const is_bool = info.class == .bool;
470 const is_float = info.class == .float;489 const is_float = info.class == .float;
471 const is_signed = info.signedness == .signed;490 const is_signed = info.signedness == .signed;
472 // **Note**: All these operations must be valid for vectors of floats and integers as well!491 // **Note**: All these operations must be valid for vectors of floats, integers and bools as well!
492 // For floating points, we generally want ordered operations (which return false if either operand is nan).
473 const opcode = switch (inst.base.tag) {493 const opcode = switch (inst.base.tag) {
474 // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,494 // The regular integer operations are all defined for wrapping. Since theyre only relevant for integers,
475 // we can just switch on both cases here.495 // we can just switch on both cases here.
...@@ -478,11 +498,24 @@ pub const DeclGen = struct {...@@ -478,11 +498,24 @@ pub const DeclGen = struct {
478 .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul,498 .mul, .mulwrap => if (is_float) Opcode.OpFMul else Opcode.OpIMul,
479 // TODO: Trap if divisor is 0?499 // TODO: Trap if divisor is 0?
480 // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful.500 // TODO: Figure out of OpSDiv for unsigned/OpUDiv for signed does anything useful.
501 // => Those are probably for divTrunc and divFloor, though the compiler does not yet generate those.
502 // => TODO: Figure out how those work on the SPIR-V side.
503 // => TODO: Test these.
481 .div => if (is_float) Opcode.OpFDiv else if (is_signed) Opcode.OpSDiv else Opcode.OpUDiv,504 .div => if (is_float) Opcode.OpFDiv else if (is_signed) Opcode.OpSDiv else Opcode.OpUDiv,
482 // Only integer versions for these.505 // Only integer versions for these.
483 .bit_and => Opcode.OpBitwiseAnd,506 .bit_and => Opcode.OpBitwiseAnd,
484 .bit_or => Opcode.OpBitwiseOr,507 .bit_or => Opcode.OpBitwiseOr,
485 .xor => Opcode.OpBitwiseXor,508 .xor => Opcode.OpBitwiseXor,
509 // Int/bool/float -> bool operations.
510 .cmp_eq => if (is_float) Opcode.OpFOrdEqual else if (is_bool) Opcode.OpLogicalEqual else Opcode.OpIEqual,
511 .cmp_neq => if (is_float) Opcode.OpFOrdNotEqual else if (is_bool) Opcode.OpLogicalNotEqual else Opcode.OpINotEqual,
512 // Int/float -> bool operations.
513 // TODO: Verify that these OpFOrd type operations produce the right value.
514 // TODO: Is there a more fundamental difference between OpU and OpS operations here than just the type?
515 .cmp_gt => if (is_float) Opcode.OpFOrdGreaterThan else if (is_signed) Opcode.OpSGreaterThan else Opcode.OpUGreaterThan,
516 .cmp_gte => if (is_float) Opcode.OpFOrdGreaterThanEqual else if (is_signed) Opcode.OpSGreaterThanEqual else Opcode.OpUGreaterThanEqual,
517 .cmp_lt => if (is_float) Opcode.OpFOrdLessThan else if (is_signed) Opcode.OpSLessThan else Opcode.OpULessThan,
518 .cmp_lte => if (is_float) Opcode.OpFOrdLessThanEqual else if (is_signed) Opcode.OpSLessThanEqual else Opcode.OpULessThanEqual,
486 else => unreachable,519 else => unreachable,
487 };520 };
488521