2021-03-08 01:32:18 +01:00
|
|
|
//===-- M68kInstrInfo.cpp - M68k 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
///
|
|
|
|
/// \file
|
|
|
|
/// This file contains the M68k declaration of the TargetInstrInfo class.
|
|
|
|
///
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
#include "M68kInstrInfo.h"
|
|
|
|
|
|
|
|
#include "M68kInstrBuilder.h"
|
|
|
|
#include "M68kMachineFunction.h"
|
|
|
|
#include "M68kTargetMachine.h"
|
|
|
|
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
|
|
#include "llvm/ADT/ScopeExit.h"
|
|
|
|
#include "llvm/CodeGen/LivePhysRegs.h"
|
|
|
|
#include "llvm/CodeGen/LiveVariables.h"
|
|
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
|
|
|
|
|
|
#include <functional>
|
|
|
|
|
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "M68k-instr-info"
|
|
|
|
|
|
|
|
#define GET_INSTRINFO_CTOR_DTOR
|
|
|
|
#include "M68kGenInstrInfo.inc"
|
|
|
|
|
|
|
|
// Pin the vtable to this file.
|
|
|
|
void M68kInstrInfo::anchor() {}
|
|
|
|
|
|
|
|
M68kInstrInfo::M68kInstrInfo(const M68kSubtarget &STI)
|
|
|
|
: M68kGenInstrInfo(M68k::ADJCALLSTACKDOWN, M68k::ADJCALLSTACKUP, 0,
|
|
|
|
M68k::RET),
|
|
|
|
Subtarget(STI), RI(STI) {}
|
|
|
|
|
|
|
|
static M68k::CondCode getCondFromBranchOpc(unsigned BrOpc) {
|
|
|
|
switch (BrOpc) {
|
|
|
|
default:
|
|
|
|
return M68k::COND_INVALID;
|
|
|
|
case M68k::Beq8:
|
|
|
|
return M68k::COND_EQ;
|
|
|
|
case M68k::Bne8:
|
|
|
|
return M68k::COND_NE;
|
|
|
|
case M68k::Blt8:
|
|
|
|
return M68k::COND_LT;
|
|
|
|
case M68k::Ble8:
|
|
|
|
return M68k::COND_LE;
|
|
|
|
case M68k::Bgt8:
|
|
|
|
return M68k::COND_GT;
|
|
|
|
case M68k::Bge8:
|
|
|
|
return M68k::COND_GE;
|
|
|
|
case M68k::Bcs8:
|
|
|
|
return M68k::COND_CS;
|
|
|
|
case M68k::Bls8:
|
|
|
|
return M68k::COND_LS;
|
|
|
|
case M68k::Bhi8:
|
|
|
|
return M68k::COND_HI;
|
|
|
|
case M68k::Bcc8:
|
|
|
|
return M68k::COND_CC;
|
|
|
|
case M68k::Bmi8:
|
|
|
|
return M68k::COND_MI;
|
|
|
|
case M68k::Bpl8:
|
|
|
|
return M68k::COND_PL;
|
|
|
|
case M68k::Bvs8:
|
|
|
|
return M68k::COND_VS;
|
|
|
|
case M68k::Bvc8:
|
|
|
|
return M68k::COND_VC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::AnalyzeBranchImpl(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock *&TBB,
|
|
|
|
MachineBasicBlock *&FBB,
|
|
|
|
SmallVectorImpl<MachineOperand> &Cond,
|
|
|
|
bool AllowModify) const {
|
|
|
|
|
|
|
|
auto UncondBranch =
|
|
|
|
std::pair<MachineBasicBlock::reverse_iterator, MachineBasicBlock *>{
|
|
|
|
MBB.rend(), nullptr};
|
|
|
|
|
|
|
|
// Erase any instructions if allowed at the end of the scope.
|
|
|
|
std::vector<std::reference_wrapper<llvm::MachineInstr>> EraseList;
|
|
|
|
auto FinalizeOnReturn = llvm::make_scope_exit([&EraseList] {
|
|
|
|
std::for_each(EraseList.begin(), EraseList.end(),
|
2021-03-09 12:06:29 +01:00
|
|
|
[](auto &ref) { ref.get().eraseFromParent(); });
|
2021-03-08 01:32:18 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
// Start from the bottom of the block and work up, examining the
|
|
|
|
// terminator instructions.
|
|
|
|
for (auto iter = MBB.rbegin(); iter != MBB.rend(); iter = std::next(iter)) {
|
|
|
|
|
|
|
|
unsigned Opcode = iter->getOpcode();
|
|
|
|
|
|
|
|
if (iter->isDebugInstr())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Working from the bottom, when we see a non-terminator instruction, we're
|
|
|
|
// done.
|
|
|
|
if (!isUnpredicatedTerminator(*iter))
|
|
|
|
break;
|
|
|
|
|
|
|
|
// A terminator that isn't a branch can't easily be handled by this
|
|
|
|
// analysis.
|
|
|
|
if (!iter->isBranch())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// Handle unconditional branches.
|
|
|
|
if (Opcode == M68k::BRA8 || Opcode == M68k::BRA16) {
|
|
|
|
if (!iter->getOperand(0).isMBB())
|
|
|
|
return true;
|
|
|
|
UncondBranch = {iter, iter->getOperand(0).getMBB()};
|
|
|
|
|
|
|
|
// TBB is used to indicate the unconditional destination.
|
|
|
|
TBB = UncondBranch.second;
|
|
|
|
|
|
|
|
if (!AllowModify)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If the block has any instructions after a JMP, erase them.
|
|
|
|
EraseList.insert(EraseList.begin(), MBB.rbegin(), iter);
|
|
|
|
|
|
|
|
Cond.clear();
|
|
|
|
FBB = nullptr;
|
|
|
|
|
|
|
|
// Erase the JMP if it's equivalent to a fall-through.
|
|
|
|
if (MBB.isLayoutSuccessor(UncondBranch.second)) {
|
|
|
|
TBB = nullptr;
|
|
|
|
EraseList.push_back(*iter);
|
|
|
|
UncondBranch = {MBB.rend(), nullptr};
|
|
|
|
}
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle conditional branches.
|
|
|
|
auto BranchCode = M68k::GetCondFromBranchOpc(Opcode);
|
|
|
|
|
|
|
|
// Can't handle indirect branch.
|
|
|
|
if (BranchCode == M68k::COND_INVALID)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// In practice we should never have an undef CCR operand, if we do
|
|
|
|
// abort here as we are not prepared to preserve the flag.
|
|
|
|
// ??? Is this required?
|
|
|
|
// if (iter->getOperand(1).isUndef())
|
|
|
|
// return true;
|
|
|
|
|
|
|
|
// Working from the bottom, handle the first conditional branch.
|
|
|
|
if (Cond.empty()) {
|
|
|
|
if (!iter->getOperand(0).isMBB())
|
|
|
|
return true;
|
|
|
|
MachineBasicBlock *CondBranchTarget = iter->getOperand(0).getMBB();
|
|
|
|
|
|
|
|
// If we see something like this:
|
|
|
|
//
|
|
|
|
// bcc l1
|
|
|
|
// bra l2
|
|
|
|
// ...
|
|
|
|
// l1:
|
|
|
|
// ...
|
|
|
|
// l2:
|
|
|
|
if (UncondBranch.first != MBB.rend()) {
|
|
|
|
|
|
|
|
assert(std::next(UncondBranch.first) == iter && "Wrong block layout.");
|
|
|
|
|
|
|
|
// And we are allowed to modify the block and the target block of the
|
|
|
|
// conditional branch is the direct successor of this block:
|
|
|
|
//
|
|
|
|
// bcc l1
|
|
|
|
// bra l2
|
|
|
|
// l1:
|
|
|
|
// ...
|
|
|
|
// l2:
|
|
|
|
//
|
|
|
|
// we change it to this if allowed:
|
|
|
|
//
|
|
|
|
// bncc l2
|
|
|
|
// l1:
|
|
|
|
// ...
|
|
|
|
// l2:
|
|
|
|
//
|
|
|
|
// Which is a bit more efficient.
|
|
|
|
if (AllowModify && MBB.isLayoutSuccessor(CondBranchTarget)) {
|
|
|
|
|
|
|
|
BranchCode = GetOppositeBranchCondition(BranchCode);
|
|
|
|
unsigned BNCC = GetCondBranchFromCond(BranchCode);
|
|
|
|
|
|
|
|
BuildMI(MBB, *UncondBranch.first, MBB.rfindDebugLoc(iter), get(BNCC))
|
|
|
|
.addMBB(UncondBranch.second);
|
|
|
|
|
|
|
|
EraseList.push_back(*iter);
|
|
|
|
EraseList.push_back(*UncondBranch.first);
|
|
|
|
|
|
|
|
TBB = UncondBranch.second;
|
|
|
|
FBB = nullptr;
|
|
|
|
Cond.push_back(MachineOperand::CreateImm(BranchCode));
|
|
|
|
|
|
|
|
// Otherwise preserve TBB, FBB and Cond as requested
|
|
|
|
} else {
|
|
|
|
TBB = CondBranchTarget;
|
|
|
|
FBB = UncondBranch.second;
|
|
|
|
Cond.push_back(MachineOperand::CreateImm(BranchCode));
|
|
|
|
}
|
|
|
|
|
|
|
|
UncondBranch = {MBB.rend(), nullptr};
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
TBB = CondBranchTarget;
|
|
|
|
FBB = nullptr;
|
|
|
|
Cond.push_back(MachineOperand::CreateImm(BranchCode));
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handle subsequent conditional branches. Only handle the case where all
|
|
|
|
// conditional branches branch to the same destination and their condition
|
|
|
|
// opcodes fit one of the special multi-branch idioms.
|
|
|
|
assert(Cond.size() == 1);
|
|
|
|
assert(TBB);
|
|
|
|
|
|
|
|
// If the conditions are the same, we can leave them alone.
|
|
|
|
auto OldBranchCode = static_cast<M68k::CondCode>(Cond[0].getImm());
|
|
|
|
if (!iter->getOperand(0).isMBB())
|
|
|
|
return true;
|
|
|
|
auto NewTBB = iter->getOperand(0).getMBB();
|
|
|
|
if (OldBranchCode == BranchCode && TBB == NewTBB)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// If they differ we cannot do much here.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock *&TBB,
|
|
|
|
MachineBasicBlock *&FBB,
|
|
|
|
SmallVectorImpl<MachineOperand> &Cond,
|
|
|
|
bool AllowModify) const {
|
|
|
|
return AnalyzeBranchImpl(MBB, TBB, FBB, Cond, AllowModify);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned M68kInstrInfo::removeBranch(MachineBasicBlock &MBB,
|
|
|
|
int *BytesRemoved) const {
|
|
|
|
assert(!BytesRemoved && "code size not handled");
|
|
|
|
|
|
|
|
MachineBasicBlock::iterator I = MBB.end();
|
|
|
|
unsigned Count = 0;
|
|
|
|
|
|
|
|
while (I != MBB.begin()) {
|
|
|
|
--I;
|
|
|
|
if (I->isDebugValue())
|
|
|
|
continue;
|
|
|
|
if (I->getOpcode() != M68k::BRA8 &&
|
|
|
|
getCondFromBranchOpc(I->getOpcode()) == M68k::COND_INVALID)
|
|
|
|
break;
|
|
|
|
// Remove the branch.
|
|
|
|
I->eraseFromParent();
|
|
|
|
I = MBB.end();
|
|
|
|
++Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned M68kInstrInfo::insertBranch(
|
|
|
|
MachineBasicBlock &MBB, MachineBasicBlock *TBB, MachineBasicBlock *FBB,
|
|
|
|
ArrayRef<MachineOperand> Cond, const DebugLoc &DL, int *BytesAdded) const {
|
|
|
|
// Shouldn't be a fall through.
|
|
|
|
assert(TBB && "InsertBranch must not be told to insert a fallthrough");
|
|
|
|
assert((Cond.size() == 1 || Cond.size() == 0) &&
|
|
|
|
"M68k branch conditions have one component!");
|
|
|
|
assert(!BytesAdded && "code size not handled");
|
|
|
|
|
|
|
|
if (Cond.empty()) {
|
|
|
|
// Unconditional branch?
|
|
|
|
assert(!FBB && "Unconditional branch with multiple successors!");
|
|
|
|
BuildMI(&MBB, DL, get(M68k::BRA8)).addMBB(TBB);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If FBB is null, it is implied to be a fall-through block.
|
|
|
|
bool FallThru = FBB == nullptr;
|
|
|
|
|
|
|
|
// Conditional branch.
|
|
|
|
unsigned Count = 0;
|
|
|
|
M68k::CondCode CC = (M68k::CondCode)Cond[0].getImm();
|
|
|
|
unsigned Opc = GetCondBranchFromCond(CC);
|
|
|
|
BuildMI(&MBB, DL, get(Opc)).addMBB(TBB);
|
|
|
|
++Count;
|
|
|
|
if (!FallThru) {
|
|
|
|
// Two-way Conditional branch. Insert the second branch.
|
|
|
|
BuildMI(&MBB, DL, get(M68k::BRA8)).addMBB(FBB);
|
|
|
|
++Count;
|
|
|
|
}
|
|
|
|
return Count;
|
|
|
|
}
|
|
|
|
|
|
|
|
void M68kInstrInfo::AddSExt(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock::iterator I, DebugLoc DL,
|
|
|
|
unsigned Reg, MVT From, MVT To) const {
|
|
|
|
if (From == MVT::i8) {
|
|
|
|
unsigned R = Reg;
|
|
|
|
// EXT16 requires i16 register
|
|
|
|
if (To == MVT::i32) {
|
|
|
|
R = RI.getSubReg(Reg, M68k::MxSubRegIndex16Lo);
|
|
|
|
assert(R && "No viable SUB register available");
|
|
|
|
}
|
|
|
|
BuildMI(MBB, I, DL, get(M68k::EXT16), R).addReg(R);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (To == MVT::i32)
|
|
|
|
BuildMI(MBB, I, DL, get(M68k::EXT32), Reg).addReg(Reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
void M68kInstrInfo::AddZExt(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock::iterator I, DebugLoc DL,
|
|
|
|
unsigned Reg, MVT From, MVT To) const {
|
|
|
|
|
|
|
|
unsigned Mask, And;
|
|
|
|
if (From == MVT::i8)
|
|
|
|
Mask = 0xFF;
|
|
|
|
else
|
|
|
|
Mask = 0xFFFF;
|
|
|
|
|
|
|
|
if (To == MVT::i16)
|
|
|
|
And = M68k::AND16di;
|
|
|
|
else // i32
|
|
|
|
And = M68k::AND32di;
|
|
|
|
|
|
|
|
// TODO use xor r,r to decrease size
|
|
|
|
BuildMI(MBB, I, DL, get(And), Reg).addReg(Reg).addImm(Mask);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::ExpandMOVX_RR(MachineInstrBuilder &MIB, MVT MVTDst,
|
|
|
|
MVT MVTSrc) const {
|
|
|
|
unsigned Move = MVTDst == MVT::i16 ? M68k::MOV16rr : M68k::MOV32rr;
|
|
|
|
unsigned Dst = MIB->getOperand(0).getReg();
|
|
|
|
unsigned Src = MIB->getOperand(1).getReg();
|
|
|
|
|
|
|
|
assert(Dst != Src && "You cannot use the same Regs with MOVX_RR");
|
|
|
|
|
|
|
|
const auto &TRI = getRegisterInfo();
|
|
|
|
|
|
|
|
const auto *RCDst = TRI.getMaximalPhysRegClass(Dst, MVTDst);
|
|
|
|
const auto *RCSrc = TRI.getMaximalPhysRegClass(Src, MVTSrc);
|
|
|
|
|
|
|
|
assert(RCDst && RCSrc && "Wrong use of MOVX_RR");
|
|
|
|
assert(RCDst != RCSrc && "You cannot use the same Reg Classes with MOVX_RR");
|
|
|
|
|
|
|
|
// We need to find the super source register that matches the size of Dst
|
|
|
|
unsigned SSrc = RI.getMatchingMegaReg(Src, RCDst);
|
|
|
|
assert(SSrc && "No viable MEGA register available");
|
|
|
|
|
|
|
|
DebugLoc DL = MIB->getDebugLoc();
|
|
|
|
|
|
|
|
// If it happens to that super source register is the destination register
|
|
|
|
// we do nothing
|
|
|
|
if (Dst == SSrc) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Remove " << *MIB.getInstr() << '\n');
|
|
|
|
MIB->eraseFromParent();
|
|
|
|
} else { // otherwise we need to MOV
|
|
|
|
LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to MOV\n");
|
|
|
|
MIB->setDesc(get(Move));
|
|
|
|
MIB->getOperand(1).setReg(SSrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Expand SExt MOVE pseudos into a MOV and a EXT if the operands are two
|
|
|
|
/// different registers or just EXT if it is the same register
|
|
|
|
bool M68kInstrInfo::ExpandMOVSZX_RR(MachineInstrBuilder &MIB, bool IsSigned,
|
|
|
|
MVT MVTDst, MVT MVTSrc) const {
|
|
|
|
LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to ");
|
|
|
|
|
|
|
|
unsigned Move;
|
|
|
|
|
|
|
|
if (MVTDst == MVT::i16)
|
|
|
|
Move = M68k::MOV16rr;
|
|
|
|
else // i32
|
|
|
|
Move = M68k::MOV32rr;
|
|
|
|
|
|
|
|
unsigned Dst = MIB->getOperand(0).getReg();
|
|
|
|
unsigned Src = MIB->getOperand(1).getReg();
|
|
|
|
|
|
|
|
assert(Dst != Src && "You cannot use the same Regs with MOVSX_RR");
|
|
|
|
|
|
|
|
const auto &TRI = getRegisterInfo();
|
|
|
|
|
|
|
|
const auto *RCDst = TRI.getMaximalPhysRegClass(Dst, MVTDst);
|
|
|
|
const auto *RCSrc = TRI.getMaximalPhysRegClass(Src, MVTSrc);
|
|
|
|
|
|
|
|
assert(RCDst && RCSrc && "Wrong use of MOVSX_RR");
|
|
|
|
assert(RCDst != RCSrc && "You cannot use the same Reg Classes with MOVSX_RR");
|
|
|
|
|
|
|
|
// We need to find the super source register that matches the size of Dst
|
|
|
|
unsigned SSrc = RI.getMatchingMegaReg(Src, RCDst);
|
|
|
|
assert(SSrc && "No viable MEGA register available");
|
|
|
|
|
|
|
|
MachineBasicBlock &MBB = *MIB->getParent();
|
|
|
|
DebugLoc DL = MIB->getDebugLoc();
|
|
|
|
|
|
|
|
if (Dst != SSrc) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Move and " << '\n');
|
|
|
|
BuildMI(MBB, MIB.getInstr(), DL, get(Move), Dst).addReg(SSrc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IsSigned) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Sign Extend" << '\n');
|
|
|
|
AddSExt(MBB, MIB.getInstr(), DL, Dst, MVTSrc, MVTDst);
|
|
|
|
} else {
|
|
|
|
LLVM_DEBUG(dbgs() << "Zero Extend" << '\n');
|
|
|
|
AddZExt(MBB, MIB.getInstr(), DL, Dst, MVTSrc, MVTDst);
|
|
|
|
}
|
|
|
|
|
|
|
|
MIB->eraseFromParent();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::ExpandMOVSZX_RM(MachineInstrBuilder &MIB, bool IsSigned,
|
|
|
|
const MCInstrDesc &Desc, MVT MVTDst,
|
|
|
|
MVT MVTSrc) const {
|
|
|
|
LLVM_DEBUG(dbgs() << "Expand " << *MIB.getInstr() << " to LOAD and ");
|
|
|
|
|
|
|
|
unsigned Dst = MIB->getOperand(0).getReg();
|
|
|
|
|
|
|
|
// We need the subreg of Dst to make instruction verifier happy because the
|
|
|
|
// real machine instruction consumes and produces values of the same size and
|
|
|
|
// the registers the will be used here fall into different classes and this
|
|
|
|
// makes IV cry. We could of course use bigger operation but this will put
|
|
|
|
// some pressure on cache and memory so no.
|
|
|
|
unsigned SubDst =
|
|
|
|
RI.getSubReg(Dst, MVTSrc == MVT::i8 ? M68k::MxSubRegIndex8Lo
|
|
|
|
: M68k::MxSubRegIndex16Lo);
|
|
|
|
assert(SubDst && "No viable SUB register available");
|
|
|
|
|
|
|
|
// Make this a plain move
|
|
|
|
MIB->setDesc(Desc);
|
|
|
|
MIB->getOperand(0).setReg(SubDst);
|
|
|
|
|
|
|
|
MachineBasicBlock::iterator I = MIB.getInstr();
|
|
|
|
I++;
|
|
|
|
MachineBasicBlock &MBB = *MIB->getParent();
|
|
|
|
DebugLoc DL = MIB->getDebugLoc();
|
|
|
|
|
|
|
|
if (IsSigned) {
|
|
|
|
LLVM_DEBUG(dbgs() << "Sign Extend" << '\n');
|
|
|
|
AddSExt(MBB, I, DL, Dst, MVTSrc, MVTDst);
|
|
|
|
} else {
|
|
|
|
LLVM_DEBUG(dbgs() << "Zero Extend" << '\n');
|
|
|
|
AddZExt(MBB, I, DL, Dst, MVTSrc, MVTDst);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::ExpandPUSH_POP(MachineInstrBuilder &MIB,
|
|
|
|
const MCInstrDesc &Desc, bool IsPush) const {
|
|
|
|
MachineBasicBlock::iterator I = MIB.getInstr();
|
|
|
|
I++;
|
|
|
|
MachineBasicBlock &MBB = *MIB->getParent();
|
|
|
|
MachineOperand MO = MIB->getOperand(0);
|
|
|
|
DebugLoc DL = MIB->getDebugLoc();
|
|
|
|
if (IsPush)
|
|
|
|
BuildMI(MBB, I, DL, Desc).addReg(RI.getStackRegister()).add(MO);
|
|
|
|
else
|
|
|
|
BuildMI(MBB, I, DL, Desc, MO.getReg()).addReg(RI.getStackRegister());
|
|
|
|
|
|
|
|
MIB->eraseFromParent();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::ExpandCCR(MachineInstrBuilder &MIB, bool IsToCCR) const {
|
|
|
|
|
|
|
|
// Replace the pseudo instruction with the real one
|
|
|
|
if (IsToCCR)
|
|
|
|
MIB->setDesc(get(M68k::MOV16cd));
|
|
|
|
else
|
|
|
|
// FIXME M68010 or later is required
|
|
|
|
MIB->setDesc(get(M68k::MOV16dc));
|
|
|
|
|
|
|
|
// Promote used register to the next class
|
|
|
|
auto &Opd = MIB->getOperand(1);
|
|
|
|
Opd.setReg(getRegisterInfo().getMatchingSuperReg(
|
|
|
|
Opd.getReg(), M68k::MxSubRegIndex8Lo, &M68k::DR16RegClass));
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::ExpandMOVEM(MachineInstrBuilder &MIB,
|
|
|
|
const MCInstrDesc &Desc, bool IsRM) const {
|
|
|
|
int Reg = 0, Offset = 0, Base = 0;
|
|
|
|
auto XR32 = RI.getRegClass(M68k::XR32RegClassID);
|
|
|
|
auto DL = MIB->getDebugLoc();
|
|
|
|
auto MI = MIB.getInstr();
|
|
|
|
auto &MBB = *MIB->getParent();
|
|
|
|
|
|
|
|
if (IsRM) {
|
|
|
|
Reg = MIB->getOperand(0).getReg();
|
|
|
|
Offset = MIB->getOperand(1).getImm();
|
|
|
|
Base = MIB->getOperand(2).getReg();
|
|
|
|
} else {
|
|
|
|
Offset = MIB->getOperand(0).getImm();
|
|
|
|
Base = MIB->getOperand(1).getReg();
|
|
|
|
Reg = MIB->getOperand(2).getReg();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the register is not in XR32 then it is smaller than 32 bit, we
|
|
|
|
// implicitly promote it to 32
|
|
|
|
if (!XR32->contains(Reg)) {
|
|
|
|
Reg = RI.getMatchingMegaReg(Reg, XR32);
|
|
|
|
assert(Reg && "Has not meaningful MEGA register");
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned Mask = 1 << RI.getSpillRegisterOrder(Reg);
|
|
|
|
if (IsRM) {
|
|
|
|
BuildMI(MBB, MI, DL, Desc)
|
|
|
|
.addImm(Mask)
|
|
|
|
.addImm(Offset)
|
|
|
|
.addReg(Base)
|
|
|
|
.addReg(Reg, RegState::ImplicitDefine)
|
|
|
|
.copyImplicitOps(*MIB);
|
|
|
|
} else {
|
|
|
|
BuildMI(MBB, MI, DL, Desc)
|
|
|
|
.addImm(Offset)
|
|
|
|
.addReg(Base)
|
|
|
|
.addImm(Mask)
|
|
|
|
.addReg(Reg, RegState::Implicit)
|
|
|
|
.copyImplicitOps(*MIB);
|
|
|
|
}
|
|
|
|
|
|
|
|
MIB->eraseFromParent();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Expand a single-def pseudo instruction to a two-addr
|
|
|
|
/// instruction with two undef reads of the register being defined.
|
|
|
|
/// This is used for mapping:
|
|
|
|
/// %d0 = SETCS_C32d
|
|
|
|
/// to:
|
|
|
|
/// %d0 = SUBX32dd %d0<undef>, %d0<undef>
|
|
|
|
///
|
|
|
|
static bool Expand2AddrUndef(MachineInstrBuilder &MIB,
|
|
|
|
const MCInstrDesc &Desc) {
|
|
|
|
assert(Desc.getNumOperands() == 3 && "Expected two-addr instruction.");
|
|
|
|
unsigned Reg = MIB->getOperand(0).getReg();
|
|
|
|
MIB->setDesc(Desc);
|
|
|
|
|
|
|
|
// MachineInstr::addOperand() will insert explicit operands before any
|
|
|
|
// implicit operands.
|
|
|
|
MIB.addReg(Reg, RegState::Undef).addReg(Reg, RegState::Undef);
|
|
|
|
// But we don't trust that.
|
|
|
|
assert(MIB->getOperand(1).getReg() == Reg &&
|
|
|
|
MIB->getOperand(2).getReg() == Reg && "Misplaced operand");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
|
|
|
|
MachineInstrBuilder MIB(*MI.getParent()->getParent(), MI);
|
|
|
|
switch (MI.getOpcode()) {
|
|
|
|
case M68k::PUSH8d:
|
|
|
|
return ExpandPUSH_POP(MIB, get(M68k::MOV8ed), true);
|
|
|
|
case M68k::PUSH16d:
|
|
|
|
return ExpandPUSH_POP(MIB, get(M68k::MOV16er), true);
|
|
|
|
case M68k::PUSH32r:
|
|
|
|
return ExpandPUSH_POP(MIB, get(M68k::MOV32er), true);
|
|
|
|
|
|
|
|
case M68k::POP8d:
|
|
|
|
return ExpandPUSH_POP(MIB, get(M68k::MOV8do), false);
|
|
|
|
case M68k::POP16d:
|
|
|
|
return ExpandPUSH_POP(MIB, get(M68k::MOV16ro), false);
|
|
|
|
case M68k::POP32r:
|
|
|
|
return ExpandPUSH_POP(MIB, get(M68k::MOV32ro), false);
|
|
|
|
|
|
|
|
case M68k::SETCS_C8d:
|
|
|
|
return Expand2AddrUndef(MIB, get(M68k::SUBX8dd));
|
|
|
|
case M68k::SETCS_C16d:
|
|
|
|
return Expand2AddrUndef(MIB, get(M68k::SUBX16dd));
|
|
|
|
case M68k::SETCS_C32d:
|
|
|
|
return Expand2AddrUndef(MIB, get(M68k::SUBX32dd));
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool M68kInstrInfo::isPCRelRegisterOperandLegal(
|
|
|
|
const MachineOperand &MO) const {
|
|
|
|
assert(MO.isReg());
|
|
|
|
const auto *MI = MO.getParent();
|
|
|
|
const uint8_t *Beads = M68k::getMCInstrBeads(MI->getOpcode());
|
|
|
|
assert(*Beads);
|
|
|
|
|
|
|
|
// Only addressing mode k has (non-pc) register with PCRel
|
|
|
|
// So we're looking for EA Beads equal to
|
|
|
|
// `3Bits<011>_1Bit<1>_2Bits<11>`
|
|
|
|
// FIXME: There is an important caveat and two assumptions
|
|
|
|
// here: The caveat is that EA encoding always sit on the LSB.
|
|
|
|
// Where the assumptions are that if there are more than one
|
|
|
|
// operands, the EA encoding for the source operand always sit
|
|
|
|
// on the LSB. At the same time, k addressing mode can not be used
|
|
|
|
// on destination operand.
|
|
|
|
// The last assumption is kinda dirty so we need to find a way around
|
|
|
|
// it
|
|
|
|
const uint8_t EncEAk[3] = {0b011, 0b1, 0b11};
|
|
|
|
for (const uint8_t Pat : EncEAk) {
|
|
|
|
uint8_t Bead = *(Beads++);
|
|
|
|
if (!Bead)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
switch (Bead & 0xF) {
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
case M68kBeads::Bits1:
|
|
|
|
case M68kBeads::Bits2:
|
|
|
|
case M68kBeads::Bits3: {
|
|
|
|
uint8_t Val = (Bead & 0xF0) >> 4;
|
|
|
|
if (Val != Pat)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void M68kInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock::iterator MI,
|
|
|
|
const DebugLoc &DL, MCRegister DstReg,
|
|
|
|
MCRegister SrcReg, bool KillSrc) const {
|
|
|
|
unsigned Opc = 0;
|
|
|
|
|
|
|
|
// First deal with the normal symmetric copies.
|
|
|
|
if (M68k::XR32RegClass.contains(DstReg, SrcReg))
|
|
|
|
Opc = M68k::MOV32rr;
|
|
|
|
else if (M68k::XR16RegClass.contains(DstReg, SrcReg))
|
|
|
|
Opc = M68k::MOV16rr;
|
|
|
|
else if (M68k::DR8RegClass.contains(DstReg, SrcReg))
|
|
|
|
Opc = M68k::MOV8dd;
|
|
|
|
|
|
|
|
if (Opc) {
|
|
|
|
BuildMI(MBB, MI, DL, get(Opc), DstReg)
|
|
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now deal with asymmetrically sized copies. The cases that follow are upcast
|
|
|
|
// moves.
|
|
|
|
//
|
|
|
|
// NOTE
|
|
|
|
// These moves are not aware of type nature of these values and thus
|
|
|
|
// won't do any SExt or ZExt and upper bits will basically contain garbage.
|
|
|
|
MachineInstrBuilder MIB(*MBB.getParent(), MI);
|
|
|
|
if (M68k::DR8RegClass.contains(SrcReg)) {
|
|
|
|
if (M68k::XR16RegClass.contains(DstReg))
|
|
|
|
Opc = M68k::MOVXd16d8;
|
|
|
|
else if (M68k::XR32RegClass.contains(DstReg))
|
|
|
|
Opc = M68k::MOVXd32d8;
|
|
|
|
} else if (M68k::XR16RegClass.contains(SrcReg) &&
|
|
|
|
M68k::XR32RegClass.contains(DstReg))
|
|
|
|
Opc = M68k::MOVXd32d16;
|
|
|
|
|
|
|
|
if (Opc) {
|
|
|
|
BuildMI(MBB, MI, DL, get(Opc), DstReg)
|
|
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool FromCCR = SrcReg == M68k::CCR;
|
|
|
|
bool FromSR = SrcReg == M68k::SR;
|
|
|
|
bool ToCCR = DstReg == M68k::CCR;
|
|
|
|
bool ToSR = DstReg == M68k::SR;
|
|
|
|
|
|
|
|
if (FromCCR) {
|
|
|
|
assert(M68k::DR8RegClass.contains(DstReg) &&
|
|
|
|
"Need DR8 register to copy CCR");
|
|
|
|
Opc = M68k::MOV8dc;
|
|
|
|
} else if (ToCCR) {
|
|
|
|
assert(M68k::DR8RegClass.contains(SrcReg) &&
|
|
|
|
"Need DR8 register to copy CCR");
|
|
|
|
Opc = M68k::MOV8cd;
|
|
|
|
} else if (FromSR || ToSR)
|
|
|
|
llvm_unreachable("Cannot emit SR copy instruction");
|
|
|
|
|
|
|
|
if (Opc) {
|
|
|
|
BuildMI(MBB, MI, DL, get(Opc), DstReg)
|
|
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
LLVM_DEBUG(dbgs() << "Cannot copy " << RI.getName(SrcReg) << " to "
|
|
|
|
<< RI.getName(DstReg) << '\n');
|
|
|
|
llvm_unreachable("Cannot emit physreg copy instruction");
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
unsigned getLoadStoreRegOpcode(unsigned Reg, const TargetRegisterClass *RC,
|
|
|
|
const TargetRegisterInfo *TRI,
|
|
|
|
const M68kSubtarget &STI, bool load) {
|
|
|
|
switch (TRI->getRegSizeInBits(*RC)) {
|
|
|
|
default:
|
|
|
|
llvm_unreachable("Unknown spill size");
|
|
|
|
case 8:
|
|
|
|
if (M68k::DR8RegClass.hasSubClassEq(RC))
|
|
|
|
return load ? M68k::MOVM8mp_P : M68k::MOVM8pm_P;
|
|
|
|
if (M68k::CCRCRegClass.hasSubClassEq(RC))
|
|
|
|
return load ? M68k::MOV16cp : M68k::MOV16pc;
|
|
|
|
|
|
|
|
llvm_unreachable("Unknown 1-byte regclass");
|
|
|
|
case 16:
|
|
|
|
assert(M68k::XR16RegClass.hasSubClassEq(RC) && "Unknown 2-byte regclass");
|
|
|
|
return load ? M68k::MOVM16mp_P : M68k::MOVM16pm_P;
|
|
|
|
case 32:
|
|
|
|
assert(M68k::XR32RegClass.hasSubClassEq(RC) && "Unknown 4-byte regclass");
|
|
|
|
return load ? M68k::MOVM32mp_P : M68k::MOVM32pm_P;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned getStoreRegOpcode(unsigned SrcReg, const TargetRegisterClass *RC,
|
|
|
|
const TargetRegisterInfo *TRI,
|
|
|
|
const M68kSubtarget &STI) {
|
|
|
|
return getLoadStoreRegOpcode(SrcReg, RC, TRI, STI, false);
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned getLoadRegOpcode(unsigned DstReg, const TargetRegisterClass *RC,
|
|
|
|
const TargetRegisterInfo *TRI,
|
|
|
|
const M68kSubtarget &STI) {
|
|
|
|
return getLoadStoreRegOpcode(DstReg, RC, TRI, STI, true);
|
|
|
|
}
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
|
|
|
bool M68kInstrInfo::getStackSlotRange(const TargetRegisterClass *RC,
|
|
|
|
unsigned SubIdx, unsigned &Size,
|
|
|
|
unsigned &Offset,
|
|
|
|
const MachineFunction &MF) const {
|
|
|
|
// The slot size must be the maximum size so we can easily use MOVEM.L
|
|
|
|
Size = 4;
|
|
|
|
Offset = 0;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void M68kInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock::iterator MI,
|
|
|
|
Register SrcReg, bool IsKill,
|
|
|
|
int FrameIndex,
|
|
|
|
const TargetRegisterClass *RC,
|
|
|
|
const TargetRegisterInfo *TRI) const {
|
|
|
|
const MachineFunction &MF = *MBB.getParent();
|
|
|
|
assert(MF.getFrameInfo().getObjectSize(FrameIndex) == 4 &&
|
|
|
|
"Stack slot too small for store");
|
|
|
|
unsigned Opc = getStoreRegOpcode(SrcReg, RC, TRI, Subtarget);
|
|
|
|
DebugLoc DL = MBB.findDebugLoc(MI);
|
|
|
|
// (0,FrameIndex) <- $reg
|
|
|
|
M68k::addFrameReference(BuildMI(MBB, MI, DL, get(Opc)), FrameIndex)
|
|
|
|
.addReg(SrcReg, getKillRegState(IsKill));
|
|
|
|
}
|
|
|
|
|
|
|
|
void M68kInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
|
|
|
|
MachineBasicBlock::iterator MI,
|
|
|
|
Register DstReg, int FrameIndex,
|
|
|
|
const TargetRegisterClass *RC,
|
|
|
|
const TargetRegisterInfo *TRI) const {
|
|
|
|
const MachineFunction &MF = *MBB.getParent();
|
|
|
|
assert(MF.getFrameInfo().getObjectSize(FrameIndex) == 4 &&
|
|
|
|
"Stack slot too small for store");
|
|
|
|
unsigned Opc = getLoadRegOpcode(DstReg, RC, TRI, Subtarget);
|
|
|
|
DebugLoc DL = MBB.findDebugLoc(MI);
|
|
|
|
M68k::addFrameReference(BuildMI(MBB, MI, DL, get(Opc), DstReg), FrameIndex);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Return a virtual register initialized with the the global base register
|
|
|
|
/// value. Output instructions required to initialize the register in the
|
|
|
|
/// function entry block, if necessary.
|
|
|
|
///
|
|
|
|
/// TODO Move this function to M68kMachineFunctionInfo.
|
|
|
|
unsigned M68kInstrInfo::getGlobalBaseReg(MachineFunction *MF) const {
|
|
|
|
M68kMachineFunctionInfo *MxFI = MF->getInfo<M68kMachineFunctionInfo>();
|
|
|
|
unsigned GlobalBaseReg = MxFI->getGlobalBaseReg();
|
|
|
|
if (GlobalBaseReg != 0)
|
|
|
|
return GlobalBaseReg;
|
|
|
|
|
|
|
|
// Create the register. The code to initialize it is inserted later,
|
|
|
|
// by the CGBR pass (below).
|
|
|
|
//
|
|
|
|
// NOTE
|
|
|
|
// Normally M68k uses A5 register as global base pointer but this will
|
|
|
|
// create unnecessary spill if we use less then 4 registers in code; since A5
|
|
|
|
// is callee-save anyway we could try to allocate caller-save first and if
|
|
|
|
// lucky get one, otherwise it does not really matter which callee-save to
|
|
|
|
// use.
|
|
|
|
MachineRegisterInfo &RegInfo = MF->getRegInfo();
|
|
|
|
GlobalBaseReg = RegInfo.createVirtualRegister(&M68k::AR32_NOSPRegClass);
|
|
|
|
MxFI->setGlobalBaseReg(GlobalBaseReg);
|
|
|
|
return GlobalBaseReg;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::pair<unsigned, unsigned>
|
|
|
|
M68kInstrInfo::decomposeMachineOperandsTargetFlags(unsigned TF) const {
|
|
|
|
return std::make_pair(TF, 0u);
|
|
|
|
}
|
|
|
|
|
|
|
|
ArrayRef<std::pair<unsigned, const char *>>
|
|
|
|
M68kInstrInfo::getSerializableDirectMachineOperandTargetFlags() const {
|
|
|
|
using namespace M68kII;
|
|
|
|
static const std::pair<unsigned, const char *> TargetFlags[] = {
|
|
|
|
{MO_ABSOLUTE_ADDRESS, "m68k-absolute"},
|
|
|
|
{MO_PC_RELATIVE_ADDRESS, "m68k-pcrel"},
|
|
|
|
{MO_GOT, "m68k-got"},
|
|
|
|
{MO_GOTOFF, "m68k-gotoff"},
|
|
|
|
{MO_GOTPCREL, "m68k-gotpcrel"},
|
|
|
|
{MO_PLT, "m68k-plt"}};
|
|
|
|
return makeArrayRef(TargetFlags);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
/// Create Global Base Reg pass. This initializes the PIC global base register
|
|
|
|
struct CGBR : public MachineFunctionPass {
|
|
|
|
static char ID;
|
|
|
|
CGBR() : MachineFunctionPass(ID) {}
|
|
|
|
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override {
|
|
|
|
const M68kSubtarget &STI = MF.getSubtarget<M68kSubtarget>();
|
|
|
|
M68kMachineFunctionInfo *MxFI = MF.getInfo<M68kMachineFunctionInfo>();
|
|
|
|
|
|
|
|
unsigned GlobalBaseReg = MxFI->getGlobalBaseReg();
|
|
|
|
|
|
|
|
// If we didn't need a GlobalBaseReg, don't insert code.
|
|
|
|
if (GlobalBaseReg == 0)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// Insert the set of GlobalBaseReg into the first MBB of the function
|
|
|
|
MachineBasicBlock &FirstMBB = MF.front();
|
|
|
|
MachineBasicBlock::iterator MBBI = FirstMBB.begin();
|
|
|
|
DebugLoc DL = FirstMBB.findDebugLoc(MBBI);
|
|
|
|
const M68kInstrInfo *TII = STI.getInstrInfo();
|
|
|
|
|
|
|
|
// Generate lea (__GLOBAL_OFFSET_TABLE_,%PC), %A5
|
|
|
|
BuildMI(FirstMBB, MBBI, DL, TII->get(M68k::LEA32q), GlobalBaseReg)
|
|
|
|
.addExternalSymbol("_GLOBAL_OFFSET_TABLE_", M68kII::MO_GOTPCREL);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
StringRef getPassName() const override {
|
|
|
|
return "M68k PIC Global Base Reg Initialization";
|
|
|
|
}
|
|
|
|
|
|
|
|
void getAnalysisUsage(AnalysisUsage &AU) const override {
|
|
|
|
AU.setPreservesCFG();
|
|
|
|
MachineFunctionPass::getAnalysisUsage(AU);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
char CGBR::ID = 0;
|
|
|
|
FunctionPass *llvm::createM68kGlobalBaseRegPass() { return new CGBR(); }
|