Skip to content

Commit a9f80c0

Browse files
committed
[EVM][DAGCombine] Expand SELECT to arithmetic when possible
The EVM has no conditional move, so this patch replaces certain `select` nodes with straight‑line arithmetic: | Source pattern | Rewritten form | Proof (IR equivalent) | |-----------------|------------------|-------------------------------------| | select C, X, 0 | X * C | https://alive2.llvm.org/ce/z/MqUacX | | select C, 0, Y | (1 − C) * Y | https://alive2.llvm.org/ce/z/tB_ww7 | | select C, X, 1 | X * C + (1 − C) | https://alive2.llvm.org/ce/z/Uje2ts | | select C, 1, Y | C + (1 − C) * Y | https://alive2.llvm.org/ce/z/XrBMpA | | select C, X, ‑1 | (C - 1) | X | https://alive2.llvm.org/ce/z/e5oNmW | | select C, ‑1, Y | -C | Y | https://alive2.llvm.org/ce/z/6-86BJ | This patch also canonicalises select C, (op Y, X), Y -> op Y, (select C, X, 0) -> op Y, C * X iff 0 is neutral element for op. Alive2 proofs: i256, sub, no undef inputs – https://alive2.llvm.org/ce/z/-BPfbO i4, sub, undef inputs allowed – https://alive2.llvm.org/ce/z/6fhhZx
1 parent 33f0078 commit a9f80c0

File tree

3 files changed

+465
-0
lines changed

3 files changed

+465
-0
lines changed

llvm/lib/Target/EVM/EVMISelLowering.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ EVMTargetLowering::EVMTargetLowering(const TargetMachine &TM,
9090
setOperationAction(ISD::INTRINSIC_VOID, MVT::Other, Custom);
9191
setOperationAction(ISD::INTRINSIC_WO_CHAIN, MVT::Other, Custom);
9292

93+
// Custom DAGCombine patterns.
94+
setTargetDAGCombine(ISD::SELECT);
95+
9396
setJumpIsExpensive(true);
9497
setMaximumJumpTableSize(0);
9598
}
@@ -766,3 +769,127 @@ MachineBasicBlock *EVMTargetLowering::emitSelect(MachineInstr &MI,
766769
MI.eraseFromParent(); // The pseudo instruction is gone now.
767770
return BB;
768771
}
772+
773+
// Fold
774+
// (select C, (op Y, X), Y) -> (op Y, (select C, X, NEUTRAL))
775+
// and
776+
// (select C, Y, (op Y, X)) -> (op Y, (select not C, X, NEUTRAL))
777+
// when 0 is the neutral element for op.
778+
//
779+
// That lets a later combine turn (select C, X, 0) into (C * X).
780+
//
781+
// Examples:
782+
// (select C, (sub Y, X), Y) -> (sub Y, (select C, X, 0))
783+
// (select C, (or Y, X), Y) -> (or Y, (select C, X, 0))
784+
// (select C, (or X, Y), Y) -> (or Y, (select C, X, 0)) ; commutativity
785+
// handled.
786+
static SDValue tryFoldSelectIntoOp(SDNode *N, SelectionDAG &DAG, SDValue TrueV,
787+
SDValue FalseV, bool Swapped) {
788+
789+
if (!TrueV.hasOneUse() || isa<ConstantSDNode>(FalseV))
790+
return SDValue();
791+
792+
bool Commutative = true;
793+
switch (TrueV.getOpcode()) {
794+
default:
795+
return SDValue();
796+
case ISD::SHL:
797+
case ISD::SRA:
798+
case ISD::SRL:
799+
case ISD::SUB:
800+
Commutative = false;
801+
break;
802+
case ISD::ADD:
803+
case ISD::OR:
804+
case ISD::XOR:
805+
break;
806+
}
807+
808+
if (FalseV != TrueV.getOperand(0) &&
809+
(!Commutative || FalseV != TrueV.getOperand(1)))
810+
return SDValue();
811+
812+
EVT VT = N->getValueType(0);
813+
SDLoc DL(N);
814+
SDValue OtherOp =
815+
FalseV == TrueV.getOperand(0) ? TrueV.getOperand(1) : TrueV.getOperand(0);
816+
EVT OtherOpVT = OtherOp.getValueType();
817+
SDValue IdentityOperand = DAG.getConstant(0, DL, OtherOpVT);
818+
819+
if (Swapped)
820+
std::swap(OtherOp, IdentityOperand);
821+
SDValue NewSel =
822+
DAG.getSelect(DL, OtherOpVT, N->getOperand(0), OtherOp, IdentityOperand);
823+
return DAG.getNode(TrueV.getOpcode(), DL, VT, FalseV, NewSel);
824+
}
825+
826+
SDValue EVMTargetLowering::combineSELECT(SDNode *N,
827+
DAGCombinerInfo &DCI) const {
828+
// Perform combines only after DAG legalisation.
829+
if (!DCI.isAfterLegalizeDAG())
830+
return SDValue();
831+
832+
SelectionDAG &DAG = DCI.DAG;
833+
SDValue CondV = N->getOperand(0);
834+
SDValue TrueV = N->getOperand(1);
835+
SDValue FalseV = N->getOperand(2);
836+
SDLoc DL(N);
837+
MVT VT = N->getSimpleValueType(0);
838+
839+
const auto freeze = [&DAG](const SDValue V) { return DAG.getFreeze(V); };
840+
const auto iszero = [&DAG, DL, VT](const SDValue V) {
841+
return SDValue(DAG.getMachineNode(EVM::ISZERO, DL, VT, V), 0);
842+
};
843+
844+
// X* means freeze(X)
845+
// fold (Cond ? X : 0) -> (X* * Cond)
846+
if (isNullConstant(FalseV))
847+
return DAG.getNode(ISD::MUL, DL, VT, freeze(TrueV), CondV);
848+
849+
// fold (Cond ? 0 : Y) -> (~Cond * Y*)
850+
if (isNullConstant(TrueV))
851+
return DAG.getNode(ISD::MUL, DL, VT, iszero(CondV), freeze(FalseV));
852+
853+
// fold (Cond ? X : 1) -> ((X* * Cond*) + ~Cond*)
854+
if (isOneConstant(FalseV))
855+
return DAG.getNode(
856+
ISD::ADD, DL, VT, iszero(freeze(CondV)),
857+
DAG.getNode(ISD::MUL, DL, VT, freeze(TrueV), freeze(CondV)));
858+
859+
// fold (Cond ? 1 : Y) -> (Cond* + ~Cond* * Y*)
860+
if (isOneConstant(TrueV))
861+
return DAG.getNode(
862+
ISD::ADD, DL, VT, freeze(CondV),
863+
DAG.getNode(ISD::MUL, DL, VT, freeze(FalseV), iszero(freeze(CondV))));
864+
865+
// fold (Cond ? X : -1) -> (Cond - 1) | X*)
866+
if (isAllOnesConstant(FalseV)) {
867+
const SDValue Const1 = DAG.getConstant(1, DL, VT);
868+
return DAG.getNode(ISD::OR, DL, VT,
869+
DAG.getNode(ISD::SUB, DL, VT, CondV, Const1),
870+
freeze(TrueV));
871+
}
872+
873+
// fold (Cond ? -1 : Y) -> (-Cond | Y*)
874+
if (isAllOnesConstant(TrueV)) {
875+
SDValue Neg = DAG.getNegative(CondV, DL, VT);
876+
return DAG.getNode(ISD::OR, DL, VT, Neg, DAG.getFreeze(FalseV));
877+
}
878+
879+
if (SDValue V = tryFoldSelectIntoOp(N, DAG, TrueV, FalseV, /*Swapped=*/false))
880+
return V;
881+
// NOLINTNEXTLINE(readability-suspicious-call-argument)
882+
return tryFoldSelectIntoOp(N, DAG, FalseV, TrueV, /*Swapped=*/true);
883+
}
884+
885+
SDValue EVMTargetLowering::PerformDAGCombine(SDNode *N,
886+
DAGCombinerInfo &DCI) const {
887+
switch (N->getOpcode()) {
888+
default:
889+
break;
890+
case ISD::SELECT:
891+
return combineSELECT(N, DCI);
892+
}
893+
894+
return SDValue();
895+
}

llvm/lib/Target/EVM/EVMISelLowering.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ class EVMTargetLowering final : public TargetLowering {
139139
void ReplaceNodeResults(SDNode *N, SmallVectorImpl<SDValue> &Results,
140140
SelectionDAG &DAG) const override;
141141

142+
SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override;
143+
SDValue combineSELECT(SDNode *N, DAGCombinerInfo &DCI) const;
144+
142145
SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override;
143146

144147
SDValue LowerBSWAP(SDValue BSWAP, SelectionDAG &DAG) const;

0 commit comments

Comments
 (0)