575 lines
17 KiB
C++
575 lines
17 KiB
C++
//===-- AVRInstrInfo.cpp - AVR Instruction Information --------------------===//
|
|
//
|
|
// 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 AVR implementation of the TargetInstrInfo class.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AVRInstrInfo.h"
|
|
|
|
#include "llvm/ADT/STLExtras.h"
|
|
#include "llvm/CodeGen/MachineConstantPool.h"
|
|
#include "llvm/CodeGen/MachineFrameInfo.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineMemOperand.h"
|
|
#include "llvm/IR/Constants.h"
|
|
#include "llvm/IR/Function.h"
|
|
#include "llvm/MC/MCContext.h"
|
|
#include "llvm/Support/Debug.h"
|
|
#include "llvm/Support/ErrorHandling.h"
|
|
#include "llvm/Support/TargetRegistry.h"
|
|
|
|
#include "AVR.h"
|
|
#include "AVRMachineFunctionInfo.h"
|
|
#include "AVRRegisterInfo.h"
|
|
#include "AVRTargetMachine.h"
|
|
#include "MCTargetDesc/AVRMCTargetDesc.h"
|
|
|
|
#define GET_INSTRINFO_CTOR_DTOR
|
|
#include "AVRGenInstrInfo.inc"
|
|
|
|
namespace llvm {
|
|
|
|
AVRInstrInfo::AVRInstrInfo()
|
|
: AVRGenInstrInfo(AVR::ADJCALLSTACKDOWN, AVR::ADJCALLSTACKUP), RI() {}
|
|
|
|
void AVRInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MI,
|
|
const DebugLoc &DL, MCRegister DestReg,
|
|
MCRegister SrcReg, bool KillSrc) const {
|
|
const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
|
|
const AVRRegisterInfo &TRI = *STI.getRegisterInfo();
|
|
unsigned Opc;
|
|
|
|
// Not all AVR devices support the 16-bit `MOVW` instruction.
|
|
if (AVR::DREGSRegClass.contains(DestReg, SrcReg)) {
|
|
if (STI.hasMOVW() && AVR::DREGSMOVWRegClass.contains(DestReg, SrcReg)) {
|
|
BuildMI(MBB, MI, DL, get(AVR::MOVWRdRr), DestReg)
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
} else {
|
|
Register DestLo, DestHi, SrcLo, SrcHi;
|
|
|
|
TRI.splitReg(DestReg, DestLo, DestHi);
|
|
TRI.splitReg(SrcReg, SrcLo, SrcHi);
|
|
|
|
// Copy each individual register with the `MOV` instruction.
|
|
BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestLo)
|
|
.addReg(SrcLo, getKillRegState(KillSrc));
|
|
BuildMI(MBB, MI, DL, get(AVR::MOVRdRr), DestHi)
|
|
.addReg(SrcHi, getKillRegState(KillSrc));
|
|
}
|
|
} else {
|
|
if (AVR::GPR8RegClass.contains(DestReg, SrcReg)) {
|
|
Opc = AVR::MOVRdRr;
|
|
} else if (SrcReg == AVR::SP && AVR::DREGSRegClass.contains(DestReg)) {
|
|
Opc = AVR::SPREAD;
|
|
} else if (DestReg == AVR::SP && AVR::DREGSRegClass.contains(SrcReg)) {
|
|
Opc = AVR::SPWRITE;
|
|
} else {
|
|
llvm_unreachable("Impossible reg-to-reg copy");
|
|
}
|
|
|
|
BuildMI(MBB, MI, DL, get(Opc), DestReg)
|
|
.addReg(SrcReg, getKillRegState(KillSrc));
|
|
}
|
|
}
|
|
|
|
unsigned AVRInstrInfo::isLoadFromStackSlot(const MachineInstr &MI,
|
|
int &FrameIndex) const {
|
|
switch (MI.getOpcode()) {
|
|
case AVR::LDDRdPtrQ:
|
|
case AVR::LDDWRdYQ: { //:FIXME: remove this once PR13375 gets fixed
|
|
if (MI.getOperand(1).isFI() && MI.getOperand(2).isImm() &&
|
|
MI.getOperand(2).getImm() == 0) {
|
|
FrameIndex = MI.getOperand(1).getIndex();
|
|
return MI.getOperand(0).getReg();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned AVRInstrInfo::isStoreToStackSlot(const MachineInstr &MI,
|
|
int &FrameIndex) const {
|
|
switch (MI.getOpcode()) {
|
|
case AVR::STDPtrQRr:
|
|
case AVR::STDWPtrQRr: {
|
|
if (MI.getOperand(0).isFI() && MI.getOperand(1).isImm() &&
|
|
MI.getOperand(1).getImm() == 0) {
|
|
FrameIndex = MI.getOperand(0).getIndex();
|
|
return MI.getOperand(2).getReg();
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void AVRInstrInfo::storeRegToStackSlot(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MI,
|
|
Register SrcReg, bool isKill,
|
|
int FrameIndex,
|
|
const TargetRegisterClass *RC,
|
|
const TargetRegisterInfo *TRI) const {
|
|
MachineFunction &MF = *MBB.getParent();
|
|
AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
|
|
|
|
AFI->setHasSpills(true);
|
|
|
|
DebugLoc DL;
|
|
if (MI != MBB.end()) {
|
|
DL = MI->getDebugLoc();
|
|
}
|
|
|
|
const MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
|
|
MachineMemOperand *MMO = MF.getMachineMemOperand(
|
|
MachinePointerInfo::getFixedStack(MF, FrameIndex),
|
|
MachineMemOperand::MOStore, MFI.getObjectSize(FrameIndex),
|
|
MFI.getObjectAlign(FrameIndex));
|
|
|
|
unsigned Opcode = 0;
|
|
if (TRI->isTypeLegalForClass(*RC, MVT::i8)) {
|
|
Opcode = AVR::STDPtrQRr;
|
|
} else if (TRI->isTypeLegalForClass(*RC, MVT::i16)) {
|
|
Opcode = AVR::STDWPtrQRr;
|
|
} else {
|
|
llvm_unreachable("Cannot store this register into a stack slot!");
|
|
}
|
|
|
|
BuildMI(MBB, MI, DL, get(Opcode))
|
|
.addFrameIndex(FrameIndex)
|
|
.addImm(0)
|
|
.addReg(SrcReg, getKillRegState(isKill))
|
|
.addMemOperand(MMO);
|
|
}
|
|
|
|
void AVRInstrInfo::loadRegFromStackSlot(MachineBasicBlock &MBB,
|
|
MachineBasicBlock::iterator MI,
|
|
Register DestReg, int FrameIndex,
|
|
const TargetRegisterClass *RC,
|
|
const TargetRegisterInfo *TRI) const {
|
|
DebugLoc DL;
|
|
if (MI != MBB.end()) {
|
|
DL = MI->getDebugLoc();
|
|
}
|
|
|
|
MachineFunction &MF = *MBB.getParent();
|
|
const MachineFrameInfo &MFI = MF.getFrameInfo();
|
|
|
|
MachineMemOperand *MMO = MF.getMachineMemOperand(
|
|
MachinePointerInfo::getFixedStack(MF, FrameIndex),
|
|
MachineMemOperand::MOLoad, MFI.getObjectSize(FrameIndex),
|
|
MFI.getObjectAlign(FrameIndex));
|
|
|
|
unsigned Opcode = 0;
|
|
if (TRI->isTypeLegalForClass(*RC, MVT::i8)) {
|
|
Opcode = AVR::LDDRdPtrQ;
|
|
} else if (TRI->isTypeLegalForClass(*RC, MVT::i16)) {
|
|
// Opcode = AVR::LDDWRdPtrQ;
|
|
//:FIXME: remove this once PR13375 gets fixed
|
|
Opcode = AVR::LDDWRdYQ;
|
|
} else {
|
|
llvm_unreachable("Cannot load this register from a stack slot!");
|
|
}
|
|
|
|
BuildMI(MBB, MI, DL, get(Opcode), DestReg)
|
|
.addFrameIndex(FrameIndex)
|
|
.addImm(0)
|
|
.addMemOperand(MMO);
|
|
}
|
|
|
|
const MCInstrDesc &AVRInstrInfo::getBrCond(AVRCC::CondCodes CC) const {
|
|
switch (CC) {
|
|
default:
|
|
llvm_unreachable("Unknown condition code!");
|
|
case AVRCC::COND_EQ:
|
|
return get(AVR::BREQk);
|
|
case AVRCC::COND_NE:
|
|
return get(AVR::BRNEk);
|
|
case AVRCC::COND_GE:
|
|
return get(AVR::BRGEk);
|
|
case AVRCC::COND_LT:
|
|
return get(AVR::BRLTk);
|
|
case AVRCC::COND_SH:
|
|
return get(AVR::BRSHk);
|
|
case AVRCC::COND_LO:
|
|
return get(AVR::BRLOk);
|
|
case AVRCC::COND_MI:
|
|
return get(AVR::BRMIk);
|
|
case AVRCC::COND_PL:
|
|
return get(AVR::BRPLk);
|
|
}
|
|
}
|
|
|
|
AVRCC::CondCodes AVRInstrInfo::getCondFromBranchOpc(unsigned Opc) const {
|
|
switch (Opc) {
|
|
default:
|
|
return AVRCC::COND_INVALID;
|
|
case AVR::BREQk:
|
|
return AVRCC::COND_EQ;
|
|
case AVR::BRNEk:
|
|
return AVRCC::COND_NE;
|
|
case AVR::BRSHk:
|
|
return AVRCC::COND_SH;
|
|
case AVR::BRLOk:
|
|
return AVRCC::COND_LO;
|
|
case AVR::BRMIk:
|
|
return AVRCC::COND_MI;
|
|
case AVR::BRPLk:
|
|
return AVRCC::COND_PL;
|
|
case AVR::BRGEk:
|
|
return AVRCC::COND_GE;
|
|
case AVR::BRLTk:
|
|
return AVRCC::COND_LT;
|
|
}
|
|
}
|
|
|
|
AVRCC::CondCodes AVRInstrInfo::getOppositeCondition(AVRCC::CondCodes CC) const {
|
|
switch (CC) {
|
|
default:
|
|
llvm_unreachable("Invalid condition!");
|
|
case AVRCC::COND_EQ:
|
|
return AVRCC::COND_NE;
|
|
case AVRCC::COND_NE:
|
|
return AVRCC::COND_EQ;
|
|
case AVRCC::COND_SH:
|
|
return AVRCC::COND_LO;
|
|
case AVRCC::COND_LO:
|
|
return AVRCC::COND_SH;
|
|
case AVRCC::COND_GE:
|
|
return AVRCC::COND_LT;
|
|
case AVRCC::COND_LT:
|
|
return AVRCC::COND_GE;
|
|
case AVRCC::COND_MI:
|
|
return AVRCC::COND_PL;
|
|
case AVRCC::COND_PL:
|
|
return AVRCC::COND_MI;
|
|
}
|
|
}
|
|
|
|
bool AVRInstrInfo::analyzeBranch(MachineBasicBlock &MBB,
|
|
MachineBasicBlock *&TBB,
|
|
MachineBasicBlock *&FBB,
|
|
SmallVectorImpl<MachineOperand> &Cond,
|
|
bool AllowModify) const {
|
|
// Start from the bottom of the block and work up, examining the
|
|
// terminator instructions.
|
|
MachineBasicBlock::iterator I = MBB.end();
|
|
MachineBasicBlock::iterator UnCondBrIter = MBB.end();
|
|
|
|
while (I != MBB.begin()) {
|
|
--I;
|
|
if (I->isDebugInstr()) {
|
|
continue;
|
|
}
|
|
|
|
// Working from the bottom, when we see a non-terminator
|
|
// instruction, we're done.
|
|
if (!isUnpredicatedTerminator(*I)) {
|
|
break;
|
|
}
|
|
|
|
// A terminator that isn't a branch can't easily be handled
|
|
// by this analysis.
|
|
if (!I->getDesc().isBranch()) {
|
|
return true;
|
|
}
|
|
|
|
// Handle unconditional branches.
|
|
//:TODO: add here jmp
|
|
if (I->getOpcode() == AVR::RJMPk) {
|
|
UnCondBrIter = I;
|
|
|
|
if (!AllowModify) {
|
|
TBB = I->getOperand(0).getMBB();
|
|
continue;
|
|
}
|
|
|
|
// If the block has any instructions after a JMP, delete them.
|
|
while (std::next(I) != MBB.end()) {
|
|
std::next(I)->eraseFromParent();
|
|
}
|
|
|
|
Cond.clear();
|
|
FBB = 0;
|
|
|
|
// Delete the JMP if it's equivalent to a fall-through.
|
|
if (MBB.isLayoutSuccessor(I->getOperand(0).getMBB())) {
|
|
TBB = 0;
|
|
I->eraseFromParent();
|
|
I = MBB.end();
|
|
UnCondBrIter = MBB.end();
|
|
continue;
|
|
}
|
|
|
|
// TBB is used to indicate the unconditinal destination.
|
|
TBB = I->getOperand(0).getMBB();
|
|
continue;
|
|
}
|
|
|
|
// Handle conditional branches.
|
|
AVRCC::CondCodes BranchCode = getCondFromBranchOpc(I->getOpcode());
|
|
if (BranchCode == AVRCC::COND_INVALID) {
|
|
return true; // Can't handle indirect branch.
|
|
}
|
|
|
|
// Working from the bottom, handle the first conditional branch.
|
|
if (Cond.empty()) {
|
|
MachineBasicBlock *TargetBB = I->getOperand(0).getMBB();
|
|
if (AllowModify && UnCondBrIter != MBB.end() &&
|
|
MBB.isLayoutSuccessor(TargetBB)) {
|
|
// If we can modify the code and it ends in something like:
|
|
//
|
|
// jCC L1
|
|
// jmp L2
|
|
// L1:
|
|
// ...
|
|
// L2:
|
|
//
|
|
// Then we can change this to:
|
|
//
|
|
// jnCC L2
|
|
// L1:
|
|
// ...
|
|
// L2:
|
|
//
|
|
// Which is a bit more efficient.
|
|
// We conditionally jump to the fall-through block.
|
|
BranchCode = getOppositeCondition(BranchCode);
|
|
unsigned JNCC = getBrCond(BranchCode).getOpcode();
|
|
MachineBasicBlock::iterator OldInst = I;
|
|
|
|
BuildMI(MBB, UnCondBrIter, MBB.findDebugLoc(I), get(JNCC))
|
|
.addMBB(UnCondBrIter->getOperand(0).getMBB());
|
|
BuildMI(MBB, UnCondBrIter, MBB.findDebugLoc(I), get(AVR::RJMPk))
|
|
.addMBB(TargetBB);
|
|
|
|
OldInst->eraseFromParent();
|
|
UnCondBrIter->eraseFromParent();
|
|
|
|
// Restart the analysis.
|
|
UnCondBrIter = MBB.end();
|
|
I = MBB.end();
|
|
continue;
|
|
}
|
|
|
|
FBB = TBB;
|
|
TBB = I->getOperand(0).getMBB();
|
|
Cond.push_back(MachineOperand::CreateImm(BranchCode));
|
|
continue;
|
|
}
|
|
|
|
// Handle subsequent conditional branches. Only handle the case where all
|
|
// conditional branches branch to the same destination.
|
|
assert(Cond.size() == 1);
|
|
assert(TBB);
|
|
|
|
// Only handle the case where all conditional branches branch to
|
|
// the same destination.
|
|
if (TBB != I->getOperand(0).getMBB()) {
|
|
return true;
|
|
}
|
|
|
|
AVRCC::CondCodes OldBranchCode = (AVRCC::CondCodes)Cond[0].getImm();
|
|
// If the conditions are the same, we can leave them alone.
|
|
if (OldBranchCode == BranchCode) {
|
|
continue;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned AVRInstrInfo::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() == 1 || Cond.size() == 0) &&
|
|
"AVR branch conditions have one component!");
|
|
|
|
if (Cond.empty()) {
|
|
assert(!FBB && "Unconditional branch with multiple successors!");
|
|
auto &MI = *BuildMI(&MBB, DL, get(AVR::RJMPk)).addMBB(TBB);
|
|
if (BytesAdded)
|
|
*BytesAdded += getInstSizeInBytes(MI);
|
|
return 1;
|
|
}
|
|
|
|
// Conditional branch.
|
|
unsigned Count = 0;
|
|
AVRCC::CondCodes CC = (AVRCC::CondCodes)Cond[0].getImm();
|
|
auto &CondMI = *BuildMI(&MBB, DL, getBrCond(CC)).addMBB(TBB);
|
|
|
|
if (BytesAdded) *BytesAdded += getInstSizeInBytes(CondMI);
|
|
++Count;
|
|
|
|
if (FBB) {
|
|
// Two-way Conditional branch. Insert the second branch.
|
|
auto &MI = *BuildMI(&MBB, DL, get(AVR::RJMPk)).addMBB(FBB);
|
|
if (BytesAdded) *BytesAdded += getInstSizeInBytes(MI);
|
|
++Count;
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
unsigned AVRInstrInfo::removeBranch(MachineBasicBlock &MBB,
|
|
int *BytesRemoved) const {
|
|
if (BytesRemoved) *BytesRemoved = 0;
|
|
|
|
MachineBasicBlock::iterator I = MBB.end();
|
|
unsigned Count = 0;
|
|
|
|
while (I != MBB.begin()) {
|
|
--I;
|
|
if (I->isDebugInstr()) {
|
|
continue;
|
|
}
|
|
//:TODO: add here the missing jmp instructions once they are implemented
|
|
// like jmp, {e}ijmp, and other cond branches, ...
|
|
if (I->getOpcode() != AVR::RJMPk &&
|
|
getCondFromBranchOpc(I->getOpcode()) == AVRCC::COND_INVALID) {
|
|
break;
|
|
}
|
|
|
|
// Remove the branch.
|
|
if (BytesRemoved) *BytesRemoved += getInstSizeInBytes(*I);
|
|
I->eraseFromParent();
|
|
I = MBB.end();
|
|
++Count;
|
|
}
|
|
|
|
return Count;
|
|
}
|
|
|
|
bool AVRInstrInfo::reverseBranchCondition(
|
|
SmallVectorImpl<MachineOperand> &Cond) const {
|
|
assert(Cond.size() == 1 && "Invalid AVR branch condition!");
|
|
|
|
AVRCC::CondCodes CC = static_cast<AVRCC::CondCodes>(Cond[0].getImm());
|
|
Cond[0].setImm(getOppositeCondition(CC));
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned AVRInstrInfo::getInstSizeInBytes(const MachineInstr &MI) const {
|
|
unsigned Opcode = MI.getOpcode();
|
|
|
|
switch (Opcode) {
|
|
// A regular instruction
|
|
default: {
|
|
const MCInstrDesc &Desc = get(Opcode);
|
|
return Desc.getSize();
|
|
}
|
|
case TargetOpcode::EH_LABEL:
|
|
case TargetOpcode::IMPLICIT_DEF:
|
|
case TargetOpcode::KILL:
|
|
case TargetOpcode::DBG_VALUE:
|
|
return 0;
|
|
case TargetOpcode::INLINEASM:
|
|
case TargetOpcode::INLINEASM_BR: {
|
|
const MachineFunction &MF = *MI.getParent()->getParent();
|
|
const AVRTargetMachine &TM = static_cast<const AVRTargetMachine&>(MF.getTarget());
|
|
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
|
|
const TargetInstrInfo &TII = *STI.getInstrInfo();
|
|
|
|
return TII.getInlineAsmLength(MI.getOperand(0).getSymbolName(),
|
|
*TM.getMCAsmInfo());
|
|
}
|
|
}
|
|
}
|
|
|
|
MachineBasicBlock *
|
|
AVRInstrInfo::getBranchDestBlock(const MachineInstr &MI) const {
|
|
switch (MI.getOpcode()) {
|
|
default:
|
|
llvm_unreachable("unexpected opcode!");
|
|
case AVR::JMPk:
|
|
case AVR::CALLk:
|
|
case AVR::RCALLk:
|
|
case AVR::RJMPk:
|
|
case AVR::BREQk:
|
|
case AVR::BRNEk:
|
|
case AVR::BRSHk:
|
|
case AVR::BRLOk:
|
|
case AVR::BRMIk:
|
|
case AVR::BRPLk:
|
|
case AVR::BRGEk:
|
|
case AVR::BRLTk:
|
|
return MI.getOperand(0).getMBB();
|
|
case AVR::BRBSsk:
|
|
case AVR::BRBCsk:
|
|
return MI.getOperand(1).getMBB();
|
|
case AVR::SBRCRrB:
|
|
case AVR::SBRSRrB:
|
|
case AVR::SBICAb:
|
|
case AVR::SBISAb:
|
|
llvm_unreachable("unimplemented branch instructions");
|
|
}
|
|
}
|
|
|
|
bool AVRInstrInfo::isBranchOffsetInRange(unsigned BranchOp,
|
|
int64_t BrOffset) const {
|
|
|
|
switch (BranchOp) {
|
|
default:
|
|
llvm_unreachable("unexpected opcode!");
|
|
case AVR::JMPk:
|
|
case AVR::CALLk:
|
|
return true;
|
|
case AVR::RCALLk:
|
|
case AVR::RJMPk:
|
|
return isIntN(13, BrOffset);
|
|
case AVR::BRBSsk:
|
|
case AVR::BRBCsk:
|
|
case AVR::BREQk:
|
|
case AVR::BRNEk:
|
|
case AVR::BRSHk:
|
|
case AVR::BRLOk:
|
|
case AVR::BRMIk:
|
|
case AVR::BRPLk:
|
|
case AVR::BRGEk:
|
|
case AVR::BRLTk:
|
|
return isIntN(7, BrOffset);
|
|
}
|
|
}
|
|
|
|
unsigned AVRInstrInfo::insertIndirectBranch(MachineBasicBlock &MBB,
|
|
MachineBasicBlock &NewDestBB,
|
|
const DebugLoc &DL,
|
|
int64_t BrOffset,
|
|
RegScavenger *RS) const {
|
|
// This method inserts a *direct* branch (JMP), despite its name.
|
|
// LLVM calls this method to fixup unconditional branches; it never calls
|
|
// insertBranch or some hypothetical "insertDirectBranch".
|
|
// See lib/CodeGen/RegisterRelaxation.cpp for details.
|
|
// We end up here when a jump is too long for a RJMP instruction.
|
|
auto &MI = *BuildMI(&MBB, DL, get(AVR::JMPk)).addMBB(&NewDestBB);
|
|
|
|
return getInstSizeInBytes(MI);
|
|
}
|
|
|
|
} // end of namespace llvm
|
|
|