forked from OSchip/llvm-project
1718 lines
51 KiB
C++
1718 lines
51 KiB
C++
//===-- AVRExpandPseudoInsts.cpp - Expand pseudo instructions -------------===//
|
|
//
|
|
// 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 a pass that expands pseudo instructions into target
|
|
// instructions. This pass should be run after register allocation but before
|
|
// the post-regalloc scheduling pass.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "AVR.h"
|
|
#include "AVRInstrInfo.h"
|
|
#include "AVRTargetMachine.h"
|
|
#include "MCTargetDesc/AVRMCTargetDesc.h"
|
|
|
|
#include "llvm/CodeGen/MachineFunctionPass.h"
|
|
#include "llvm/CodeGen/MachineInstrBuilder.h"
|
|
#include "llvm/CodeGen/MachineRegisterInfo.h"
|
|
#include "llvm/CodeGen/RegisterScavenging.h"
|
|
#include "llvm/CodeGen/TargetRegisterInfo.h"
|
|
|
|
using namespace llvm;
|
|
|
|
#define AVR_EXPAND_PSEUDO_NAME "AVR pseudo instruction expansion pass"
|
|
|
|
namespace {
|
|
|
|
/// Expands "placeholder" instructions marked as pseudo into
|
|
/// actual AVR instructions.
|
|
class AVRExpandPseudo : public MachineFunctionPass {
|
|
public:
|
|
static char ID;
|
|
|
|
AVRExpandPseudo() : MachineFunctionPass(ID) {
|
|
initializeAVRExpandPseudoPass(*PassRegistry::getPassRegistry());
|
|
}
|
|
|
|
bool runOnMachineFunction(MachineFunction &MF) override;
|
|
|
|
StringRef getPassName() const override { return AVR_EXPAND_PSEUDO_NAME; }
|
|
|
|
private:
|
|
typedef MachineBasicBlock Block;
|
|
typedef Block::iterator BlockIt;
|
|
|
|
const AVRRegisterInfo *TRI;
|
|
const TargetInstrInfo *TII;
|
|
|
|
/// The register to be used for temporary storage.
|
|
const Register SCRATCH_REGISTER = AVR::R0;
|
|
/// The register that will always contain zero.
|
|
const Register ZERO_REGISTER = AVR::R1;
|
|
/// The IO address of the status register.
|
|
const unsigned SREG_ADDR = 0x3f;
|
|
|
|
bool expandMBB(Block &MBB);
|
|
bool expandMI(Block &MBB, BlockIt MBBI);
|
|
template <unsigned OP> bool expand(Block &MBB, BlockIt MBBI);
|
|
|
|
MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode) {
|
|
return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode));
|
|
}
|
|
|
|
MachineInstrBuilder buildMI(Block &MBB, BlockIt MBBI, unsigned Opcode,
|
|
Register DstReg) {
|
|
return BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(Opcode), DstReg);
|
|
}
|
|
|
|
MachineRegisterInfo &getRegInfo(Block &MBB) { return MBB.getParent()->getRegInfo(); }
|
|
|
|
bool expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI);
|
|
bool expandLogic(unsigned Op, Block &MBB, BlockIt MBBI);
|
|
bool expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI);
|
|
bool isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const;
|
|
|
|
template<typename Func>
|
|
bool expandAtomic(Block &MBB, BlockIt MBBI, Func f);
|
|
|
|
template<typename Func>
|
|
bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI, Func f);
|
|
|
|
bool expandAtomicBinaryOp(unsigned Opcode, Block &MBB, BlockIt MBBI);
|
|
|
|
bool expandAtomicArithmeticOp(unsigned MemOpcode,
|
|
unsigned ArithOpcode,
|
|
Block &MBB,
|
|
BlockIt MBBI);
|
|
|
|
/// Scavenges a free GPR8 register for use.
|
|
Register scavengeGPR8(MachineInstr &MI);
|
|
};
|
|
|
|
char AVRExpandPseudo::ID = 0;
|
|
|
|
bool AVRExpandPseudo::expandMBB(MachineBasicBlock &MBB) {
|
|
bool Modified = false;
|
|
|
|
BlockIt MBBI = MBB.begin(), E = MBB.end();
|
|
while (MBBI != E) {
|
|
BlockIt NMBBI = std::next(MBBI);
|
|
Modified |= expandMI(MBB, MBBI);
|
|
MBBI = NMBBI;
|
|
}
|
|
|
|
return Modified;
|
|
}
|
|
|
|
bool AVRExpandPseudo::runOnMachineFunction(MachineFunction &MF) {
|
|
bool Modified = false;
|
|
|
|
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
|
|
TRI = STI.getRegisterInfo();
|
|
TII = STI.getInstrInfo();
|
|
|
|
// We need to track liveness in order to use register scavenging.
|
|
MF.getProperties().set(MachineFunctionProperties::Property::TracksLiveness);
|
|
|
|
for (Block &MBB : MF) {
|
|
bool ContinueExpanding = true;
|
|
unsigned ExpandCount = 0;
|
|
|
|
// Continue expanding the block until all pseudos are expanded.
|
|
do {
|
|
assert(ExpandCount < 10 && "pseudo expand limit reached");
|
|
|
|
bool BlockModified = expandMBB(MBB);
|
|
Modified |= BlockModified;
|
|
ExpandCount++;
|
|
|
|
ContinueExpanding = BlockModified;
|
|
} while (ContinueExpanding);
|
|
}
|
|
|
|
return Modified;
|
|
}
|
|
|
|
bool AVRExpandPseudo::
|
|
expandArith(unsigned OpLo, unsigned OpHi, Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg, DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(2).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
bool ImpIsDead = MI.getOperand(3).isDead();
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBHI->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
bool AVRExpandPseudo::
|
|
expandLogic(unsigned Op, Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg, DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(2).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
bool ImpIsDead = MI.getOperand(3).isDead();
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, Op)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
// SREG is always implicitly dead
|
|
MIBLO->getOperand(3).setIsDead();
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, Op)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
bool AVRExpandPseudo::
|
|
isLogicImmOpRedundant(unsigned Op, unsigned ImmVal) const {
|
|
|
|
// ANDI Rd, 0xff is redundant.
|
|
if (Op == AVR::ANDIRdK && ImmVal == 0xff)
|
|
return true;
|
|
|
|
// ORI Rd, 0x0 is redundant.
|
|
if (Op == AVR::ORIRdK && ImmVal == 0x0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool AVRExpandPseudo::
|
|
expandLogicImm(unsigned Op, Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(3).isDead();
|
|
unsigned Imm = MI.getOperand(2).getImm();
|
|
unsigned Lo8 = Imm & 0xff;
|
|
unsigned Hi8 = (Imm >> 8) & 0xff;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
if (!isLogicImmOpRedundant(Op, Lo8)) {
|
|
auto MIBLO = buildMI(MBB, MBBI, Op)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(SrcIsKill))
|
|
.addImm(Lo8);
|
|
|
|
// SREG is always implicitly dead
|
|
MIBLO->getOperand(3).setIsDead();
|
|
}
|
|
|
|
if (!isLogicImmOpRedundant(Op, Hi8)) {
|
|
auto MIBHI = buildMI(MBB, MBBI, Op)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(SrcIsKill))
|
|
.addImm(Hi8);
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
}
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ADDWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandArith(AVR::ADDRdRr, AVR::ADCRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ADCWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandArith(AVR::ADCRdRr, AVR::ADCRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::SUBWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandArith(AVR::SUBRdRr, AVR::SBCRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::SUBIWRdK>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(3).isDead();
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, AVR::SUBIRdK)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, AVR::SBCIRdK)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(SrcIsKill));
|
|
|
|
switch (MI.getOperand(2).getType()) {
|
|
case MachineOperand::MO_GlobalAddress: {
|
|
const GlobalValue *GV = MI.getOperand(2).getGlobal();
|
|
int64_t Offs = MI.getOperand(2).getOffset();
|
|
unsigned TF = MI.getOperand(2).getTargetFlags();
|
|
MIBLO.addGlobalAddress(GV, Offs, TF | AVRII::MO_NEG | AVRII::MO_LO);
|
|
MIBHI.addGlobalAddress(GV, Offs, TF | AVRII::MO_NEG | AVRII::MO_HI);
|
|
break;
|
|
}
|
|
case MachineOperand::MO_Immediate: {
|
|
unsigned Imm = MI.getOperand(2).getImm();
|
|
MIBLO.addImm(Imm & 0xff);
|
|
MIBHI.addImm((Imm >> 8) & 0xff);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown operand type!");
|
|
}
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBHI->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::SBCWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandArith(AVR::SBCRdRr, AVR::SBCRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::SBCIWRdK>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(3).isDead();
|
|
unsigned Imm = MI.getOperand(2).getImm();
|
|
unsigned Lo8 = Imm & 0xff;
|
|
unsigned Hi8 = (Imm >> 8) & 0xff;
|
|
unsigned OpLo = AVR::SBCIRdK;
|
|
unsigned OpHi = AVR::SBCIRdK;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(SrcIsKill))
|
|
.addImm(Lo8);
|
|
|
|
// SREG is always implicitly killed
|
|
MIBLO->getOperand(4).setIsKill();
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(SrcIsKill))
|
|
.addImm(Hi8);
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBHI->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ANDWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandLogic(AVR::ANDRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ANDIWRdK>(Block &MBB, BlockIt MBBI) {
|
|
return expandLogicImm(AVR::ANDIRdK, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ORWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandLogic(AVR::ORRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ORIWRdK>(Block &MBB, BlockIt MBBI) {
|
|
return expandLogicImm(AVR::ORIRdK, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::EORWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
return expandLogic(AVR::EORRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::COMWRd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
unsigned OpLo = AVR::COMRd;
|
|
unsigned OpHi = AVR::COMRd;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
// SREG is always implicitly dead
|
|
MIBLO->getOperand(2).setIsDead();
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(2).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::NEGWRd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Do NEG on the upper byte.
|
|
auto MIBHI =
|
|
buildMI(MBB, MBBI, AVR::NEGRd)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
// SREG is always implicitly dead
|
|
MIBHI->getOperand(2).setIsDead();
|
|
|
|
// Do NEG on the lower byte.
|
|
buildMI(MBB, MBBI, AVR::NEGRd)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
// Do an extra SBCI.
|
|
auto MISBCI =
|
|
buildMI(MBB, MBBI, AVR::SBCIRdK)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addImm(0);
|
|
if (ImpIsDead)
|
|
MISBCI->getOperand(3).setIsDead();
|
|
// SREG is always implicitly killed
|
|
MISBCI->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::CPWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg, DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsKill = MI.getOperand(0).isKill();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
unsigned OpLo = AVR::CPRdRr;
|
|
unsigned OpHi = AVR::CPCRdRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Low part
|
|
buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(2).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBHI->getOperand(3).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::CPCWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg, DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsKill = MI.getOperand(0).isKill();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
unsigned OpLo = AVR::CPCRdRr;
|
|
unsigned OpHi = AVR::CPCRdRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
// SREG is always implicitly killed
|
|
MIBLO->getOperand(3).setIsKill();
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(2).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBHI->getOperand(3).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LDIWRdK>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
unsigned OpLo = AVR::LDIRdK;
|
|
unsigned OpHi = AVR::LDIRdK;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead));
|
|
|
|
switch (MI.getOperand(1).getType()) {
|
|
case MachineOperand::MO_GlobalAddress: {
|
|
const GlobalValue *GV = MI.getOperand(1).getGlobal();
|
|
int64_t Offs = MI.getOperand(1).getOffset();
|
|
unsigned TF = MI.getOperand(1).getTargetFlags();
|
|
|
|
MIBLO.addGlobalAddress(GV, Offs, TF | AVRII::MO_LO);
|
|
MIBHI.addGlobalAddress(GV, Offs, TF | AVRII::MO_HI);
|
|
break;
|
|
}
|
|
case MachineOperand::MO_BlockAddress: {
|
|
const BlockAddress *BA = MI.getOperand(1).getBlockAddress();
|
|
unsigned TF = MI.getOperand(1).getTargetFlags();
|
|
|
|
MIBLO.add(MachineOperand::CreateBA(BA, TF | AVRII::MO_LO));
|
|
MIBHI.add(MachineOperand::CreateBA(BA, TF | AVRII::MO_HI));
|
|
break;
|
|
}
|
|
case MachineOperand::MO_Immediate: {
|
|
unsigned Imm = MI.getOperand(1).getImm();
|
|
|
|
MIBLO.addImm(Imm & 0xff);
|
|
MIBHI.addImm((Imm >> 8) & 0xff);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown operand type!");
|
|
}
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LDSWRdK>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
unsigned OpLo = AVR::LDSRdK;
|
|
unsigned OpHi = AVR::LDSRdK;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead));
|
|
|
|
switch (MI.getOperand(1).getType()) {
|
|
case MachineOperand::MO_GlobalAddress: {
|
|
const GlobalValue *GV = MI.getOperand(1).getGlobal();
|
|
int64_t Offs = MI.getOperand(1).getOffset();
|
|
unsigned TF = MI.getOperand(1).getTargetFlags();
|
|
|
|
MIBLO.addGlobalAddress(GV, Offs, TF);
|
|
MIBHI.addGlobalAddress(GV, Offs + 1, TF);
|
|
break;
|
|
}
|
|
case MachineOperand::MO_Immediate: {
|
|
unsigned Imm = MI.getOperand(1).getImm();
|
|
|
|
MIBLO.addImm(Imm);
|
|
MIBHI.addImm(Imm + 1);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown operand type!");
|
|
}
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LDWRdPtr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register TmpReg = 0; // 0 for no temporary register
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::LDRdPtr;
|
|
unsigned OpHi = AVR::LDDRdPtrQ;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Use a temporary register if src and dst registers are the same.
|
|
if (DstReg == SrcReg)
|
|
TmpReg = scavengeGPR8(MI);
|
|
|
|
Register CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg;
|
|
Register CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg;
|
|
|
|
// Load low byte.
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(CurDstLoReg, RegState::Define)
|
|
.addReg(SrcReg);
|
|
|
|
// Push low byte onto stack if necessary.
|
|
if (TmpReg)
|
|
buildMI(MBB, MBBI, AVR::PUSHRr).addReg(TmpReg);
|
|
|
|
// Load high byte.
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(CurDstHiReg, RegState::Define)
|
|
.addReg(SrcReg, getKillRegState(SrcIsKill))
|
|
.addImm(1);
|
|
|
|
if (TmpReg) {
|
|
// Move the high byte into the final destination.
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg);
|
|
|
|
// Move the low byte from the scratch space into the final destination.
|
|
buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg);
|
|
}
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LDWRdPtrPi>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsDead = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::LDRdPtrPi;
|
|
unsigned OpHi = AVR::LDRdPtrPi;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(SrcReg, RegState::Define)
|
|
.addReg(SrcReg, RegState::Kill);
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(SrcReg, RegState::Define | getDeadRegState(SrcIsDead))
|
|
.addReg(SrcReg, RegState::Kill);
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LDWRdPtrPd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsDead = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::LDRdPtrPd;
|
|
unsigned OpHi = AVR::LDRdPtrPd;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(SrcReg, RegState::Define)
|
|
.addReg(SrcReg, RegState::Kill);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(SrcReg, RegState::Define | getDeadRegState(SrcIsDead))
|
|
.addReg(SrcReg, RegState::Kill);
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register TmpReg = 0; // 0 for no temporary register
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
unsigned Imm = MI.getOperand(2).getImm();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::LDDRdPtrQ;
|
|
unsigned OpHi = AVR::LDDRdPtrQ;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
|
|
// allowed for the instruction, 62 is the limit here.
|
|
assert(Imm <= 62 && "Offset is out of range");
|
|
|
|
// Use a temporary register if src and dst registers are the same.
|
|
if (DstReg == SrcReg)
|
|
TmpReg = scavengeGPR8(MI);
|
|
|
|
Register CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg;
|
|
Register CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg;
|
|
|
|
// Load low byte.
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(CurDstLoReg, RegState::Define)
|
|
.addReg(SrcReg)
|
|
.addImm(Imm);
|
|
|
|
// Push low byte onto stack if necessary.
|
|
if (TmpReg)
|
|
buildMI(MBB, MBBI, AVR::PUSHRr).addReg(TmpReg);
|
|
|
|
// Load high byte.
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(CurDstHiReg, RegState::Define)
|
|
.addReg(SrcReg, getKillRegState(SrcIsKill))
|
|
.addImm(Imm + 1);
|
|
|
|
if (TmpReg) {
|
|
// Move the high byte into the final destination.
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg);
|
|
|
|
// Move the low byte from the scratch space into the final destination.
|
|
buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg);
|
|
}
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LPMWRdZ>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register TmpReg = 0; // 0 for no temporary register
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::LPMRdZPi;
|
|
unsigned OpHi = AVR::LPMRdZ;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Use a temporary register if src and dst registers are the same.
|
|
if (DstReg == SrcReg)
|
|
TmpReg = scavengeGPR8(MI);
|
|
|
|
Register CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg;
|
|
Register CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg;
|
|
|
|
// Load low byte.
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(CurDstLoReg, RegState::Define)
|
|
.addReg(SrcReg);
|
|
|
|
// Push low byte onto stack if necessary.
|
|
if (TmpReg)
|
|
buildMI(MBB, MBBI, AVR::PUSHRr).addReg(TmpReg);
|
|
|
|
// Load high byte.
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(CurDstHiReg, RegState::Define)
|
|
.addReg(SrcReg, getKillRegState(SrcIsKill));
|
|
|
|
if (TmpReg) {
|
|
// Move the high byte into the final destination.
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg);
|
|
|
|
// Move the low byte from the scratch space into the final destination.
|
|
buildMI(MBB, MBBI, AVR::POPRd).addReg(DstLoReg);
|
|
}
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LPMWRdZPi>(Block &MBB, BlockIt MBBI) {
|
|
llvm_unreachable("wide LPMPi is unimplemented");
|
|
}
|
|
|
|
template<typename Func>
|
|
bool AVRExpandPseudo::expandAtomic(Block &MBB, BlockIt MBBI, Func f) {
|
|
// Remove the pseudo instruction.
|
|
MachineInstr &MI = *MBBI;
|
|
|
|
// Store the SREG.
|
|
buildMI(MBB, MBBI, AVR::INRdA)
|
|
.addReg(SCRATCH_REGISTER, RegState::Define)
|
|
.addImm(SREG_ADDR);
|
|
|
|
// Disable exceptions.
|
|
buildMI(MBB, MBBI, AVR::BCLRs).addImm(7); // CLI
|
|
|
|
f(MI);
|
|
|
|
// Restore the status reg.
|
|
buildMI(MBB, MBBI, AVR::OUTARr)
|
|
.addImm(SREG_ADDR)
|
|
.addReg(SCRATCH_REGISTER);
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template<typename Func>
|
|
bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode,
|
|
Block &MBB,
|
|
BlockIt MBBI,
|
|
Func f) {
|
|
return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) {
|
|
auto Op1 = MI.getOperand(0);
|
|
auto Op2 = MI.getOperand(1);
|
|
|
|
MachineInstr &NewInst =
|
|
*buildMI(MBB, MBBI, Opcode).add(Op1).add(Op2).getInstr();
|
|
f(NewInst);
|
|
});
|
|
}
|
|
|
|
bool AVRExpandPseudo::expandAtomicBinaryOp(unsigned Opcode,
|
|
Block &MBB,
|
|
BlockIt MBBI) {
|
|
return expandAtomicBinaryOp(Opcode, MBB, MBBI, [](MachineInstr &MI) {});
|
|
}
|
|
|
|
bool AVRExpandPseudo::expandAtomicArithmeticOp(unsigned Width,
|
|
unsigned ArithOpcode,
|
|
Block &MBB,
|
|
BlockIt MBBI) {
|
|
return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) {
|
|
auto Op1 = MI.getOperand(0);
|
|
auto Op2 = MI.getOperand(1);
|
|
|
|
unsigned LoadOpcode = (Width == 8) ? AVR::LDRdPtr : AVR::LDWRdPtr;
|
|
unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr;
|
|
|
|
// Create the load
|
|
buildMI(MBB, MBBI, LoadOpcode).add(Op1).add(Op2);
|
|
|
|
// Create the arithmetic op
|
|
buildMI(MBB, MBBI, ArithOpcode).add(Op1).add(Op1).add(Op2);
|
|
|
|
// Create the store
|
|
buildMI(MBB, MBBI, StoreOpcode).add(Op2).add(Op1);
|
|
});
|
|
}
|
|
|
|
Register AVRExpandPseudo::scavengeGPR8(MachineInstr &MI) {
|
|
MachineBasicBlock &MBB = *MI.getParent();
|
|
RegScavenger RS;
|
|
|
|
RS.enterBasicBlock(MBB);
|
|
RS.forward(MI);
|
|
|
|
BitVector Candidates =
|
|
TRI->getAllocatableSet
|
|
(*MBB.getParent(), &AVR::GPR8RegClass);
|
|
|
|
// Exclude all the registers being used by the instruction.
|
|
for (MachineOperand &MO : MI.operands()) {
|
|
if (MO.isReg() && MO.getReg() != 0 && !MO.isDef() &&
|
|
!Register::isVirtualRegister(MO.getReg()))
|
|
Candidates.reset(MO.getReg());
|
|
}
|
|
|
|
BitVector Available = RS.getRegsAvailable(&AVR::GPR8RegClass);
|
|
Available &= Candidates;
|
|
|
|
signed Reg = Available.find_first();
|
|
assert(Reg != -1 && "ran out of registers");
|
|
return Reg;
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoad8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicBinaryOp(AVR::LDRdPtr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoad16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicBinaryOp(AVR::LDWRdPtr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicStore8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicBinaryOp(AVR::STPtrRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicStore16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicBinaryOp(AVR::STWPtrRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadAdd8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(8, AVR::ADDRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadAdd16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(16, AVR::ADDWRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadSub8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(8, AVR::SUBRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadSub16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(16, AVR::SUBWRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadAnd8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(8, AVR::ANDRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadAnd16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(16, AVR::ANDWRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadOr8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(8, AVR::ORRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadOr16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(16, AVR::ORWRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadXor8>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(8, AVR::EORRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicLoadXor16>(Block &MBB, BlockIt MBBI) {
|
|
return expandAtomicArithmeticOp(16, AVR::EORWRdRr, MBB, MBBI);
|
|
}
|
|
|
|
template<>
|
|
bool AVRExpandPseudo::expand<AVR::AtomicFence>(Block &MBB, BlockIt MBBI) {
|
|
// On AVR, there is only one core and so atomic fences do nothing.
|
|
MBBI->eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::STSWKRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::STSKRr;
|
|
unsigned OpHi = AVR::STSKRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
// Write the high byte first in case this address belongs to a special
|
|
// I/O address with a special temporary register.
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi);
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo);
|
|
|
|
switch (MI.getOperand(0).getType()) {
|
|
case MachineOperand::MO_GlobalAddress: {
|
|
const GlobalValue *GV = MI.getOperand(0).getGlobal();
|
|
int64_t Offs = MI.getOperand(0).getOffset();
|
|
unsigned TF = MI.getOperand(0).getTargetFlags();
|
|
|
|
MIBLO.addGlobalAddress(GV, Offs, TF);
|
|
MIBHI.addGlobalAddress(GV, Offs + 1, TF);
|
|
break;
|
|
}
|
|
case MachineOperand::MO_Immediate: {
|
|
unsigned Imm = MI.getOperand(0).getImm();
|
|
|
|
MIBLO.addImm(Imm);
|
|
MIBHI.addImm(Imm + 1);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown operand type!");
|
|
}
|
|
|
|
MIBLO.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
MIBHI.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::STWPtrRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::STPtrRr;
|
|
unsigned OpHi = AVR::STDPtrQRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
//:TODO: need to reverse this order like inw and stsw?
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstReg)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstReg)
|
|
.addImm(1)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::STWPtrPiRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(2).getReg();
|
|
unsigned Imm = MI.getOperand(3).getImm();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
unsigned OpLo = AVR::STPtrPiRr;
|
|
unsigned OpHi = AVR::STPtrPiRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstReg, RegState::Define)
|
|
.addReg(DstReg, RegState::Kill)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill))
|
|
.addImm(Imm);
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, RegState::Kill)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill))
|
|
.addImm(Imm);
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::STWPtrPdRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(2).getReg();
|
|
unsigned Imm = MI.getOperand(3).getImm();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
unsigned OpLo = AVR::STPtrPdRr;
|
|
unsigned OpHi = AVR::STPtrPdRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
assert(DstReg != SrcReg && "SrcReg and DstReg cannot be the same");
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstReg, RegState::Define)
|
|
.addReg(DstReg, RegState::Kill)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill))
|
|
.addImm(Imm);
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, RegState::Kill)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill))
|
|
.addImm(Imm);
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(2).getReg();
|
|
unsigned Imm = MI.getOperand(1).getImm();
|
|
bool DstIsKill = MI.getOperand(0).isKill();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
unsigned OpLo = AVR::STDPtrQRr;
|
|
unsigned OpHi = AVR::STDPtrQRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
// Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
|
|
// allowed for the instruction, 62 is the limit here.
|
|
assert(Imm <= 62 && "Offset is out of range");
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstReg)
|
|
.addImm(Imm)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addImm(Imm + 1)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::INWRdA>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
unsigned Imm = MI.getOperand(1).getImm();
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
unsigned OpLo = AVR::INRdA;
|
|
unsigned OpHi = AVR::INRdA;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
|
|
// allowed for the instruction, 62 is the limit here.
|
|
assert(Imm <= 62 && "Address is out of range");
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addImm(Imm);
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addImm(Imm + 1);
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::OUTWARr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
unsigned Imm = MI.getOperand(0).getImm();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned OpLo = AVR::OUTARr;
|
|
unsigned OpHi = AVR::OUTARr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
// Since we add 1 to the Imm value for the high byte below, and 63 is the highest Imm value
|
|
// allowed for the instruction, 62 is the limit here.
|
|
assert(Imm <= 62 && "Address is out of range");
|
|
|
|
// 16 bit I/O writes need the high byte first
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addImm(Imm + 1)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addImm(Imm)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
MIBLO.setMemRefs(MI.memoperands());
|
|
MIBHI.setMemRefs(MI.memoperands());
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::PUSHWRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register SrcReg = MI.getOperand(0).getReg();
|
|
bool SrcIsKill = MI.getOperand(0).isKill();
|
|
unsigned Flags = MI.getFlags();
|
|
unsigned OpLo = AVR::PUSHRr;
|
|
unsigned OpHi = AVR::PUSHRr;
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
// Low part
|
|
buildMI(MBB, MBBI, OpLo)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill))
|
|
.setMIFlags(Flags);
|
|
|
|
// High part
|
|
buildMI(MBB, MBBI, OpHi)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill))
|
|
.setMIFlags(Flags);
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::POPWRd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
unsigned Flags = MI.getFlags();
|
|
unsigned OpLo = AVR::POPRd;
|
|
unsigned OpHi = AVR::POPRd;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
buildMI(MBB, MBBI, OpHi, DstHiReg).setMIFlags(Flags); // High
|
|
buildMI(MBB, MBBI, OpLo, DstLoReg).setMIFlags(Flags); // Low
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ROLBRd>(Block &MBB, BlockIt MBBI) {
|
|
// In AVR, the rotate instructions behave quite unintuitively. They rotate
|
|
// bits through the carry bit in SREG, effectively rotating over 9 bits,
|
|
// instead of 8. This is useful when we are dealing with numbers over
|
|
// multiple registers, but when we actually need to rotate stuff, we have
|
|
// to explicitly add the carry bit.
|
|
|
|
MachineInstr &MI = *MBBI;
|
|
unsigned OpShift, OpCarry;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
OpShift = AVR::ADDRdRr;
|
|
OpCarry = AVR::ADCRdRr;
|
|
|
|
// add r16, r16
|
|
// adc r16, r1
|
|
|
|
// Shift part
|
|
buildMI(MBB, MBBI, OpShift)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg)
|
|
.addReg(DstReg);
|
|
|
|
// Add the carry bit
|
|
auto MIB = buildMI(MBB, MBBI, OpCarry)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg)
|
|
.addReg(ZERO_REGISTER);
|
|
|
|
// SREG is always implicitly killed
|
|
MIB->getOperand(2).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::RORBRd>(Block &MBB, BlockIt MBBI) {
|
|
// In AVR, the rotate instructions behave quite unintuitively. They rotate
|
|
// bits through the carry bit in SREG, effectively rotating over 9 bits,
|
|
// instead of 8. This is useful when we are dealing with numbers over
|
|
// multiple registers, but when we actually need to rotate stuff, we have
|
|
// to explicitly add the carry bit.
|
|
|
|
MachineInstr &MI = *MBBI;
|
|
unsigned OpShiftOut, OpLoad, OpShiftIn, OpAdd;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
OpShiftOut = AVR::LSRRd;
|
|
OpLoad = AVR::LDIRdK;
|
|
OpShiftIn = AVR::RORRd;
|
|
OpAdd = AVR::ORRdRr;
|
|
|
|
// lsr r16
|
|
// ldi r0, 0
|
|
// ror r0
|
|
// or r16, r17
|
|
|
|
// Shift out
|
|
buildMI(MBB, MBBI, OpShiftOut)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg);
|
|
|
|
// Put 0 in temporary register
|
|
buildMI(MBB, MBBI, OpLoad)
|
|
.addReg(SCRATCH_REGISTER, RegState::Define | getDeadRegState(true))
|
|
.addImm(0x00);
|
|
|
|
// Shift in
|
|
buildMI(MBB, MBBI, OpShiftIn)
|
|
.addReg(SCRATCH_REGISTER, RegState::Define | getDeadRegState(true))
|
|
.addReg(SCRATCH_REGISTER);
|
|
|
|
// Add the results together using an or-instruction
|
|
auto MIB = buildMI(MBB, MBBI, OpAdd)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg)
|
|
.addReg(SCRATCH_REGISTER);
|
|
|
|
// SREG is always implicitly killed
|
|
MIB->getOperand(2).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSLWRd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
unsigned OpLo = AVR::ADDRdRr; // ADD Rd, Rd <==> LSL Rd
|
|
unsigned OpHi = AVR::ADCRdRr; // ADC Rd, Rd <==> ROL Rd
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Low part
|
|
buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg)
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg)
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBHI->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSRWRd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
unsigned OpLo = AVR::RORRd;
|
|
unsigned OpHi = AVR::LSRRd;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// High part
|
|
buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBLO->getOperand(2).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBLO->getOperand(3).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::RORWRd>(Block &MBB, BlockIt MBBI) {
|
|
llvm_unreachable("RORW unimplemented");
|
|
return false;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ROLWRd>(Block &MBB, BlockIt MBBI) {
|
|
llvm_unreachable("ROLW unimplemented");
|
|
return false;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ASRWRd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
unsigned OpLo = AVR::RORRd;
|
|
unsigned OpHi = AVR::ASRRd;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// High part
|
|
buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIBLO->getOperand(2).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIBLO->getOperand(3).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <> bool AVRExpandPseudo::expand<AVR::SEXT>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
// sext R17:R16, R17
|
|
// mov r16, r17
|
|
// lsl r17
|
|
// sbc r17, r17
|
|
// sext R17:R16, R13
|
|
// mov r16, r13
|
|
// mov r17, r13
|
|
// lsl r17
|
|
// sbc r17, r17
|
|
// sext R17:R16, R16
|
|
// mov r17, r16
|
|
// lsl r17
|
|
// sbc r17, r17
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
if (SrcReg != DstLoReg) {
|
|
auto MOV = buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(SrcReg);
|
|
|
|
if (SrcReg == DstHiReg) {
|
|
MOV->getOperand(1).setIsKill();
|
|
}
|
|
}
|
|
|
|
if (SrcReg != DstHiReg) {
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstHiReg, RegState::Define)
|
|
.addReg(SrcReg, getKillRegState(SrcIsKill));
|
|
}
|
|
|
|
buildMI(MBB, MBBI, AVR::ADDRdRr) // LSL Rd <==> ADD Rd, Rr
|
|
.addReg(DstHiReg, RegState::Define)
|
|
.addReg(DstHiReg)
|
|
.addReg(DstHiReg, RegState::Kill);
|
|
|
|
auto SBC = buildMI(MBB, MBBI, AVR::SBCRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, RegState::Kill)
|
|
.addReg(DstHiReg, RegState::Kill);
|
|
|
|
if (ImpIsDead)
|
|
SBC->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
SBC->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <> bool AVRExpandPseudo::expand<AVR::ZEXT>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
// zext R25:R24, R20
|
|
// mov R24, R20
|
|
// eor R25, R25
|
|
// zext R25:R24, R24
|
|
// eor R25, R25
|
|
// zext R25:R24, R25
|
|
// mov R24, R25
|
|
// eor R25, R25
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
if (SrcReg != DstLoReg) {
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(SrcReg, getKillRegState(SrcIsKill));
|
|
}
|
|
|
|
auto EOR = buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, RegState::Kill)
|
|
.addReg(DstHiReg, RegState::Kill);
|
|
|
|
if (ImpIsDead)
|
|
EOR->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::SPREAD>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstLoReg, DstHiReg;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
unsigned Flags = MI.getFlags();
|
|
unsigned OpLo = AVR::INRdA;
|
|
unsigned OpHi = AVR::INRdA;
|
|
TRI->splitReg(DstReg, DstLoReg, DstHiReg);
|
|
|
|
// Low part
|
|
buildMI(MBB, MBBI, OpLo)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addImm(0x3d)
|
|
.setMIFlags(Flags);
|
|
|
|
// High part
|
|
buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addImm(0x3e)
|
|
.setMIFlags(Flags);
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::SPWRITE>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register SrcLoReg, SrcHiReg;
|
|
Register SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
unsigned Flags = MI.getFlags();
|
|
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
|
|
|
|
buildMI(MBB, MBBI, AVR::INRdA)
|
|
.addReg(AVR::R0, RegState::Define)
|
|
.addImm(SREG_ADDR)
|
|
.setMIFlags(Flags);
|
|
|
|
buildMI(MBB, MBBI, AVR::BCLRs).addImm(0x07).setMIFlags(Flags);
|
|
|
|
buildMI(MBB, MBBI, AVR::OUTARr)
|
|
.addImm(0x3e)
|
|
.addReg(SrcHiReg, getKillRegState(SrcIsKill))
|
|
.setMIFlags(Flags);
|
|
|
|
buildMI(MBB, MBBI, AVR::OUTARr)
|
|
.addImm(SREG_ADDR)
|
|
.addReg(AVR::R0, RegState::Kill)
|
|
.setMIFlags(Flags);
|
|
|
|
buildMI(MBB, MBBI, AVR::OUTARr)
|
|
.addImm(0x3d)
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill))
|
|
.setMIFlags(Flags);
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
int Opcode = MBBI->getOpcode();
|
|
|
|
#define EXPAND(Op) \
|
|
case Op: \
|
|
return expand<Op>(MBB, MI)
|
|
|
|
switch (Opcode) {
|
|
EXPAND(AVR::ADDWRdRr);
|
|
EXPAND(AVR::ADCWRdRr);
|
|
EXPAND(AVR::SUBWRdRr);
|
|
EXPAND(AVR::SUBIWRdK);
|
|
EXPAND(AVR::SBCWRdRr);
|
|
EXPAND(AVR::SBCIWRdK);
|
|
EXPAND(AVR::ANDWRdRr);
|
|
EXPAND(AVR::ANDIWRdK);
|
|
EXPAND(AVR::ORWRdRr);
|
|
EXPAND(AVR::ORIWRdK);
|
|
EXPAND(AVR::EORWRdRr);
|
|
EXPAND(AVR::COMWRd);
|
|
EXPAND(AVR::NEGWRd);
|
|
EXPAND(AVR::CPWRdRr);
|
|
EXPAND(AVR::CPCWRdRr);
|
|
EXPAND(AVR::LDIWRdK);
|
|
EXPAND(AVR::LDSWRdK);
|
|
EXPAND(AVR::LDWRdPtr);
|
|
EXPAND(AVR::LDWRdPtrPi);
|
|
EXPAND(AVR::LDWRdPtrPd);
|
|
case AVR::LDDWRdYQ: //:FIXME: remove this once PR13375 gets fixed
|
|
EXPAND(AVR::LDDWRdPtrQ);
|
|
EXPAND(AVR::LPMWRdZ);
|
|
EXPAND(AVR::LPMWRdZPi);
|
|
EXPAND(AVR::AtomicLoad8);
|
|
EXPAND(AVR::AtomicLoad16);
|
|
EXPAND(AVR::AtomicStore8);
|
|
EXPAND(AVR::AtomicStore16);
|
|
EXPAND(AVR::AtomicLoadAdd8);
|
|
EXPAND(AVR::AtomicLoadAdd16);
|
|
EXPAND(AVR::AtomicLoadSub8);
|
|
EXPAND(AVR::AtomicLoadSub16);
|
|
EXPAND(AVR::AtomicLoadAnd8);
|
|
EXPAND(AVR::AtomicLoadAnd16);
|
|
EXPAND(AVR::AtomicLoadOr8);
|
|
EXPAND(AVR::AtomicLoadOr16);
|
|
EXPAND(AVR::AtomicLoadXor8);
|
|
EXPAND(AVR::AtomicLoadXor16);
|
|
EXPAND(AVR::AtomicFence);
|
|
EXPAND(AVR::STSWKRr);
|
|
EXPAND(AVR::STWPtrRr);
|
|
EXPAND(AVR::STWPtrPiRr);
|
|
EXPAND(AVR::STWPtrPdRr);
|
|
EXPAND(AVR::STDWPtrQRr);
|
|
EXPAND(AVR::INWRdA);
|
|
EXPAND(AVR::OUTWARr);
|
|
EXPAND(AVR::PUSHWRr);
|
|
EXPAND(AVR::POPWRd);
|
|
EXPAND(AVR::ROLBRd);
|
|
EXPAND(AVR::RORBRd);
|
|
EXPAND(AVR::LSLWRd);
|
|
EXPAND(AVR::LSRWRd);
|
|
EXPAND(AVR::RORWRd);
|
|
EXPAND(AVR::ROLWRd);
|
|
EXPAND(AVR::ASRWRd);
|
|
EXPAND(AVR::SEXT);
|
|
EXPAND(AVR::ZEXT);
|
|
EXPAND(AVR::SPREAD);
|
|
EXPAND(AVR::SPWRITE);
|
|
}
|
|
#undef EXPAND
|
|
return false;
|
|
}
|
|
|
|
} // end of anonymous namespace
|
|
|
|
INITIALIZE_PASS(AVRExpandPseudo, "avr-expand-pseudo",
|
|
AVR_EXPAND_PSEUDO_NAME, false, false)
|
|
namespace llvm {
|
|
|
|
FunctionPass *createAVRExpandPseudoPass() { return new AVRExpandPseudo(); }
|
|
|
|
} // end of namespace llvm
|