mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-23 19:23:23 +01:00
bc2293e2b6
Summary: This patch introduces the ROLBRd and RORBRd pseudo-instructions, which implemenent the "traditional" rotate operations; instead of the AVR rotate instructions that use the carry bit. The code is not optimized at all. Especially when dealing with loops of rotate instructions, this codegen should be improved some day. Related bug: 41358 <https://bugs.llvm.org/show_bug.cgi?id=41358> //Note//: This is my first submitted patch. Reviewers: dylanmckay, Jim Reviewed By: dylanmckay Subscribers: hiraditya, llvm-commits, dylanmckay, dsprenkels Tags: #llvm Patched by dsprenkels (Daan Sprenkels) Differential Revision: https://reviews.llvm.org/D60365
1679 lines
50 KiB
C++
1679 lines
50 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 unsigned SCRATCH_REGISTER = AVR::R0;
|
|
/// The register that will always contain zero.
|
|
const unsigned 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,
|
|
unsigned 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.
|
|
unsigned 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;
|
|
unsigned 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;
|
|
unsigned 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;
|
|
unsigned 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;
|
|
unsigned DstLoReg, DstHiReg;
|
|
unsigned 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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned 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;
|
|
OpLo = AVR::SBCIRdK;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
OpLo = AVR::COMRd;
|
|
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::CPWRdRr>(Block &MBB, BlockIt MBBI) {
|
|
MachineInstr &MI = *MBBI;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsKill = MI.getOperand(0).isKill();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
OpLo = AVR::CPRdRr;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsKill = MI.getOperand(0).isKill();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
OpLo = AVR::CPCRdRr;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
OpLo = AVR::LDIRdK;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
OpLo = AVR::LDSRdK;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned TmpReg = 0; // 0 for no temporary register
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
OpLo = AVR::LDRdPtr;
|
|
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);
|
|
|
|
unsigned CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg;
|
|
unsigned CurDstHiReg = (DstReg == SrcReg) ? TmpReg : DstHiReg;
|
|
|
|
// Load low byte.
|
|
auto MIBLO = buildMI(MBB, MBBI, OpLo)
|
|
.addReg(CurDstLoReg, RegState::Define)
|
|
.addReg(SrcReg, RegState::Define);
|
|
|
|
// 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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsDead = MI.getOperand(1).isKill();
|
|
OpLo = AVR::LDRdPtrPi;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsDead = MI.getOperand(1).isKill();
|
|
OpLo = AVR::LDRdPtrPd;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned TmpReg = 0; // 0 for no temporary register
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
unsigned Imm = MI.getOperand(2).getImm();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
OpLo = AVR::LDDRdPtrQ;
|
|
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);
|
|
|
|
unsigned CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg;
|
|
unsigned 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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned TmpReg = 0; // 0 for no temporary register
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
OpLo = AVR::LPMRdZPi;
|
|
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);
|
|
|
|
unsigned CurDstLoReg = (DstReg == SrcReg) ? TmpReg : DstLoReg;
|
|
unsigned 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);
|
|
});
|
|
}
|
|
|
|
unsigned 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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
OpLo = AVR::STSKRr;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
OpLo = AVR::STPtrRr;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(2).getReg();
|
|
unsigned Imm = MI.getOperand(3).getImm();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
OpLo = AVR::STPtrPiRr;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(2).getReg();
|
|
unsigned Imm = MI.getOperand(3).getImm();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
OpLo = AVR::STPtrPdRr;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned SrcReg = MI.getOperand(2).getReg();
|
|
unsigned Imm = MI.getOperand(1).getImm();
|
|
bool DstIsKill = MI.getOperand(0).isKill();
|
|
bool SrcIsKill = MI.getOperand(2).isKill();
|
|
OpLo = AVR::STDPtrQRr;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned Imm = MI.getOperand(1).getImm();
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
OpLo = AVR::INRdA;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned Imm = MI.getOperand(0).getImm();
|
|
unsigned SrcReg = MI.getOperand(1).getReg();
|
|
bool SrcIsKill = MI.getOperand(1).isKill();
|
|
OpLo = AVR::OUTARr;
|
|
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;
|
|
unsigned OpLo, OpHi, SrcLoReg, SrcHiReg;
|
|
unsigned SrcReg = MI.getOperand(0).getReg();
|
|
bool SrcIsKill = MI.getOperand(0).isKill();
|
|
unsigned Flags = MI.getFlags();
|
|
OpLo = AVR::PUSHRr;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned Flags = MI.getFlags();
|
|
OpLo = AVR::POPRd;
|
|
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;
|
|
unsigned 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;
|
|
unsigned 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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
OpLo = AVR::ADDRdRr; // ADD Rd, Rd <==> LSL Rd
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
OpLo = AVR::RORRd;
|
|
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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
bool DstIsKill = MI.getOperand(1).isKill();
|
|
bool ImpIsDead = MI.getOperand(2).isDead();
|
|
OpLo = AVR::RORRd;
|
|
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;
|
|
unsigned 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
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned 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;
|
|
unsigned 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
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
unsigned 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;
|
|
unsigned OpLo, OpHi, DstLoReg, DstHiReg;
|
|
unsigned DstReg = MI.getOperand(0).getReg();
|
|
bool DstIsDead = MI.getOperand(0).isDead();
|
|
unsigned Flags = MI.getFlags();
|
|
OpLo = AVR::INRdA;
|
|
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;
|
|
unsigned SrcLoReg, SrcHiReg;
|
|
unsigned 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::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
|