blob: ba7a95e92c5c5c04e419a6a2dd7fde9d54a9bac1 [file] [log] [blame]
//===-- 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, unsigned DestReg,
unsigned 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()) {
BuildMI(MBB, MI, DL, get(AVR::MOVWRdRr), DestReg)
.addReg(SrcReg, getKillRegState(KillSrc));
} else {
unsigned 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,
unsigned 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.getObjectAlignment(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,
unsigned 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.getObjectAlignment(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