Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2790,25 +2790,40 @@ static bool isOptimizeCompareCandidate(MachineInstr *MI, bool &IsThumb1) {
[[fallthrough]];
case ARM::RSBrr:
case ARM::RSBri:
case ARM::RSBrsi:
case ARM::RSBrsr:
case ARM::RSCrr:
case ARM::RSCri:
case ARM::RSCrsr:
case ARM::RSCrsi:
case ARM::ADDrr:
case ARM::ADDri:
case ARM::ADDrsi:
case ARM::ADDrsr:
case ARM::ADCrr:
case ARM::ADCri:
case ARM::SUBrr:
case ARM::SUBri:
case ARM::SUBrsr:
case ARM::SUBrsi:
case ARM::SBCrr:
case ARM::SBCri:
case ARM::SBCrsi:
case ARM::SBCrsr:
case ARM::t2RSBri:
case ARM::t2RSBrr:
case ARM::t2RSBrs:
case ARM::t2ADDrr:
case ARM::t2ADDri:
case ARM::t2ADDrs:
case ARM::t2ADCrr:
case ARM::t2ADCri:
case ARM::t2SUBrr:
case ARM::t2SUBri:
case ARM::t2SUBrs:
case ARM::t2SBCrr:
case ARM::t2SBCri:
case ARM::t2SBCrs:
case ARM::ANDrr:
case ARM::ANDri:
case ARM::ANDrsr:
Expand Down
84 changes: 83 additions & 1 deletion llvm/lib/Target/ARM/ARMISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,10 @@ class ARMDAGToDAGISel : public SelectionDAGISel {
return ARM_AM::getT2SOImmVal(~Imm) != -1;
}

// Include the pieces autogenerated from the target description.
// Preference helper: for SUB with encodable immediate LHS, select RSBri
// and materialize any RHS shift first when needed. Returns true if handled.
bool tryPreferRSBForSUB(SDNode *N);

#include "ARMGenDAGISel.inc"

private:
Expand Down Expand Up @@ -3492,6 +3495,81 @@ getContiguousRangeOfSetBits(const APInt &A) {
return std::make_pair(FirstOne, LastOne);
}

bool ARMDAGToDAGISel::tryPreferRSBForSUB(SDNode *N) {
if (Subtarget->isThumb1Only())
return false;

SDValue LHS = N->getOperand(0);
SDValue RHS = N->getOperand(1);
const auto *CI = dyn_cast<ConstantSDNode>(LHS);
if (!CI)
return false;

unsigned Imm = (unsigned)CI->getZExtValue();
bool Encodable = Subtarget->isThumb() ? is_t2_so_imm(Imm) : is_so_imm(Imm);
if (!Encodable)
return false;

SDLoc dl(N);
SDValue Reg0 = CurDAG->getRegister(0, MVT::i32);
SDValue ImmOp = CurDAG->getTargetConstant(Imm, dl, MVT::i32);

// Materialize shift if RHS is shifted
ARM_AM::ShiftOpc ShOpcVal = ARM_AM::getShiftOpcForNode(RHS.getOpcode());
SDValue Rn = RHS;
if (ShOpcVal != ARM_AM::no_shift) {
const ConstantSDNode *ShC = dyn_cast<ConstantSDNode>(RHS.getOperand(1));
if (!ShC)
return false; // can't safely materialize variable shift here
unsigned ShAmt = ShC->getZExtValue();
if (Subtarget->isThumb()) {
unsigned ShOpc = 0;
switch (ShOpcVal) {
default:
ShOpc = 0;
break;
case ARM_AM::lsl:
ShOpc = ARM::t2LSLri;
break;
case ARM_AM::lsr:
ShOpc = ARM::t2LSRri;
break;
case ARM_AM::asr:
ShOpc = ARM::t2ASRri;
break;
case ARM_AM::ror:
ShOpc = ARM::t2RORri;
break;
}
if (!ShOpc)
return false;
SDValue ShAmtOp = CurDAG->getTargetConstant(ShAmt, dl, MVT::i32);
SDValue OpsShift[] = {RHS.getOperand(0), ShAmtOp, getAL(CurDAG, dl), Reg0,
Reg0};
MachineSDNode *ShN =
CurDAG->getMachineNode(ShOpc, dl, MVT::i32, OpsShift);
Rn = SDValue(ShN, 0);
} else {
unsigned SOpc = ARM_AM::getSORegOpc(ShOpcVal, ShAmt);
SDValue ShImmOp = CurDAG->getTargetConstant(SOpc, dl, MVT::i32);
SDValue OpsShift[] = {RHS.getOperand(0), ShImmOp, getAL(CurDAG, dl), Reg0,
Reg0};
MachineSDNode *ShN =
CurDAG->getMachineNode(ARM::MOVsi, dl, MVT::i32, OpsShift);
Rn = SDValue(ShN, 0);
}
}

if (Subtarget->isThumb()) {
SDValue Ops[] = {Rn, ImmOp, getAL(CurDAG, dl), Reg0, Reg0};
CurDAG->SelectNodeTo(N, ARM::t2RSBri, MVT::i32, Ops);
} else {
SDValue Ops[] = {Rn, ImmOp, getAL(CurDAG, dl), Reg0, Reg0};
CurDAG->SelectNodeTo(N, ARM::RSBri, MVT::i32, Ops);
}
return true;
}

void ARMDAGToDAGISel::SelectCMPZ(SDNode *N, bool &SwitchEQNEToPLMI) {
assert(N->getOpcode() == ARMISD::CMPZ);
SwitchEQNEToPLMI = false;
Expand Down Expand Up @@ -3643,6 +3721,10 @@ void ARMDAGToDAGISel::Select(SDNode *N) {
if (tryInlineAsm(N))
return;
break;
case ISD::SUB:
if (tryPreferRSBForSUB(N))
return;
break;
case ISD::Constant: {
unsigned Val = N->getAsZExtVal();
// If we can't materialize the constant we need to use a literal pool
Expand Down
108 changes: 107 additions & 1 deletion llvm/lib/Target/ARM/ARMISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,11 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM_,
if (!Subtarget->hasV8_1MMainlineOps())
setOperationAction(ISD::UCMP, MVT::i32, Custom);

if (!Subtarget->isThumb1Only())
if (!Subtarget->isThumb1Only()) {
setOperationAction(ISD::ABS, MVT::i32, Custom);
setOperationAction(ISD::ABDS, MVT::i32, Custom);
}
setOperationAction(ISD::ABDU, MVT::i32, Custom);

setOperationAction(ISD::ConstantFP, MVT::f32, Custom);
setOperationAction(ISD::ConstantFP, MVT::f64, Custom);
Expand Down Expand Up @@ -5093,6 +5096,28 @@ SDValue ARMTargetLowering::LowerSELECT(SDValue Op, SelectionDAG &DAG) const {
}
}

// Canonicalise absolute difference patterns in SELECT before converting to
// SELECT_CC:
// select(setcc LHS, RHS, cc), sub(LHS, RHS), sub(RHS, LHS) ->
// select(setcc LHS, RHS, cc), sub(LHS, RHS), neg(sub(LHS, RHS))
if (Cond.getOpcode() == ISD::SETCC && SelectTrue.getOpcode() == ISD::SUB &&
SelectFalse.getOpcode() == ISD::SUB) {
SDValue LHS = Cond.getOperand(0);
SDValue RHS = Cond.getOperand(1);

if (SelectTrue.getOperand(0) == LHS && SelectTrue.getOperand(1) == RHS &&
SelectFalse.getOperand(0) == RHS && SelectFalse.getOperand(1) == LHS) {
SelectTrue->dropFlags(SDNodeFlags::PoisonGeneratingFlags);
SelectFalse = DAG.getNegative(SelectTrue, dl, SelectTrue.getValueType());
} else if (SelectTrue.getOperand(0) == RHS &&
SelectTrue.getOperand(1) == LHS &&
SelectFalse.getOperand(0) == LHS &&
SelectFalse.getOperand(1) == RHS) {
SelectFalse->dropFlags(SDNodeFlags::PoisonGeneratingFlags);
SelectTrue = DAG.getNegative(SelectFalse, dl, SelectFalse.getValueType());
}
}

// ARM's BooleanContents value is UndefinedBooleanContent. Mask out the
// undefined bits before doing a full-word comparison with zero.
Cond = DAG.getNode(ISD::AND, dl, Cond.getValueType(), Cond,
Expand Down Expand Up @@ -5383,6 +5408,28 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {

return DAG.getNode(ISD::AND, dl, VT, LHS, Shift);
}

// Canonicalise absolute difference patterns:
// select_cc LHS, RHS, sub(LHS, RHS), sub(RHS, LHS), cc ->
// select_cc LHS, RHS, sub(LHS, RHS), neg(sub(LHS, RHS)), cc
//
// select_cc LHS, RHS, sub(RHS, LHS), sub(LHS, RHS), cc ->
// select_cc LHS, RHS, neg(sub(LHS, RHS)), sub(LHS, RHS), cc
// The second forms can be matched into subs+cmov with negation.
// NOTE: Drop poison generating flags from the negated operand to avoid
// inadvertently propagating poison after the canonicalisation.
if (TrueVal.getOpcode() == ISD::SUB && FalseVal.getOpcode() == ISD::SUB) {
if (TrueVal.getOperand(0) == LHS && TrueVal.getOperand(1) == RHS &&
FalseVal.getOperand(0) == RHS && FalseVal.getOperand(1) == LHS) {
TrueVal->dropFlags(SDNodeFlags::PoisonGeneratingFlags);
FalseVal = DAG.getNegative(TrueVal, dl, TrueVal.getValueType());
} else if (TrueVal.getOperand(0) == RHS && TrueVal.getOperand(1) == LHS &&
FalseVal.getOperand(0) == LHS &&
FalseVal.getOperand(1) == RHS) {
FalseVal->dropFlags(SDNodeFlags::PoisonGeneratingFlags);
TrueVal = DAG.getNegative(FalseVal, dl, FalseVal.getValueType());
}
}
}

if (Subtarget->hasV8_1MMainlineOps() && CFVal && CTVal &&
Expand Down Expand Up @@ -5509,6 +5556,62 @@ SDValue ARMTargetLowering::LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const {
return Result;
}

SDValue ARMTargetLowering::LowerABD(SDValue Op, SelectionDAG &DAG) const {
SDValue LHS = Op.getOperand(0);
SDValue RHS = Op.getOperand(1);
SDLoc DL(Op);
EVT VT = Op.getValueType();

// If the subtract doesn't overflow then just use abs(sub())
bool IsNonNegative = DAG.SignBitIsZero(LHS) && DAG.SignBitIsZero(RHS);
bool IsSigned = Op.getOpcode() == ISD::ABDS;
if (DAG.willNotOverflowSub(IsSigned || IsNonNegative, LHS, RHS))
return DAG.getNode(ISD::ABS, DL, VT,
DAG.getNode(ISD::SUB, DL, VT, LHS, RHS));

if (DAG.willNotOverflowSub(IsSigned || IsNonNegative, RHS, LHS))
return DAG.getNode(ISD::ABS, DL, VT,
DAG.getNode(ISD::SUB, DL, VT, RHS, LHS));

if (Subtarget->isThumb1Only()) {
assert(!IsSigned && "Signed ABS not supported on Thumb1");
// abdu: subs; sbcs r1,r1,r1(mask from borrow); eors; subs

// First subtraction: LHS - RHS
SDValue Sub1WithFlags =
DAG.getNode(ARMISD::SUBC, DL, DAG.getVTList(VT, FlagsVT), LHS, RHS);
SDValue Sub1Result = Sub1WithFlags.getValue(0);
SDValue Flags1 = Sub1WithFlags.getValue(1);

// sbcs r1,r1,r1 (mask from borrow)
SDValue Sbc1 = DAG.getNode(ARMISD::SUBE, DL, DAG.getVTList(VT, FlagsVT),
RHS, RHS, Flags1);

// eors (XOR)
SDValue Xor = DAG.getNode(ISD::XOR, DL, VT, Sub1Result, Sbc1.getValue(0));

// subs (final subtraction)
return DAG.getNode(ISD::SUB, DL, VT, Xor, Sbc1.getValue(0));
}

// Generate SUBS and CSEL for absolute difference (like LowerABS)
// Compute a - b with flags
SDValue Cmp =
DAG.getNode(ARMISD::SUBC, DL, DAG.getVTList(MVT::i32, FlagsVT), LHS, RHS);

// Compute b - a (negative of a - b)
SDValue Neg = DAG.getNode(ISD::SUB, DL, MVT::i32,
DAG.getConstant(0, DL, MVT::i32), Cmp.getValue(0));

// For unsigned: use HS (a >= b) to select a-b, otherwise b-a
// For signed: use GE (a >= b) to select a-b, otherwise b-a
ARMCC::CondCodes CC = IsSigned ? ARMCC::LT : ARMCC::LO;

// CSEL: if a > b, select a-b, otherwise b-a
return DAG.getNode(ARMISD::CMOV, DL, MVT::i32, Cmp.getValue(0), Neg,
DAG.getConstant(CC, DL, MVT::i32), Cmp.getValue(1));
}

/// canChangeToInt - Given the fp compare operand, return true if it is suitable
/// to morph to an integer compare sequence.
static bool canChangeToInt(SDValue Op, bool &SeenZero,
Expand Down Expand Up @@ -10599,6 +10702,9 @@ SDValue ARMTargetLowering::LowerOperation(SDValue Op, SelectionDAG &DAG) const {
case ISD::GlobalTLSAddress: return LowerGlobalTLSAddress(Op, DAG);
case ISD::SELECT: return LowerSELECT(Op, DAG);
case ISD::SELECT_CC: return LowerSELECT_CC(Op, DAG);
case ISD::ABDS:
case ISD::ABDU:
return LowerABD(Op, DAG);
case ISD::BRCOND: return LowerBRCOND(Op, DAG);
case ISD::BR_CC: return LowerBR_CC(Op, DAG);
case ISD::BR_JT: return LowerBR_JT(Op, DAG);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/ARM/ARMISelLowering.h
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,7 @@ class VectorType;
SDValue LowerUnsignedALUO(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerSELECT(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerSELECT_CC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerABD(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBRCOND(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
SDValue LowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const;
Expand Down
Loading