Index: lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp =================================================================== --- lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp +++ lib/Target/Lanai/AsmParser/LanaiAsmParser.cpp @@ -357,6 +357,20 @@ return isInt<10>(Value); } + bool isCondCode() { + if (!isImm()) + return false; + + const MCConstantExpr *ConstExpr = dyn_cast(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 @@ 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 @@ 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)); Index: lib/Target/Lanai/CMakeLists.txt =================================================================== --- lib/Target/Lanai/CMakeLists.txt +++ lib/Target/Lanai/CMakeLists.txt @@ -23,7 +23,6 @@ LanaiMemAluCombiner.cpp LanaiRegisterInfo.cpp LanaiSelectionDAGInfo.cpp - LanaiSetflagAluCombiner.cpp LanaiSubtarget.cpp LanaiTargetMachine.cpp LanaiTargetObjectFile.cpp Index: lib/Target/Lanai/Disassembler/LanaiDisassembler.cpp =================================================================== --- lib/Target/Lanai/Disassembler/LanaiDisassembler.cpp +++ lib/Target/Lanai/Disassembler/LanaiDisassembler.cpp @@ -62,6 +62,10 @@ 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 @@ 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; +} Index: lib/Target/Lanai/InstPrinter/LanaiInstPrinter.h =================================================================== --- lib/Target/Lanai/InstPrinter/LanaiInstPrinter.h +++ lib/Target/Lanai/InstPrinter/LanaiInstPrinter.h @@ -29,6 +29,7 @@ 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, Index: lib/Target/Lanai/InstPrinter/LanaiInstPrinter.cpp =================================================================== --- lib/Target/Lanai/InstPrinter/LanaiInstPrinter.cpp +++ lib/Target/Lanai/InstPrinter/LanaiInstPrinter.cpp @@ -284,6 +284,22 @@ void LanaiInstPrinter::printCCOperand(const MCInst *MI, int OpNo, raw_ostream &OS) { - const int CC = static_cast(MI->getOperand(OpNo).getImm()); - OS << lanaiCondCodeToString(static_cast(CC)); + LPCC::CondCode CC = + static_cast(MI->getOperand(OpNo).getImm()); + // Handle the undefined value here for printing so we don't abort(). + if (CC >= LPCC::UNKNOWN) + OS << ""; + else + OS << lanaiCondCodeToString(CC); +} + +void LanaiInstPrinter::printPredicateOperand(const MCInst *MI, unsigned OpNo, + raw_ostream &OS) { + LPCC::CondCode CC = + static_cast(MI->getOperand(OpNo).getImm()); + // Handle the undefined value here for printing so we don't abort(). + if (CC >= LPCC::UNKNOWN) + OS << ""; + else if (CC != LPCC::ICC_T) + OS << "." << lanaiCondCodeToString(CC); } Index: lib/Target/Lanai/LanaiAsmPrinter.cpp =================================================================== --- lib/Target/Lanai/LanaiAsmPrinter.cpp +++ lib/Target/Lanai/LanaiAsmPrinter.cpp @@ -65,7 +65,6 @@ 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 @@ 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 @@ } 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 @@ } // 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) { Index: lib/Target/Lanai/LanaiISelLowering.h =================================================================== --- lib/Target/Lanai/LanaiISelLowering.h +++ lib/Target/Lanai/LanaiISelLowering.h @@ -103,6 +103,8 @@ std::vector &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, Index: lib/Target/Lanai/LanaiISelLowering.cpp =================================================================== --- lib/Target/Lanai/LanaiISelLowering.cpp +++ lib/Target/Lanai/LanaiISelLowering.cpp @@ -124,6 +124,12 @@ 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 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(); +} Index: lib/Target/Lanai/LanaiInstrInfo.h =================================================================== --- lib/Target/Lanai/LanaiInstrInfo.h +++ lib/Target/Lanai/LanaiInstrInfo.h @@ -75,6 +75,12 @@ int64_t &Offset, unsigned &Width, const TargetRegisterInfo *TRI) const; + std::pair + decomposeMachineOperandsTargetFlags(unsigned TF) const override; + + ArrayRef> + getSerializableDirectMachineOperandTargetFlags() const override; + bool AnalyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TrueBlock, MachineBasicBlock *&FalseBlock, SmallVectorImpl &Condition, @@ -82,6 +88,47 @@ 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 &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 &SeenMIs, + bool PreferFalse) const override; + bool ReverseBranchCondition( SmallVectorImpl &Condition) const override; Index: lib/Target/Lanai/LanaiInstrInfo.cpp =================================================================== --- lib/Target/Lanai/LanaiInstrInfo.cpp +++ lib/Target/Lanai/LanaiInstrInfo.cpp @@ -122,7 +122,7 @@ 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 @@ } } +std::pair +LanaiInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const { + return std::make_pair(TF, 0u); +} + +ArrayRef> +LanaiInstrInfo::getSerializableDirectMachineOperandTargetFlags() const { + using namespace LanaiII; + static const std::pair 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, 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 = 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 &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 &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 @@ LPCC::CondCode BranchCond = static_cast(Condition[0].getImm()); - Condition[0].setImm(GetOppositeBranchCondition(BranchCond)); + Condition[0].setImm(getOppositeCondition(BranchCond)); return false; } Index: lib/Target/Lanai/LanaiInstrInfo.td =================================================================== --- lib/Target/Lanai/LanaiInstrInfo.td +++ lib/Target/Lanai/LanaiInstrInfo.td @@ -235,6 +235,15 @@ let PrintMethod = "printCCOperand"; } +// Predicate operand. Default to 0 = true. +def CondCodeOperand : AsmOperandClass { let Name = "CondCode"; } + +def pred : PredicateOperand { + 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 @@ defm I_ : ALUbase; // Register Register - let JJJJJ = 0, DDDI = 0 in - def R : InstRR; - // RR Conditional - let JJJJJ = 0, Uses = [SR] in - def R_CC : InstRR; } multiclass ALUlogic subOp, string AsmStr, SDNode OpNode, @@ -302,16 +305,10 @@ [(set GPR:$Rd, (OpNode GPR:$Rs1, HiExt:$imm16))]>; // Register Register - let JJJJJ = 0, DDDI = 0 in - def R : InstRR; - // RR Conditional - let JJJJJ = 0, Uses = [SR] in - def R_CC : InstRR; } // Non flag setting ALU operations @@ -407,7 +404,7 @@ 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 @@ !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; + let F = 1, Rd = R0.Num, H = 1, Defs = [SR] in + def _RI_HI : InstRI; } -let isCodeGenOnly = 1 in { +let isCodeGenOnly = 1, isCompare = 1 in { defm SFSUB_F : SF<0b010, "sub.f">; } Index: lib/Target/Lanai/LanaiRegisterInfo.cpp =================================================================== --- lib/Target/Lanai/LanaiRegisterInfo.cpp +++ lib/Target/Lanai/LanaiRegisterInfo.cpp @@ -197,11 +197,16 @@ } // 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 Index: lib/Target/Lanai/LanaiSetflagAluCombiner.cpp =================================================================== --- lib/Target/Lanai/LanaiSetflagAluCombiner.cpp +++ /dev/null @@ -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 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( - 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().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(); -} Index: lib/Target/Lanai/LanaiTargetMachine.cpp =================================================================== --- lib/Target/Lanai/LanaiTargetMachine.cpp +++ lib/Target/Lanai/LanaiTargetMachine.cpp @@ -28,7 +28,6 @@ namespace llvm { void initializeLanaiMemAluCombinerPass(PassRegistry &); -void initializeLanaiSetflagAluCombinerPass(PassRegistry &); } // namespace llvm extern "C" void LLVMInitializeLanaiTarget() { @@ -112,5 +111,4 @@ // scheduling pass. void LanaiPassConfig::addPreSched2() { addPass(createLanaiMemAluCombinerPass()); - addPass(createLanaiSetflagAluCombinerPass()); } Index: lib/Target/Lanai/MCTargetDesc/LanaiBaseInfo.h =================================================================== --- lib/Target/Lanai/MCTargetDesc/LanaiBaseInfo.h +++ lib/Target/Lanai/MCTargetDesc/LanaiBaseInfo.h @@ -24,7 +24,6 @@ // 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 @@ // 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 Index: test/CodeGen/Lanai/combined_alu_setcc.ll =================================================================== --- test/CodeGen/Lanai/combined_alu_setcc.ll +++ /dev/null @@ -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"} Index: test/CodeGen/Lanai/sub-cmp-peephole.ll =================================================================== --- /dev/null +++ test/CodeGen/Lanai/sub-cmp-peephole.ll @@ -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 +} Index: test/CodeGen/MIR/Lanai/lit.local.cfg =================================================================== --- /dev/null +++ test/CodeGen/MIR/Lanai/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'Lanai' in config.root.targets: + config.unsupported = True Index: test/CodeGen/MIR/Lanai/peephole-compare.mir =================================================================== --- /dev/null +++ test/CodeGen/MIR/Lanai/peephole-compare.mir @@ -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 + +... Index: test/MC/Lanai/v11.s =================================================================== --- test/MC/Lanai/v11.s +++ test/MC/Lanai/v11.s @@ -12,13 +12,13 @@ ! 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 @@ ! 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 @@ ! 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 @@ ! 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 @@ ! 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 @@ ! 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