blob: a4b359af303a3e4c7c49c7604084ea0ba1dd6806 [file] [log] [blame]
//===------- HexagonTfrCleanup.cpp - Hexagon Transfer Cleanup Pass -------===//
//
// 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 pass is to address a situation that appears after register allocaion
// evey now and then, namely a register copy from a source that was defined
// as an immediate value in the same block (usually just before the copy).
//
// Here is an example of actual code emitted that shows this problem:
//
// .LBB0_5:
// {
// r5 = zxtb(r8)
// r6 = or(r6, ##12345)
// }
// {
// r3 = xor(r1, r2)
// r1 = #0 <-- r1 set to #0
// }
// {
// r7 = r1 <-- r7 set to r1
// r0 = zxtb(r3)
// }
#define DEBUG_TYPE "tfr-cleanup"
#include "HexagonTargetMachine.h"
#include "llvm/CodeGen/LiveInterval.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/CodeGen/TargetInstrInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Target/TargetMachine.h"
using namespace llvm;
namespace llvm {
FunctionPass *createHexagonTfrCleanup();
void initializeHexagonTfrCleanupPass(PassRegistry &);
} // namespace llvm
namespace {
class HexagonTfrCleanup : public MachineFunctionPass {
public:
static char ID;
HexagonTfrCleanup() : MachineFunctionPass(ID), HII(0), TRI(0) {
PassRegistry &R = *PassRegistry::getPassRegistry();
initializeHexagonTfrCleanupPass(R);
}
StringRef getPassName() const override { return "Hexagon TFR Cleanup"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
MachineFunctionPass::getAnalysisUsage(AU);
}
bool runOnMachineFunction(MachineFunction &MF) override;
private:
const HexagonInstrInfo *HII;
const TargetRegisterInfo *TRI;
typedef DenseMap<unsigned, uint64_t> ImmediateMap;
bool isIntReg(unsigned Reg, bool &Is32);
void setReg(unsigned R32, uint32_t V32, ImmediateMap &IMap);
bool getReg(unsigned Reg, uint64_t &Val, ImmediateMap &IMap);
bool updateImmMap(MachineInstr *MI, ImmediateMap &IMap);
bool rewriteIfImm(MachineInstr *MI, ImmediateMap &IMap, SlotIndexes *Indexes);
bool eraseIfRedundant(MachineInstr *MI, SlotIndexes *Indexes);
};
} // namespace
char HexagonTfrCleanup::ID = 0;
namespace llvm {
char &HexagonTfrCleanupID = HexagonTfrCleanup::ID;
}
bool HexagonTfrCleanup::isIntReg(unsigned Reg, bool &Is32) {
Is32 = Hexagon::IntRegsRegClass.contains(Reg);
return Is32 || Hexagon::DoubleRegsRegClass.contains(Reg);
}
// Assign given value V32 to the specified the register R32 in the map. Only
// 32-bit registers are valid arguments.
void HexagonTfrCleanup::setReg(unsigned R32, uint32_t V32, ImmediateMap &IMap) {
ImmediateMap::iterator F = IMap.find(R32);
if (F == IMap.end())
IMap.insert(std::make_pair(R32, V32));
else
F->second = V32;
}
// Retrieve a value of the provided register Reg and store it into Val.
// Return "true" if a value was found, "false" otherwise.
bool HexagonTfrCleanup::getReg(unsigned Reg, uint64_t &Val,
ImmediateMap &IMap) {
bool Is32;
if (!isIntReg(Reg, Is32))
return false;
if (Is32) {
ImmediateMap::iterator F = IMap.find(Reg);
if (F == IMap.end())
return false;
Val = F->second;
return true;
}
// For 64-bit registers, compose the value from the values of its
// subregisters.
unsigned SubL = TRI->getSubReg(Reg, Hexagon::isub_lo);
unsigned SubH = TRI->getSubReg(Reg, Hexagon::isub_hi);
ImmediateMap::iterator FL = IMap.find(SubL), FH = IMap.find(SubH);
if (FL == IMap.end() || FH == IMap.end())
return false;
Val = (FH->second << 32) | FL->second;
return true;
}
// Process an instruction and record the relevant information in the imme-
// diate map.
bool HexagonTfrCleanup::updateImmMap(MachineInstr *MI, ImmediateMap &IMap) {
using namespace Hexagon;
if (MI->isCall()) {
IMap.clear();
return true;
}
// If this is an instruction that loads a constant into a register,
// record this information in IMap.
unsigned Opc = MI->getOpcode();
if (Opc == A2_tfrsi || Opc == A2_tfrpi) {
unsigned DefR = MI->getOperand(0).getReg();
bool Is32;
if (!isIntReg(DefR, Is32))
return false;
if (!MI->getOperand(1).isImm()) {
if (!Is32) {
IMap.erase(TRI->getSubReg(DefR, isub_lo));
IMap.erase(TRI->getSubReg(DefR, isub_hi));
} else {
IMap.erase(DefR);
}
return false;
}
uint64_t Val = MI->getOperand(1).getImm();
// If it's a 64-bit register, break it up into subregisters.
if (!Is32) {
uint32_t VH = (Val >> 32), VL = (Val & 0xFFFFFFFFU);
setReg(TRI->getSubReg(DefR, isub_lo), VL, IMap);
setReg(TRI->getSubReg(DefR, isub_hi), VH, IMap);
} else {
setReg(DefR, Val, IMap);
}
return true;
}
// Not a A2_tfr[sp]i. Invalidate all modified registers in IMap.
for (MachineInstr::mop_iterator Mo = MI->operands_begin(),
E = MI->operands_end();
Mo != E; ++Mo) {
if (Mo->isRegMask()) {
IMap.clear();
return true;
}
if (!Mo->isReg() || !Mo->isDef())
continue;
unsigned R = Mo->getReg();
for (MCRegAliasIterator AR(R, TRI, true); AR.isValid(); ++AR) {
ImmediateMap::iterator F = IMap.find(*AR);
if (F != IMap.end())
IMap.erase(F);
}
}
return true;
}
// Rewrite the instruction as A2_tfrsi/A2_tfrpi, it is a copy of a source that
// has a known constant value.
bool HexagonTfrCleanup::rewriteIfImm(MachineInstr *MI, ImmediateMap &IMap,
SlotIndexes *Indexes) {
using namespace Hexagon;
unsigned Opc = MI->getOpcode();
switch (Opc) {
case A2_tfr:
case A2_tfrp:
case COPY:
break;
default:
return false;
}
unsigned DstR = MI->getOperand(0).getReg();
unsigned SrcR = MI->getOperand(1).getReg();
bool Tmp, Is32;
if (!isIntReg(DstR, Is32) || !isIntReg(SrcR, Tmp))
return false;
assert(Tmp == Is32 && "Register size mismatch");
uint64_t Val;
bool Found = getReg(SrcR, Val, IMap);
if (!Found)
return false;
MachineBasicBlock &B = *MI->getParent();
DebugLoc DL = MI->getDebugLoc();
int64_t SVal = Is32 ? int32_t(Val) : Val;
auto &HST = B.getParent()->getSubtarget<HexagonSubtarget>();
MachineInstr *NewMI;
if (Is32)
NewMI = BuildMI(B, MI, DL, HII->get(A2_tfrsi), DstR).addImm(SVal);
else if (isInt<8>(SVal))
NewMI = BuildMI(B, MI, DL, HII->get(A2_tfrpi), DstR).addImm(SVal);
else if (isInt<8>(SVal >> 32) && isInt<8>(int32_t(Val & 0xFFFFFFFFLL)))
NewMI = BuildMI(B, MI, DL, HII->get(A2_combineii), DstR)
.addImm(int32_t(SVal >> 32))
.addImm(int32_t(Val & 0xFFFFFFFFLL));
else if (HST.isTinyCore())
// Disable generating CONST64 since it requires load resource.
return false;
else
NewMI = BuildMI(B, MI, DL, HII->get(CONST64), DstR).addImm(Val);
// Replace the MI to reuse the same slot index
if (Indexes)
Indexes->replaceMachineInstrInMaps(*MI, *NewMI);
MI->eraseFromParent();
return true;
}
// Remove the instruction if it is a self-assignment.
bool HexagonTfrCleanup::eraseIfRedundant(MachineInstr *MI,
SlotIndexes *Indexes) {
unsigned Opc = MI->getOpcode();
unsigned DefR, SrcR;
bool IsUndef = false;
switch (Opc) {
case Hexagon::A2_tfr:
// Rd = Rd
DefR = MI->getOperand(0).getReg();
SrcR = MI->getOperand(1).getReg();
IsUndef = MI->getOperand(1).isUndef();
break;
case Hexagon::A2_tfrt:
case Hexagon::A2_tfrf:
// if ([!]Pu) Rd = Rd
DefR = MI->getOperand(0).getReg();
SrcR = MI->getOperand(2).getReg();
IsUndef = MI->getOperand(2).isUndef();
break;
default:
return false;
}
if (DefR != SrcR)
return false;
if (IsUndef) {
MachineBasicBlock &B = *MI->getParent();
DebugLoc DL = MI->getDebugLoc();
auto DefI = BuildMI(B, MI, DL, HII->get(TargetOpcode::IMPLICIT_DEF), DefR);
for (auto &Op : MI->operands())
if (Op.isReg() && Op.isDef() && Op.isImplicit())
DefI->addOperand(Op);
}
if (Indexes)
Indexes->removeMachineInstrFromMaps(*MI);
MI->eraseFromParent();
return true;
}
bool HexagonTfrCleanup::runOnMachineFunction(MachineFunction &MF) {
bool Changed = false;
// Map: 32-bit register -> immediate value.
// 64-bit registers are stored through their subregisters.
ImmediateMap IMap;
SlotIndexes *Indexes = this->getAnalysisIfAvailable<SlotIndexes>();
auto &HST = MF.getSubtarget<HexagonSubtarget>();
HII = HST.getInstrInfo();
TRI = HST.getRegisterInfo();
for (MachineFunction::iterator I = MF.begin(), E = MF.end(); I != E; ++I) {
MachineBasicBlock &B = *I;
MachineBasicBlock::iterator J, F, NextJ;
IMap.clear();
bool Inserted = false, Erased = false;
for (J = B.begin(), F = B.end(); J != F; J = NextJ) {
NextJ = std::next(J);
MachineInstr *MI = &*J;
bool E = eraseIfRedundant(MI, Indexes);
Erased |= E;
if (E)
continue;
Inserted |= rewriteIfImm(MI, IMap, Indexes);
MachineBasicBlock::iterator NewJ = std::prev(NextJ);
updateImmMap(&*NewJ, IMap);
}
bool BlockC = Inserted | Erased;
Changed |= BlockC;
if (BlockC && Indexes)
Indexes->repairIndexesInRange(&B, B.begin(), B.end());
}
return Changed;
}
//===----------------------------------------------------------------------===//
// Public Constructor Functions
//===----------------------------------------------------------------------===//
INITIALIZE_PASS(HexagonTfrCleanup, "tfr-cleanup", "Hexagon TFR Cleanup", false,
false)
FunctionPass *llvm::createHexagonTfrCleanup() {
return new HexagonTfrCleanup();
}