mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-25 20:23:11 +01:00
67c9ef4b49
This patch is a large number of small changes that should hopefully not affect the generated machine code but are still important to get right so that the machine verifier won't complain about them. The llvm/test/CodeGen/AVR/pseudo/*.mir changes are also necessary because without the liveins the used registers are considered undefined by the machine verifier and it will complain about them. Differential Revision: https://reviews.llvm.org/D97172
2128 lines
64 KiB
C++
2128 lines
64 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 SBC.
|
|
auto MISBCI =
|
|
buildMI(MBB, MBBI, AVR::SBCRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(ZERO_REGISTER);
|
|
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, DstHiReg).addReg(TmpReg);
|
|
|
|
// Move the low byte from the scratch space into the final destination.
|
|
buildMI(MBB, MBBI, AVR::POPRd, 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, DstHiReg).addReg(TmpReg);
|
|
|
|
// Move the low byte from the scratch space into the final destination.
|
|
buildMI(MBB, MBBI, AVR::POPRd, 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, DstHiReg).addReg(TmpReg);
|
|
|
|
// Move the low byte from the scratch space into the final destination.
|
|
buildMI(MBB, MBBI, AVR::POPRd, 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 DstIsUndef = MI.getOperand(0).isUndef();
|
|
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, getUndefRegState(DstIsUndef))
|
|
.addReg(SrcLoReg, getKillRegState(SrcIsKill));
|
|
|
|
auto MIBHI = buildMI(MBB, MBBI, OpHi)
|
|
.addReg(DstReg, getUndefRegState(DstIsUndef))
|
|
.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::LSLW4Rd>(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);
|
|
|
|
// swap Rh
|
|
// swap Rl
|
|
buildMI(MBB, MBBI, AVR::SWAPRd)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
buildMI(MBB, MBBI, AVR::SWAPRd)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
// andi Rh, 0xf0
|
|
auto MI0 =
|
|
buildMI(MBB, MBBI, AVR::ANDIRdK)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addImm(0xf0);
|
|
// SREG is implicitly dead.
|
|
MI0->getOperand(3).setIsDead();
|
|
|
|
// eor Rh, Rl
|
|
auto MI1 =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(DstLoReg);
|
|
// SREG is implicitly dead.
|
|
MI1->getOperand(3).setIsDead();
|
|
|
|
// andi Rl, 0xf0
|
|
auto MI2 =
|
|
buildMI(MBB, MBBI, AVR::ANDIRdK)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addImm(0xf0);
|
|
// SREG is implicitly dead.
|
|
MI2->getOperand(3).setIsDead();
|
|
|
|
// eor Rh, Rl
|
|
auto MI3 =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(DstLoReg);
|
|
if (ImpIsDead)
|
|
MI3->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSLW8Rd>(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);
|
|
|
|
// mov Rh, Rl
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg);
|
|
|
|
// clr Rl
|
|
auto MIBLO =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
if (ImpIsDead)
|
|
MIBLO->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSLW12Rd>(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);
|
|
|
|
// mov Rh, Rl
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg);
|
|
|
|
// swap Rh
|
|
buildMI(MBB, MBBI, AVR::SWAPRd)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
|
|
// andi Rh, 0xf0
|
|
auto MI0 =
|
|
buildMI(MBB, MBBI, AVR::ANDIRdK)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addImm(0xf0);
|
|
// SREG is implicitly dead.
|
|
MI0->getOperand(3).setIsDead();
|
|
|
|
// clr Rl
|
|
auto MI1 =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
if (ImpIsDead)
|
|
MI1->getOperand(3).setIsDead();
|
|
|
|
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::LSRW4Rd>(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);
|
|
|
|
// swap Rh
|
|
// swap Rl
|
|
buildMI(MBB, MBBI, AVR::SWAPRd)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
buildMI(MBB, MBBI, AVR::SWAPRd)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
// andi Rl, 0xf
|
|
auto MI0 =
|
|
buildMI(MBB, MBBI, AVR::ANDIRdK)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addImm(0xf);
|
|
// SREG is implicitly dead.
|
|
MI0->getOperand(3).setIsDead();
|
|
|
|
// eor Rl, Rh
|
|
auto MI1 =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(DstHiReg);
|
|
// SREG is implicitly dead.
|
|
MI1->getOperand(3).setIsDead();
|
|
|
|
// andi Rh, 0xf
|
|
auto MI2 =
|
|
buildMI(MBB, MBBI, AVR::ANDIRdK)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addImm(0xf);
|
|
// SREG is implicitly dead.
|
|
MI2->getOperand(3).setIsDead();
|
|
|
|
// eor Rl, Rh
|
|
auto MI3 =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addReg(DstHiReg);
|
|
if (ImpIsDead)
|
|
MI3->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSRW8Rd>(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);
|
|
|
|
// Move upper byte to lower byte.
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg);
|
|
|
|
// Clear upper byte.
|
|
auto MIBHI =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSRW12Rd>(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);
|
|
|
|
// Move upper byte to lower byte.
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg);
|
|
|
|
// swap Rl
|
|
buildMI(MBB, MBBI, AVR::SWAPRd)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill));
|
|
|
|
// andi Rl, 0xf
|
|
auto MI0 =
|
|
buildMI(MBB, MBBI, AVR::ANDIRdK)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstLoReg, getKillRegState(DstIsKill))
|
|
.addImm(0xf);
|
|
// SREG is implicitly dead.
|
|
MI0->getOperand(3).setIsDead();
|
|
|
|
// Clear upper byte.
|
|
auto MIBHI =
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
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::ASRW8Rd>(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);
|
|
|
|
// Move upper byte to lower byte.
|
|
buildMI(MBB, MBBI, AVR::MOVRdRr)
|
|
.addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg);
|
|
|
|
// Move the sign bit to the C flag.
|
|
buildMI(MBB, MBBI, AVR::ADDRdRr)
|
|
.addReg(DstHiReg, RegState::Define, getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill) | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
|
|
// Set upper byte to 0 or -1.
|
|
auto MIBHI =
|
|
buildMI(MBB, MBBI, AVR::SBCRdRr)
|
|
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill))
|
|
.addReg(DstHiReg, getKillRegState(DstIsKill));
|
|
if (ImpIsDead)
|
|
MIBHI->getOperand(3).setIsDead();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSLB7Rd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
|
|
// ror r24
|
|
// clr r24
|
|
// ror r24
|
|
|
|
buildMI(MBB, MBBI, AVR::RORRd)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
->getOperand(3).setIsUndef(true);
|
|
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addReg(DstReg, getKillRegState(DstIsKill));
|
|
|
|
auto MIRRC =
|
|
buildMI(MBB, MBBI, AVR::RORRd)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIRRC->getOperand(2).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIRRC->getOperand(3).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::LSRB7Rd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
|
|
// rol r24
|
|
// clr r24
|
|
// rol r24
|
|
|
|
buildMI(MBB, MBBI, AVR::ADCRdRr)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
->getOperand(4).setIsUndef(true);
|
|
|
|
buildMI(MBB, MBBI, AVR::EORRdRr)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addReg(DstReg, getKillRegState(DstIsKill));
|
|
|
|
auto MIRRC =
|
|
buildMI(MBB, MBBI, AVR::ADCRdRr)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addReg(DstReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIRRC->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIRRC->getOperand(4).setIsKill();
|
|
|
|
MI.eraseFromParent();
|
|
return true;
|
|
}
|
|
|
|
template <>
|
|
bool AVRExpandPseudo::expand<AVR::ASRB7Rd>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
Register DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
|
|
// lsl r24
|
|
// sbc r24, r24
|
|
|
|
buildMI(MBB, MBBI, AVR::ADDRdRr)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addReg(DstReg, getKillRegState(DstIsKill));
|
|
|
|
auto MIRRC = buildMI(MBB, MBBI, AVR::SBCRdRr)
|
|
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
|
|
.addReg(DstReg, getKillRegState(DstIsKill))
|
|
.addReg(DstReg, getKillRegState(DstIsKill));
|
|
|
|
if (ImpIsDead)
|
|
MIRRC->getOperand(3).setIsDead();
|
|
|
|
// SREG is always implicitly killed
|
|
MIRRC->getOperand(4).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 | RegState::Undef)
|
|
.addReg(DstHiReg, RegState::Kill | RegState::Undef);
|
|
|
|
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::LSLW4Rd);
|
|
EXPAND(AVR::LSLW8Rd);
|
|
EXPAND(AVR::LSLW12Rd);
|
|
EXPAND(AVR::LSRWRd);
|
|
EXPAND(AVR::LSRW4Rd);
|
|
EXPAND(AVR::LSRW8Rd);
|
|
EXPAND(AVR::LSRW12Rd);
|
|
EXPAND(AVR::RORWRd);
|
|
EXPAND(AVR::ROLWRd);
|
|
EXPAND(AVR::ASRWRd);
|
|
EXPAND(AVR::ASRW8Rd);
|
|
EXPAND(AVR::LSLB7Rd);
|
|
EXPAND(AVR::LSRB7Rd);
|
|
EXPAND(AVR::ASRB7Rd);
|
|
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
|