diff options
Diffstat (limited to 'llvm/lib/Target/AVR')
-rw-r--r-- | llvm/lib/Target/AVR/AVR.h | 2 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRAsmPrinter.cpp | 17 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 488 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRFrameLowering.cpp | 7 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRISelLowering.cpp | 178 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRISelLowering.h | 9 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRInstrInfo.td | 106 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRRegisterInfo.td | 27 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRShiftExpand.cpp | 147 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/AVRTargetMachine.cpp | 11 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp | 2 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp | 7 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/MCTargetDesc/AVRMCELFStreamer.cpp | 4 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp | 6 | ||||
-rw-r--r-- | llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h | 3 |
16 files changed, 838 insertions, 178 deletions
diff --git a/llvm/lib/Target/AVR/AVR.h b/llvm/lib/Target/AVR/AVR.h index f0746d73c95f..7332307c07a3 100644 --- a/llvm/lib/Target/AVR/AVR.h +++ b/llvm/lib/Target/AVR/AVR.h @@ -22,6 +22,7 @@ namespace llvm { class AVRTargetMachine; class FunctionPass; +Pass *createAVRShiftExpandPass(); FunctionPass *createAVRISelDag(AVRTargetMachine &TM, CodeGenOpt::Level OptLevel); FunctionPass *createAVRExpandPseudoPass(); @@ -30,6 +31,7 @@ FunctionPass *createAVRRelaxMemPass(); FunctionPass *createAVRDynAllocaSRPass(); FunctionPass *createAVRBranchSelectionPass(); +void initializeAVRShiftExpandPass(PassRegistry &); void initializeAVRExpandPseudoPass(PassRegistry&); void initializeAVRRelaxMemPass(PassRegistry&); diff --git a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp index 722eecdc16a1..e8a13c712210 100644 --- a/llvm/lib/Target/AVR/AVRAsmPrinter.cpp +++ b/llvm/lib/Target/AVR/AVRAsmPrinter.cpp @@ -15,6 +15,7 @@ #include "AVRMCInstLower.h" #include "AVRSubtarget.h" #include "MCTargetDesc/AVRInstPrinter.h" +#include "MCTargetDesc/AVRMCExpr.h" #include "TargetInfo/AVRTargetInfo.h" #include "llvm/CodeGen/AsmPrinter.h" @@ -53,6 +54,8 @@ public: void emitInstruction(const MachineInstr *MI) override; + const MCExpr *lowerConstant(const Constant *CV) override; + private: const MCRegisterInfo &MRI; }; @@ -176,6 +179,20 @@ void AVRAsmPrinter::emitInstruction(const MachineInstr *MI) { EmitToStreamer(*OutStreamer, I); } +const MCExpr *AVRAsmPrinter::lowerConstant(const Constant *CV) { + MCContext &Ctx = OutContext; + + if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) { + bool IsProgMem = GV->getAddressSpace() == AVR::ProgramMemory; + if (IsProgMem) { + const MCExpr *Expr = MCSymbolRefExpr::create(getSymbol(GV), Ctx); + return AVRMCExpr::create(AVRMCExpr::VK_AVR_PM, Expr, false, Ctx); + } + } + + return AsmPrinter::lowerConstant(CV); +} + } // end of namespace llvm extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAVRAsmPrinter() { diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp index a48d3d134bb5..f9f91f50c9d5 100644 --- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp +++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp @@ -90,6 +90,18 @@ private: Block &MBB, BlockIt MBBI); + /// Specific shift implementation. + bool expandLSLB7Rd(Block &MBB, BlockIt MBBI); + bool expandLSRB7Rd(Block &MBB, BlockIt MBBI); + bool expandASRB7Rd(Block &MBB, BlockIt MBBI); + bool expandLSLW4Rd(Block &MBB, BlockIt MBBI); + bool expandLSRW4Rd(Block &MBB, BlockIt MBBI); + bool expandLSLW8Rd(Block &MBB, BlockIt MBBI); + bool expandLSRW8Rd(Block &MBB, BlockIt MBBI); + bool expandASRW8Rd(Block &MBB, BlockIt MBBI); + bool expandLSLW12Rd(Block &MBB, BlockIt MBBI); + bool expandLSRW12Rd(Block &MBB, BlockIt MBBI); + /// Scavenges a free GPR8 register for use. Register scavengeGPR8(MachineInstr &MI); }; @@ -438,12 +450,12 @@ bool AVRExpandPseudo::expand<AVR::NEGWRd>(Block &MBB, BlockIt MBBI) { .addReg(DstLoReg, RegState::Define | getDeadRegState(DstIsDead)) .addReg(DstLoReg, getKillRegState(DstIsKill)); - // Do an extra SBCI. + // Do an extra SBC. auto MISBCI = - buildMI(MBB, MBBI, AVR::SBCIRdK) + buildMI(MBB, MBBI, AVR::SBCRdRr) .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) .addReg(DstHiReg, getKillRegState(DstIsKill)) - .addImm(0); + .addReg(ZERO_REGISTER); if (ImpIsDead) MISBCI->getOperand(3).setIsDead(); // SREG is always implicitly killed @@ -650,10 +662,10 @@ bool AVRExpandPseudo::expand<AVR::LDWRdPtr>(Block &MBB, BlockIt MBBI) { if (TmpReg) { // Move the high byte into the final destination. - buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); + 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).addReg(DstLoReg); + buildMI(MBB, MBBI, AVR::POPRd, DstLoReg); } MIBLO.setMemRefs(MI.memoperands()); @@ -767,10 +779,10 @@ bool AVRExpandPseudo::expand<AVR::LDDWRdPtrQ>(Block &MBB, BlockIt MBBI) { if (TmpReg) { // Move the high byte into the final destination. - buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); + 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).addReg(DstLoReg); + buildMI(MBB, MBBI, AVR::POPRd, DstLoReg); } MIBLO.setMemRefs(MI.memoperands()); @@ -815,10 +827,10 @@ bool AVRExpandPseudo::expand<AVR::LPMWRdZ>(Block &MBB, BlockIt MBBI) { if (TmpReg) { // Move the high byte into the final destination. - buildMI(MBB, MBBI, AVR::MOVRdRr).addReg(DstHiReg).addReg(TmpReg); + 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).addReg(DstLoReg); + buildMI(MBB, MBBI, AVR::POPRd, DstLoReg); } MIBLO.setMemRefs(MI.memoperands()); @@ -883,20 +895,24 @@ bool AVRExpandPseudo::expandAtomicArithmeticOp(unsigned Width, Block &MBB, BlockIt MBBI) { return expandAtomic(MBB, MBBI, [&](MachineInstr &MI) { - auto Op1 = MI.getOperand(0); - auto Op2 = MI.getOperand(1); + auto DstReg = MI.getOperand(0).getReg(); + auto PtrOp = MI.getOperand(1); + auto SrcReg = MI.getOperand(2).getReg(); unsigned LoadOpcode = (Width == 8) ? AVR::LDRdPtr : AVR::LDWRdPtr; unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr; + // FIXME: this returns the new value (after the operation), not the old + // value as the atomicrmw instruction is supposed to do! + // Create the load - buildMI(MBB, MBBI, LoadOpcode).add(Op1).add(Op2); + buildMI(MBB, MBBI, LoadOpcode, DstReg).addReg(PtrOp.getReg()); // Create the arithmetic op - buildMI(MBB, MBBI, ArithOpcode).add(Op1).add(Op1).add(Op2); + buildMI(MBB, MBBI, ArithOpcode, DstReg).addReg(DstReg).addReg(SrcReg); // Create the store - buildMI(MBB, MBBI, StoreOpcode).add(Op2).add(Op1); + buildMI(MBB, MBBI, StoreOpcode).add(PtrOp).addReg(DstReg); }); } @@ -1055,6 +1071,7 @@ bool AVRExpandPseudo::expand<AVR::STWPtrRr>(Block &MBB, BlockIt 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; @@ -1062,11 +1079,11 @@ bool AVRExpandPseudo::expand<AVR::STWPtrRr>(Block &MBB, BlockIt MBBI) { //:TODO: need to reverse this order like inw and stsw? auto MIBLO = buildMI(MBB, MBBI, OpLo) - .addReg(DstReg) + .addReg(DstReg, getUndefRegState(DstIsUndef)) .addReg(SrcLoReg, getKillRegState(SrcIsKill)); auto MIBHI = buildMI(MBB, MBBI, OpHi) - .addReg(DstReg) + .addReg(DstReg, getUndefRegState(DstIsUndef)) .addImm(1) .addReg(SrcHiReg, getKillRegState(SrcIsKill)); @@ -1328,42 +1345,20 @@ bool AVRExpandPseudo::expand<AVR::RORBRd>(Block &MBB, BlockIt MBBI) { // 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); + // bst r16, 0 + // ror r16 + // bld r16, 7 - // Shift in - buildMI(MBB, MBBI, OpShiftIn) - .addReg(SCRATCH_REGISTER, RegState::Define | getDeadRegState(true)) - .addReg(SCRATCH_REGISTER); + // Move the lowest bit from DstReg into the T bit + buildMI(MBB, MBBI, AVR::BST).addReg(DstReg).addImm(0); - // 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); + // Rotate to the right + buildMI(MBB, MBBI, AVR::RORRd, DstReg).addReg(DstReg); - // SREG is always implicitly killed - MIB->getOperand(2).setIsKill(); + // Move the T bit into the highest bit of DstReg. + buildMI(MBB, MBBI, AVR::BLD, DstReg).addReg(DstReg).addImm(7); MI.eraseFromParent(); return true; @@ -1402,6 +1397,149 @@ bool AVRExpandPseudo::expand<AVR::LSLWRd>(Block &MBB, BlockIt MBBI) { return true; } +bool AVRExpandPseudo::expandLSLW4Rd(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(3).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; +} + +bool AVRExpandPseudo::expandLSLW8Rd(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(3).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; +} + +bool AVRExpandPseudo::expandLSLW12Rd(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(3).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::LSLWNRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned Imm = MI.getOperand(2).getImm(); + switch (Imm) { + case 4: + return expandLSLW4Rd(MBB, MBBI); + case 8: + return expandLSLW8Rd(MBB, MBBI); + case 12: + return expandLSLW12Rd(MBB, MBBI); + default: + llvm_unreachable("unimplemented lslwn"); + return false; + } +} + template <> bool AVRExpandPseudo::expand<AVR::LSRWRd>(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; @@ -1433,6 +1571,149 @@ bool AVRExpandPseudo::expand<AVR::LSRWRd>(Block &MBB, BlockIt MBBI) { return true; } +bool AVRExpandPseudo::expandLSRW4Rd(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(3).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; +} + +bool AVRExpandPseudo::expandLSRW8Rd(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(3).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; +} + +bool AVRExpandPseudo::expandLSRW12Rd(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(3).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::LSRWNRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned Imm = MI.getOperand(2).getImm(); + switch (Imm) { + case 4: + return expandLSRW4Rd(MBB, MBBI); + case 8: + return expandLSRW8Rd(MBB, MBBI); + case 12: + return expandLSRW12Rd(MBB, MBBI); + default: + llvm_unreachable("unimplemented lsrwn"); + return false; + } +} + template <> bool AVRExpandPseudo::expand<AVR::RORWRd>(Block &MBB, BlockIt MBBI) { llvm_unreachable("RORW unimplemented"); @@ -1476,13 +1757,58 @@ bool AVRExpandPseudo::expand<AVR::ASRWRd>(Block &MBB, BlockIt MBBI) { return true; } +bool AVRExpandPseudo::expandASRW8Rd(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(3).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) { +bool AVRExpandPseudo::expand<AVR::ASRWNRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned Imm = MI.getOperand(2).getImm(); + switch (Imm) { + case 8: + return expandASRW8Rd(MBB, MBBI); + default: + llvm_unreachable("unimplemented asrwn"); + return false; + } +} + +bool AVRExpandPseudo::expandLSLB7Rd(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(); + bool ImpIsDead = MI.getOperand(3).isDead(); // ror r24 // clr r24 @@ -1490,7 +1816,8 @@ bool AVRExpandPseudo::expand<AVR::LSLB7Rd>(Block &MBB, BlockIt MBBI) { buildMI(MBB, MBBI, AVR::RORRd) .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) - .addReg(DstReg, getKillRegState(DstIsKill)); + .addReg(DstReg, getKillRegState(DstIsKill)) + ->getOperand(3).setIsUndef(true); buildMI(MBB, MBBI, AVR::EORRdRr) .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) @@ -1513,12 +1840,24 @@ bool AVRExpandPseudo::expand<AVR::LSLB7Rd>(Block &MBB, BlockIt MBBI) { } template <> -bool AVRExpandPseudo::expand<AVR::LSRB7Rd>(Block &MBB, BlockIt MBBI) { +bool AVRExpandPseudo::expand<AVR::LSLBNRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned Imm = MI.getOperand(2).getImm(); + switch (Imm) { + case 7: + return expandLSLB7Rd(MBB, MBBI); + default: + llvm_unreachable("unimplemented lslbn"); + return false; + } +} + +bool AVRExpandPseudo::expandLSRB7Rd(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(); + bool ImpIsDead = MI.getOperand(3).isDead(); // rol r24 // clr r24 @@ -1527,7 +1866,8 @@ bool AVRExpandPseudo::expand<AVR::LSRB7Rd>(Block &MBB, BlockIt MBBI) { buildMI(MBB, MBBI, AVR::ADCRdRr) .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) .addReg(DstReg, getKillRegState(DstIsKill)) - .addReg(DstReg, getKillRegState(DstIsKill)); + .addReg(DstReg, getKillRegState(DstIsKill)) + ->getOperand(4).setIsUndef(true); buildMI(MBB, MBBI, AVR::EORRdRr) .addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead)) @@ -1551,12 +1891,24 @@ bool AVRExpandPseudo::expand<AVR::LSRB7Rd>(Block &MBB, BlockIt MBBI) { } template <> -bool AVRExpandPseudo::expand<AVR::ASRB7Rd>(Block &MBB, BlockIt MBBI) { +bool AVRExpandPseudo::expand<AVR::LSRBNRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned Imm = MI.getOperand(2).getImm(); + switch (Imm) { + case 7: + return expandLSRB7Rd(MBB, MBBI); + default: + llvm_unreachable("unimplemented lsrbn"); + return false; + } +} + +bool AVRExpandPseudo::expandASRB7Rd(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(); + bool ImpIsDead = MI.getOperand(3).isDead(); // lsl r24 // sbc r24, r24 @@ -1581,6 +1933,19 @@ bool AVRExpandPseudo::expand<AVR::ASRB7Rd>(Block &MBB, BlockIt MBBI) { return true; } +template <> +bool AVRExpandPseudo::expand<AVR::ASRBNRd>(Block &MBB, BlockIt MBBI) { + MachineInstr &MI = *MBBI; + unsigned Imm = MI.getOperand(2).getImm(); + switch (Imm) { + case 7: + return expandASRB7Rd(MBB, MBBI); + default: + llvm_unreachable("unimplemented asrbn"); + return false; + } +} + template <> bool AVRExpandPseudo::expand<AVR::SEXT>(Block &MBB, BlockIt MBBI) { MachineInstr &MI = *MBBI; Register DstLoReg, DstHiReg; @@ -1666,8 +2031,8 @@ template <> bool AVRExpandPseudo::expand<AVR::ZEXT>(Block &MBB, BlockIt MBBI) { auto EOR = buildMI(MBB, MBBI, AVR::EORRdRr) .addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead)) - .addReg(DstHiReg, RegState::Kill) - .addReg(DstHiReg, RegState::Kill); + .addReg(DstHiReg, RegState::Kill | RegState::Undef) + .addReg(DstHiReg, RegState::Kill | RegState::Undef); if (ImpIsDead) EOR->getOperand(3).setIsDead(); @@ -1802,9 +2167,12 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) { EXPAND(AVR::RORWRd); EXPAND(AVR::ROLWRd); EXPAND(AVR::ASRWRd); - EXPAND(AVR::LSLB7Rd); - EXPAND(AVR::LSRB7Rd); - EXPAND(AVR::ASRB7Rd); + EXPAND(AVR::LSLWNRd); + EXPAND(AVR::LSRWNRd); + EXPAND(AVR::ASRWNRd); + EXPAND(AVR::LSLBNRd); + EXPAND(AVR::LSRBNRd); + EXPAND(AVR::ASRBNRd); EXPAND(AVR::SEXT); EXPAND(AVR::ZEXT); EXPAND(AVR::SPREAD); diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp index 757b41466c3f..89ed30e8bcdb 100644 --- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp +++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp @@ -83,6 +83,11 @@ void AVRFrameLowering::emitPrologue(MachineFunction &MF, .addReg(AVR::R0, RegState::Kill) .addReg(AVR::R0, RegState::Kill) .setMIFlag(MachineInstr::FrameSetup); + BuildMI(MBB, MBBI, DL, TII.get(AVR::EORRdRr)) + .addReg(AVR::R1, RegState::Define) + .addReg(AVR::R1, RegState::Kill) + .addReg(AVR::R1, RegState::Kill) + .setMIFlag(MachineInstr::FrameSetup); } // Early exit if the frame pointer is not needed in this function. @@ -362,7 +367,7 @@ MachineBasicBlock::iterator AVRFrameLowering::eliminateCallFramePseudoInstr( New->getOperand(3).setIsDead(); BuildMI(MBB, MI, DL, TII.get(AVR::SPWRITE), AVR::SP) - .addReg(AVR::R31R30, RegState::Kill); + .addReg(AVR::R31R30); // Make sure the remaining stack stores are converted to real store // instructions. diff --git a/llvm/lib/Target/AVR/AVRISelLowering.cpp b/llvm/lib/Target/AVR/AVRISelLowering.cpp index 3e7c2984655a..58a7aed91cdf 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.cpp +++ b/llvm/lib/Target/AVR/AVRISelLowering.cpp @@ -334,7 +334,7 @@ SDValue AVRTargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { llvm_unreachable("Invalid shift opcode"); } - // Optimize int8 shifts. + // Optimize int8/int16 shifts. if (VT.getSizeInBits() == 8) { if (Op.getOpcode() == ISD::SHL && 4 <= ShiftAmount && ShiftAmount < 7) { // Optimize LSL when 4 <= ShiftAmount <= 6. @@ -351,17 +351,71 @@ SDValue AVRTargetLowering::LowerShifts(SDValue Op, SelectionDAG &DAG) const { ShiftAmount -= 4; } else if (Op.getOpcode() == ISD::SHL && ShiftAmount == 7) { // Optimize LSL when ShiftAmount == 7. - Victim = DAG.getNode(AVRISD::LSL7, dl, VT, Victim); + Victim = DAG.getNode(AVRISD::LSLBN, dl, VT, Victim, + DAG.getConstant(7, dl, VT)); ShiftAmount = 0; } else if (Op.getOpcode() == ISD::SRL && ShiftAmount == 7) { // Optimize LSR when ShiftAmount == 7. - Victim = DAG.getNode(AVRISD::LSR7, dl, VT, Victim); + Victim = DAG.getNode(AVRISD::LSRBN, dl, VT, Victim, + DAG.getConstant(7, dl, VT)); ShiftAmount = 0; } else if (Op.getOpcode() == ISD::SRA && ShiftAmount == 7) { // Optimize ASR when ShiftAmount == 7. - Victim = DAG.getNode(AVRISD::ASR7, dl, VT, Victim); + Victim = DAG.getNode(AVRISD::ASRBN, dl, VT, Victim, + DAG.getConstant(7, dl, VT)); ShiftAmount = 0; } + } else if (VT.getSizeInBits() == 16) { + if (4 <= ShiftAmount && ShiftAmount < 8) + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(AVRISD::LSLWN, dl, VT, Victim, + DAG.getConstant(4, dl, VT)); + ShiftAmount -= 4; + break; + case ISD::SRL: + Victim = DAG.getNode(AVRISD::LSRWN, dl, VT, Victim, + DAG.getConstant(4, dl, VT)); + ShiftAmount -= 4; + break; + default: + break; + } + else if (8 <= ShiftAmount && ShiftAmount < 12) + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(AVRISD::LSLWN, dl, VT, Victim, + DAG.getConstant(8, dl, VT)); + ShiftAmount -= 8; + break; + case ISD::SRL: + Victim = DAG.getNode(AVRISD::LSRWN, dl, VT, Victim, + DAG.getConstant(8, dl, VT)); + ShiftAmount -= 8; + break; + case ISD::SRA: + Victim = DAG.getNode(AVRISD::ASRWN, dl, VT, Victim, + DAG.getConstant(8, dl, VT)); + ShiftAmount -= 8; + break; + default: + break; + } + else if (12 <= ShiftAmount) + switch (Op.getOpcode()) { + case ISD::SHL: + Victim = DAG.getNode(AVRISD::LSLWN, dl, VT, Victim, + DAG.getConstant(12, dl, VT)); + ShiftAmount -= 12; + break; + case ISD::SRL: + Victim = DAG.getNode(AVRISD::LSRWN, dl, VT, Victim, + DAG.getConstant(12, dl, VT)); + ShiftAmount -= 12; + break; + default: + break; + } } while (ShiftAmount--) { @@ -477,7 +531,7 @@ SDValue AVRTargetLowering::getAVRCmp(SDValue LHS, SDValue RHS, SDValue Cmp; - if (LHS.getSimpleValueType() == MVT::i16 && dyn_cast<ConstantSDNode>(RHS)) { + if (LHS.getSimpleValueType() == MVT::i16 && isa<ConstantSDNode>(RHS)) { // Generate a CPI/CPC pair if RHS is a 16-bit constant. SDValue LHSlo = DAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i8, LHS, DAG.getIntPtrConstant(0, DL)); @@ -1269,15 +1323,17 @@ SDValue AVRTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); } - // Second, stack arguments have to walked in reverse order by inserting - // chained stores, this ensures their order is not changed by the scheduler - // and that the push instruction sequence generated is correct, otherwise they - // can be freely intermixed. + // Second, stack arguments have to walked. + // Previously this code created chained stores but those chained stores appear + // to be unchained in the legalization phase. Therefore, do not attempt to + // chain them here. In fact, chaining them here somehow causes the first and + // second store to be reversed which is the exact opposite of the intended + // effect. if (HasStackArgs) { - for (AE = AI, AI = ArgLocs.size(); AI != AE; --AI) { - unsigned Loc = AI - 1; - CCValAssign &VA = ArgLocs[Loc]; - SDValue Arg = OutVals[Loc]; + SmallVector<SDValue, 8> MemOpChains; + for (; AI != AE; AI++) { + CCValAssign &VA = ArgLocs[AI]; + SDValue Arg = OutVals[AI]; assert(VA.isMemLoc()); @@ -1287,10 +1343,13 @@ SDValue AVRTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, DAG.getRegister(AVR::SP, getPointerTy(DAG.getDataLayout())), DAG.getIntPtrConstant(VA.getLocMemOffset() + 1, DL)); - Chain = + MemOpChains.push_back( DAG.getStore(Chain, DL, Arg, PtrOff, - MachinePointerInfo::getStack(MF, VA.getLocMemOffset())); + MachinePointerInfo::getStack(MF, VA.getLocMemOffset()))); } + + if (!MemOpChains.empty()) + Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); } // Build a sequence of copy-to-reg nodes chained together with token chain and @@ -1871,44 +1930,65 @@ std::pair<unsigned, const TargetRegisterClass *> AVRTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { - // We only support i8 and i16. - // - //:FIXME: remove this assert for now since it gets sometimes executed - // assert((VT == MVT::i16 || VT == MVT::i8) && "Wrong operand type."); - if (Constraint.size() == 1) { switch (Constraint[0]) { case 'a': // Simple upper registers r16..r23. - return std::make_pair(0U, &AVR::LD8loRegClass); + if (VT == MVT::i8) + return std::make_pair(0U, &AVR::LD8loRegClass); + else if (VT == MVT::i16) + return std::make_pair(0U, &AVR::DREGSLD8loRegClass); + break; case 'b': // Base pointer registers: y, z. - return std::make_pair(0U, &AVR::PTRDISPREGSRegClass); + if (VT == MVT::i8 || VT == MVT::i16) + return std::make_pair(0U, &AVR::PTRDISPREGSRegClass); + break; case 'd': // Upper registers r16..r31. - return std::make_pair(0U, &AVR::LD8RegClass); + if (VT == MVT::i8) + return std::make_pair(0U, &AVR::LD8RegClass); + else if (VT == MVT::i16) + return std::make_pair(0U, &AVR::DLDREGSRegClass); + break; case 'l': // Lower registers r0..r15. - return std::make_pair(0U, &AVR::GPR8loRegClass); + if (VT == MVT::i8) + return std::make_pair(0U, &AVR::GPR8loRegClass); + else if (VT == MVT::i16) + return std::make_pair(0U, &AVR::DREGSloRegClass); + break; case 'e': // Pointer register pairs: x, y, z. - return std::make_pair(0U, &AVR::PTRREGSRegClass); + if (VT == MVT::i8 || VT == MVT::i16) + return std::make_pair(0U, &AVR::PTRREGSRegClass); + break; case 'q': // Stack pointer register: SPH:SPL. return std::make_pair(0U, &AVR::GPRSPRegClass); case 'r': // Any register: r0..r31. if (VT == MVT::i8) return std::make_pair(0U, &AVR::GPR8RegClass); - - assert(VT == MVT::i16 && "inline asm constraint too large"); - return std::make_pair(0U, &AVR::DREGSRegClass); + else if (VT == MVT::i16) + return std::make_pair(0U, &AVR::DREGSRegClass); + break; case 't': // Temporary register: r0. - return std::make_pair(unsigned(AVR::R0), &AVR::GPR8RegClass); + if (VT == MVT::i8) + return std::make_pair(unsigned(AVR::R0), &AVR::GPR8RegClass); + break; case 'w': // Special upper register pairs: r24, r26, r28, r30. - return std::make_pair(0U, &AVR::IWREGSRegClass); + if (VT == MVT::i8 || VT == MVT::i16) + return std::make_pair(0U, &AVR::IWREGSRegClass); + break; case 'x': // Pointer register pair X: r27:r26. case 'X': - return std::make_pair(unsigned(AVR::R27R26), &AVR::PTRREGSRegClass); + if (VT == MVT::i8 || VT == MVT::i16) + return std::make_pair(unsigned(AVR::R27R26), &AVR::PTRREGSRegClass); + break; case 'y': // Pointer register pair Y: r29:r28. case 'Y': - return std::make_pair(unsigned(AVR::R29R28), &AVR::PTRREGSRegClass); + if (VT == MVT::i8 || VT == MVT::i16) + return std::make_pair(unsigned(AVR::R29R28), &AVR::PTRREGSRegClass); + break; case 'z': // Pointer register pair Z: r31:r30. case 'Z': - return std::make_pair(unsigned(AVR::R31R30), &AVR::PTRREGSRegClass); + if (VT == MVT::i8 || VT == MVT::i16) + return std::make_pair(unsigned(AVR::R31R30), &AVR::PTRREGSRegClass); + break; default: break; } @@ -2031,37 +2111,21 @@ Register AVRTargetLowering::getRegisterByName(const char *RegName, LLT VT, if (VT == LLT::scalar(8)) { Reg = StringSwitch<unsigned>(RegName) - .Case("r0", AVR::R0).Case("r1", AVR::R1).Case("r2", AVR::R2) - .Case("r3", AVR::R3).Case("r4", AVR::R4).Case("r5", AVR::R5) - .Case("r6", AVR::R6).Case("r7", AVR::R7).Case("r8", AVR::R8) - .Case("r9", AVR::R9).Case("r10", AVR::R10).Case("r11", AVR::R11) - .Case("r12", AVR::R12).Case("r13", AVR::R13).Case("r14", AVR::R14) - .Case("r15", AVR::R15).Case("r16", AVR::R16).Case("r17", AVR::R17) - .Case("r18", AVR::R18).Case("r19", AVR::R19).Case("r20", AVR::R20) - .Case("r21", AVR::R21).Case("r22", AVR::R22).Case("r23", AVR::R23) - .Case("r24", AVR::R24).Case("r25", AVR::R25).Case("r26", AVR::R26) - .Case("r27", AVR::R27).Case("r28", AVR::R28).Case("r29", AVR::R29) - .Case("r30", AVR::R30).Case("r31", AVR::R31) - .Case("X", AVR::R27R26).Case("Y", AVR::R29R28).Case("Z", AVR::R31R30) - .Default(0); + .Case("r0", AVR::R0) + .Case("r1", AVR::R1) + .Default(0); } else { Reg = StringSwitch<unsigned>(RegName) - .Case("r0", AVR::R1R0).Case("r2", AVR::R3R2) - .Case("r4", AVR::R5R4).Case("r6", AVR::R7R6) - .Case("r8", AVR::R9R8).Case("r10", AVR::R11R10) - .Case("r12", AVR::R13R12).Case("r14", AVR::R15R14) - .Case("r16", AVR::R17R16).Case("r18", AVR::R19R18) - .Case("r20", AVR::R21R20).Case("r22", AVR::R23R22) - .Case("r24", AVR::R25R24).Case("r26", AVR::R27R26) - .Case("r28", AVR::R29R28).Case("r30", AVR::R31R30) - .Case("X", AVR::R27R26).Case("Y", AVR::R29R28).Case("Z", AVR::R31R30) - .Default(0); + .Case("r0", AVR::R1R0) + .Case("sp", AVR::SP) + .Default(0); } if (Reg) return Reg; - report_fatal_error("Invalid register name global variable"); + report_fatal_error( + Twine("Invalid register name \"" + StringRef(RegName) + "\".")); } } // end of namespace llvm diff --git a/llvm/lib/Target/AVR/AVRISelLowering.h b/llvm/lib/Target/AVR/AVRISelLowering.h index 7aff4159211b..8130cf045fa8 100644 --- a/llvm/lib/Target/AVR/AVRISelLowering.h +++ b/llvm/lib/Target/AVR/AVRISelLowering.h @@ -36,11 +36,14 @@ enum NodeType { /// TargetExternalSymbol, and TargetGlobalAddress. WRAPPER, LSL, ///< Logical shift left. + LSLBN, ///< Byte logical shift left N bits. + LSLWN, ///< Word logical shift left N bits. LSR, ///< Logical shift right. + LSRBN, ///< Byte logical shift right N bits. + LSRWN, ///< Word logical shift right N bits. ASR, ///< Arithmetic shift right. - LSL7, ///< Logical shift left 7 bits. - LSR7, ///< Logical shift right 7 bits. - ASR7, ///< Arithmetic shift right 7 bits. + ASRBN, ///< Byte arithmetic shift right N bits. + ASRWN, ///< Word arithmetic shift right N bits. ROR, ///< Bit rotate right. ROL, ///< Bit rotate left. LSLLOOP, ///< A loop of single logical shift left instructions. diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td index 9f7c16fc96d2..c7c9656d3bfb 100644 --- a/llvm/lib/Target/AVR/AVRInstrInfo.td +++ b/llvm/lib/Target/AVR/AVRInstrInfo.td @@ -59,9 +59,12 @@ def AVRlsr : SDNode<"AVRISD::LSR", SDTIntUnaryOp>; def AVRrol : SDNode<"AVRISD::ROL", SDTIntUnaryOp>; def AVRror : SDNode<"AVRISD::ROR", SDTIntUnaryOp>; def AVRasr : SDNode<"AVRISD::ASR", SDTIntUnaryOp>; -def AVRlsl7 : SDNode<"AVRISD::LSL7", SDTIntUnaryOp>; -def AVRlsr7 : SDNode<"AVRISD::LSR7", SDTIntUnaryOp>; -def AVRasr7 : SDNode<"AVRISD::ASR7", SDTIntUnaryOp>; +def AVRlslbn : SDNode<"AVRISD::LSLBN", SDTIntBinOp>; +def AVRlsrbn : SDNode<"AVRISD::LSRBN", SDTIntBinOp>; +def AVRasrbn : SDNode<"AVRISD::ASRBN", SDTIntBinOp>; +def AVRlslwn : SDNode<"AVRISD::LSLWN", SDTIntBinOp>; +def AVRlsrwn : SDNode<"AVRISD::LSRWN", SDTIntBinOp>; +def AVRasrwn : SDNode<"AVRISD::ASRWN", SDTIntBinOp>; // Pseudo shift nodes for non-constant shift amounts. def AVRlslLoop : SDNode<"AVRISD::LSLLOOP", SDTIntShiftOp>; @@ -357,11 +360,13 @@ Uses = [SP] in "#ADJCALLSTACKDOWN", [(AVRcallseq_start timm:$amt, timm:$amt2)]>; - // R31R30 is used to update SP, since it is a scratch reg and this instruction - // is placed after the function call then R31R30 should be always free. - //let Defs = [R31R30], - //Uses = [R31R30] in - //:TODO: if we enable this, the pseudo is killed because it looks dead + // R31R30 is used to update SP. It is normally free because it is a + // call-clobbered register but it is necessary to set it as a def as the + // register allocator might use it in rare cases (for rematerialization, it + // seems). hasSideEffects needs to be set to true so this instruction isn't + // considered dead. + let Defs = [R31R30], + hasSideEffects=1 in def ADJCALLSTACKUP : Pseudo<(outs), (ins i16imm:$amt1, i16imm:$amt2), "#ADJCALLSTACKUP", @@ -750,7 +755,7 @@ Defs = [SREG] in // Expands to: // neg Rd+1 // neg Rd - // sbci Rd+1, 0 + // sbc Rd+1, r1 def NEGWRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "negw\t$rd", @@ -1292,6 +1297,7 @@ class AtomicStore<PatFrag Op, RegisterClass DRC, Pseudo<(outs), (ins PTRRC:$rd, DRC:$rr), "atomic_op", [(Op i16:$rd, DRC:$rr)]>; +let Constraints = "@earlyclobber $rd" in class AtomicLoadOp<PatFrag Op, RegisterClass DRC, RegisterClass PTRRC> : Pseudo<(outs DRC:$rd), (ins PTRRC:$rr, DRC:$operand), @@ -1669,10 +1675,17 @@ Defs = [SREG] in "lslw\t$rd", [(set i16:$rd, (AVRlsl i16:$src)), (implicit SREG)]>; - def LSLB7Rd : Pseudo<(outs GPR8:$rd), - (ins GPR8:$src), - "lslb7\t$rd", - [(set i8:$rd, (AVRlsl7 i8:$src)), (implicit SREG)]>; + def LSLWNRd : Pseudo<(outs DLDREGS:$rd), + (ins DREGS:$src, imm16:$bits), + "lslwn\t$rd, $bits", + [(set i16:$rd, (AVRlslwn i16:$src, imm:$bits)), + (implicit SREG)]>; + + def LSLBNRd : Pseudo<(outs LD8:$rd), + (ins GPR8:$src, imm_ldi8:$bits), + "lslbn\t$rd, $bits", + [(set i8:$rd, (AVRlslbn i8:$src, imm:$bits)), + (implicit SREG)]>; def LSRRd : FRd<0b1001, 0b0100110, @@ -1681,16 +1694,23 @@ Defs = [SREG] in "lsr\t$rd", [(set i8:$rd, (AVRlsr i8:$src)), (implicit SREG)]>; - def LSRB7Rd : Pseudo<(outs GPR8:$rd), - (ins GPR8:$src), - "lsrb7\t$rd", - [(set i8:$rd, (AVRlsr7 i8:$src)), (implicit SREG)]>; - def LSRWRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "lsrw\t$rd", [(set i16:$rd, (AVRlsr i16:$src)), (implicit SREG)]>; + def LSRWNRd : Pseudo<(outs DLDREGS:$rd), + (ins DREGS:$src, imm16:$bits), + "lsrwn\t$rd, $bits", + [(set i16:$rd, (AVRlsrwn i16:$src, imm:$bits)), + (implicit SREG)]>; + + def LSRBNRd : Pseudo<(outs LD8:$rd), + (ins GPR8:$src, imm_ldi8:$bits), + "lsrbn\t$rd, $bits", + [(set i8:$rd, (AVRlsrbn i8:$src, imm:$bits)), + (implicit SREG)]>; + def ASRRd : FRd<0b1001, 0b0100101, (outs GPR8:$rd), @@ -1698,30 +1718,36 @@ Defs = [SREG] in "asr\t$rd", [(set i8:$rd, (AVRasr i8:$src)), (implicit SREG)]>; - def ASRB7Rd : Pseudo<(outs GPR8:$rd), - (ins GPR8:$src), - "asrb7\t$rd", - [(set i8:$rd, (AVRasr7 i8:$src)), (implicit SREG)]>; + def ASRWNRd : Pseudo<(outs DLDREGS:$rd), + (ins DREGS:$src, imm16:$bits), + "asrwn\t$rd, $bits", + [(set i16:$rd, (AVRasrwn i16:$src, imm:$bits)), + (implicit SREG)]>; + + def ASRBNRd : Pseudo<(outs LD8:$rd), + (ins GPR8:$src, imm_ldi8:$bits), + "asrbn\t$rd, $bits", + [(set i8:$rd, (AVRasrbn i8:$src, imm:$bits)), + (implicit SREG)]>; def ASRWRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "asrw\t$rd", [(set i16:$rd, (AVRasr i16:$src)), (implicit SREG)]>; + def ROLBRd : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "rolb\t$rd", + [(set i8:$rd, (AVRrol i8:$src)), (implicit SREG)]>; + + def RORBRd : Pseudo<(outs GPR8:$rd), + (ins GPR8:$src), + "rorb\t$rd", + [(set i8:$rd, (AVRror i8:$src)), (implicit SREG)]>; + // Bit rotate operations. let Uses = [SREG] in { - // 8-bit ROL is an alias of ADC Rd, Rd - - def ROLBRd : Pseudo<(outs GPR8:$rd), - (ins GPR8:$src), - "rolb\t$rd", - [(set i8:$rd, (AVRrol i8:$src)), (implicit SREG)]>; - - def RORBRd : Pseudo<(outs GPR8:$rd), - (ins GPR8:$src), - "rorb\t$rd", - [(set i8:$rd, (AVRror i8:$src)), (implicit SREG)]>; def ROLWRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), @@ -1777,10 +1803,11 @@ def BST : FRdB<0b01, "bst\t$rd, $b", []>; -let Uses = [SREG] in +let Constraints = "$src = $rd", +Uses = [SREG] in def BLD : FRdB<0b00, - (outs), - (ins GPR8:$rd, i8imm:$b), + (outs GPR8:$rd), + (ins GPR8:$src, i8imm:$b), "bld\t$rd, $b", []>; @@ -2123,12 +2150,7 @@ def : Pat<(store i16:$src, (i16 (AVRWrapper tglobaladdr:$dst))), def : Pat<(i16 (AVRWrapper tblockaddress:$dst)), (LDIWRdK tblockaddress:$dst)>; -// hi-reg truncation : trunc(int16 >> 8) -//:FIXME: i think it's better to emit an extract subreg node in the DAG than -// all this mess once we get optimal shift code -// lol... I think so, too. [@agnat] -def : Pat<(i8 (trunc (AVRlsr (AVRlsr (AVRlsr (AVRlsr (AVRlsr (AVRlsr (AVRlsr - (AVRlsr DREGS:$src)))))))))), +def : Pat<(i8 (trunc (AVRlsrwn DLDREGS:$src, (i16 8)))), (EXTRACT_SUBREG DREGS:$src, sub_hi)>; // :FIXME: DAGCombiner produces an shl node after legalization from these seq: diff --git a/llvm/lib/Target/AVR/AVRRegisterInfo.td b/llvm/lib/Target/AVR/AVRRegisterInfo.td index ab5d02356c9d..1948fcbaf75a 100644 --- a/llvm/lib/Target/AVR/AVRRegisterInfo.td +++ b/llvm/lib/Target/AVR/AVRRegisterInfo.td @@ -67,12 +67,12 @@ def R22 : AVRReg<22, "r22">, DwarfRegNum<[22]>; def R23 : AVRReg<23, "r23">, DwarfRegNum<[23]>; def R24 : AVRReg<24, "r24">, DwarfRegNum<[24]>; def R25 : AVRReg<25, "r25">, DwarfRegNum<[25]>; -def R26 : AVRReg<26, "r26">, DwarfRegNum<[26]>; -def R27 : AVRReg<27, "r27">, DwarfRegNum<[27]>; -def R28 : AVRReg<28, "r28">, DwarfRegNum<[28]>; -def R29 : AVRReg<29, "r29">, DwarfRegNum<[29]>; -def R30 : AVRReg<30, "r30">, DwarfRegNum<[30]>; -def R31 : AVRReg<31, "r31">, DwarfRegNum<[31]>; +def R26 : AVRReg<26, "r26", [], ["xl"]>, DwarfRegNum<[26]>; +def R27 : AVRReg<27, "r27", [], ["xh"]>, DwarfRegNum<[27]>; +def R28 : AVRReg<28, "r28", [], ["yl"]>, DwarfRegNum<[28]>; +def R29 : AVRReg<29, "r29", [], ["yh"]>, DwarfRegNum<[29]>; +def R30 : AVRReg<30, "r30", [], ["zl"]>, DwarfRegNum<[30]>; +def R31 : AVRReg<31, "r31", [], ["zh"]>, DwarfRegNum<[31]>; def SPL : AVRReg<32, "SPL">, DwarfRegNum<[32]>; def SPH : AVRReg<33, "SPH">, DwarfRegNum<[33]>; @@ -171,6 +171,21 @@ def DREGS : RegisterClass<"AVR", [i16], 8, R14R13, R12R11, R10R9 )>; +// Lower 16-bit pair registers in R0..R15, only used in inline assembly. +def DREGSlo : RegisterClass<"AVR", [i16], 8, + ( + add R15R14, R13R12, R11R10, R9R8, R7R6, R5R4, R3R2, R1R0 + )>; + +// Lower 16-bit pair registers in r16..r23, only used in inline assembly. +def DREGSLD8lo : RegisterClass<"AVR", [i16], 8, + ( + // Return value and arguments. + add R19R18, R21R20, R23R22, + // Callee saved registers. + R17R16 + )>; + // 16-bit pair register class for movw def DREGSMOVW : RegisterClass<"AVR", [i16], 8, ( diff --git a/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp b/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp index 6be901743e82..7d2d19de7578 100644 --- a/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp +++ b/llvm/lib/Target/AVR/AVRRelaxMemOperations.cpp @@ -113,7 +113,7 @@ bool AVRRelaxMem::relax<AVR::STDWPtrQRr>(Block &MBB, BlockIt MBBI) { // Pop the original state of the pointer register. buildMI(MBB, MBBI, AVR::POPWRd) - .addReg(Ptr.getReg(), getKillRegState(Ptr.isKill())); + .addDef(Ptr.getReg(), getKillRegState(Ptr.isKill())); MI.removeFromParent(); } diff --git a/llvm/lib/Target/AVR/AVRShiftExpand.cpp b/llvm/lib/Target/AVR/AVRShiftExpand.cpp new file mode 100644 index 000000000000..b7dcd860467d --- /dev/null +++ b/llvm/lib/Target/AVR/AVRShiftExpand.cpp @@ -0,0 +1,147 @@ +//===- AVRShift.cpp - Shift Expansion Pass --------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +// +/// \file +/// Expand 32-bit shift instructions (shl, lshr, ashr) to inline loops, just +/// like avr-gcc. This must be done in IR because otherwise the type legalizer +/// will turn 32-bit shifts into (non-existing) library calls such as __ashlsi3. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" + +using namespace llvm; + +namespace { + +class AVRShiftExpand : public FunctionPass { +public: + static char ID; + + AVRShiftExpand() : FunctionPass(ID) {} + + bool runOnFunction(Function &F) override; + + StringRef getPassName() const override { return "AVR Shift Expansion"; } + +private: + void expand(BinaryOperator *BI); +}; + +} // end of anonymous namespace + +char AVRShiftExpand::ID = 0; + +INITIALIZE_PASS(AVRShiftExpand, "avr-shift-expand", "AVR Shift Expansion", + false, false) + +Pass *llvm::createAVRShiftExpandPass() { return new AVRShiftExpand(); } + +bool AVRShiftExpand::runOnFunction(Function &F) { + SmallVector<BinaryOperator *, 1> ShiftInsts; + auto &Ctx = F.getContext(); + for (Instruction &I : instructions(F)) { + if (!I.isShift()) + // Only expand shift instructions (shl, lshr, ashr). + continue; + if (I.getType() != Type::getInt32Ty(Ctx)) + // Only expand plain i32 types. + continue; + if (isa<ConstantInt>(I.getOperand(1))) + // Only expand when the shift amount is not known. + // Known shift amounts are (currently) better expanded inline. + continue; + ShiftInsts.push_back(cast<BinaryOperator>(&I)); + } + + // The expanding itself needs to be done separately as expand() will remove + // these instructions. Removing instructions while iterating over a basic + // block is not a great idea. + for (auto *I : ShiftInsts) { + expand(I); + } + + // Return whether this function expanded any shift instructions. + return ShiftInsts.size() > 0; +} + +void AVRShiftExpand::expand(BinaryOperator *BI) { + auto &Ctx = BI->getContext(); + IRBuilder<> Builder(BI); + Type *Int32Ty = Type::getInt32Ty(Ctx); + Type *Int8Ty = Type::getInt8Ty(Ctx); + Value *Int8Zero = ConstantInt::get(Int8Ty, 0); + + // Split the current basic block at the point of the existing shift + // instruction and insert a new basic block for the loop. + BasicBlock *BB = BI->getParent(); + Function *F = BB->getParent(); + BasicBlock *EndBB = BB->splitBasicBlock(BI, "shift.done"); + BasicBlock *LoopBB = BasicBlock::Create(Ctx, "shift.loop", F, EndBB); + + // Truncate the shift amount to i8, which is trivially lowered to a single + // AVR register. + Builder.SetInsertPoint(&BB->back()); + Value *ShiftAmount = Builder.CreateTrunc(BI->getOperand(1), Int8Ty); + + // Replace the unconditional branch that splitBasicBlock created with a + // conditional branch. + Value *Cmp1 = Builder.CreateICmpEQ(ShiftAmount, Int8Zero); + Builder.CreateCondBr(Cmp1, EndBB, LoopBB); + BB->back().eraseFromParent(); + + // Create the loop body starting with PHI nodes. + Builder.SetInsertPoint(LoopBB); + PHINode *ShiftAmountPHI = Builder.CreatePHI(Int8Ty, 2); + ShiftAmountPHI->addIncoming(ShiftAmount, BB); + PHINode *ValuePHI = Builder.CreatePHI(Int32Ty, 2); + ValuePHI->addIncoming(BI->getOperand(0), BB); + + // Subtract the shift amount by one, as we're shifting one this loop + // iteration. + Value *ShiftAmountSub = + Builder.CreateSub(ShiftAmountPHI, ConstantInt::get(Int8Ty, 1)); + ShiftAmountPHI->addIncoming(ShiftAmountSub, LoopBB); + + // Emit the actual shift instruction. The difference is that this shift + // instruction has a constant shift amount, which can be emitted inline + // without a library call. + Value *ValueShifted; + switch (BI->getOpcode()) { + case Instruction::Shl: + ValueShifted = Builder.CreateShl(ValuePHI, ConstantInt::get(Int32Ty, 1)); + break; + case Instruction::LShr: + ValueShifted = Builder.CreateLShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); + break; + case Instruction::AShr: + ValueShifted = Builder.CreateAShr(ValuePHI, ConstantInt::get(Int32Ty, 1)); + break; + default: + llvm_unreachable("asked to expand an instruction that is not a shift"); + } + ValuePHI->addIncoming(ValueShifted, LoopBB); + + // Branch to either the loop again (if there is more to shift) or to the + // basic block after the loop (if all bits are shifted). + Value *Cmp2 = Builder.CreateICmpEQ(ShiftAmountSub, Int8Zero); + Builder.CreateCondBr(Cmp2, EndBB, LoopBB); + + // Collect the resulting value. This is necessary in the IR but won't produce + // any actual instructions. + Builder.SetInsertPoint(BI); + PHINode *Result = Builder.CreatePHI(Int32Ty, 2); + Result->addIncoming(BI->getOperand(0), BB); + Result->addIncoming(ValueShifted, LoopBB); + + // Replace the original shift instruction. + BI->replaceAllUsesWith(Result); + BI->eraseFromParent(); +} diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/llvm/lib/Target/AVR/AVRTargetMachine.cpp index 0fa8623e2fb7..5be4260ce035 100644 --- a/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -65,6 +65,7 @@ public: return getTM<AVRTargetMachine>(); } + void addIRPasses() override; bool addInstSelector() override; void addPreSched2() override; void addPreEmitPass() override; @@ -76,6 +77,15 @@ TargetPassConfig *AVRTargetMachine::createPassConfig(PassManagerBase &PM) { return new AVRPassConfig(*this, PM); } +void AVRPassConfig::addIRPasses() { + // Expand instructions like + // %result = shl i32 %n, %amount + // to a loop so that library calls are avoided. + addPass(createAVRShiftExpandPass()); + + TargetPassConfig::addIRPasses(); +} + extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAVRTarget() { // Register the target. RegisterTargetMachine<AVRTargetMachine> X(getTheAVRTarget()); @@ -83,6 +93,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAVRTarget() { auto &PR = *PassRegistry::getPassRegistry(); initializeAVRExpandPseudoPass(PR); initializeAVRRelaxMemPass(PR); + initializeAVRShiftExpandPass(PR); } const AVRSubtarget *AVRTargetMachine::getSubtargetImpl() const { diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp index 1c69fea5962d..bedf68db08ca 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRELFObjectWriter.cpp @@ -7,6 +7,7 @@ //===----------------------------------------------------------------------===// #include "MCTargetDesc/AVRFixupKinds.h" +#include "MCTargetDesc/AVRMCExpr.h" #include "MCTargetDesc/AVRMCTargetDesc.h" #include "llvm/MC/MCAssembler.h" @@ -72,6 +73,7 @@ unsigned AVRELFObjectWriter::getRelocType(MCContext &Ctx, case MCSymbolRefExpr::VK_None: return ELF::R_AVR_16; case MCSymbolRefExpr::VK_AVR_NONE: + case MCSymbolRefExpr::VK_AVR_PM: return ELF::R_AVR_16_PM; case MCSymbolRefExpr::VK_AVR_DIFF16: return ELF::R_AVR_DIFF16; diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp index db995e247562..50872d6d7a92 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCCodeEmitter.cpp @@ -254,11 +254,8 @@ unsigned AVRMCCodeEmitter::getMachineOpValue(const MCInst &MI, if (MO.isReg()) return Ctx.getRegisterInfo()->getEncodingValue(MO.getReg()); if (MO.isImm()) return static_cast<unsigned>(MO.getImm()); - if (MO.isFPImm()) - return static_cast<unsigned>(APFloat(MO.getFPImm()) - .bitcastToAPInt() - .getHiBits(32) - .getLimitedValue()); + if (MO.isDFPImm()) + return static_cast<unsigned>(bit_cast<double>(MO.getDFPImm())); // MO must be an Expr. assert(MO.isExpr()); diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCELFStreamer.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCELFStreamer.cpp index 77b49931843b..0743344bc1ed 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCELFStreamer.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCELFStreamer.cpp @@ -10,14 +10,14 @@ // instructions on to the real streamer. // //===----------------------------------------------------------------------===// -#define DEBUG_TYPE "avrmcelfstreamer" - #include "MCTargetDesc/AVRMCELFStreamer.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/MCObjectWriter.h" +#define DEBUG_TYPE "avrmcelfstreamer" + using namespace llvm; void AVRMCELFStreamer::emitValueForModiferKind( diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp index 9eff554a082b..a4f8787e5667 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.cpp @@ -26,6 +26,7 @@ const struct ModifierEntry { {"hh8", AVRMCExpr::VK_AVR_HH8}, // synonym with hlo8 {"hlo8", AVRMCExpr::VK_AVR_HH8}, {"hhi8", AVRMCExpr::VK_AVR_HHI8}, + {"pm", AVRMCExpr::VK_AVR_PM}, {"pm_lo8", AVRMCExpr::VK_AVR_PM_LO8}, {"pm_hi8", AVRMCExpr::VK_AVR_PM_HI8}, {"pm_hh8", AVRMCExpr::VK_AVR_PM_HH8}, @@ -87,6 +88,9 @@ bool AVRMCExpr::evaluateAsRelocatableImpl(MCValue &Result, MCSymbolRefExpr::VariantKind Modifier = Sym->getKind(); if (Modifier != MCSymbolRefExpr::VK_None) return false; + if (Kind == VK_AVR_PM) { + Modifier = MCSymbolRefExpr::VK_AVR_PM; + } Sym = MCSymbolRefExpr::create(&Sym->getSymbol(), Modifier, Context); Result = MCValue::get(Sym, Value.getSymB(), Value.getConstant()); @@ -131,6 +135,7 @@ int64_t AVRMCExpr::evaluateAsInt64(int64_t Value) const { Value &= 0xff0000; Value >>= 16; break; + case AVRMCExpr::VK_AVR_PM: case AVRMCExpr::VK_AVR_GS: Value >>= 1; // Program memory addresses must always be shifted by one. break; @@ -167,6 +172,7 @@ AVR::Fixups AVRMCExpr::getFixupKind() const { case VK_AVR_PM_HH8: Kind = isNegated() ? AVR::fixup_hh8_ldi_pm_neg : AVR::fixup_hh8_ldi_pm; break; + case VK_AVR_PM: case VK_AVR_GS: Kind = AVR::fixup_16_pm; break; diff --git a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h index 3b696bab1715..e35385ebd90a 100644 --- a/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h +++ b/llvm/lib/Target/AVR/MCTargetDesc/AVRMCExpr.h @@ -20,13 +20,14 @@ class AVRMCExpr : public MCTargetExpr { public: /// Specifies the type of an expression. enum VariantKind { - VK_AVR_None, + VK_AVR_None = 0, VK_AVR_HI8, ///< Corresponds to `hi8()`. VK_AVR_LO8, ///< Corresponds to `lo8()`. VK_AVR_HH8, ///< Corresponds to `hlo8() and hh8()`. VK_AVR_HHI8, ///< Corresponds to `hhi8()`. + VK_AVR_PM, ///< Corresponds to `pm()`, reference to program memory. VK_AVR_PM_LO8, ///< Corresponds to `pm_lo8()`. VK_AVR_PM_HI8, ///< Corresponds to `pm_hi8()`. VK_AVR_PM_HH8, ///< Corresponds to `pm_hh8()`. |