forked from OSchip/llvm-project
484 lines
16 KiB
C++
484 lines
16 KiB
C++
//=- LoongArchInstrInfo.cpp - LoongArch Instruction Information -*- C++ -*-===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file contains the LoongArch implementation of the TargetInstrInfo class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "LoongArchInstrInfo.h"
|
|
#include "LoongArch.h"
|
|
#include "LoongArchMachineFunctionInfo.h"
|
|
#include "LoongArchRegisterInfo.h"
|
|
#include "MCTargetDesc/LoongArchMCTargetDesc.h"
|
|
#include "MCTargetDesc/LoongArchMatInt.h"
|
|
#include "llvm/CodeGen/RegisterScavenging.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define GET_INSTRINFO_CTOR_DTOR
|
|
#include "LoongArchGenInstrInfo.inc"
|
|
|
|
LoongArchInstrInfo::LoongArchInstrInfo(LoongArchSubtarget &STI)
|
|
: LoongArchGenInstrInfo(LoongArch::ADJCALLSTACKDOWN,
|
|
LoongArch::ADJCALLSTACKUP),
|
|
STI(STI) {}
|
|
|
|
void LoongArchInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI,
|
|
const DebugLoc &DL, MCRegister DstReg,
|
|
MCRegister SrcReg, bool KillSrc) const {
|
|
if (LoongArch::GPRRegClass.contains(DstReg, SrcReg)) {
|
|
BuildMI(MBB, MBBI, DL, get(LoongArch::OR), DstReg)
|
|
.addReg(SrcReg, getKillRegState(KillSrc))
|
|
.addReg(LoongArch::R0);
|
|
return;
|
|
}
|
|
|
|
// GPR->CFR copy.
|
|
if (LoongArch::CFRRegClass.contains(DstReg) &&
|
|
LoongArch::GPRRegClass.contains(SrcReg)) {
|
|
BuildMI(MBB, MBBI, DL, get(LoongArch::MOVGR2CF), DstReg)
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
return;
|
|
}
|
|
// CFR->GPR copy.
|
|
if (LoongArch::GPRRegClass.contains(DstReg) &&
|
|
LoongArch::CFRRegClass.contains(SrcReg)) {
|
|
BuildMI(MBB, MBBI, DL, get(LoongArch::MOVCF2GR), DstReg)
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
return;
|
|
}
|
|
|
|
// FPR->FPR copies.
|
|
unsigned Opc;
|
|
if (LoongArch::FPR32RegClass.contains(DstReg, SrcReg)) {
|
|
Opc = LoongArch::FMOV_S;
|
|
} else if (LoongArch::FPR64RegClass.contains(DstReg, SrcReg)) {
|
|
Opc = LoongArch::FMOV_D;
|
|
} else {
|
|
// TODO: support other copies.
|
|
llvm_unreachable("Impossible reg-to-reg copy");
|
|
}
|
|
|
|
BuildMI(MBB, MBBI, DL, get(Opc), DstReg)
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
}
|
|
|
|
void LoongArchInstrInfo::storeRegToStackSlot(
|
|
MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Register SrcReg,
|
|
bool IsKill, int FI, const TargetRegisterClass *RC,
|
|
const TargetRegisterInfo *TRI) const {
|
|
DebugLoc DL;
|
|
if (I != MBB.end())
|
|
DL = I->getDebugLoc();
|
|
MachineFunction *MF = MBB.getParent();
|
|
MachineFrameInfo &MFI = MF->getFrameInfo();
|
|
|
|
unsigned Opcode;
|
|
if (LoongArch::GPRRegClass.hasSubClassEq(RC))
|
|
Opcode = TRI->getRegSizeInBits(LoongArch::GPRRegClass) == 32
|
|
? LoongArch::ST_W
|
|
: LoongArch::ST_D;
|
|
else if (LoongArch::FPR32RegClass.hasSubClassEq(RC))
|
|
Opcode = LoongArch::FST_S;
|
|
else if (LoongArch::FPR64RegClass.hasSubClassEq(RC))
|
|
Opcode = LoongArch::FST_D;
|
|
else if (LoongArch::CFRRegClass.hasSubClassEq(RC))
|
|
Opcode = LoongArch::PseudoST_CFR;
|
|
else
|
|
llvm_unreachable("Can't store this register to stack slot");
|
|
|
|
MachineMemOperand *MMO = MF->getMachineMemOperand(
|
|
MachinePointerInfo::getFixedStack(*MF, FI), MachineMemOperand::MOStore,
|
|
MFI.getObjectSize(FI), MFI.getObjectAlign(FI));
|
|
|
|
BuildMI(MBB, I, DL, get(Opcode))
|
|
.addReg(SrcReg, getKillRegState(IsKill))
|
|
.addFrameIndex(FI)
|
|
.addImm(0)
|
|
.addMemOperand(MMO);
|
|
}
|
|
|
|
void LoongArchInstrInfo::loadRegFromStackSlot(
|
|
MachineBasicBlock &MBB, MachineBasicBlock::iterator I, Register DstReg,
|
|
int FI, const TargetRegisterClass *RC,
|
|
const TargetRegisterInfo *TRI) const {
|
|
DebugLoc DL;
|
|
if (I != MBB.end())
|
|
DL = I->getDebugLoc();
|
|
MachineFunction *MF = MBB.getParent();
|
|
MachineFrameInfo &MFI = MF->getFrameInfo();
|
|
|
|
unsigned Opcode;
|
|
if (LoongArch::GPRRegClass.hasSubClassEq(RC))
|
|
Opcode = TRI->getRegSizeInBits(LoongArch::GPRRegClass) == 32
|
|
? LoongArch::LD_W
|
|
: LoongArch::LD_D;
|
|
else if (LoongArch::FPR32RegClass.hasSubClassEq(RC))
|
|
Opcode = LoongArch::FLD_S;
|
|
else if (LoongArch::FPR64RegClass.hasSubClassEq(RC))
|
|
Opcode = LoongArch::FLD_D;
|
|
else if (LoongArch::CFRRegClass.hasSubClassEq(RC))
|
|
Opcode = LoongArch::PseudoLD_CFR;
|
|
else
|
|
llvm_unreachable("Can't load this register from stack slot");
|
|
|
|
MachineMemOperand *MMO = MF->getMachineMemOperand(
|
|
MachinePointerInfo::getFixedStack(*MF, FI), MachineMemOperand::MOLoad,
|
|
MFI.getObjectSize(FI), MFI.getObjectAlign(FI));
|
|
|
|
BuildMI(MBB, I, DL, get(Opcode), DstReg)
|
|
.addFrameIndex(FI)
|
|
.addImm(0)
|
|
.addMemOperand(MMO);
|
|
}
|
|
|
|
void LoongArchInstrInfo::movImm(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MBBI,
|
|
const DebugLoc &DL, Register DstReg,
|
|
uint64_t Val, MachineInstr::MIFlag Flag) const {
|
|
Register SrcReg = LoongArch::R0;
|
|
|
|
if (!STI.is64Bit() && !isInt<32>(Val))
|
|
report_fatal_error("Should only materialize 32-bit constants for LA32");
|
|
|
|
auto Seq = LoongArchMatInt::generateInstSeq(Val);
|
|
assert(!Seq.empty());
|
|
|
|
for (auto &Inst : Seq) {
|
|
switch (Inst.Opc) {
|
|
case LoongArch::LU12I_W:
|
|
BuildMI(MBB, MBBI, DL, get(Inst.Opc), DstReg)
|
|
.addImm(Inst.Imm)
|
|
.setMIFlag(Flag);
|
|
break;
|
|
case LoongArch::ADDI_W:
|
|
case LoongArch::ORI:
|
|
case LoongArch::LU32I_D: // "rj" is needed due to InstrInfo pattern
|
|
case LoongArch::LU52I_D:
|
|
BuildMI(MBB, MBBI, DL, get(Inst.Opc), DstReg)
|
|
.addReg(SrcReg, RegState::Kill)
|
|
.addImm(Inst.Imm)
|
|
.setMIFlag(Flag);
|
|
break;
|
|
default:
|
|
assert(false && "Unknown insn emitted by LoongArchMatInt");
|
|
}
|
|
|
|
// Only the first instruction has $zero as its source.
|
|
SrcReg = DstReg;
|
|
}
|
|
}
|
|
|
|
unsigned LoongArchInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
|
|
if (MI.getOpcode() == TargetOpcode::INLINEASM) {
|
|
const MachineFunction *MF = MI.getParent()->getParent();
|
|
const MCAsmInfo *MAI = MF->getTarget().getMCAsmInfo();
|
|
return getInlineAsmLength(MI.getOperand(0).getSymbolName(), *MAI);
|
|
}
|
|
return MI.getDesc().getSize();
|
|
}
|
|
|
|
MachineBasicBlock *
|
|
LoongArchInstrInfo::getBranchDestBlock(const MachineInstr &MI) const {
|
|
assert(MI.getDesc().isBranch() && "Unexpected opcode!");
|
|
// The branch target is always the last operand.
|
|
return MI.getOperand(MI.getNumExplicitOperands() - 1).getMBB();
|
|
}
|
|
|
|
static void parseCondBranch(MachineInstr &LastInst, MachineBasicBlock *&Target,
|
|
SmallVectorImpl<MachineOperand> &Cond) {
|
|
// Block ends with fall-through condbranch.
|
|
assert(LastInst.getDesc().isConditionalBranch() &&
|
|
"Unknown conditional branch");
|
|
int NumOp = LastInst.getNumExplicitOperands();
|
|
Target = LastInst.getOperand(NumOp - 1).getMBB();
|
|
|
|
Cond.push_back(MachineOperand::CreateImm(LastInst.getOpcode()));
|
|
for (int i = 0; i < NumOp - 1; i++)
|
|
Cond.push_back(LastInst.getOperand(i));
|
|
}
|
|
|
|
bool LoongArchInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
|
|
MachineBasicBlock *&TBB,
|
|
MachineBasicBlock *&FBB,
|
|
SmallVectorImpl<MachineOperand> &Cond,
|
|
bool AllowModify) const {
|
|
TBB = FBB = nullptr;
|
|
Cond.clear();
|
|
|
|
// If the block has no terminators, it just falls into the block after it.
|
|
MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr();
|
|
if (I == MBB.end() || !isUnpredicatedTerminator(*I))
|
|
return false;
|
|
|
|
// Count the number of terminators and find the first unconditional or
|
|
// indirect branch.
|
|
MachineBasicBlock::iterator FirstUncondOrIndirectBr = MBB.end();
|
|
int NumTerminators = 0;
|
|
for (auto J = I.getReverse(); J != MBB.rend() && isUnpredicatedTerminator(*J);
|
|
J++) {
|
|
NumTerminators++;
|
|
if (J->getDesc().isUnconditionalBranch() ||
|
|
J->getDesc().isIndirectBranch()) {
|
|
FirstUncondOrIndirectBr = J.getReverse();
|
|
}
|
|
}
|
|
|
|
// If AllowModify is true, we can erase any terminators after
|
|
// FirstUncondOrIndirectBR.
|
|
if (AllowModify && FirstUncondOrIndirectBr != MBB.end()) {
|
|
while (std::next(FirstUncondOrIndirectBr) != MBB.end()) {
|
|
std::next(FirstUncondOrIndirectBr)->eraseFromParent();
|
|
NumTerminators--;
|
|
}
|
|
I = FirstUncondOrIndirectBr;
|
|
}
|
|
|
|
// Handle a single unconditional branch.
|
|
if (NumTerminators == 1 && I->getDesc().isUnconditionalBranch()) {
|
|
TBB = getBranchDestBlock(*I);
|
|
return false;
|
|
}
|
|
|
|
// Handle a single conditional branch.
|
|
if (NumTerminators == 1 && I->getDesc().isConditionalBranch()) {
|
|
parseCondBranch(*I, TBB, Cond);
|
|
return false;
|
|
}
|
|
|
|
// Handle a conditional branch followed by an unconditional branch.
|
|
if (NumTerminators == 2 && std::prev(I)->getDesc().isConditionalBranch() &&
|
|
I->getDesc().isUnconditionalBranch()) {
|
|
parseCondBranch(*std::prev(I), TBB, Cond);
|
|
FBB = getBranchDestBlock(*I);
|
|
return false;
|
|
}
|
|
|
|
// Otherwise, we can't handle this.
|
|
return true;
|
|
}
|
|
|
|
bool LoongArchInstrInfo::isBranchOffsetInRange(unsigned BranchOp,
|
|
int64_t BrOffset) const {
|
|
switch (BranchOp) {
|
|
default:
|
|
llvm_unreachable("Unknown branch instruction!");
|
|
case LoongArch::BEQ:
|
|
case LoongArch::BNE:
|
|
case LoongArch::BLT:
|
|
case LoongArch::BGE:
|
|
case LoongArch::BLTU:
|
|
case LoongArch::BGEU:
|
|
return isInt<18>(BrOffset);
|
|
case LoongArch::BEQZ:
|
|
case LoongArch::BNEZ:
|
|
case LoongArch::BCEQZ:
|
|
case LoongArch::BCNEZ:
|
|
return isInt<23>(BrOffset);
|
|
case LoongArch::B:
|
|
case LoongArch::PseudoBR:
|
|
return isInt<28>(BrOffset);
|
|
}
|
|
}
|
|
|
|
unsigned LoongArchInstrInfo::removeBranch(MachineBasicBlock &MBB,
|
|
int *BytesRemoved) const {
|
|
if (BytesRemoved)
|
|
*BytesRemoved = 0;
|
|
MachineBasicBlock::iterator I = MBB.getLastNonDebugInstr();
|
|
if (I == MBB.end())
|
|
return 0;
|
|
|
|
if (!I->getDesc().isBranch())
|
|
return 0;
|
|
|
|
// Remove the branch.
|
|
if (BytesRemoved)
|
|
*BytesRemoved += getInstSizeInBytes(*I);
|
|
I->eraseFromParent();
|
|
|
|
I = MBB.end();
|
|
|
|
if (I == MBB.begin())
|
|
return 1;
|
|
--I;
|
|
if (!I->getDesc().isConditionalBranch())
|
|
return 1;
|
|
|
|
// Remove the branch.
|
|
if (BytesRemoved)
|
|
*BytesRemoved += getInstSizeInBytes(*I);
|
|
I->eraseFromParent();
|
|
return 2;
|
|
}
|
|
|
|
// Inserts a branch into the end of the specific MachineBasicBlock, returning
|
|
// the number of instructions inserted.
|
|
unsigned LoongArchInstrInfo::insertBranch(
|
|
MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
|
|
ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
|
|
if (BytesAdded)
|
|
*BytesAdded = 0;
|
|
|
|
// Shouldn't be a fall through.
|
|
assert(TBB && "insertBranch must not be told to insert a fallthrough");
|
|
assert(Cond.size() <= 3 && Cond.size() != 1 &&
|
|
"LoongArch branch conditions have at most two components!");
|
|
|
|
// Unconditional branch.
|
|
if (Cond.empty()) {
|
|
MachineInstr &MI = *BuildMI(&MBB, DL, get(LoongArch::PseudoBR)).addMBB(TBB);
|
|
if (BytesAdded)
|
|
*BytesAdded += getInstSizeInBytes(MI);
|
|
return 1;
|
|
}
|
|
|
|
// Either a one or two-way conditional branch.
|
|
MachineInstrBuilder MIB = BuildMI(&MBB, DL, get(Cond[0].getImm()));
|
|
for (unsigned i = 1; i < Cond.size(); ++i)
|
|
MIB.add(Cond[i]);
|
|
MIB.addMBB(TBB);
|
|
if (BytesAdded)
|
|
*BytesAdded += getInstSizeInBytes(*MIB);
|
|
|
|
// One-way conditional branch.
|
|
if (!FBB)
|
|
return 1;
|
|
|
|
// Two-way conditional branch.
|
|
MachineInstr &MI = *BuildMI(&MBB, DL, get(LoongArch::PseudoBR)).addMBB(FBB);
|
|
if (BytesAdded)
|
|
*BytesAdded += getInstSizeInBytes(MI);
|
|
return 2;
|
|
}
|
|
|
|
void LoongArchInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
|
|
MachineBasicBlock &DestBB,
|
|
MachineBasicBlock &RestoreBB,
|
|
const DebugLoc &DL,
|
|
int64_t BrOffset,
|
|
RegScavenger *RS) const {
|
|
assert(RS && "RegScavenger required for long branching");
|
|
assert(MBB.empty() &&
|
|
"new block should be inserted for expanding unconditional branch");
|
|
assert(MBB.pred_size() == 1);
|
|
|
|
MachineFunction *MF = MBB.getParent();
|
|
MachineRegisterInfo &MRI = MF->getRegInfo();
|
|
const TargetRegisterInfo *TRI = MF->getSubtarget().getRegisterInfo();
|
|
LoongArchMachineFunctionInfo *LAFI =
|
|
MF->getInfo<LoongArchMachineFunctionInfo>();
|
|
|
|
if (!isInt<32>(BrOffset))
|
|
report_fatal_error(
|
|
"Branch offsets outside of the signed 32-bit range not supported");
|
|
|
|
Register ScratchReg = MRI.createVirtualRegister(&LoongArch::GPRRegClass);
|
|
auto II = MBB.end();
|
|
|
|
MachineInstr &PCALAU12I =
|
|
*BuildMI(MBB, II, DL, get(LoongArch::PCALAU12I), ScratchReg)
|
|
.addMBB(&DestBB, LoongArchII::MO_PCREL_HI);
|
|
MachineInstr &ADDI =
|
|
*BuildMI(MBB, II, DL,
|
|
get(STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W),
|
|
ScratchReg)
|
|
.addReg(ScratchReg)
|
|
.addMBB(&DestBB, LoongArchII::MO_PCREL_LO);
|
|
BuildMI(MBB, II, DL, get(LoongArch::PseudoBRIND))
|
|
.addReg(ScratchReg, RegState::Kill)
|
|
.addImm(0);
|
|
|
|
RS->enterBasicBlockEnd(MBB);
|
|
Register Scav = RS->scavengeRegisterBackwards(
|
|
LoongArch::GPRRegClass, PCALAU12I.getIterator(), /*RestoreAfter=*/false,
|
|
/*SPAdj=*/0, /*AllowSpill=*/false);
|
|
if (Scav != LoongArch::NoRegister)
|
|
RS->setRegUsed(Scav);
|
|
else {
|
|
// When there is no scavenged register, it needs to specify a register.
|
|
// Specify t8 register because it won't be used too often.
|
|
Scav = LoongArch::R20;
|
|
int FrameIndex = LAFI->getBranchRelaxationSpillFrameIndex();
|
|
if (FrameIndex == -1)
|
|
report_fatal_error("The function size is incorrectly estimated.");
|
|
storeRegToStackSlot(MBB, PCALAU12I, Scav, /*IsKill=*/true, FrameIndex,
|
|
&LoongArch::GPRRegClass, TRI);
|
|
TRI->eliminateFrameIndex(std::prev(PCALAU12I.getIterator()),
|
|
/*SpAdj=*/0, /*FIOperandNum=*/1);
|
|
PCALAU12I.getOperand(1).setMBB(&RestoreBB);
|
|
ADDI.getOperand(2).setMBB(&RestoreBB);
|
|
loadRegFromStackSlot(RestoreBB, RestoreBB.end(), Scav, FrameIndex,
|
|
&LoongArch::GPRRegClass, TRI);
|
|
TRI->eliminateFrameIndex(RestoreBB.back(),
|
|
/*SpAdj=*/0, /*FIOperandNum=*/1);
|
|
}
|
|
MRI.replaceRegWith(ScratchReg, Scav);
|
|
MRI.clearVirtRegs();
|
|
}
|
|
|
|
static unsigned getOppositeBranchOpc(unsigned Opc) {
|
|
switch (Opc) {
|
|
default:
|
|
llvm_unreachable("Unrecognized conditional branch");
|
|
case LoongArch::BEQ:
|
|
return LoongArch::BNE;
|
|
case LoongArch::BNE:
|
|
return LoongArch::BEQ;
|
|
case LoongArch::BEQZ:
|
|
return LoongArch::BNEZ;
|
|
case LoongArch::BNEZ:
|
|
return LoongArch::BEQZ;
|
|
case LoongArch::BCEQZ:
|
|
return LoongArch::BCNEZ;
|
|
case LoongArch::BCNEZ:
|
|
return LoongArch::BCEQZ;
|
|
case LoongArch::BLT:
|
|
return LoongArch::BGE;
|
|
case LoongArch::BGE:
|
|
return LoongArch::BLT;
|
|
case LoongArch::BLTU:
|
|
return LoongArch::BGEU;
|
|
case LoongArch::BGEU:
|
|
return LoongArch::BLTU;
|
|
}
|
|
}
|
|
|
|
bool LoongArchInstrInfo::reverseBranchCondition(
|
|
SmallVectorImpl<MachineOperand> &Cond) const {
|
|
assert((Cond.size() && Cond.size() <= 3) && "Invalid branch condition!");
|
|
Cond[0].setImm(getOppositeBranchOpc(Cond[0].getImm()));
|
|
return false;
|
|
}
|
|
|
|
std::pair<unsigned, unsigned>
|
|
LoongArchInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const {
|
|
return std::make_pair(TF, 0u);
|
|
}
|
|
|
|
ArrayRef<std::pair<unsigned, const char *>>
|
|
LoongArchInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
|
|
using namespace LoongArchII;
|
|
// TODO: Add more target flags.
|
|
static const std::pair<unsigned, const char *> TargetFlags[] = {
|
|
{MO_CALL, "loongarch-call"},
|
|
{MO_CALL_PLT, "loongarch-call-plt"},
|
|
{MO_PCREL_HI, "loongarch-pcrel-hi"},
|
|
{MO_PCREL_LO, "loongarch-pcrel-lo"},
|
|
{MO_GOT_PC_HI, "loongarch-got-pc-hi"},
|
|
{MO_GOT_PC_LO, "loongarch-got-pc-lo"},
|
|
{MO_LE_HI, "loongarch-le-hi"},
|
|
{MO_LE_LO, "loongarch-le-lo"},
|
|
{MO_IE_PC_HI, "loongarch-ie-pc-hi"},
|
|
{MO_IE_PC_LO, "loongarch-ie-pc-lo"},
|
|
{MO_LD_PC_HI, "loongarch-ld-pc-hi"},
|
|
{MO_GD_PC_HI, "loongarch-gd-pc-hi"}};
|
|
return makeArrayRef(TargetFlags);
|
|
}
|