forked from OSchip/llvm-project
328 lines
9.3 KiB
C++
328 lines
9.3 KiB
C++
//===-- 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;
|
|
|
|
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(unsigned Opcode) {
|
|
switch (Opcode) {
|
|
case Lanai::ADDC_F_I_HI:
|
|
case Lanai::ADDC_F_I_LO:
|
|
case Lanai::ADDC_F_R:
|
|
case Lanai::ADDC_F_R_CC:
|
|
case Lanai::ADD_F_I_HI:
|
|
case Lanai::ADD_F_I_LO:
|
|
case Lanai::ADD_F_R:
|
|
case Lanai::ADD_F_R_CC:
|
|
case Lanai::AND_F_I_HI:
|
|
case Lanai::AND_F_I_LO:
|
|
case Lanai::AND_F_R:
|
|
case Lanai::AND_F_R_CC:
|
|
case Lanai::OR_F_I_HI:
|
|
case Lanai::OR_F_I_LO:
|
|
case Lanai::OR_F_R:
|
|
case Lanai::OR_F_R_CC:
|
|
case Lanai::SFSUB_F_RI:
|
|
case Lanai::SFSUB_F_RR:
|
|
case Lanai::SA_F_I:
|
|
case Lanai::SL_F_I:
|
|
case Lanai::SHL_F_R:
|
|
case Lanai::SRA_F_R:
|
|
case Lanai::SRL_F_R:
|
|
case Lanai::SUBB_F_I_HI:
|
|
case Lanai::SUBB_F_I_LO:
|
|
case Lanai::SUBB_F_R:
|
|
case Lanai::SUBB_F_R_CC:
|
|
case Lanai::SUB_F_I_HI:
|
|
case Lanai::SUB_F_I_LO:
|
|
case Lanai::SUB_F_R:
|
|
case Lanai::SUB_F_R_CC:
|
|
case Lanai::XOR_F_I_HI:
|
|
case Lanai::XOR_F_I_LO:
|
|
case Lanai::XOR_F_R:
|
|
case Lanai::XOR_F_R_CC:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 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;
|
|
// Early exit when encountering flag setting instruction.
|
|
if (isFlagSettingInstruction(SCCUserIter->getOpcode()))
|
|
break;
|
|
int CCIndex = getCCOperandPosition(SCCUserIter->getOpcode());
|
|
if (CCIndex != -1) {
|
|
LPCC::CondCode CC = static_cast<LPCC::CondCode>(
|
|
SCCUserIter->getOperand(CCIndex).getImm());
|
|
if (CC != LPCC::ICC_EQ && CC != LPCC::ICC_NE && CC != LPCC::ICC_PL &&
|
|
CC != LPCC::ICC_MI)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
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->getOpcode())) {
|
|
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 (SetflagIter == End)
|
|
break;
|
|
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();
|
|
}
|