Skip to content

[Xtensa] Implement support for the BranchRelaxation. #113450

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions llvm/include/llvm/CodeGen/MachineFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,10 @@ class LLVM_ABI MachineFunction {
/// it are renumbered.
void RenumberBlocks(MachineBasicBlock *MBBFrom = nullptr);

/// Return an estimate of the function's code size,
/// taking into account block and function alignment
int64_t estimateFunctionSizeInBytes();

/// print - Print out the MachineFunction in a format suitable for debugging
/// to the specified stream.
void print(raw_ostream &OS, const SlotIndexes* = nullptr) const;
Expand Down
31 changes: 31 additions & 0 deletions llvm/lib/CodeGen/MachineFunction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,37 @@ void MachineFunction::RenumberBlocks(MachineBasicBlock *MBB) {
MBBNumberingEpoch++;
}

int64_t MachineFunction::estimateFunctionSizeInBytes() {
const TargetInstrInfo &TII = *getSubtarget().getInstrInfo();
const Align FunctionAlignment = getAlignment();
MachineFunction::iterator MBBI = begin(), E = end();
/// Offset - Distance from the beginning of the function to the end
/// of the basic block.
int64_t Offset = 0;

for (; MBBI != E; ++MBBI) {
const Align Alignment = MBBI->getAlignment();
int64_t BlockSize = 0;

for (auto &MI : *MBBI) {
BlockSize += TII.getInstSizeInBytes(MI);
}

int64_t OffsetBB;
if (Alignment <= FunctionAlignment) {
OffsetBB = alignTo(Offset, Alignment);
} else {
// The alignment of this MBB is larger than the function's alignment, so
// we can't tell whether or not it will insert nops. Assume that it will.
Comment on lines +401 to +402
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if this should be a verifier error

Copy link
Contributor Author

@andreisfr andreisfr Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean that this case shouldn't happen and verifier should throw an error? in such situation? Can we implement this in later commits (if we need this)?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I'm not sure it makes sense to have a block with less alignment than the function. you'd have to have some strange code realignment?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't seen an example for Xtensa at this time with block alignment large then function. Actually I implemented alignment handling like it is done in Branch Relaxation pass in postOffset function. I thought it may be useful for general case.

unsigned postOffset(const MachineBasicBlock &MBB) const {

OffsetBB = alignTo(Offset, Alignment) + Alignment.value() -
FunctionAlignment.value();
}
Offset = OffsetBB + BlockSize;
}

return Offset;
}

/// This method iterates over the basic blocks and assigns their IsBeginSection
/// and IsEndSection fields. This must be called after MBB layout is finalized
/// and the SectionID's are assigned to MBBs.
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,9 @@ void XtensaAsmPrinter::emitMachineConstantPoolValue(
const BlockAddress *BA =
cast<XtensaConstantPoolConstant>(ACPV)->getBlockAddress();
MCSym = GetBlockAddressSymbol(BA);
} else if (ACPV->isMachineBasicBlock()) {
const MachineBasicBlock *MBB = cast<XtensaConstantPoolMBB>(ACPV)->getMBB();
MCSym = MBB->getSymbol();
} else if (ACPV->isJumpTable()) {
unsigned Idx = cast<XtensaConstantPoolJumpTable>(ACPV)->getIndex();
MCSym = this->GetJTISymbol(Idx, false);
Expand Down
21 changes: 17 additions & 4 deletions llvm/lib/Target/Xtensa/XtensaFrameLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "XtensaFrameLowering.h"
#include "XtensaInstrInfo.h"
#include "XtensaMachineFunctionInfo.h"
#include "XtensaSubtarget.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
Expand Down Expand Up @@ -260,14 +261,26 @@ void XtensaFrameLowering::processFunctionBeforeFrameFinalized(
// Set scavenging frame index if necessary.
MachineFrameInfo &MFI = MF.getFrameInfo();
uint64_t MaxSPOffset = MFI.estimateStackSize(MF);
auto *XtensaFI = MF.getInfo<XtensaMachineFunctionInfo>();
unsigned ScavSlotsNum = 0;

if (isInt<12>(MaxSPOffset))
return;
if (!isInt<12>(MaxSPOffset))
ScavSlotsNum = 1;

// Far branches over 18-bit offset require a spill slot for scratch register.
bool IsLargeFunction = !isInt<18>(MF.estimateFunctionSizeInBytes());
if (IsLargeFunction)
ScavSlotsNum = std::max(ScavSlotsNum, 1u);

const TargetRegisterClass &RC = Xtensa::ARRegClass;
unsigned Size = TRI->getSpillSize(RC);
Align Alignment = TRI->getSpillAlign(RC);
int FI = MF.getFrameInfo().CreateStackObject(Size, Alignment, false);
for (unsigned I = 0; I < ScavSlotsNum; I++) {
int FI = MFI.CreateStackObject(Size, Alignment, false);
RS->addScavengingFrameIndex(FI);

RS->addScavengingFrameIndex(FI);
if (IsLargeFunction &&
XtensaFI->getBranchRelaxationScratchFrameIndex() == -1)
XtensaFI->setBranchRelaxationScratchFrameIndex(FI);
}
}
207 changes: 207 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaInstrInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,14 @@
//===----------------------------------------------------------------------===//

#include "XtensaInstrInfo.h"
#include "XtensaConstantPoolValue.h"
#include "XtensaMachineFunctionInfo.h"
#include "XtensaTargetMachine.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFrameInfo.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"

#define GET_INSTRINFO_CTOR_DTOR
#include "XtensaGenInstrInfo.inc"
Expand Down Expand Up @@ -186,6 +189,18 @@ void XtensaInstrInfo::loadImmediate(MachineBasicBlock &MBB,
}
}

unsigned XtensaInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
switch (MI.getOpcode()) {
case TargetOpcode::INLINEASM: { // Inline Asm: Variable size.
const MachineFunction *MF = MI.getParent()->getParent();
const char *AsmStr = MI.getOperand(0).getSymbolName();
return getInlineAsmLength(AsmStr, *MF->getTarget().getMCAsmInfo());
}
default:
return MI.getDesc().getSize();
}
}

bool XtensaInstrInfo::reverseBranchCondition(
SmallVectorImpl<MachineOperand> &Cond) const {
assert(Cond.size() <= 4 && "Invalid branch condition!");
Expand Down Expand Up @@ -244,6 +259,74 @@ bool XtensaInstrInfo::reverseBranchCondition(
}
}

MachineBasicBlock *
XtensaInstrInfo::getBranchDestBlock(const MachineInstr &MI) const {
unsigned OpCode = MI.getOpcode();
switch (OpCode) {
case Xtensa::BR_JT:
case Xtensa::JX:
return nullptr;
case Xtensa::J:
return MI.getOperand(0).getMBB();
case Xtensa::BEQ:
case Xtensa::BNE:
case Xtensa::BLT:
case Xtensa::BLTU:
case Xtensa::BGE:
case Xtensa::BGEU:
return MI.getOperand(2).getMBB();
case Xtensa::BEQI:
case Xtensa::BNEI:
case Xtensa::BLTI:
case Xtensa::BLTUI:
case Xtensa::BGEI:
case Xtensa::BGEUI:
return MI.getOperand(2).getMBB();
case Xtensa::BEQZ:
case Xtensa::BNEZ:
case Xtensa::BLTZ:
case Xtensa::BGEZ:
return MI.getOperand(1).getMBB();
default:
llvm_unreachable("Unknown branch opcode");
}
}

bool XtensaInstrInfo::isBranchOffsetInRange(unsigned BranchOp,
int64_t BrOffset) const {
switch (BranchOp) {
case Xtensa::J:
BrOffset -= 4;
return isIntN(18, BrOffset);
case Xtensa::JX:
return true;
case Xtensa::BR_JT:
return true;
case Xtensa::BEQ:
case Xtensa::BNE:
case Xtensa::BLT:
case Xtensa::BLTU:
case Xtensa::BGE:
case Xtensa::BGEU:
case Xtensa::BEQI:
case Xtensa::BNEI:
case Xtensa::BLTI:
case Xtensa::BLTUI:
case Xtensa::BGEI:
case Xtensa::BGEUI:
BrOffset -= 4;
return isIntN(8, BrOffset);
case Xtensa::BEQZ:
case Xtensa::BNEZ:
case Xtensa::BLTZ:
case Xtensa::BGEZ:
BrOffset -= 4;
return isIntN(12, BrOffset);
default:
llvm_unreachable("Unknown branch opcode");
}
}

bool XtensaInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
Expand Down Expand Up @@ -376,6 +459,130 @@ unsigned XtensaInstrInfo::insertBranch(
return Count;
}

void XtensaInstrInfo::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();
MachineConstantPool *ConstantPool = MF->getConstantPool();
auto *XtensaFI = MF->getInfo<XtensaMachineFunctionInfo>();
MachineBasicBlock *JumpToMBB = &DestBB;

if (!isInt<32>(BrOffset))
report_fatal_error(
"Branch offsets outside of the signed 32-bit range not supported");

Register ScratchReg = MRI.createVirtualRegister(&Xtensa::ARRegClass);
auto II = MBB.end();

// Create l32r without last operand. We will add this operand later when
// JumpToMMB will be calculated and placed to the ConstantPool.
MachineInstr &L32R = *BuildMI(MBB, II, DL, get(Xtensa::L32R), ScratchReg);
BuildMI(MBB, II, DL, get(Xtensa::JX)).addReg(ScratchReg, RegState::Kill);

RS->enterBasicBlockEnd(MBB);
Register ScavRegister =
RS->scavengeRegisterBackwards(Xtensa::ARRegClass, L32R.getIterator(),
/*RestoreAfter=*/false, /*SpAdj=*/0,
/*AllowSpill=*/false);
if (ScavRegister != Xtensa::NoRegister)
RS->setRegUsed(ScavRegister);
else {
// The case when there is no scavenged register needs special handling.
// Pick A8 because it doesn't make a difference
ScavRegister = Xtensa::A12;

int FrameIndex = XtensaFI->getBranchRelaxationScratchFrameIndex();
if (FrameIndex == -1)
report_fatal_error(
"Unable to properly handle scavenged register for indirect jump, "
"function code size is significantly larger than estimated");

storeRegToStackSlot(MBB, L32R, ScavRegister, /*IsKill=*/true, FrameIndex,
&Xtensa::ARRegClass, &RI, Register());
RI.eliminateFrameIndex(std::prev(L32R.getIterator()),
/*SpAdj=*/0, /*FIOperandNum=*/1);

loadRegFromStackSlot(RestoreBB, RestoreBB.end(), ScavRegister, FrameIndex,
&Xtensa::ARRegClass, &RI, Register());
RI.eliminateFrameIndex(RestoreBB.back(),
/*SpAdj=*/0, /*FIOperandNum=*/1);
JumpToMBB = &RestoreBB;
}

XtensaConstantPoolValue *C = XtensaConstantPoolMBB::Create(
MF->getFunction().getContext(), JumpToMBB, 0);
unsigned Idx = ConstantPool->getConstantPoolIndex(C, Align(4));
L32R.addOperand(MachineOperand::CreateCPI(Idx, 0));

MRI.replaceRegWith(ScratchReg, ScavRegister);
MRI.clearVirtRegs();
}

unsigned XtensaInstrInfo::insertConstBranchAtInst(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is unused and it compiles with a warning.

MachineBasicBlock &MBB, MachineInstr *I, int64_t offset,
ArrayRef<MachineOperand> Cond, DebugLoc DL, int *BytesAdded) const {
// Shouldn't be a fall through.
assert(&MBB && "InsertBranch must not be told to insert a fallthrough");
Copy link
Member

@MaskRay MaskRay Nov 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This led to a Clang warning -Wundefined-bool-conversion. We ensure that the code base is warning free for recent Clang versions. Fixed at head.

Personally I use https://raw.githubusercontent.com/chromium/chromium/main/tools/clang/scripts/update.py

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@MaskRay , thank you very much for a fix. Sorry, I missed this problem.

assert(Cond.size() <= 4 &&
"Xtensa branch conditions have less than four components!");

if (Cond.empty() || (Cond[0].getImm() == Xtensa::J)) {
// Unconditional branch
MachineInstr *MI = BuildMI(MBB, I, DL, get(Xtensa::J)).addImm(offset);
if (BytesAdded && MI)
*BytesAdded += getInstSizeInBytes(*MI);
return 1;
}

unsigned Count = 0;
unsigned BR_C = Cond[0].getImm();
MachineInstr *MI = nullptr;
switch (BR_C) {
case Xtensa::BEQ:
case Xtensa::BNE:
case Xtensa::BLT:
case Xtensa::BLTU:
case Xtensa::BGE:
case Xtensa::BGEU:
MI = BuildMI(MBB, I, DL, get(BR_C))
.addImm(offset)
.addReg(Cond[1].getReg())
.addReg(Cond[2].getReg());
break;
case Xtensa::BEQI:
case Xtensa::BNEI:
case Xtensa::BLTI:
case Xtensa::BLTUI:
case Xtensa::BGEI:
case Xtensa::BGEUI:
MI = BuildMI(MBB, I, DL, get(BR_C))
.addImm(offset)
.addReg(Cond[1].getReg())
.addImm(Cond[2].getImm());
break;
case Xtensa::BEQZ:
case Xtensa::BNEZ:
case Xtensa::BLTZ:
case Xtensa::BGEZ:
MI = BuildMI(MBB, I, DL, get(BR_C)).addImm(offset).addReg(Cond[1].getReg());
break;
default:
llvm_unreachable("Invalid branch type!");
}
if (BytesAdded && MI)
*BytesAdded += getInstSizeInBytes(*MI);
++Count;
return Count;
}

unsigned XtensaInstrInfo::insertBranchAtInst(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I,
MachineBasicBlock *TBB,
Expand Down
17 changes: 17 additions & 0 deletions llvm/lib/Target/Xtensa/XtensaInstrInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class XtensaInstrInfo : public XtensaGenInstrInfo {
void adjustStackPtr(unsigned SP, int64_t Amount, MachineBasicBlock &MBB,
MachineBasicBlock::iterator I) const;

unsigned getInstSizeInBytes(const MachineInstr &MI) const override;

// Return the XtensaRegisterInfo, which this class owns.
const XtensaRegisterInfo &getRegisterInfo() const { return RI; }

Expand Down Expand Up @@ -77,6 +79,11 @@ class XtensaInstrInfo : public XtensaGenInstrInfo {
bool
reverseBranchCondition(SmallVectorImpl<MachineOperand> &Cond) const override;

MachineBasicBlock *getBranchDestBlock(const MachineInstr &MI) const override;

bool isBranchOffsetInRange(unsigned BranchOpc,
int64_t BrOffset) const override;

bool analyzeBranch(MachineBasicBlock &MBB, MachineBasicBlock *&TBB,
MachineBasicBlock *&FBB,
SmallVectorImpl<MachineOperand> &Cond,
Expand All @@ -90,12 +97,22 @@ class XtensaInstrInfo : public XtensaGenInstrInfo {
const DebugLoc &DL,
int *BytesAdded = nullptr) const override;

void insertIndirectBranch(MachineBasicBlock &MBB, MachineBasicBlock &DestBB,
MachineBasicBlock &RestoreBB, const DebugLoc &DL,
int64_t BrOffset = 0,
RegScavenger *RS = nullptr) const override;

unsigned insertBranchAtInst(MachineBasicBlock &MBB,
MachineBasicBlock::iterator I,
MachineBasicBlock *TBB,
ArrayRef<MachineOperand> Cond, const DebugLoc &DL,
int *BytesAdded) const;

unsigned insertConstBranchAtInst(MachineBasicBlock &MBB, MachineInstr *I,
int64_t offset,
ArrayRef<MachineOperand> Cond, DebugLoc DL,
int *BytesAdded) const;

// Return true if MI is a conditional or unconditional branch.
// When returning true, set Cond to the mask of condition-code
// values on which the instruction will branch, and set Target
Expand Down
Loading
Loading