blob: 26d36f1c5058fd88881282bf74af14aef54fd3dc [file] [log] [blame]
//=- 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"
#include "llvm/CodeGen/StackMaps.h"
#include "llvm/MC/MCInstBuilder.h"
using namespace llvm;
#define GET_INSTRINFO_CTOR_DTOR
#include "LoongArchGenInstrInfo.inc"
LoongArchInstrInfo::LoongArchInstrInfo(LoongArchSubtarget &STI)
: LoongArchGenInstrInfo(LoongArch::ADJCALLSTACKDOWN,
LoongArch::ADJCALLSTACKUP),
STI(STI) {}
MCInst LoongArchInstrInfo::getNop() const {
return MCInstBuilder(LoongArch::ANDI)
.addReg(LoongArch::R0)
.addReg(LoongArch::R0)
.addImm(0);
}
void LoongArchInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MBBI,
const DebugLoc &DL, Register DstReg,
Register SrcReg, bool KillSrc,
bool RenamableDest,
bool RenamableSrc) const {
if (LoongArch::GPRRegClass.contains(DstReg, SrcReg)) {
BuildMI(MBB, MBBI, DL, get(LoongArch::OR), DstReg)
.addReg(SrcReg, getKillRegState(KillSrc))
.addReg(LoongArch::R0);
return;
}
// VR->VR copies.
if (LoongArch::LSX128RegClass.contains(DstReg, SrcReg)) {
BuildMI(MBB, MBBI, DL, get(LoongArch::VORI_B), DstReg)
.addReg(SrcReg, getKillRegState(KillSrc))
.addImm(0);
return;
}
// XR->XR copies.
if (LoongArch::LASX256RegClass.contains(DstReg, SrcReg)) {
BuildMI(MBB, MBBI, DL, get(LoongArch::XVORI_B), DstReg)
.addReg(SrcReg, getKillRegState(KillSrc))
.addImm(0);
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;
}
// CFR->CFR copy.
if (LoongArch::CFRRegClass.contains(DstReg, SrcReg)) {
BuildMI(MBB, MBBI, DL, get(LoongArch::PseudoCopyCFR), 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 if (LoongArch::GPRRegClass.contains(DstReg) &&
LoongArch::FPR32RegClass.contains(SrcReg)) {
// FPR32 -> GPR copies
Opc = LoongArch::MOVFR2GR_S;
} else if (LoongArch::GPRRegClass.contains(DstReg) &&
LoongArch::FPR64RegClass.contains(SrcReg)) {
// FPR64 -> GPR copies
Opc = LoongArch::MOVFR2GR_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, Register VReg,
MachineInstr::MIFlag Flags) const {
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::LSX128RegClass.hasSubClassEq(RC))
Opcode = LoongArch::VST;
else if (LoongArch::LASX256RegClass.hasSubClassEq(RC))
Opcode = LoongArch::XVST;
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, DebugLoc(), 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,
Register VReg, MachineInstr::MIFlag Flags) const {
MachineFunction *MF = MBB.getParent();
MachineFrameInfo &MFI = MF->getFrameInfo();
DebugLoc DL;
if (I != MBB.end())
DL = I->getDebugLoc();
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::LSX128RegClass.hasSubClassEq(RC))
Opcode = LoongArch::VLD;
else if (LoongArch::LASX256RegClass.hasSubClassEq(RC))
Opcode = LoongArch::XVLD;
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;
case LoongArch::BSTRINS_D:
BuildMI(MBB, MBBI, DL, get(Inst.Opc), DstReg)
.addReg(SrcReg, RegState::Kill)
.addReg(SrcReg, RegState::Kill)
.addImm(Inst.Imm >> 32)
.addImm(Inst.Imm & 0xFF)
.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 {
unsigned Opcode = MI.getOpcode();
if (Opcode == TargetOpcode::INLINEASM ||
Opcode == TargetOpcode::INLINEASM_BR) {
const MachineFunction *MF = MI.getParent()->getParent();
const MCAsmInfo *MAI = MF->getTarget().getMCAsmInfo();
return getInlineAsmLength(MI.getOperand(0).getSymbolName(), *MAI);
}
unsigned NumBytes = 0;
const MCInstrDesc &Desc = MI.getDesc();
// Size should be preferably set in
// llvm/lib/Target/LoongArch/LoongArch*InstrInfo.td (default case).
// Specific cases handle instructions of variable sizes.
switch (Desc.getOpcode()) {
default:
return Desc.getSize();
case TargetOpcode::STATEPOINT:
NumBytes = StatepointOpers(&MI).getNumPatchBytes();
assert(NumBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
// No patch bytes means a normal call inst (i.e. `bl`) is emitted.
if (NumBytes == 0)
NumBytes = 4;
break;
}
return NumBytes;
}
bool LoongArchInstrInfo::isAsCheapAsAMove(const MachineInstr &MI) const {
const unsigned Opcode = MI.getOpcode();
switch (Opcode) {
default:
break;
case LoongArch::ADDI_D:
case LoongArch::ORI:
case LoongArch::XORI:
return (MI.getOperand(1).isReg() &&
MI.getOperand(1).getReg() == LoongArch::R0) ||
(MI.getOperand(2).isImm() && MI.getOperand(2).getImm() == 0);
}
return MI.isAsCheapAsAMove();
}
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);
}
}
bool LoongArchInstrInfo::isSchedulingBoundary(const MachineInstr &MI,
const MachineBasicBlock *MBB,
const MachineFunction &MF) const {
if (TargetInstrInfo::isSchedulingBoundary(MI, MBB, MF))
return true;
auto MII = MI.getIterator();
auto MIE = MBB->end();
// According to psABI v2.30:
//
// https://github.com/loongson/la-abi-specs/releases/tag/v2.30
//
// The following instruction patterns are prohibited from being reordered:
//
// * pcalau12i $a0, %pc_hi20(s)
// addi.d $a1, $zero, %pc_lo12(s)
// lu32i.d $a1, %pc64_lo20(s)
// lu52i.d $a1, $a1, %pc64_hi12(s)
//
// * pcalau12i $a0, %got_pc_hi20(s) | %ld_pc_hi20(s) | %gd_pc_hi20(s)
// addi.d $a1, $zero, %got_pc_lo12(s)
// lu32i.d $a1, %got64_pc_lo20(s)
// lu52i.d $a1, $a1, %got64_pc_hi12(s)
//
// * pcalau12i $a0, %ie_pc_hi20(s)
// addi.d $a1, $zero, %ie_pc_lo12(s)
// lu32i.d $a1, %ie64_pc_lo20(s)
// lu52i.d $a1, $a1, %ie64_pc_hi12(s)
//
// * pcalau12i $a0, %desc_pc_hi20(s)
// addi.d $a1, $zero, %desc_pc_lo12(s)
// lu32i.d $a1, %desc64_pc_lo20(s)
// lu52i.d $a1, $a1, %desc64_pc_hi12(s)
//
// For simplicity, only pcalau12i and lu52i.d are marked as scheduling
// boundaries, and the instructions between them are guaranteed to be
// ordered according to data dependencies.
switch (MI.getOpcode()) {
case LoongArch::PCALAU12I: {
auto AddI = std::next(MII);
if (AddI == MIE || AddI->getOpcode() != LoongArch::ADDI_D)
break;
auto Lu32I = std::next(AddI);
if (Lu32I == MIE || Lu32I->getOpcode() != LoongArch::LU32I_D)
break;
auto MO0 = MI.getOperand(1).getTargetFlags();
auto MO1 = AddI->getOperand(2).getTargetFlags();
auto MO2 = Lu32I->getOperand(2).getTargetFlags();
if (MO0 == LoongArchII::MO_PCREL_HI && MO1 == LoongArchII::MO_PCREL_LO &&
MO2 == LoongArchII::MO_PCREL64_LO)
return true;
if ((MO0 == LoongArchII::MO_GOT_PC_HI || MO0 == LoongArchII::MO_LD_PC_HI ||
MO0 == LoongArchII::MO_GD_PC_HI) &&
MO1 == LoongArchII::MO_GOT_PC_LO && MO2 == LoongArchII::MO_GOT_PC64_LO)
return true;
if (MO0 == LoongArchII::MO_IE_PC_HI && MO1 == LoongArchII::MO_IE_PC_LO &&
MO2 == LoongArchII::MO_IE_PC64_LO)
return true;
if (MO0 == LoongArchII::MO_DESC_PC_HI &&
MO1 == LoongArchII::MO_DESC_PC_LO &&
MO2 == LoongArchII::MO_DESC64_PC_LO)
return true;
break;
}
case LoongArch::LU52I_D: {
auto MO = MI.getOperand(2).getTargetFlags();
if (MO == LoongArchII::MO_PCREL64_HI || MO == LoongArchII::MO_GOT_PC64_HI ||
MO == LoongArchII::MO_IE_PC64_HI || MO == LoongArchII::MO_DESC64_PC_HI)
return true;
break;
}
default:
break;
}
const auto &STI = MF.getSubtarget<LoongArchSubtarget>();
if (STI.hasFeature(LoongArch::FeatureRelax)) {
// When linker relaxation enabled, the following instruction patterns are
// prohibited from being reordered:
//
// * pcalau12i $a0, %pc_hi20(s)
// addi.w/d $a0, $a0, %pc_lo12(s)
//
// * pcalau12i $a0, %got_pc_hi20(s)
// ld.w/d $a0, $a0, %got_pc_lo12(s)
//
// * pcalau12i $a0, %ld_pc_hi20(s) | %gd_pc_hi20(s)
// addi.w/d $a0, $a0, %got_pc_lo12(s)
//
// * pcalau12i $a0, %desc_pc_hi20(s)
// addi.w/d $a0, $a0, %desc_pc_lo12(s)
// ld.w/d $ra, $a0, %desc_ld(s)
// jirl $ra, $ra, %desc_call(s)
unsigned AddiOp = STI.is64Bit() ? LoongArch::ADDI_D : LoongArch::ADDI_W;
unsigned LdOp = STI.is64Bit() ? LoongArch::LD_D : LoongArch::LD_W;
switch (MI.getOpcode()) {
case LoongArch::PCALAU12I: {
auto MO0 = LoongArchII::getDirectFlags(MI.getOperand(1));
auto SecondOp = std::next(MII);
if (MO0 == LoongArchII::MO_DESC_PC_HI) {
if (SecondOp == MIE || SecondOp->getOpcode() != AddiOp)
break;
auto Ld = std::next(SecondOp);
if (Ld == MIE || Ld->getOpcode() != LdOp)
break;
auto MO1 = LoongArchII::getDirectFlags(SecondOp->getOperand(2));
auto MO2 = LoongArchII::getDirectFlags(Ld->getOperand(2));
if (MO1 == LoongArchII::MO_DESC_PC_LO && MO2 == LoongArchII::MO_DESC_LD)
return true;
break;
}
if (SecondOp == MIE ||
(SecondOp->getOpcode() != AddiOp && SecondOp->getOpcode() != LdOp))
break;
auto MO1 = LoongArchII::getDirectFlags(SecondOp->getOperand(2));
if (MO0 == LoongArchII::MO_PCREL_HI && SecondOp->getOpcode() == AddiOp &&
MO1 == LoongArchII::MO_PCREL_LO)
return true;
if (MO0 == LoongArchII::MO_GOT_PC_HI && SecondOp->getOpcode() == LdOp &&
MO1 == LoongArchII::MO_GOT_PC_LO)
return true;
if ((MO0 == LoongArchII::MO_LD_PC_HI ||
MO0 == LoongArchII::MO_GD_PC_HI) &&
SecondOp->getOpcode() == AddiOp && MO1 == LoongArchII::MO_GOT_PC_LO)
return true;
break;
}
case LoongArch::ADDI_W:
case LoongArch::ADDI_D: {
auto MO = LoongArchII::getDirectFlags(MI.getOperand(2));
if (MO == LoongArchII::MO_PCREL_LO || MO == LoongArchII::MO_GOT_PC_LO)
return true;
break;
}
case LoongArch::LD_W:
case LoongArch::LD_D: {
auto MO = LoongArchII::getDirectFlags(MI.getOperand(2));
if (MO == LoongArchII::MO_GOT_PC_LO)
return true;
break;
}
case LoongArch::PseudoDESC_CALL: {
auto MO = LoongArchII::getDirectFlags(MI.getOperand(2));
if (MO == LoongArchII::MO_DESC_CALL)
return true;
break;
}
default:
break;
}
}
return false;
}
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, Register());
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, Register());
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 {
const unsigned Mask = LoongArchII::MO_DIRECT_FLAG_MASK;
return std::make_pair(TF & Mask, TF & ~Mask);
}
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_PCREL64_LO, "loongarch-pcrel64-lo"},
{MO_PCREL64_HI, "loongarch-pcrel64-hi"},
{MO_GOT_PC_HI, "loongarch-got-pc-hi"},
{MO_GOT_PC_LO, "loongarch-got-pc-lo"},
{MO_GOT_PC64_LO, "loongarch-got-pc64-lo"},
{MO_GOT_PC64_HI, "loongarch-got-pc64-hi"},
{MO_LE_HI, "loongarch-le-hi"},
{MO_LE_LO, "loongarch-le-lo"},
{MO_LE64_LO, "loongarch-le64-lo"},
{MO_LE64_HI, "loongarch-le64-hi"},
{MO_IE_PC_HI, "loongarch-ie-pc-hi"},
{MO_IE_PC_LO, "loongarch-ie-pc-lo"},
{MO_IE_PC64_LO, "loongarch-ie-pc64-lo"},
{MO_IE_PC64_HI, "loongarch-ie-pc64-hi"},
{MO_LD_PC_HI, "loongarch-ld-pc-hi"},
{MO_GD_PC_HI, "loongarch-gd-pc-hi"},
{MO_CALL36, "loongarch-call36"},
{MO_DESC_PC_HI, "loongarch-desc-pc-hi"},
{MO_DESC_PC_LO, "loongarch-desc-pc-lo"},
{MO_DESC64_PC_LO, "loongarch-desc64-pc-lo"},
{MO_DESC64_PC_HI, "loongarch-desc64-pc-hi"},
{MO_DESC_LD, "loongarch-desc-ld"},
{MO_DESC_CALL, "loongarch-desc-call"},
{MO_LE_HI_R, "loongarch-le-hi-r"},
{MO_LE_ADD_R, "loongarch-le-add-r"},
{MO_LE_LO_R, "loongarch-le-lo-r"}};
return ArrayRef(TargetFlags);
}
ArrayRef<std::pair<unsigned, const char *>>
LoongArchInstrInfo::getSerializableBitmaskMachineOperandTargetFlags() const {
using namespace LoongArchII;
static const std::pair<unsigned, const char *> TargetFlags[] = {
{MO_RELAX, "loongarch-relax"}};
return ArrayRef(TargetFlags);
}
// Returns true if this is the sext.w pattern, addi.w rd, rs, 0.
bool LoongArch::isSEXT_W(const MachineInstr &MI) {
return MI.getOpcode() == LoongArch::ADDI_W && MI.getOperand(1).isReg() &&
MI.getOperand(2).isImm() && MI.getOperand(2).getImm() == 0;
}