C++: Support the spaceship operator in the IR

This commit is contained in:
Jeroen Ketema
2025-07-16 17:53:55 +02:00
parent 9b8302f983
commit f319381f27
12 changed files with 110 additions and 62 deletions

View File

@@ -42,6 +42,7 @@ private newtype TOpcode =
TCompareGT() or
TCompareLE() or
TCompareGE() or
TSpaceship() or
TPointerAdd() or
TPointerSub() or
TPointerDiff() or
@@ -765,6 +766,15 @@ module Opcode {
final override string toString() { result = "CompareGE" }
}
/**
* The `Opcode` for a `SpaceshipInstruction`.
*
* See the `SpaceshipInstruction` documentation for more details.
*/
class Spaceship extends BinaryOpcode, TSpaceship {
final override string toString() { result = "Spaceship" }
}
/**
* The `Opcode` for a `PointerAddInstruction`.
*

View File

@@ -1604,6 +1604,13 @@ class CompareGEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
/**
* An instruction that represents a three-way comparison operator.
*/
class CompareThreeWayInstruction extends BinaryInstruction {
CompareThreeWayInstruction() { this.getOpcode() instanceof Opcode::Spaceship }
}
/**
* An instruction that branches to one of multiple successor instructions based on the value of an
* integer operand.

View File

@@ -1604,6 +1604,13 @@ class CompareGEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
/**
* An instruction that represents a three-way comparison operator.
*/
class CompareThreeWayInstruction extends BinaryInstruction {
CompareThreeWayInstruction() { this.getOpcode() instanceof Opcode::Spaceship }
}
/**
* An instruction that branches to one of multiple successor instructions based on the value of an
* integer operand.

View File

@@ -1808,6 +1808,11 @@ private Opcode comparisonOpcode(ComparisonOperation expr) {
expr instanceof GEExpr and result instanceof Opcode::CompareGE
}
private Opcode spaceShipOpcode(SpaceshipExpr expr) {
exists(expr) and
result instanceof Opcode::Spaceship
}
/**
* IR translation of a simple binary operation.
*/
@@ -1867,7 +1872,8 @@ class TranslatedBinaryOperation extends TranslatedSingleInstructionExpr {
override Opcode getOpcode() {
result = binaryArithmeticOpcode(expr) or
result = binaryBitwiseOpcode(expr) or
result = comparisonOpcode(expr)
result = comparisonOpcode(expr) or
result = spaceShipOpcode(expr)
}
override Type getExprType() {

View File

@@ -1604,6 +1604,13 @@ class CompareGEInstruction extends RelationalInstruction {
override predicate isStrict() { none() }
}
/**
* An instruction that represents a three-way comparison operator.
*/
class CompareThreeWayInstruction extends BinaryInstruction {
CompareThreeWayInstruction() { this.getOpcode() instanceof Opcode::Spaceship }
}
/**
* An instruction that branches to one of multiple successor instructions based on the value of an
* integer operand.

View File

@@ -20336,26 +20336,55 @@ ir.cpp:
# 2766| r2766_20(glval<ThreeWay>) = CopyValue : r2766_19
# 2766| r2766_21(glval<int>) = FieldAddress[x] : r2766_20
# 2766| r2766_22(int) = Load[?] : &:r2766_21, ~m2766_12
# 2766| r2766_23(strong_ordering) = Spaceship : r2766_17, r2766_22
# 2766| m2766_24(strong_ordering) = Store[#return] : &:r2766_13, r2766_23
# 2766| v2766_25(void) = ReturnIndirection[#this] : &:r2766_7, m2766_8
# 2766| v2766_26(void) = ReturnIndirection[y] : &:r2766_11, m2766_12
# 2766| r2766_27(glval<strong_ordering>) = VariableAddress[#return] :
# 2766| v2766_28(void) = ReturnValue : &:r2766_27, m2766_24
# 2766| v2766_29(void) = AliasedUse : m2766_3
# 2766| v2766_30(void) = ExitFunction :
# 2769| void test_three_way(int, int, ThreeWay, ThreeWay)
# 2769| Block 0
# 2769| v2769_1(void) = EnterFunction :
# 2769| m2769_2(unknown) = AliasedDefinition :
# 2769| m2769_3(unknown) = InitializeNonLocal :
# 2769| m2769_4(unknown) = Chi : total:m2769_2, partial:m2769_3
# 2769| r2769_5(glval<int>) = VariableAddress[a] :
# 2769| m2769_6(int) = InitializeParameter[a] : &:r2769_5
# 2769| r2769_7(glval<int>) = VariableAddress[b] :
# 2769| m2769_8(int) = InitializeParameter[b] : &:r2769_7
# 2769| r2769_9(glval<ThreeWay>) = VariableAddress[c] :
# 2769| m2769_10(ThreeWay) = InitializeParameter[c] : &:r2769_9
# 2769| r2769_11(glval<ThreeWay>) = VariableAddress[d] :
# 2769| m2769_12(ThreeWay) = InitializeParameter[d] : &:r2769_11
# 2770| r2770_1(glval<strong_ordering>) = VariableAddress[x] :
# 2770| r2770_2(glval<int>) = VariableAddress[a] :
# 2770| r2770_3(int) = Load[a] : &:r2770_2, m2769_6
# 2770| r2770_4(glval<int>) = VariableAddress[b] :
# 2770| r2770_5(int) = Load[b] : &:r2770_4, m2769_8
# 2769| v2769_1(void) = EnterFunction :
# 2769| m2769_2(unknown) = AliasedDefinition :
# 2769| m2769_3(unknown) = InitializeNonLocal :
# 2769| m2769_4(unknown) = Chi : total:m2769_2, partial:m2769_3
# 2769| r2769_5(glval<int>) = VariableAddress[a] :
# 2769| m2769_6(int) = InitializeParameter[a] : &:r2769_5
# 2769| r2769_7(glval<int>) = VariableAddress[b] :
# 2769| m2769_8(int) = InitializeParameter[b] : &:r2769_7
# 2769| r2769_9(glval<ThreeWay>) = VariableAddress[c] :
# 2769| m2769_10(ThreeWay) = InitializeParameter[c] : &:r2769_9
# 2769| r2769_11(glval<ThreeWay>) = VariableAddress[d] :
# 2769| m2769_12(ThreeWay) = InitializeParameter[d] : &:r2769_11
# 2770| r2770_1(glval<strong_ordering>) = VariableAddress[x] :
# 2770| r2770_2(glval<int>) = VariableAddress[a] :
# 2770| r2770_3(int) = Load[a] : &:r2770_2, m2769_6
# 2770| r2770_4(glval<int>) = VariableAddress[b] :
# 2770| r2770_5(int) = Load[b] : &:r2770_4, m2769_8
# 2770| r2770_6(strong_ordering) = Spaceship : r2770_3, r2770_5
# 2770| m2770_7(strong_ordering) = Store[x] : &:r2770_1, r2770_6
# 2771| r2771_1(glval<strong_ordering>) = VariableAddress[y] :
# 2771| r2771_2(glval<ThreeWay>) = VariableAddress[c] :
# 2771| r2771_3(glval<unknown>) = FunctionAddress[operator<=>] :
# 2771| r2771_4(glval<ThreeWay>) = VariableAddress[d] :
# 2771| r2771_5(ThreeWay &) = CopyValue : r2771_4
# 2771| r2771_6(strong_ordering) = Call[operator<=>] : func:r2771_3, this:r2771_2, 0:r2771_5
# 2771| m2771_7(unknown) = ^CallSideEffect : ~m2769_4
# 2771| m2771_8(unknown) = Chi : total:m2769_4, partial:m2771_7
# 2771| v2771_9(void) = ^IndirectReadSideEffect[-1] : &:r2771_2, m2769_10
# 2771| v2771_10(void) = ^BufferReadSideEffect[0] : &:r2771_5, ~m2769_12
# 2771| m2771_11(ThreeWay) = ^IndirectMayWriteSideEffect[-1] : &:r2771_2
# 2771| m2771_12(ThreeWay) = Chi : total:m2769_10, partial:m2771_11
# 2771| m2771_13(unknown) = ^BufferMayWriteSideEffect[0] : &:r2771_5
# 2771| m2771_14(ThreeWay) = Chi : total:m2769_12, partial:m2771_13
# 2771| m2771_15(strong_ordering) = Store[y] : &:r2771_1, r2771_6
# 2772| v2772_1(void) = NoOp :
# 2769| v2769_13(void) = ReturnVoid :
# 2769| v2769_14(void) = AliasedUse : ~m2771_8
# 2769| v2769_15(void) = ExitFunction :
ir23.cpp:
# 1| bool consteval_1()

View File

@@ -6,8 +6,6 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:2766:72:2766:72 | Load: x | Instruction 'Load: x' has no successors in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:18:2770:18 | Load: b | Instruction 'Load: b' has no successors in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -6,8 +6,6 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:2766:72:2766:72 | Load: x | Instruction 'Load: x' has no successors in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:18:2770:18 | Load: b | Instruction 'Load: b' has no successors in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -1,6 +1,4 @@
missingOperand
| ir.cpp:2766:58:2766:72 | Store: ... <=> ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:12:2770:18 | Store: ... <=> ... | Instruction 'Store' is missing an expected operand with tag 'StoreValue' in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
unexpectedOperand
duplicateOperand
missingPhiOperand
@@ -8,8 +6,6 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:2766:72:2766:72 | Load: x | Instruction 'Load: x' has no successors in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:18:2770:18 | Load: b | Instruction 'Load: b' has no successors in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction
@@ -25,10 +21,6 @@ lostReachability
backEdgeCountMismatch
useNotDominatedByDefinition
| ir.cpp:1535:8:1535:8 | Unary | Operand 'Unary' is not dominated by its definition in function '$@'. | ir.cpp:1535:8:1535:8 | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() | void StructuredBindingDataMemberStruct::StructuredBindingDataMemberStruct() |
| ir.cpp:2766:24:2766:34 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2766:46:2766:46 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2766:51:2766:73 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:8:2770:8 | Address | Operand 'Address' is not dominated by its definition in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
switchInstructionWithoutDefaultEdge
notMarkedAsConflated
wronglyMarkedAsConflated

View File

@@ -18492,37 +18492,35 @@ ir.cpp:
# 2766| r2766_19(glval<ThreeWay>) = CopyValue : r2766_18
# 2766| r2766_20(glval<int>) = FieldAddress[x] : r2766_19
# 2766| r2766_21(int) = Load[?] : &:r2766_20, ~m?
# 2766| Block 1
# 2766| mu2766_22(strong_ordering) = Store[#return] : &:r2766_12
# 2766| v2766_23(void) = ReturnIndirection[#this] : &:r2766_6, ~m?
# 2766| v2766_24(void) = ReturnIndirection[y] : &:r2766_10, ~m?
# 2766| r2766_25(glval<strong_ordering>) = VariableAddress[#return] :
# 2766| v2766_26(void) = ReturnValue : &:r2766_25, ~m?
# 2766| v2766_27(void) = AliasedUse : ~m?
# 2766| v2766_28(void) = ExitFunction :
# 2766| r2766_22(strong_ordering) = Spaceship : r2766_16, r2766_21
# 2766| mu2766_23(strong_ordering) = Store[#return] : &:r2766_12, r2766_22
# 2766| v2766_24(void) = ReturnIndirection[#this] : &:r2766_6, ~m?
# 2766| v2766_25(void) = ReturnIndirection[y] : &:r2766_10, ~m?
# 2766| r2766_26(glval<strong_ordering>) = VariableAddress[#return] :
# 2766| v2766_27(void) = ReturnValue : &:r2766_26, ~m?
# 2766| v2766_28(void) = AliasedUse : ~m?
# 2766| v2766_29(void) = ExitFunction :
# 2769| void test_three_way(int, int, ThreeWay, ThreeWay)
# 2769| Block 0
# 2769| v2769_1(void) = EnterFunction :
# 2769| mu2769_2(unknown) = AliasedDefinition :
# 2769| mu2769_3(unknown) = InitializeNonLocal :
# 2769| r2769_4(glval<int>) = VariableAddress[a] :
# 2769| mu2769_5(int) = InitializeParameter[a] : &:r2769_4
# 2769| r2769_6(glval<int>) = VariableAddress[b] :
# 2769| mu2769_7(int) = InitializeParameter[b] : &:r2769_6
# 2769| r2769_8(glval<ThreeWay>) = VariableAddress[c] :
# 2769| mu2769_9(ThreeWay) = InitializeParameter[c] : &:r2769_8
# 2769| r2769_10(glval<ThreeWay>) = VariableAddress[d] :
# 2769| mu2769_11(ThreeWay) = InitializeParameter[d] : &:r2769_10
# 2770| r2770_1(glval<strong_ordering>) = VariableAddress[x] :
# 2770| r2770_2(glval<int>) = VariableAddress[a] :
# 2770| r2770_3(int) = Load[a] : &:r2770_2, ~m?
# 2770| r2770_4(glval<int>) = VariableAddress[b] :
# 2770| r2770_5(int) = Load[b] : &:r2770_4, ~m?
# 2770| Block 1
# 2770| mu2770_6(strong_ordering) = Store[x] : &:r2770_1
# 2769| v2769_1(void) = EnterFunction :
# 2769| mu2769_2(unknown) = AliasedDefinition :
# 2769| mu2769_3(unknown) = InitializeNonLocal :
# 2769| r2769_4(glval<int>) = VariableAddress[a] :
# 2769| mu2769_5(int) = InitializeParameter[a] : &:r2769_4
# 2769| r2769_6(glval<int>) = VariableAddress[b] :
# 2769| mu2769_7(int) = InitializeParameter[b] : &:r2769_6
# 2769| r2769_8(glval<ThreeWay>) = VariableAddress[c] :
# 2769| mu2769_9(ThreeWay) = InitializeParameter[c] : &:r2769_8
# 2769| r2769_10(glval<ThreeWay>) = VariableAddress[d] :
# 2769| mu2769_11(ThreeWay) = InitializeParameter[d] : &:r2769_10
# 2770| r2770_1(glval<strong_ordering>) = VariableAddress[x] :
# 2770| r2770_2(glval<int>) = VariableAddress[a] :
# 2770| r2770_3(int) = Load[a] : &:r2770_2, ~m?
# 2770| r2770_4(glval<int>) = VariableAddress[b] :
# 2770| r2770_5(int) = Load[b] : &:r2770_4, ~m?
# 2770| r2770_6(strong_ordering) = Spaceship : r2770_3, r2770_5
# 2770| mu2770_7(strong_ordering) = Store[x] : &:r2770_1, r2770_6
# 2771| r2771_1(glval<strong_ordering>) = VariableAddress[y] :
# 2771| r2771_2(glval<ThreeWay>) = VariableAddress[c] :
# 2771| r2771_3(glval<unknown>) = FunctionAddress[operator<=>] :

View File

@@ -6,8 +6,6 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:2766:72:2766:72 | Load: x | Instruction 'Load: x' has no successors in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:18:2770:18 | Load: b | Instruction 'Load: b' has no successors in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction

View File

@@ -6,8 +6,6 @@ missingOperandType
duplicateChiOperand
sideEffectWithoutPrimary
instructionWithoutSuccessor
| ir.cpp:2766:72:2766:72 | Load: x | Instruction 'Load: x' has no successors in function '$@'. | ir.cpp:2766:24:2766:34 | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) | std::strong_ordering ThreeWay::operator<=>(ThreeWay&) |
| ir.cpp:2770:18:2770:18 | Load: b | Instruction 'Load: b' has no successors in function '$@'. | ir.cpp:2769:6:2769:19 | void test_three_way(int, int, ThreeWay, ThreeWay) | void test_three_way(int, int, ThreeWay, ThreeWay) |
ambiguousSuccessors
unexplainedLoop
unnecessaryPhiInstruction