summaryrefslogtreecommitdiff
path: root/contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp')
-rw-r--r--contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp91
1 files changed, 91 insertions, 0 deletions
diff --git a/contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
index 83d0f6845332..f466c5c053ad 100644
--- a/contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
+++ b/contrib/llvm-project/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
@@ -52,6 +52,8 @@ private:
/// 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;
@@ -1243,6 +1245,93 @@ bool AVRExpandPseudo::expand<AVR::POPWRd>(Block &MBB, BlockIt MBBI) {
}
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;
@@ -1562,6 +1651,8 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) {
EXPAND(AVR::OUTWARr);
EXPAND(AVR::PUSHWRr);
EXPAND(AVR::POPWRd);
+ EXPAND(AVR::ROLBRd);
+ EXPAND(AVR::RORBRd);
EXPAND(AVR::LSLWRd);
EXPAND(AVR::LSRWRd);
EXPAND(AVR::RORWRd);