[lanai] Use peephole optimizer to generate more conditional ALU operations.

Summary:
* Similiar to the ARM backend yse the peephole optimizer to generate more conditional ALU operations;
* Add predicated type with default always true to RR instructions in LanaiInstrInfo.td;
* Move LanaiSetflagAluCombiner into optimizeCompare;
* The ASM parser can currently only handle explicitly specified CC, so specify ".t" (true) where needed in the ASM test;
* Remove unused MachineOperand flags;

Reviewers: eliben

Subscribers: aemerson

Differential Revision: http://reviews.llvm.org/D22072

llvm-svn: 274807
This commit is contained in:
Jacques Pienaar 2016-07-07 23:36:04 +00:00
parent 3f36781dec
commit 6d3eecc843
20 changed files with 1560 additions and 550 deletions

View File

@ -357,6 +357,20 @@ public:
return isInt<10>(Value);
}
bool isCondCode() {
if (!isImm())
return false;
const MCConstantExpr *ConstExpr = dyn_cast<MCConstantExpr>(Imm.Value);
if (!ConstExpr)
return false;
uint64_t Value = ConstExpr->getValue();
// The condition codes are between 0 (ICC_T) and 15 (ICC_LE). If the
// unsigned value of the immediate is less than LPCC::UNKNOWN (16) then
// value corresponds to a valid condition code.
return Value < LPCC::UNKNOWN;
}
void addExpr(MCInst &Inst, const MCExpr *Expr) const {
// Add as immediates where possible. Null MCExpr = 0
if (Expr == nullptr)
@ -388,6 +402,11 @@ public:
addExpr(Inst, getImm());
}
void addCondCodeOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
addExpr(Inst, getImm());
}
void addMemImmOperands(MCInst &Inst, unsigned N) const {
assert(N == 1 && "Invalid number of operands!");
const MCExpr *Expr = getMemOffset();
@ -1031,7 +1050,16 @@ StringRef LanaiAsmParser::splitMnemonic(StringRef Name, SMLoc NameLoc,
LPCC::CondCode CondCode = LPCC::suffixToLanaiCondCode(Mnemonic);
if (CondCode != LPCC::UNKNOWN) {
size_t Next = Mnemonic.rfind('.', Name.size());
Mnemonic = Mnemonic.substr(0, Next + 1);
// 'sel' doesn't use a predicate operand whose printer adds the period,
// but instead has the period as part of the identifier (i.e., 'sel.' is
// expected by the generated matcher). If the mnemonic starts with 'sel'
// then include the period as part of the mnemonic, else don't include it
// as part of the mnemonic.
if (Mnemonic.startswith("sel")) {
Mnemonic = Mnemonic.substr(0, Next + 1);
} else {
Mnemonic = Mnemonic.substr(0, Next);
}
Operands->push_back(LanaiOperand::CreateToken(Mnemonic, NameLoc));
Operands->push_back(LanaiOperand::createImm(
MCConstantExpr::create(CondCode, getContext()), NameLoc, NameLoc));

View File

@ -23,7 +23,6 @@ add_llvm_target(LanaiCodeGen
LanaiMemAluCombiner.cpp
LanaiRegisterInfo.cpp
LanaiSelectionDAGInfo.cpp
LanaiSetflagAluCombiner.cpp
LanaiSubtarget.cpp
LanaiTargetMachine.cpp
LanaiTargetObjectFile.cpp

View File

@ -62,6 +62,10 @@ static DecodeStatus decodeSplsValue(MCInst &Inst, unsigned Insn,
static DecodeStatus decodeBranch(MCInst &Inst, unsigned Insn, uint64_t Address,
const void *Decoder);
static DecodeStatus decodePredicateOperand(MCInst &Inst, unsigned Val,
uint64_t Address,
const void *Decoder);
static DecodeStatus decodeShiftImm(MCInst &Inst, unsigned Insn,
uint64_t Address, const void *Decoder);
@ -226,3 +230,12 @@ static DecodeStatus decodeShiftImm(MCInst &Inst, unsigned Insn,
return MCDisassembler::Success;
}
static DecodeStatus decodePredicateOperand(MCInst &Inst, unsigned Val,
uint64_t Address,
const void *Decoder) {
if (Val >= LPCC::UNKNOWN)
return MCDisassembler::Fail;
Inst.addOperand(MCOperand::createImm(Val));
return MCDisassembler::Success;
}

View File

@ -284,6 +284,22 @@ void LanaiInstPrinter::printMemSplsOperand(const MCInst *MI, int OpNo,
void LanaiInstPrinter::printCCOperand(const MCInst *MI, int OpNo,
raw_ostream &OS) {
const int CC = static_cast<const int>(MI->getOperand(OpNo).getImm());
OS << lanaiCondCodeToString(static_cast<LPCC::CondCode>(CC));
LPCC::CondCode CC =
static_cast<LPCC::CondCode>(MI->getOperand(OpNo).getImm());
// Handle the undefined value here for printing so we don't abort().
if (CC >= LPCC::UNKNOWN)
OS << "<und>";
else
OS << lanaiCondCodeToString(CC);
}
void LanaiInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNo,
raw_ostream &OS) {
LPCC::CondCode CC =
static_cast<LPCC::CondCode>(MI->getOperand(OpNo).getImm());
// Handle the undefined value here for printing so we don't abort().
if (CC >= LPCC::UNKNOWN)
OS << "<und>";
else if (CC != LPCC::ICC_T)
OS << "." << lanaiCondCodeToString(CC);
}

View File

@ -29,6 +29,7 @@ public:
const MCSubtargetInfo &STI) override;
void printOperand(const MCInst *MI, unsigned OpNo, raw_ostream &O,
const char *Modifier = 0);
void printPredicateOperand(const MCInst *MI, unsigned OpNum, raw_ostream &O);
void printMemRiOperand(const MCInst *MI, int OpNo, raw_ostream &O,
const char *Modifier = 0);
void printMemRrOperand(const MCInst *MI, int OpNo, raw_ostream &O,

View File

@ -65,7 +65,6 @@ private:
void LanaiAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
raw_ostream &O, const char *Modifier) {
const MachineOperand &MO = MI->getOperand(OpNum);
unsigned TF = MO.getTargetFlags();
switch (MO.getType()) {
case MachineOperand::MO_Register:
@ -81,10 +80,7 @@ void LanaiAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
break;
case MachineOperand::MO_GlobalAddress:
if (TF == LanaiII::MO_PLT)
O << "plt(" << *getSymbol(MO.getGlobal()) << ")";
else
O << *getSymbol(MO.getGlobal());
O << *getSymbol(MO.getGlobal());
break;
case MachineOperand::MO_BlockAddress: {
@ -94,10 +90,7 @@ void LanaiAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
}
case MachineOperand::MO_ExternalSymbol:
if (TF == LanaiII::MO_PLT)
O << "plt(" << *GetExternalSymbolSymbol(MO.getSymbolName()) << ")";
else
O << *GetExternalSymbolSymbol(MO.getSymbolName());
O << *GetExternalSymbolSymbol(MO.getSymbolName());
break;
case MachineOperand::MO_JumpTableIndex:
@ -116,7 +109,6 @@ void LanaiAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
}
// PrintAsmOperand - Print out an operand for an inline asm expression.
//
bool LanaiAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
unsigned AsmVariant,
const char *ExtraCode, raw_ostream &O) {

View File

@ -124,6 +124,12 @@ LanaiTargetLowering::LanaiTargetLowering(const TargetMachine &TM,
setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote);
}
setTargetDAGCombine(ISD::ADD);
setTargetDAGCombine(ISD::SUB);
setTargetDAGCombine(ISD::AND);
setTargetDAGCombine(ISD::OR);
setTargetDAGCombine(ISD::XOR);
// Function alignments (log2)
setMinFunctionAlignment(2);
setPrefFunctionAlignment(2);
@ -1268,3 +1274,167 @@ SDValue LanaiTargetLowering::LowerSRL_PARTS(SDValue Op,
SDValue Ops[2] = {Lo, Hi};
return DAG.getMergeValues(Ops, dl);
}
// Helper function that checks if N is a null or all ones constant.
static inline bool isZeroOrAllOnes(SDValue N, bool AllOnes) {
return AllOnes ? isAllOnesConstant(N) : isNullConstant(N);
}
// Return true if N is conditionally 0 or all ones.
// Detects these expressions where cc is an i1 value:
//
// (select cc 0, y) [AllOnes=0]
// (select cc y, 0) [AllOnes=0]
// (zext cc) [AllOnes=0]
// (sext cc) [AllOnes=0/1]
// (select cc -1, y) [AllOnes=1]
// (select cc y, -1) [AllOnes=1]
//
// * AllOnes determines whether to check for an all zero (AllOnes false) or an
// all ones operand (AllOnes true).
// * Invert is set when N is the all zero/ones constant when CC is false.
// * OtherOp is set to the alternative value of N.
//
// For example, for (select cc X, Y) and AllOnes = 0 if:
// * X = 0, Invert = False and OtherOp = Y
// * Y = 0, Invert = True and OtherOp = X
static bool isConditionalZeroOrAllOnes(SDNode *N, bool AllOnes, SDValue &CC,
bool &Invert, SDValue &OtherOp,
SelectionDAG &DAG) {
switch (N->getOpcode()) {
default:
return false;
case ISD::SELECT: {
CC = N->getOperand(0);
SDValue N1 = N->getOperand(1);
SDValue N2 = N->getOperand(2);
if (isZeroOrAllOnes(N1, AllOnes)) {
Invert = false;
OtherOp = N2;
return true;
}
if (isZeroOrAllOnes(N2, AllOnes)) {
Invert = true;
OtherOp = N1;
return true;
}
return false;
}
case ISD::ZERO_EXTEND: {
// (zext cc) can never be the all ones value.
if (AllOnes)
return false;
CC = N->getOperand(0);
if (CC.getValueType() != MVT::i1)
return false;
SDLoc dl(N);
EVT VT = N->getValueType(0);
OtherOp = DAG.getConstant(1, dl, VT);
Invert = true;
return true;
}
case ISD::SIGN_EXTEND: {
CC = N->getOperand(0);
if (CC.getValueType() != MVT::i1)
return false;
SDLoc dl(N);
EVT VT = N->getValueType(0);
Invert = !AllOnes;
if (AllOnes)
// When looking for an AllOnes constant, N is an sext, and the 'other'
// value is 0.
OtherOp = DAG.getConstant(0, dl, VT);
else
OtherOp =
DAG.getConstant(APInt::getAllOnesValue(VT.getSizeInBits()), dl, VT);
return true;
}
}
}
// Combine a constant select operand into its use:
//
// (add (select cc, 0, c), x) -> (select cc, x, (add, x, c))
// (sub x, (select cc, 0, c)) -> (select cc, x, (sub, x, c))
// (and (select cc, -1, c), x) -> (select cc, x, (and, x, c)) [AllOnes=1]
// (or (select cc, 0, c), x) -> (select cc, x, (or, x, c))
// (xor (select cc, 0, c), x) -> (select cc, x, (xor, x, c))
//
// The transform is rejected if the select doesn't have a constant operand that
// is null, or all ones when AllOnes is set.
//
// Also recognize sext/zext from i1:
//
// (add (zext cc), x) -> (select cc (add x, 1), x)
// (add (sext cc), x) -> (select cc (add x, -1), x)
//
// These transformations eventually create predicated instructions.
static SDValue combineSelectAndUse(SDNode *N, SDValue Slct, SDValue OtherOp,
TargetLowering::DAGCombinerInfo &DCI,
bool AllOnes) {
SelectionDAG &DAG = DCI.DAG;
EVT VT = N->getValueType(0);
SDValue NonConstantVal;
SDValue CCOp;
bool SwapSelectOps;
if (!isConditionalZeroOrAllOnes(Slct.getNode(), AllOnes, CCOp, SwapSelectOps,
NonConstantVal, DAG))
return SDValue();
// Slct is now know to be the desired identity constant when CC is true.
SDValue TrueVal = OtherOp;
SDValue FalseVal =
DAG.getNode(N->getOpcode(), SDLoc(N), VT, OtherOp, NonConstantVal);
// Unless SwapSelectOps says CC should be false.
if (SwapSelectOps)
std::swap(TrueVal, FalseVal);
return DAG.getNode(ISD::SELECT, SDLoc(N), VT, CCOp, TrueVal, FalseVal);
}
// Attempt combineSelectAndUse on each operand of a commutative operator N.
static SDValue
combineSelectAndUseCommutative(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
bool AllOnes) {
SDValue N0 = N->getOperand(0);
SDValue N1 = N->getOperand(1);
if (N0.getNode()->hasOneUse())
if (SDValue Result = combineSelectAndUse(N, N0, N1, DCI, AllOnes))
return Result;
if (N1.getNode()->hasOneUse())
if (SDValue Result = combineSelectAndUse(N, N1, N0, DCI, AllOnes))
return Result;
return SDValue();
}
// PerformSUBCombine - Target-specific dag combine xforms for ISD::SUB.
static SDValue PerformSUBCombine(SDNode *N,
TargetLowering::DAGCombinerInfo &DCI) {
SDValue N0 = N->getOperand(0);
SDValue N1 = N->getOperand(1);
// fold (sub x, (select cc, 0, c)) -> (select cc, x, (sub, x, c))
if (N1.getNode()->hasOneUse())
if (SDValue Result = combineSelectAndUse(N, N1, N0, DCI, /*AllOnes=*/false))
return Result;
return SDValue();
}
SDValue LanaiTargetLowering::PerformDAGCombine(SDNode *N,
DAGCombinerInfo &DCI) const {
switch (N->getOpcode()) {
default:
break;
case ISD::ADD:
case ISD::OR:
case ISD::XOR:
return combineSelectAndUseCommutative(N, DCI, /*AllOnes=*/false);
case ISD::AND:
return combineSelectAndUseCommutative(N, DCI, /*AllOnes=*/true);
case ISD::SUB:
return PerformSUBCombine(N, DCI);
}
return SDValue();
}

View File

@ -103,6 +103,8 @@ public:
std::vector<SDValue> &Ops,
SelectionDAG &DAG) const override;
SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override;
private:
SDValue LowerCCCCallTo(SDValue Chain, SDValue Callee,
CallingConv::ID CallConv, bool IsVarArg,

View File

@ -122,7 +122,7 @@ bool LanaiInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
return false;
}
static LPCC::CondCode GetOppositeBranchCondition(LPCC::CondCode CC) {
static LPCC::CondCode getOppositeCondition(LPCC::CondCode CC) {
switch (CC) {
case LPCC::ICC_T: // true
return LPCC::ICC_F;
@ -161,6 +161,397 @@ static LPCC::CondCode GetOppositeBranchCondition(LPCC::CondCode CC) {
}
}
std::pair<unsigned, unsigned>
LanaiInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const {
return std::make_pair(TF, 0u);
}
ArrayRef<std::pair<unsigned, const char *>>
LanaiInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
using namespace LanaiII;
static const std::pair<unsigned, const char *> TargetFlags[] = {
{MO_ABS_HI, "lanai-hi"},
{MO_ABS_LO, "lanai-lo"},
{MO_NO_FLAG, "lanai-nf"}};
return makeArrayRef(TargetFlags);
}
bool LanaiInstrInfo::analyzeCompare(const MachineInstr &MI, unsigned &SrcReg,
unsigned &SrcReg2, int &CmpMask,
int &CmpValue) const {
switch (MI.getOpcode()) {
default:
break;
case Lanai::SFSUB_F_RI_LO:
case Lanai::SFSUB_F_RI_HI:
SrcReg = MI.getOperand(0).getReg();
SrcReg2 = 0;
CmpMask = ~0;
CmpValue = MI.getOperand(1).getImm();
return true;
case Lanai::SFSUB_F_RR:
SrcReg = MI.getOperand(0).getReg();
SrcReg2 = MI.getOperand(1).getReg();
CmpMask = ~0;
CmpValue = 0;
return true;
}
return false;
}
// isRedundantFlagInstr - check whether the first instruction, whose only
// purpose is to update flags, can be made redundant.
// * SFSUB_F_RR can be made redundant by SUB_RI if the operands are the same.
// * SFSUB_F_RI can be made redundant by SUB_I if the operands are the same.
inline static bool isRedundantFlagInstr(MachineInstr *CmpI, unsigned SrcReg,
unsigned SrcReg2, int ImmValue,
MachineInstr *OI) {
if (CmpI->getOpcode() == Lanai::SFSUB_F_RR &&
OI->getOpcode() == Lanai::SUB_R &&
((OI->getOperand(1).getReg() == SrcReg &&
OI->getOperand(2).getReg() == SrcReg2) ||
(OI->getOperand(1).getReg() == SrcReg2 &&
OI->getOperand(2).getReg() == SrcReg)))
return true;
if (((CmpI->getOpcode() == Lanai::SFSUB_F_RI_LO &&
OI->getOpcode() == Lanai::SUB_I_LO) ||
(CmpI->getOpcode() == Lanai::SFSUB_F_RI_HI &&
OI->getOpcode() == Lanai::SUB_I_HI)) &&
OI->getOperand(1).getReg() == SrcReg &&
OI->getOperand(2).getImm() == ImmValue)
return true;
return false;
}
inline static unsigned flagSettingOpcodeVariant(unsigned OldOpcode) {
switch (OldOpcode) {
case Lanai::ADD_I_HI:
return Lanai::ADD_F_I_HI;
case Lanai::ADD_I_LO:
return Lanai::ADD_F_I_LO;
case Lanai::ADD_R:
return Lanai::ADD_F_R;
case Lanai::ADDC_I_HI:
return Lanai::ADDC_F_I_HI;
case Lanai::ADDC_I_LO:
return Lanai::ADDC_F_I_LO;
case Lanai::ADDC_R:
return Lanai::ADDC_F_R;
case Lanai::AND_I_HI:
return Lanai::AND_F_I_HI;
case Lanai::AND_I_LO:
return Lanai::AND_F_I_LO;
case Lanai::AND_R:
return Lanai::AND_F_R;
case Lanai::OR_I_HI:
return Lanai::OR_F_I_HI;
case Lanai::OR_I_LO:
return Lanai::OR_F_I_LO;
case Lanai::OR_R:
return Lanai::OR_F_R;
case Lanai::SL_I:
return Lanai::SL_F_I;
case Lanai::SRL_R:
return Lanai::SRL_F_R;
case Lanai::SA_I:
return Lanai::SA_F_I;
case Lanai::SRA_R:
return Lanai::SRA_F_R;
case Lanai::SUB_I_HI:
return Lanai::SUB_F_I_HI;
case Lanai::SUB_I_LO:
return Lanai::SUB_F_I_LO;
case Lanai::SUB_R:
return Lanai::SUB_F_R;
case Lanai::SUBB_I_HI:
return Lanai::SUBB_F_I_HI;
case Lanai::SUBB_I_LO:
return Lanai::SUBB_F_I_LO;
case Lanai::SUBB_R:
return Lanai::SUBB_F_R;
case Lanai::XOR_I_HI:
return Lanai::XOR_F_I_HI;
case Lanai::XOR_I_LO:
return Lanai::XOR_F_I_LO;
case Lanai::XOR_R:
return Lanai::XOR_F_R;
default:
return Lanai::NOP;
}
}
bool LanaiInstrInfo::optimizeCompareInstr(
MachineInstr &CmpInstr, unsigned SrcReg, unsigned SrcReg2, int CmpMask,
int CmpValue, const MachineRegisterInfo *MRI) const {
// Get the unique definition of SrcReg.
MachineInstr *MI = MRI->getUniqueVRegDef(SrcReg);
if (!MI)
return false;
// Get ready to iterate backward from CmpInstr.
MachineBasicBlock::iterator I = CmpInstr, E = MI,
B = CmpInstr.getParent()->begin();
// Early exit if CmpInstr is at the beginning of the BB.
if (I == B)
return false;
// There are two possible candidates which can be changed to set SR:
// One is MI, the other is a SUB instruction.
// * For SFSUB_F_RR(r1,r2), we are looking for SUB(r1,r2) or SUB(r2,r1).
// * For SFSUB_F_RI(r1, CmpValue), we are looking for SUB(r1, CmpValue).
MachineInstr *Sub = nullptr;
if (SrcReg2 != 0)
// MI is not a candidate to transform into a flag setting instruction.
MI = nullptr;
else if (MI->getParent() != CmpInstr.getParent() || CmpValue != 0) {
// Conservatively refuse to convert an instruction which isn't in the same
// BB as the comparison. Don't return if SFSUB_F_RI and CmpValue != 0 as Sub
// may still be a candidate.
if (CmpInstr.getOpcode() == Lanai::SFSUB_F_RI_LO)
MI = nullptr;
else
return false;
}
// Check that SR isn't set between the comparison instruction and the
// instruction we want to change while searching for Sub.
const TargetRegisterInfo *TRI = &getRegisterInfo();
for (--I; I != E; --I) {
const MachineInstr &Instr = *I;
if (Instr.modifiesRegister(Lanai::SR, TRI) ||
Instr.readsRegister(Lanai::SR, TRI))
// This instruction modifies or uses SR after the one we want to change.
// We can't do this transformation.
return false;
// Check whether CmpInstr can be made redundant by the current instruction.
if (isRedundantFlagInstr(&CmpInstr, SrcReg, SrcReg2, CmpValue, &*I)) {
Sub = &*I;
break;
}
// Don't search outside the containing basic block.
if (I == B)
return false;
}
// Return false if no candidates exist.
if (!MI && !Sub)
return false;
// The single candidate is called MI.
if (!MI)
MI = Sub;
if (flagSettingOpcodeVariant(MI->getOpcode()) != Lanai::NOP) {
bool isSafe = false;
SmallVector<std::pair<MachineOperand *, LPCC::CondCode>, 4>
OperandsToUpdate;
I = CmpInstr;
E = CmpInstr.getParent()->end();
while (!isSafe && ++I != E) {
const MachineInstr &Instr = *I;
for (unsigned IO = 0, EO = Instr.getNumOperands(); !isSafe && IO != EO;
++IO) {
const MachineOperand &MO = Instr.getOperand(IO);
if (MO.isRegMask() && MO.clobbersPhysReg(Lanai::SR)) {
isSafe = true;
break;
}
if (!MO.isReg() || MO.getReg() != Lanai::SR)
continue;
if (MO.isDef()) {
isSafe = true;
break;
}
// Condition code is after the operand before SR.
LPCC::CondCode CC;
CC = (LPCC::CondCode)Instr.getOperand(IO - 1).getImm();
if (Sub) {
LPCC::CondCode NewCC = getOppositeCondition(CC);
if (NewCC == LPCC::ICC_T)
return false;
// If we have SUB(r1, r2) and CMP(r2, r1), the condition code based on
// CMP needs to be updated to be based on SUB. Push the condition
// code operands to OperandsToUpdate. If it is safe to remove
// CmpInstr, the condition code of these operands will be modified.
if (SrcReg2 != 0 && Sub->getOperand(1).getReg() == SrcReg2 &&
Sub->getOperand(2).getReg() == SrcReg) {
OperandsToUpdate.push_back(
std::make_pair(&((*I).getOperand(IO - 1)), NewCC));
}
} else {
// No Sub, so this is x = <op> y, z; cmp x, 0.
switch (CC) {
case LPCC::ICC_EQ: // Z
case LPCC::ICC_NE: // Z
case LPCC::ICC_MI: // N
case LPCC::ICC_PL: // N
case LPCC::ICC_F: // none
case LPCC::ICC_T: // none
// SR can be used multiple times, we should continue.
break;
case LPCC::ICC_CS: // C
case LPCC::ICC_CC: // C
case LPCC::ICC_VS: // V
case LPCC::ICC_VC: // V
case LPCC::ICC_HI: // C Z
case LPCC::ICC_LS: // C Z
case LPCC::ICC_GE: // N V
case LPCC::ICC_LT: // N V
case LPCC::ICC_GT: // Z N V
case LPCC::ICC_LE: // Z N V
// The instruction uses the V bit or C bit which is not safe.
return false;
case LPCC::UNKNOWN:
return false;
}
}
}
}
// If SR is not killed nor re-defined, we should check whether it is
// live-out. If it is live-out, do not optimize.
if (!isSafe) {
MachineBasicBlock *MBB = CmpInstr.getParent();
for (MachineBasicBlock::succ_iterator SI = MBB->succ_begin(),
SE = MBB->succ_end();
SI != SE; ++SI)
if ((*SI)->isLiveIn(Lanai::SR))
return false;
}
// Toggle the optional operand to SR.
MI->setDesc(get(flagSettingOpcodeVariant(MI->getOpcode())));
MI->addRegisterDefined(Lanai::SR);
CmpInstr.eraseFromParent();
return true;
}
return false;
}
bool LanaiInstrInfo::analyzeSelect(const MachineInstr &MI,
SmallVectorImpl<MachineOperand> &Cond,
unsigned &TrueOp, unsigned &FalseOp,
bool &Optimizable) const {
assert(MI.getOpcode() == Lanai::SELECT && "unknown select instruction");
// Select operands:
// 0: Def.
// 1: True use.
// 2: False use.
// 3: Condition code.
TrueOp = 1;
FalseOp = 2;
Cond.push_back(MI.getOperand(3));
Optimizable = true;
return false;
}
// Identify instructions that can be folded into a SELECT instruction, and
// return the defining instruction.
static MachineInstr *canFoldIntoSelect(unsigned Reg,
const MachineRegisterInfo &MRI,
const TargetInstrInfo *TII) {
if (!TargetRegisterInfo::isVirtualRegister(Reg))
return nullptr;
if (!MRI.hasOneNonDBGUse(Reg))
return nullptr;
MachineInstr *MI = MRI.getVRegDef(Reg);
if (!MI)
return nullptr;
// MI is folded into the SELECT by predicating it.
if (!MI->isPredicable())
return nullptr;
// Check if MI has any non-dead defs or physreg uses. This also detects
// predicated instructions which will be reading SR.
for (unsigned i = 1, e = MI->getNumOperands(); i != e; ++i) {
const MachineOperand &MO = MI->getOperand(i);
// Reject frame index operands.
if (MO.isFI() || MO.isCPI() || MO.isJTI())
return nullptr;
if (!MO.isReg())
continue;
// MI can't have any tied operands, that would conflict with predication.
if (MO.isTied())
return nullptr;
if (TargetRegisterInfo::isPhysicalRegister(MO.getReg()))
return nullptr;
if (MO.isDef() && !MO.isDead())
return nullptr;
}
bool DontMoveAcrossStores = true;
if (!MI->isSafeToMove(/*AliasAnalysis=*/nullptr, DontMoveAcrossStores))
return nullptr;
return MI;
}
MachineInstr *
LanaiInstrInfo::optimizeSelect(MachineInstr &MI,
SmallPtrSetImpl<MachineInstr *> &SeenMIs,
bool PreferFalse) const {
assert(MI.getOpcode() == Lanai::SELECT && "unknown select instruction");
MachineRegisterInfo &MRI = MI.getParent()->getParent()->getRegInfo();
MachineInstr *DefMI = canFoldIntoSelect(MI.getOperand(1).getReg(), MRI, this);
bool Invert = !DefMI;
if (!DefMI)
DefMI = canFoldIntoSelect(MI.getOperand(2).getReg(), MRI, this);
if (!DefMI)
return nullptr;
// Find new register class to use.
MachineOperand FalseReg = MI.getOperand(Invert ? 1 : 2);
unsigned DestReg = MI.getOperand(0).getReg();
const TargetRegisterClass *PreviousClass = MRI.getRegClass(FalseReg.getReg());
if (!MRI.constrainRegClass(DestReg, PreviousClass))
return nullptr;
// Create a new predicated version of DefMI.
MachineInstrBuilder NewMI =
BuildMI(*MI.getParent(), MI, MI.getDebugLoc(), DefMI->getDesc(), DestReg);
// Copy all the DefMI operands, excluding its (null) predicate.
const MCInstrDesc &DefDesc = DefMI->getDesc();
for (unsigned i = 1, e = DefDesc.getNumOperands();
i != e && !DefDesc.OpInfo[i].isPredicate(); ++i)
NewMI.addOperand(DefMI->getOperand(i));
unsigned CondCode = MI.getOperand(3).getImm();
if (Invert)
NewMI.addImm(getOppositeCondition(LPCC::CondCode(CondCode)));
else
NewMI.addImm(CondCode);
NewMI.copyImplicitOps(MI);
// The output register value when the predicate is false is an implicit
// register operand tied to the first def. The tie makes the register
// allocator ensure the FalseReg is allocated the same register as operand 0.
FalseReg.setImplicit();
NewMI.addOperand(FalseReg);
NewMI->tieOperands(0, NewMI->getNumOperands() - 1);
// Update SeenMIs set: register newly created MI and erase removed DefMI.
SeenMIs.insert(NewMI);
SeenMIs.erase(DefMI);
// If MI is inside a loop, and DefMI is outside the loop, then kill flags on
// DefMI would be invalid when transferred inside the loop. Checking for a
// loop is expensive, but at least remove kill flags if they are in different
// BBs.
if (DefMI->getParent() != MI.getParent())
NewMI->clearKillInfo();
// The caller will erase MI, but not DefMI.
DefMI->eraseFromParent();
return NewMI;
}
// The AnalyzeBranch function is used to examine conditional instructions and
// remove unnecessary instructions. This method is used by BranchFolder and
// IfConverter machine function passes to improve the CFG.
@ -262,7 +653,7 @@ bool LanaiInstrInfo::ReverseBranchCondition(
LPCC::CondCode BranchCond =
static_cast<LPCC::CondCode>(Condition[0].getImm());
Condition[0].setImm(GetOppositeBranchCondition(BranchCond));
Condition[0].setImm(getOppositeCondition(BranchCond));
return false;
}

View File

@ -75,6 +75,12 @@ public:
int64_t &Offset, unsigned &Width,
const TargetRegisterInfo *TRI) const;
std::pair<unsigned, unsigned>
decomposeMachineOperandsTargetFlags(unsigned TF) const override;
ArrayRef<std::pair<unsigned, const char *>>
getSerializableDirectMachineOperandTargetFlags() const override;
bool AnalyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TrueBlock,
MachineBasicBlock *&FalseBlock,
SmallVectorImpl<MachineOperand> &Condition,
@ -82,6 +88,47 @@ public:
unsigned RemoveBranch(MachineBasicBlock &MBB) const override;
// For a comparison instruction, return the source registers in SrcReg and
// SrcReg2 if having two register operands, and the value it compares against
// in CmpValue. Return true if the comparison instruction can be analyzed.
bool analyzeCompare(const MachineInstr &MI, unsigned &SrcReg,
unsigned &SrcReg2, int &CmpMask,
int &CmpValue) const override;
// See if the comparison instruction can be converted into something more
// efficient. E.g., on Lanai register-register instructions can set the flag
// register, obviating the need for a separate compare.
bool optimizeCompareInstr(MachineInstr &CmpInstr, unsigned SrcReg,
unsigned SrcReg2, int CmpMask, int CmpValue,
const MachineRegisterInfo *MRI) const override;
// Analyze the given select instruction, returning true if it cannot be
// understood. It is assumed that MI->isSelect() is true.
//
// When successful, return the controlling condition and the operands that
// determine the true and false result values.
//
// Result = SELECT Cond, TrueOp, FalseOp
//
// Lanai can optimize certain select instructions, for example by predicating
// the instruction defining one of the operands and sets Optimizable to true.
bool analyzeSelect(const MachineInstr &MI,
SmallVectorImpl<MachineOperand> &Cond, unsigned &TrueOp,
unsigned &FalseOp, bool &Optimizable) const override;
// Given a select instruction that was understood by analyzeSelect and
// returned Optimizable = true, attempt to optimize MI by merging it with one
// of its operands. Returns NULL on failure.
//
// When successful, returns the new select instruction. The client is
// responsible for deleting MI.
//
// If both sides of the select can be optimized, the TrueOp is modifed.
// PreferFalse is not used.
MachineInstr *optimizeSelect(MachineInstr &MI,
SmallPtrSetImpl<MachineInstr *> &SeenMIs,
bool PreferFalse) const override;
bool ReverseBranchCondition(
SmallVectorImpl<MachineOperand> &Condition) const override;

View File

@ -235,6 +235,15 @@ def CCOp : Operand<i32> {
let PrintMethod = "printCCOperand";
}
// Predicate operand. Default to 0 = true.
def CondCodeOperand : AsmOperandClass { let Name = "CondCode"; }
def pred : PredicateOperand<i32, (ops i32imm), (ops (i32 0))> {
let PrintMethod = "printPredicateOperand";
let ParserMatchClass = CondCodeOperand;
let DecoderMethod = "decodePredicateOperand";
}
let hasSideEffects = 0, Inst = 0x00000001 in
def NOP : InstLanai<(outs), (ins), "nop", []>;
@ -283,16 +292,10 @@ multiclass ALUarith<bits<3> subOp, string AsmStr, SDNode OpNode,
defm I_ : ALUbase<subOp, AsmStr, OpNode, LoExt, HiExt, [], []>;
// Register Register
let JJJJJ = 0, DDDI = 0 in
def R : InstRR<subOp, (outs GPR:$Rd), (ins GPR:$Rs1, GPR:$Rs2),
!strconcat(AsmStr, "\t$Rs1, $Rs2, $Rd"),
let JJJJJ = 0 in
def R : InstRR<subOp, (outs GPR:$Rd), (ins GPR:$Rs1, GPR:$Rs2, pred:$DDDI),
!strconcat(AsmStr, "$DDDI\t$Rs1, $Rs2, $Rd"),
[(set GPR:$Rd, (OpNode GPR:$Rs1, GPR:$Rs2))]>;
// RR Conditional
let JJJJJ = 0, Uses = [SR] in
def R_CC : InstRR<subOp, (outs GPR:$Rd),
(ins GPR:$Rs1, GPR:$Rs2, CCOp:$DDDI),
!strconcat(AsmStr, ".$DDDI\t$Rs1, $Rs2, $Rd"),
[]>;
}
multiclass ALUlogic<bits<3> subOp, string AsmStr, SDNode OpNode,
@ -302,16 +305,10 @@ multiclass ALUlogic<bits<3> subOp, string AsmStr, SDNode OpNode,
[(set GPR:$Rd, (OpNode GPR:$Rs1, HiExt:$imm16))]>;
// Register Register
let JJJJJ = 0, DDDI = 0 in
def R : InstRR<subOp, (outs GPR:$Rd), (ins GPR:$Rs1, GPR:$Rs2),
!strconcat(AsmStr, "\t$Rs1, $Rs2, $Rd"),
let JJJJJ = 0 in
def R : InstRR<subOp, (outs GPR:$Rd), (ins GPR:$Rs1, GPR:$Rs2, pred:$DDDI),
!strconcat(AsmStr, "$DDDI\t$Rs1, $Rs2, $Rd"),
[(set GPR:$Rd, (OpNode GPR:$Rs1, GPR:$Rs2))]>;
// RR Conditional
let JJJJJ = 0, Uses = [SR] in
def R_CC : InstRR<subOp, (outs GPR:$Rd),
(ins GPR:$Rs1, GPR:$Rs2, CCOp:$DDDI),
!strconcat(AsmStr, ".$DDDI\t$Rs1, $Rs2, $Rd"),
[]>;
}
// Non flag setting ALU operations
@ -407,7 +404,7 @@ def : Pat<(LanaiSubbF GPR:$Rs1, i32lo16z:$imm),
def : Pat<(LanaiSubbF GPR:$Rs1, i32hi16:$imm),
(SUBB_F_I_HI GPR:$Rs1, i32hi16:$imm)>;
def : InstAlias<"mov $src, $dst", (ADD_R GPR:$dst, GPR:$src, R0)>;
def : InstAlias<"mov $src, $dst", (ADD_R GPR:$dst, GPR:$src, R0, 0)>;
let isAsCheapAsAMove = 1, Rs1 = R0.Num, isCodeGenOnly = 1, H = 1, F = 0,
isReMaterializable = 1 in
@ -716,11 +713,15 @@ multiclass SF<bits<3> op2Val, string AsmStr> {
!strconcat(AsmStr, "\t$Rs1, $Rs2, %r0"),
[(LanaiSetFlag (i32 GPR:$Rs1), (i32 GPR:$Rs2))]>;
let F = 1, Rd = R0.Num, H = 0, Defs = [SR] in
def _RI : InstRI<op2Val, (outs), (ins GPR:$Rs1, i32lo16z:$imm16),
def _RI_LO : InstRI<op2Val, (outs), (ins GPR:$Rs1, i32lo16z:$imm16),
!strconcat(AsmStr, "\t$Rs1, $imm16, %r0"),
[(LanaiSetFlag (i32 GPR:$Rs1), i32lo16z:$imm16)]>;
let F = 1, Rd = R0.Num, H = 1, Defs = [SR] in
def _RI_HI : InstRI<op2Val, (outs), (ins GPR:$Rs1, i32hi16:$imm16),
!strconcat(AsmStr, "\t$Rs1, $imm16, %r0"),
[(LanaiSetFlag (i32 GPR:$Rs1), i32hi16:$imm16)]>;
}
let isCodeGenOnly = 1 in {
let isCodeGenOnly = 1, isCompare = 1 in {
defm SFSUB_F : SF<0b010, "sub.f">;
}

View File

@ -197,11 +197,16 @@ void LanaiRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
}
// Reg = FrameReg OP Reg
if (MI.getOpcode() == Lanai::ADD_I_LO) {
if (HasNegOffset)
MI.setDesc(TII->get(Lanai::SUB_R));
else
MI.setDesc(TII->get(Lanai::ADD_R));
} else if (isSPLSOpcode(MI.getOpcode()) || isRMOpcode(MI.getOpcode())) {
BuildMI(*MI.getParent(), II, DL,
HasNegOffset ? TII->get(Lanai::SUB_R) : TII->get(Lanai::ADD_R),
MI.getOperand(0).getReg())
.addReg(FrameReg)
.addReg(Reg)
.addImm(LPCC::ICC_T);
MI.eraseFromParent();
return;
}
if (isSPLSOpcode(MI.getOpcode()) || isRMOpcode(MI.getOpcode())) {
MI.setDesc(TII->get(getRRMOpcodeVariant(MI.getOpcode())));
if (HasNegOffset) {
// Change the ALU op (operand 3) from LPAC::ADD (the default) to

View File

@ -1,294 +0,0 @@
//===-- LanaiSetflagAluCombiner.cpp - Pass to combine set flag & ALU ops --===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "Lanai.h"
#include "LanaiTargetMachine.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/RegisterScavenging.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Target/TargetInstrInfo.h"
using namespace llvm;
#define DEBUG_TYPE "lanai-setflag-alu-combiner"
STATISTIC(NumSetflagAluCombined,
"Number of SET_FLAG and ALU instructions combined");
static llvm::cl::opt<bool> DisableSetflagAluCombiner(
"disable-lanai-setflag-alu-combiner", llvm::cl::init(false),
llvm::cl::desc("Do not combine SET_FLAG and ALU operators"),
llvm::cl::Hidden);
namespace llvm {
void initializeLanaiSetflagAluCombinerPass(PassRegistry &);
} // namespace llvm
namespace {
typedef MachineBasicBlock::iterator MbbIterator;
typedef MachineFunction::iterator MfIterator;
class LanaiSetflagAluCombiner : public MachineFunctionPass {
public:
static char ID;
LanaiSetflagAluCombiner() : MachineFunctionPass(ID) {
initializeLanaiSetflagAluCombinerPass(*PassRegistry::getPassRegistry());
}
const char *getPassName() const override {
return "Lanai SET_FLAG ALU combiner pass";
}
bool runOnMachineFunction(MachineFunction &F) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::AllVRegsAllocated);
}
private:
bool CombineSetflagAluInBasicBlock(MachineFunction *MF,
MachineBasicBlock *BB);
};
} // namespace
char LanaiSetflagAluCombiner::ID = 0;
INITIALIZE_PASS(LanaiSetflagAluCombiner, DEBUG_TYPE,
"Lanai SET_FLAG ALU combiner pass", false, false)
namespace {
const unsigned kInvalid = -1;
static unsigned flagSettingOpcodeVariant(unsigned OldOpcode) {
switch (OldOpcode) {
case Lanai::ADD_I_HI:
return Lanai::ADD_F_I_HI;
case Lanai::ADD_I_LO:
return Lanai::ADD_F_I_LO;
case Lanai::ADD_R:
return Lanai::ADD_F_R;
case Lanai::ADD_R_CC:
return Lanai::ADD_F_R_CC;
case Lanai::ADDC_I_HI:
return Lanai::ADDC_F_I_HI;
case Lanai::ADDC_I_LO:
return Lanai::ADDC_F_I_LO;
case Lanai::ADDC_R:
return Lanai::ADDC_F_R;
case Lanai::ADDC_R_CC:
return Lanai::ADDC_F_R_CC;
case Lanai::AND_I_HI:
return Lanai::AND_F_I_HI;
case Lanai::AND_I_LO:
return Lanai::AND_F_I_LO;
case Lanai::AND_R:
return Lanai::AND_F_R;
case Lanai::AND_R_CC:
return Lanai::AND_F_R_CC;
case Lanai::OR_I_HI:
return Lanai::OR_F_I_HI;
case Lanai::OR_I_LO:
return Lanai::OR_F_I_LO;
case Lanai::OR_R:
return Lanai::OR_F_R;
case Lanai::OR_R_CC:
return Lanai::OR_F_R_CC;
case Lanai::SL_I:
return Lanai::SL_F_I;
case Lanai::SRL_R:
return Lanai::SRL_F_R;
case Lanai::SA_I:
return Lanai::SA_F_I;
case Lanai::SRA_R:
return Lanai::SRA_F_R;
case Lanai::SUB_I_HI:
return Lanai::SUB_F_I_HI;
case Lanai::SUB_I_LO:
return Lanai::SUB_F_I_LO;
case Lanai::SUB_R:
return Lanai::SUB_F_R;
case Lanai::SUB_R_CC:
return Lanai::SUB_F_R_CC;
case Lanai::SUBB_I_HI:
return Lanai::SUBB_F_I_HI;
case Lanai::SUBB_I_LO:
return Lanai::SUBB_F_I_LO;
case Lanai::SUBB_R:
return Lanai::SUBB_F_R;
case Lanai::SUBB_R_CC:
return Lanai::SUBB_F_R_CC;
case Lanai::XOR_I_HI:
return Lanai::XOR_F_I_HI;
case Lanai::XOR_I_LO:
return Lanai::XOR_F_I_LO;
case Lanai::XOR_R:
return Lanai::XOR_F_R;
case Lanai::XOR_R_CC:
return Lanai::XOR_F_R_CC;
default:
return kInvalid;
}
}
// Returns whether opcode corresponds to instruction that sets flags.
static bool isFlagSettingInstruction(MbbIterator Instruction) {
return Instruction->killsRegister(Lanai::SR);
}
// Return the Conditional Code operand for a given instruction kind. For
// example, operand at index 1 of a BRIND_CC instruction is the conditional code
// (eq, ne, etc.). Returns -1 if the instruction does not have a conditional
// code.
static int getCCOperandPosition(unsigned Opcode) {
switch (Opcode) {
case Lanai::BRIND_CC:
case Lanai::BRIND_CCA:
case Lanai::BRR:
case Lanai::BRCC:
case Lanai::SCC:
return 1;
case Lanai::SELECT:
case Lanai::ADDC_F_R_CC:
case Lanai::ADDC_R_CC:
case Lanai::ADD_F_R_CC:
case Lanai::ADD_R_CC:
case Lanai::AND_F_R_CC:
case Lanai::AND_R_CC:
case Lanai::OR_F_R_CC:
case Lanai::OR_R_CC:
case Lanai::SUBB_F_R_CC:
case Lanai::SUBB_R_CC:
case Lanai::SUB_F_R_CC:
case Lanai::SUB_R_CC:
case Lanai::XOR_F_R_CC:
case Lanai::XOR_R_CC:
return 3;
default:
return -1;
}
}
// Returns true if instruction is a lowered SET_FLAG instruction with 0/R0 as
// the first operand and whose conditional code is such that it can be merged
// (i.e., EQ, NE, PL and MI).
static bool isSuitableSetflag(MbbIterator Instruction, MbbIterator End) {
unsigned Opcode = Instruction->getOpcode();
if (Opcode == Lanai::SFSUB_F_RI || Opcode == Lanai::SFSUB_F_RR) {
const MachineOperand &Operand = Instruction->getOperand(1);
if (Operand.isReg() && Operand.getReg() != Lanai::R0)
return false;
if (Operand.isImm() && Operand.getImm() != 0)
return false;
MbbIterator SCCUserIter = Instruction;
while (SCCUserIter != End) {
++SCCUserIter;
if (SCCUserIter == End)
break;
// Skip debug instructions. Debug instructions don't affect codegen.
if (SCCUserIter->isDebugValue())
continue;
// Early exit when encountering flag setting or return instruction.
if (isFlagSettingInstruction(SCCUserIter))
// Only return true if flags are set post the flag setting instruction
// tested or a return is executed.
return true;
int CCIndex = getCCOperandPosition(SCCUserIter->getOpcode());
if (CCIndex != -1) {
LPCC::CondCode CC = static_cast<LPCC::CondCode>(
SCCUserIter->getOperand(CCIndex).getImm());
// Return false if the flag is used outside of a EQ, NE, PL and MI.
if (CC != LPCC::ICC_EQ && CC != LPCC::ICC_NE && CC != LPCC::ICC_PL &&
CC != LPCC::ICC_MI)
return false;
}
}
}
return false;
}
// Combines a SET_FLAG instruction comparing a register with 0 and an ALU
// operation that sets the same register used in the comparison into a single
// flag setting ALU instruction (both instructions combined are removed and new
// flag setting ALU operation inserted where ALU instruction was).
bool LanaiSetflagAluCombiner::CombineSetflagAluInBasicBlock(
MachineFunction *MF, MachineBasicBlock *BB) {
bool Modified = false;
const TargetInstrInfo *TII =
MF->getSubtarget<LanaiSubtarget>().getInstrInfo();
MbbIterator SetflagIter = BB->begin();
MbbIterator End = BB->end();
MbbIterator Begin = BB->begin();
while (SetflagIter != End) {
bool Replaced = false;
if (isSuitableSetflag(SetflagIter, End)) {
MbbIterator AluIter = SetflagIter;
while (AluIter != Begin) {
--AluIter;
// Skip debug instructions. Debug instructions don't affect codegen.
if (AluIter->isDebugValue())
continue;
// Early exit when encountering flag setting instruction.
if (isFlagSettingInstruction(AluIter))
break;
// Check that output of AluIter is equal to input of SetflagIter.
if (AluIter->getNumOperands() > 1 && AluIter->getOperand(0).isReg() &&
(AluIter->getOperand(0).getReg() ==
SetflagIter->getOperand(0).getReg())) {
unsigned NewOpc = flagSettingOpcodeVariant(AluIter->getOpcode());
if (NewOpc == kInvalid)
break;
// Change the ALU instruction to the flag setting variant.
AluIter->setDesc(TII->get(NewOpc));
AluIter->addImplicitDefUseOperands(*MF);
Replaced = true;
++NumSetflagAluCombined;
break;
}
}
// Erase the setflag instruction if merged.
if (Replaced)
BB->erase(SetflagIter++);
}
Modified |= Replaced;
if (!Replaced)
++SetflagIter;
}
return Modified;
}
// Driver function that iterates over the machine basic building blocks of a
// machine function
bool LanaiSetflagAluCombiner::runOnMachineFunction(MachineFunction &MF) {
if (DisableSetflagAluCombiner)
return false;
bool Modified = false;
MfIterator End = MF.end();
for (MfIterator MFI = MF.begin(); MFI != End; ++MFI) {
Modified |= CombineSetflagAluInBasicBlock(&MF, &*MFI);
}
return Modified;
}
} // namespace
FunctionPass *llvm::createLanaiSetflagAluCombinerPass() {
return new LanaiSetflagAluCombiner();
}

View File

@ -28,7 +28,6 @@ using namespace llvm;
namespace llvm {
void initializeLanaiMemAluCombinerPass(PassRegistry &);
void initializeLanaiSetflagAluCombinerPass(PassRegistry &);
} // namespace llvm
extern "C" void LLVMInitializeLanaiTarget() {
@ -112,5 +111,4 @@ void LanaiPassConfig::addPreEmitPass() {
// scheduling pass.
void LanaiPassConfig::addPreSched2() {
addPass(createLanaiMemAluCombinerPass());
addPass(createLanaiSetflagAluCombinerPass());
}

View File

@ -24,7 +24,6 @@ namespace llvm {
// LanaiII - This namespace holds all of the target specific flags that
// instruction info tracks.
//
namespace LanaiII {
// Target Operand Flag enum.
enum TOF {
@ -36,31 +35,6 @@ enum TOF {
// address.
MO_ABS_HI,
MO_ABS_LO,
// MO_PIC_BASE_OFFSET - On a symbol operand this indicates that the
// immediate should get the value of the symbol minus the PIC base label:
// SYMBOL_LABEL - PICBASELABEL
MO_PIC_BASE_OFFSET,
// MO_GOT - On a symbol operand this indicates that the immediate is the
// offset to the GOT entry for the symbol name from the base of the GOT.
MO_GOT,
// MO_GOTOFFHI/MO_GOTOFFLO - On a symbol operand this indicates that the
// immediate is the offset to the location of the symbol name from the
// base of the GOT.
MO_GOTOFFHI,
MO_GOTOFFLO,
// MO_GOTPCHI/MO_GOTPCLO - On a symbol operand this indicates that
// the immediate is an offset to the GOT entry for the symbol name
// from the current code location.
MO_GOTPCHI,
MO_GOTPCLO,
// MO_PLT - On a symbol operand this indicates that the immediate is
// offset to the PLT entry of symbol name from the current code location.
MO_PLT
};
} // namespace LanaiII

View File

@ -1,170 +0,0 @@
; RUN: llc < %s -march=lanai | FileCheck %s
; Test the alu setcc combiner.
; TODO: Enhance combiner to handle this case. This expands into:
; sub %r7, %r6, %r3
; sub.f %r7, %r6, %r0
; sel.eq %r18, %r3, %rv
; This is different from the pattern currently matched. If the lowered form had
; been sub.f %r3, 0, %r0 then it would have matched.
; Function Attrs: norecurse nounwind readnone
define i32 @test0a(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%sub = sub i32 %b, %a
%cmp = icmp eq i32 %sub, 0
%cond = select i1 %cmp, i32 %c, i32 %sub
ret i32 %cond
}
; CHECK-LABEL: test0a
; CHECK: sub.f %r7
; CHECK: sel.eq
; Function Attrs: norecurse nounwind readnone
define i32 @test0b(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%cmp = icmp eq i32 %b, %a
%cond = select i1 %cmp, i32 %c, i32 %b
ret i32 %cond
}
; CHECK-LABEL: test0b
; CHECK: sub.f %r7, %r6, %r0
; CHECK-NEXT: sel.eq
; Function Attrs: norecurse nounwind readnone
define i32 @test1a(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%sub = sub i32 %b, %a
%cmp = icmp slt i32 %sub, 0
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
; CHECK-LABEL: test1a
; CHECK: sub.f %r7, %r6
; CHECK-NEXT: sel.mi
; Function Attrs: norecurse nounwind readnone
define i32 @test1b(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%sub = sub i32 %b, %a
%cmp = icmp slt i32 %sub, 0
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
; CHECK-LABEL: test1b
; CHECK: sub.f %r7, %r6
; CHECK-NEXT: sel.mi
; Function Attrs: norecurse nounwind readnone
define i32 @test2a(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%sub = sub i32 %b, %a
%cmp = icmp sgt i32 %sub, -1
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
; CHECK-LABEL: test2a
; CHECK: sub.f %r7, %r6
; CHECK: sel.pl
; Function Attrs: norecurse nounwind readnone
define i32 @test2b(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%sub = sub i32 %b, %a
%cmp = icmp sgt i32 %sub, -1
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
; CHECK-LABEL: test2b
; CHECK: sub.f %r7, %r6
; CHECK: sel.pl
; Function Attrs: norecurse nounwind readnone
define i32 @test3(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%sub = sub i32 %b, %a
%cmp = icmp slt i32 %sub, 1
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
; Function Attrs: norecurse nounwind readnone
define i32 @test4(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) #0 {
entry:
%cmp = icmp ne i32 %a, 0
%cmp1 = icmp ult i32 %a, %b
%or.cond = and i1 %cmp, %cmp1
br i1 %or.cond, label %return, label %if.end
if.end: ; preds = %entry
%cmp2 = icmp ne i32 %b, 0
%cmp4 = icmp ult i32 %b, %c
%or.cond29 = and i1 %cmp2, %cmp4
br i1 %or.cond29, label %return, label %if.end6
if.end6: ; preds = %if.end
%cmp7 = icmp ne i32 %c, 0
%cmp9 = icmp ult i32 %c, %d
%or.cond30 = and i1 %cmp7, %cmp9
br i1 %or.cond30, label %return, label %if.end11
if.end11: ; preds = %if.end6
%cmp12 = icmp ne i32 %d, 0
%cmp14 = icmp ult i32 %d, %a
%or.cond31 = and i1 %cmp12, %cmp14
%b. = select i1 %or.cond31, i32 %b, i32 21
ret i32 %b.
return: ; preds = %if.end6, %if.end, %entry
%retval.0 = phi i32 [ %c, %entry ], [ %d, %if.end ], [ %a, %if.end6 ]
ret i32 %retval.0
}
; CHECK-LABEL: test4
; TODO: Re-enable test. This test is disabled post making the combiner more
; conservative.
; DISABLED_CHECK: and.f
; Test to avoid incorrect fusing that spans across basic blocks
@a = global i32 -1, align 4
@b = global i32 0, align 4
; Function Attrs: nounwind
define void @testBB() {
entry:
%0 = load i32, i32* @a, align 4, !tbaa !1
%1 = load i32, i32* @b, align 4, !tbaa !1
%sub.i = sub i32 %1, %0
%tobool = icmp sgt i32 %sub.i, -1
br i1 %tobool, label %if.end, label %if.then
if.then: ; preds = %entry
%call1 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)()
br label %while.body
while.body: ; preds = %if.then, %while.body
br label %while.body
if.end: ; preds = %entry
%cmp.i = icmp slt i32 %sub.i, 1
br i1 %cmp.i, label %if.then4, label %if.end7
if.then4: ; preds = %if.end
%call5 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)()
br label %while.body6
while.body6: ; preds = %if.then4, %while.body6
br label %while.body6
if.end7: ; preds = %if.end
ret void
}
declare i32 @g(...)
; CHECK-LABEL: testBB
; CHECK: sub.f {{.*}}, %r0
!1 = !{!2, !2, i64 0}
!2 = !{!"int", !3, i64 0}
!3 = !{!"omnipotent char", !4, i64 0}
!4 = !{!"Simple C/C++ TBAA"}

View File

@ -0,0 +1,109 @@
; RUN: llc < %s -mtriple=lanai | FileCheck %s
define i32 @f(i32 inreg %a, i32 inreg %b) nounwind ssp {
entry:
; CHECK-LABEL: f:
; CHECK: sub.f %r6, %r7, [[IN:%.*]]
; CHECK: sel.gt [[IN]], %r0, %rv
%cmp = icmp sgt i32 %a, %b
%sub = sub nsw i32 %a, %b
%sub. = select i1 %cmp, i32 %sub, i32 0
ret i32 %sub.
}
define i32 @g(i32 inreg %a, i32 inreg %b) nounwind ssp {
entry:
; CHECK-LABEL: g:
; CHECK: sub.f %r7, %r6, [[IN:%.*]]
; CHECK: sel.lt [[IN]], %r0, %rv
%cmp = icmp slt i32 %a, %b
%sub = sub nsw i32 %b, %a
%sub. = select i1 %cmp, i32 %sub, i32 0
ret i32 %sub.
}
define i32 @h(i32 inreg %a, i32 inreg %b) nounwind ssp {
entry:
; CHECK-LABEL: h:
; CHECK: sub.f %r6, 0x3, [[IN:%.*]]
; CHECK: sel.gt [[IN]], %r7, %rv
%cmp = icmp sgt i32 %a, 3
%sub = sub nsw i32 %a, 3
%sub. = select i1 %cmp, i32 %sub, i32 %b
ret i32 %sub.
}
define i32 @i(i32 inreg %a, i32 inreg %b) nounwind readnone ssp {
entry:
; CHECK-LABEL: i:
; CHECK: sub.f %r7, %r6, [[IN:%.*]]
; CHECK: sel.ult [[IN]], %r0, %rv
%cmp = icmp ult i32 %a, %b
%sub = sub i32 %b, %a
%sub. = select i1 %cmp, i32 %sub, i32 0
ret i32 %sub.
}
; If SR is live-out, we can't remove cmp if there exists a swapped sub.
define i32 @j(i32 inreg %a, i32 inreg %b) nounwind {
entry:
; CHECK-LABEL: j:
; CHECK: sub.f %r7, %r6, %r0
; CHECK: sub %r6, %r7, %rv
%cmp = icmp eq i32 %b, %a
%sub = sub nsw i32 %a, %b
br i1 %cmp, label %if.then, label %if.else
if.then:
%cmp2 = icmp sgt i32 %b, %a
%sel = select i1 %cmp2, i32 %sub, i32 %a
ret i32 %sel
if.else:
ret i32 %sub
}
declare void @abort()
declare void @exit(i32)
@t = common global i32 0
; If the comparison uses the C bit (signed overflow/underflow), we can't
; omit the comparison.
define i32 @cmp_ult0(i32 inreg %a, i32 inreg %b, i32 inreg %x, i32 inreg %y) {
entry:
; CHECK-LABEL: cmp_ult0
; CHECK: sub {{.*}}, 0x11, [[IN:%.*]]
; CHECK: sub.f [[IN]], 0x0, %r0
%load = load i32, i32* @t, align 4
%sub = sub i32 %load, 17
%cmp = icmp ult i32 %sub, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
call void @abort()
unreachable
if.else:
call void @exit(i32 0)
unreachable
}
; Same for the V bit.
; TODO: add test that exercises V bit individually (VC/VS).
define i32 @cmp_gt0(i32 inreg %a, i32 inreg %b, i32 inreg %x, i32 inreg %y) {
entry:
; CHECK-LABEL: cmp_gt0
; CHECK: sub {{.*}}, 0x11, [[IN:%.*]]
; CHECK: sub.f [[IN]], 0x1, %r0
%load = load i32, i32* @t, align 4
%sub = sub i32 %load, 17
%cmp = icmp sgt i32 %sub, 0
br i1 %cmp, label %if.then, label %if.else
if.then:
call void @abort()
unreachable
if.else:
call void @exit(i32 0)
unreachable
}

View File

@ -0,0 +1,2 @@
if not 'Lanai' in config.root.targets:
config.unsupported = True

View File

@ -0,0 +1,714 @@
# RUN: llc -run-pass=peephole-opts %s -o /dev/null 2>&1 | FileCheck %s
# Test the compare fold peephole.
# CHECK-LABEL: name: test0a
# TODO: Enhance combiner to handle this case. This expands into:
# sub %r7, %r6, %r3
# sub.f %r7, %r6, %r0
# sel.eq %r18, %r3, %rv
# This is different from the pattern currently matched. If the lowered form had
# been sub.f %r3, 0, %r0 then it would have matched.
# CHECK-LABEL: name: test1a
# CHECK: [[IN1:%.*]] = COPY %r7
# CHECK: [[IN2:%.*]] = COPY %r6
# CHECK: SUB_F_R [[IN1]], [[IN2]], 0, implicit-def %sr
# CHECK-LABEL: name: test1b
# CHECK: [[IN1:%.*]] = COPY %r7
# CHECK: [[IN2:%.*]] = COPY %r6
# CHECK: SUB_F_R [[IN1]], [[IN2]], 0, implicit-def %sr
# CHECK-LABEL: name: test2a
# CHECK: [[IN1:%.*]] = COPY %r7
# CHECK: [[IN2:%.*]] = COPY %r6
# CHECK: SUB_F_R [[IN1]], [[IN2]], 0, implicit-def %sr
# CHECK-LABEL: name: test2b
# CHECK: [[IN1:%.*]] = COPY %r7
# CHECK: [[IN2:%.*]] = COPY %r6
# CHECK: SUB_F_R [[IN1]], [[IN2]], 0, implicit-def %sr
# CHECK-LABEL: name: test3
# CHECK: AND_F_R
# CHECK: AND_F_R
# CHECK: AND_F_R
--- |
target datalayout = "E-m:e-p:32:32-i64:64-a:0:32-n32-S64"
target triple = "lanai-unknown-unknown"
@a = global i32 -1, align 4
@b = global i32 0, align 4
define i32 @test0a(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%sub = sub i32 %b, %a
%cmp = icmp eq i32 %sub, 0
%cond = select i1 %cmp, i32 %c, i32 %sub
ret i32 %cond
}
define i32 @test0b(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%cmp = icmp eq i32 %b, %a
%cond = select i1 %cmp, i32 %c, i32 %b
ret i32 %cond
}
define i32 @test1a(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%sub = sub i32 %b, %a
%cmp = icmp slt i32 %sub, 0
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
define i32 @test1b(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%sub = sub i32 %b, %a
%cmp = icmp slt i32 %sub, 0
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
define i32 @test2a(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%sub = sub i32 %b, %a
%cmp = icmp sgt i32 %sub, -1
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
define i32 @test2b(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%sub = sub i32 %b, %a
%cmp = icmp sgt i32 %sub, -1
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
define i32 @test3(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%sub = sub i32 %b, %a
%cmp = icmp slt i32 %sub, 1
%cond = select i1 %cmp, i32 %c, i32 %d
ret i32 %cond
}
define i32 @test4(i32 inreg %a, i32 inreg %b, i32 inreg %c, i32 inreg %d) {
entry:
%cmp = icmp ne i32 %a, 0
%cmp1 = icmp ult i32 %a, %b
%or.cond = and i1 %cmp, %cmp1
br i1 %or.cond, label %return, label %if.end
if.end: ; preds = %entry
%cmp2 = icmp ne i32 %b, 0
%cmp4 = icmp ult i32 %b, %c
%or.cond29 = and i1 %cmp2, %cmp4
br i1 %or.cond29, label %return, label %if.end6
if.end6: ; preds = %if.end
%cmp7 = icmp ne i32 %c, 0
%cmp9 = icmp ult i32 %c, %d
%or.cond30 = and i1 %cmp7, %cmp9
br i1 %or.cond30, label %return, label %if.end11
if.end11: ; preds = %if.end6
%cmp12 = icmp ne i32 %d, 0
%cmp14 = icmp ult i32 %d, %a
%or.cond31 = and i1 %cmp12, %cmp14
%b. = select i1 %or.cond31, i32 %b, i32 21
ret i32 %b.
return: ; preds = %if.end6, %if.end, %entry
%retval.0 = phi i32 [ %c, %entry ], [ %d, %if.end ], [ %a, %if.end6 ]
ret i32 %retval.0
}
define void @testBB() {
entry:
%0 = load i32, i32* @a, align 4, !tbaa !0
%1 = load i32, i32* @b, align 4, !tbaa !0
%sub.i = sub i32 %1, %0
%tobool = icmp sgt i32 %sub.i, -1
br i1 %tobool, label %if.end, label %if.then
if.then: ; preds = %entry
%call1 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)()
br label %while.body
while.body: ; preds = %while.body, %if.then
br label %while.body
if.end: ; preds = %entry
%cmp.i = icmp slt i32 %sub.i, 1
br i1 %cmp.i, label %if.then4, label %if.end7
if.then4: ; preds = %if.end
%call5 = tail call i32 bitcast (i32 (...)* @g to i32 ()*)()
br label %while.body6
while.body6: ; preds = %while.body6, %if.then4
br label %while.body6
if.end7: ; preds = %if.end
ret void
}
declare i32 @g(...)
; Function Attrs: nounwind
declare void @llvm.stackprotector(i8*, i8**) #0
attributes #0 = { nounwind }
!0 = !{!1, !1, i64 0}
!1 = !{!"int", !2, i64 0}
!2 = !{!"omnipotent char", !3, i64 0}
!3 = !{!"Simple C/C++ TBAA"}
...
---
name: test0a
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
%4 = SUB_R %1, %0, 0
SFSUB_F_RI_LO %4, 0, implicit-def %sr
%5 = SELECT %2, %4, 7, implicit %sr
%rv = COPY %5
RET implicit %rca, implicit %rv
...
---
name: test0b
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
SFSUB_F_RR %1, %0, implicit-def %sr
%4 = SELECT %2, %1, 7, implicit %sr
%rv = COPY %4
RET implicit %rca, implicit %rv
...
---
name: test1a
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
- { reg: '%r19', virtual-reg: '%3' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18, %r19
%3 = COPY %r19
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
%4 = SUB_R %1, %0, 0
SFSUB_F_RI_LO killed %4, 0, implicit-def %sr
%5 = SELECT %2, %3, 11, implicit %sr
%rv = COPY %5
RET implicit %rca, implicit %rv
...
---
name: test1b
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
- { reg: '%r19', virtual-reg: '%3' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18, %r19
%3 = COPY %r19
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
%4 = SUB_R %1, %0, 0
SFSUB_F_RI_LO killed %4, 0, implicit-def %sr
%5 = SELECT %2, %3, 11, implicit %sr
%rv = COPY %5
RET implicit %rca, implicit %rv
...
---
name: test2a
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
- { reg: '%r19', virtual-reg: '%3' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18, %r19
%3 = COPY %r19
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
%4 = SUB_R %1, %0, 0
SFSUB_F_RI_LO killed %4, 0, implicit-def %sr
%5 = SELECT %2, %3, 10, implicit %sr
%rv = COPY %5
RET implicit %rca, implicit %rv
...
---
name: test2b
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
- { reg: '%r19', virtual-reg: '%3' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18, %r19
%3 = COPY %r19
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
%4 = SUB_R %1, %0, 0
SFSUB_F_RI_LO killed %4, 0, implicit-def %sr
%5 = SELECT %2, %3, 10, implicit %sr
%rv = COPY %5
RET implicit %rca, implicit %rv
...
---
name: test3
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%0' }
- { reg: '%r7', virtual-reg: '%1' }
- { reg: '%r18', virtual-reg: '%2' }
- { reg: '%r19', virtual-reg: '%3' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
liveins: %r6, %r7, %r18, %r19
%3 = COPY %r19
%2 = COPY %r18
%1 = COPY %r7
%0 = COPY %r6
%4 = SUB_R %1, %0, 0
SFSUB_F_RI_LO killed %4, 1, implicit-def %sr
%5 = SELECT %2, %3, 13, implicit %sr
%rv = COPY %5
RET implicit %rca, implicit %rv
...
---
name: test4
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
- { id: 6, class: gpr }
- { id: 7, class: gpr }
- { id: 8, class: gpr }
- { id: 9, class: gpr }
- { id: 10, class: gpr }
- { id: 11, class: gpr }
- { id: 12, class: gpr }
- { id: 13, class: gpr }
- { id: 14, class: gpr }
- { id: 15, class: gpr }
- { id: 16, class: gpr }
- { id: 17, class: gpr }
- { id: 18, class: gpr }
- { id: 19, class: gpr }
- { id: 20, class: gpr }
- { id: 21, class: gpr }
- { id: 22, class: gpr }
liveins:
- { reg: '%r6', virtual-reg: '%1' }
- { reg: '%r7', virtual-reg: '%2' }
- { reg: '%r18', virtual-reg: '%3' }
- { reg: '%r19', virtual-reg: '%4' }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: false
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
successors: %bb.4.return, %bb.1.if.end
liveins: %r6, %r7, %r18, %r19
%4 = COPY %r19
%3 = COPY %r18
%2 = COPY %r7
%1 = COPY %r6
SFSUB_F_RI_LO %1, 0, implicit-def %sr
%5 = SCC 6, implicit %sr
SFSUB_F_RR %1, %2, implicit-def %sr
%6 = SCC 4, implicit %sr
%7 = AND_R killed %5, killed %6, 0
%8 = SLI 1
%9 = AND_R killed %7, %8, 0
SFSUB_F_RI_LO killed %9, 0, implicit-def %sr
BRCC %bb.4.return, 6, implicit %sr
BT %bb.1.if.end
bb.1.if.end:
successors: %bb.4.return, %bb.2.if.end6
SFSUB_F_RI_LO %2, 0, implicit-def %sr
%10 = SCC 6, implicit %sr
SFSUB_F_RR %2, %3, implicit-def %sr
%11 = SCC 4, implicit %sr
%12 = AND_R killed %10, killed %11, 0
%14 = AND_R killed %12, %8, 0
SFSUB_F_RI_LO killed %14, 0, implicit-def %sr
BRCC %bb.4.return, 6, implicit %sr
BT %bb.2.if.end6
bb.2.if.end6:
successors: %bb.4.return, %bb.3.if.end11
SFSUB_F_RI_LO %3, 0, implicit-def %sr
%15 = SCC 6, implicit %sr
SFSUB_F_RR %3, %4, implicit-def %sr
%16 = SCC 4, implicit %sr
%17 = AND_R killed %15, killed %16, 0
%18 = SLI 1
%19 = AND_R killed %17, killed %18, 0
SFSUB_F_RI_LO killed %19, 0, implicit-def %sr
BRCC %bb.4.return, 6, implicit %sr
BT %bb.3.if.end11
bb.3.if.end11:
%20 = SLI 21
SFSUB_F_RR %4, %1, implicit-def %sr
%21 = SELECT %2, %20, 4, implicit %sr
SFSUB_F_RI_LO %4, 0, implicit-def %sr
%22 = SELECT killed %21, %20, 6, implicit %sr
%rv = COPY %22
RET implicit %rca, implicit %rv
bb.4.return:
%0 = PHI %3, %bb.0.entry, %4, %bb.1.if.end, %1, %bb.2.if.end6
%rv = COPY %0
RET implicit %rca, implicit %rv
...
---
name: testBB
alignment: 2
exposesReturnsTwice: false
hasInlineAsm: false
allVRegsAllocated: false
isSSA: true
tracksRegLiveness: true
tracksSubRegLiveness: false
registers:
- { id: 0, class: gpr }
- { id: 1, class: gpr }
- { id: 2, class: gpr }
- { id: 3, class: gpr }
- { id: 4, class: gpr }
- { id: 5, class: gpr }
- { id: 6, class: gpr }
- { id: 7, class: gpr }
- { id: 8, class: gpr }
frameInfo:
isFrameAddressTaken: false
isReturnAddressTaken: false
hasStackMap: false
hasPatchPoint: false
stackSize: 0
offsetAdjustment: 0
maxAlignment: 0
adjustsStack: false
hasCalls: true
maxCallFrameSize: 0
hasOpaqueSPAdjustment: false
hasVAStart: false
hasMustTailInVarArgFunc: false
body: |
bb.0.entry:
successors: %bb.3.if.end, %bb.1.if.then
%1 = MOVHI target-flags(lanai-hi) @a
%2 = OR_I_LO killed %1, target-flags(lanai-lo) @a
%3 = LDW_RI killed %2, 0, 0 :: (load 4 from @a, !tbaa !0)
%4 = MOVHI target-flags(lanai-hi) @b
%5 = OR_I_LO killed %4, target-flags(lanai-lo) @b
%6 = LDW_RI killed %5, 0, 0 :: (load 4 from @b, !tbaa !0)
%0 = SUB_R killed %6, killed %3, 0
SFSUB_F_RI_LO %0, 0, implicit-def %sr
BRCC %bb.3.if.end, 10, implicit %sr
BT %bb.1.if.then
bb.1.if.then:
successors: %bb.2.while.body
ADJCALLSTACKDOWN 0, implicit-def dead %sp, implicit %sp
CALL @g, csr, implicit-def dead %rca, implicit %sp, implicit-def %sp, implicit-def %rv
ADJCALLSTACKUP 0, 0, implicit-def dead %sp, implicit %sp
bb.2.while.body:
successors: %bb.2.while.body
BT %bb.2.while.body
bb.3.if.end:
successors: %bb.4.if.then4, %bb.6.if.end7
liveins: %sr
BRCC %bb.6.if.end7, 14, implicit %sr
BT %bb.4.if.then4
bb.4.if.then4:
successors: %bb.5.while.body6
ADJCALLSTACKDOWN 0, implicit-def dead %sp, implicit %sp
CALL @g, csr, implicit-def dead %rca, implicit %sp, implicit-def %sp, implicit-def %rv
ADJCALLSTACKUP 0, 0, implicit-def dead %sp, implicit %sp
bb.5.while.body6:
successors: %bb.5.while.body6
BT %bb.5.while.body6
bb.6.if.end7:
RET implicit %rca
...

View File

@ -12,13 +12,13 @@ add.f %r17, 0x00001234, %r21
! CHECK: 0x0a,0xc6,0x12,0x34
add.f %r17, 0x12340000, %r21
! CHECK: 0x0a,0xc7,0x12,0x34
add %r17, %r18, %r21
add.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x90,0x00
add.f %r17, %r18, %r21
add.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x90,0x00
addc %r17, %r18, %r21
addc.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x91,0x00
addc.f %r17, %r18, %r21
addc.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x91,0x00
addc %r17, 0, %r21
! CHECK: 0x1a,0xc4,0x00,0x00
@ -40,9 +40,9 @@ and.f %r17, 0xffff1234, %r21
! CHECK: 0x4a,0xc6,0x12,0x34
and.f %r17, 0x1234ffff, %r21
! CHECK: 0x4a,0xc7,0x12,0x34
and %r17, %r18, %r21
and.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x94,0x00
and.f %r17, %r18, %r21
and.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x94,0x00
bt 0x123454
! CHECK: 0xe0,0x12,0x34,0x54
@ -418,9 +418,9 @@ or.f %r17, 0x00001234, %r21
! CHECK: 0x5a,0xc6,0x12,0x34
or.f %r17, 0x12340000, %r21
! CHECK: 0x5a,0xc7,0x12,0x34
or %r17, %r18, %r21
or.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x95,0x00
or.f %r17, %r18, %r21
or.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x95,0x00
popc %r17, %r21
! CHECK: 0xda,0xc4,0x00,0x01
@ -790,9 +790,9 @@ sub.f %r17, 0x00001234, %r21
! CHECK: 0x2a,0xc6,0x12,0x34
sub.f %r17, 0x12340000, %r21
! CHECK: 0x2a,0xc7,0x12,0x34
sub %r17, %r18, %r21
sub.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x92,0x00
sub.f %r17, %r18, %r21
sub.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x92,0x00
subb %r17, 0, %r21
! CHECK: 0x3a,0xc4,0x00,0x00
@ -806,9 +806,9 @@ subb.f %r17, 0x00001234, %r21
! CHECK: 0x3a,0xc6,0x12,0x34
subb.f %r17, 0x12340000, %r21
! CHECK: 0x3a,0xc7,0x12,0x34
subb %r17, %r18, %r21
subb.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x93,0x00
subb.f %r17, %r18, %r21
subb.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x93,0x00
xor %r17, 0, %r21
! CHECK: 0x6a,0xc4,0x00,0x00
@ -822,9 +822,21 @@ xor.f %r17, 0x00001234, %r21
! CHECK: 0x6a,0xc6,0x12,0x34
xor.f %r17, 0x12340000, %r21
! CHECK: 0x6a,0xc7,0x12,0x34
xor %r17, %r18, %r21
xor.t %r17, %r18, %r21
! CHECK: 0xca,0xc4,0x96,0x00
xor.f %r17, %r18, %r21
xor.f.t %r17, %r18, %r21
! CHECK: 0xca,0xc6,0x96,0x00
sel.ne %r9, %r15, %r12
! CHECK: 0xc6,0x24,0x7f,0x03
sel.gt %r9, %r15, %r12
! CHECK: 0xc6,0x24,0x7f,0x07
xor.lt %r17, %r18, %r21
! CHECK: 0xca,0xc5,0x96,0x06
xor.f.eq %r17, %r18, %r21
! CHECK: 0xca,0xc7,0x96,0x03
add.ge %r13, %r14, %r18
! CHECK: 0xc9,0x34,0x70,0x06
spl %r19
! CHECK: 0xea,0x4c,0x00,0x02
bt 0x1234
! CHECK: 0xe0,0x00,0x12,0x34