| ... | @@ -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 (when | 91 | /// 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(); |
| 197 | | 200 | |
| 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 them | 455 | // 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 { |
| 458 | | 473 | |
| 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)); |
| 462 | | 478 | |
| 463 | // Binary operations are generally applicable to both scalar and vector operations in SPIR-V, but int and float | 479 | // 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); |
| 466 | | 484 | |
| 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", .{}); |
| 469 | | 487 | |
| | 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 | }; |
| 488 | | 521 | |