const AVRRegisterInfo *TRI;
const TargetInstrInfo *TII;
- /// The register to be used for temporary storage.
- const Register SCRATCH_REGISTER = AVR::R0;
- /// The register that will always contain zero.
- const Register ZERO_REGISTER = AVR::R1;
-
bool expandMBB(Block &MBB);
bool expandMI(Block &MBB, BlockIt MBBI);
template <unsigned OP> bool expand(Block &MBB, BlockIt MBBI);
MachineInstr &MI = *MBBI;
Register DstLoReg, DstHiReg;
Register DstReg = MI.getOperand(0).getReg();
+ Register ZeroReg = MI.getOperand(2).getReg();
bool DstIsDead = MI.getOperand(0).isDead();
bool DstIsKill = MI.getOperand(1).isKill();
bool ImpIsDead = MI.getOperand(2).isDead();
buildMI(MBB, MBBI, AVR::SBCRdRr)
.addReg(DstHiReg, RegState::Define | getDeadRegState(DstIsDead))
.addReg(DstHiReg, getKillRegState(DstIsKill))
- .addReg(ZERO_REGISTER);
+ .addReg(ZeroReg);
if (ImpIsDead)
MISBCI->getOperand(3).setIsDead();
// SREG is always implicitly killed
// Store the SREG.
buildMI(MBB, MBBI, AVR::INRdA)
- .addReg(SCRATCH_REGISTER, RegState::Define)
+ .addReg(STI.getTmpRegister(), RegState::Define)
.addImm(STI.getIORegSREG());
// Disable exceptions.
// Restore the status reg.
buildMI(MBB, MBBI, AVR::OUTARr)
.addImm(STI.getIORegSREG())
- .addReg(SCRATCH_REGISTER);
+ .addReg(STI.getTmpRegister());
MI.eraseFromParent();
return true;
MachineInstr &MI = *MBBI;
unsigned OpShift, OpCarry;
Register DstReg = MI.getOperand(0).getReg();
+ Register ZeroReg = MI.getOperand(2).getReg();
bool DstIsDead = MI.getOperand(0).isDead();
bool DstIsKill = MI.getOperand(1).isKill();
OpShift = AVR::ADDRdRr;
auto MIB = buildMI(MBB, MBBI, OpCarry)
.addReg(DstReg, RegState::Define | getDeadRegState(DstIsDead))
.addReg(DstReg, getKillRegState(DstIsKill))
- .addReg(ZERO_REGISTER);
+ .addReg(ZeroReg);
MIB->getOperand(3).setIsDead(); // SREG is always dead
MIB->getOperand(4).setIsKill(); // SREG is always implicitly killed
TRI->splitReg(SrcReg, SrcLoReg, SrcHiReg);
buildMI(MBB, MBBI, AVR::INRdA)
- .addReg(AVR::R0, RegState::Define)
+ .addReg(STI.getTmpRegister(), RegState::Define)
.addImm(STI.getIORegSREG())
.setMIFlags(Flags);
buildMI(MBB, MBBI, AVR::OUTARr)
.addImm(STI.getIORegSREG())
- .addReg(AVR::R0, RegState::Kill)
+ .addReg(STI.getTmpRegister(), RegState::Kill)
.setMIFlags(Flags);
buildMI(MBB, MBBI, AVR::OUTARr)
// handlers before saving any other registers.
if (AFI->isInterruptOrSignalHandler()) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHRr))
- .addReg(AVR::R0, RegState::Kill)
+ .addReg(STI.getTmpRegister(), RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
- BuildMI(MBB, MBBI, DL, TII.get(AVR::INRdA), AVR::R0)
+ BuildMI(MBB, MBBI, DL, TII.get(AVR::INRdA), STI.getTmpRegister())
.addImm(STI.getIORegSREG())
.setMIFlag(MachineInstr::FrameSetup);
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHRr))
- .addReg(AVR::R0, RegState::Kill)
+ .addReg(STI.getTmpRegister(), RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
- if (!MRI.reg_empty(AVR::R1)) {
+ if (!MRI.reg_empty(STI.getZeroRegister())) {
BuildMI(MBB, MBBI, DL, TII.get(AVR::PUSHRr))
- .addReg(AVR::R1, RegState::Kill)
+ .addReg(STI.getZeroRegister(), 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)
+ .addReg(STI.getZeroRegister(), RegState::Define)
+ .addReg(STI.getZeroRegister(), RegState::Kill)
+ .addReg(STI.getZeroRegister(), RegState::Kill)
.setMIFlag(MachineInstr::FrameSetup);
}
}
// Emit special epilogue code to restore R1, R0 and SREG in interrupt/signal
// handlers at the very end of the function, just before reti.
if (AFI->isInterruptOrSignalHandler()) {
- if (!MRI.reg_empty(AVR::R1)) {
- BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R1);
+ if (!MRI.reg_empty(STI.getZeroRegister())) {
+ BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), STI.getZeroRegister());
}
- BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
+ BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), STI.getTmpRegister());
BuildMI(MBB, MBBI, DL, TII.get(AVR::OUTARr))
.addImm(STI.getIORegSREG())
- .addReg(AVR::R0, RegState::Kill);
- BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), AVR::R0);
+ .addReg(STI.getTmpRegister(), RegState::Kill);
+ BuildMI(MBB, MBBI, DL, TII.get(AVR::POPRd), STI.getTmpRegister());
}
}
MachinePointerInfo(SV));
}
-// Modify the existing ISD::INLINEASM node to add the implicit register r1.
+// Modify the existing ISD::INLINEASM node to add the implicit zero register.
SDValue AVRTargetLowering::LowerINLINEASM(SDValue Op, SelectionDAG &DAG) const {
- SDValue R1Reg = DAG.getRegister(AVR::R1, MVT::i8);
- if (Op.getOperand(Op.getNumOperands() - 1) == R1Reg ||
- Op.getOperand(Op.getNumOperands() - 2) == R1Reg) {
- // R1 has already been added. Don't add it again.
+ SDValue ZeroReg = DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8);
+ if (Op.getOperand(Op.getNumOperands() - 1) == ZeroReg ||
+ Op.getOperand(Op.getNumOperands() - 2) == ZeroReg) {
+ // Zero register has already been added. Don't add it again.
// If this isn't handled, we get called over and over again.
return Op;
}
// with some edits.
// Add the following operands at the end (but before the glue node, if it's
// there):
- // - The flags of the implicit R1 register operand.
- // - The implicit R1 register operand itself.
+ // - The flags of the implicit zero register operand.
+ // - The implicit zero register operand itself.
SDLoc dl(Op);
SmallVector<SDValue, 8> Ops;
SDNode *N = Op.getNode();
}
unsigned Flags = InlineAsm::getFlagWord(InlineAsm::Kind_RegUse, 1);
Ops.push_back(DAG.getTargetConstant(Flags, dl, MVT::i32));
- Ops.push_back(R1Reg);
+ Ops.push_back(ZeroReg);
if (Glue) {
Ops.push_back(Glue);
}
- // Replace the current INLINEASM node with a new one that has R1 as implicit
- // parameter.
+ // Replace the current INLINEASM node with a new one that has the zero
+ // register as implicit parameter.
SDValue New = DAG.getNode(N->getOpcode(), dl, N->getVTList(), Ops);
DAG.ReplaceAllUsesOfValueWith(Op, New);
DAG.ReplaceAllUsesOfValueWith(Op.getValue(1), New.getValue(1));
Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType()));
}
- // The R1 register must be passed as an implicit register so that R1 is
- // correctly zeroed in interrupts.
- Ops.push_back(DAG.getRegister(AVR::R1, MVT::i8));
+ // The zero register (usually R1) must be passed as an implicit register so
+ // that this register is correctly zeroed in interrupts.
+ Ops.push_back(DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8));
// Add a register mask operand representing the call-preserved registers.
const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo();
const AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
if (!AFI->isInterruptOrSignalHandler()) {
- // The return instruction has an implicit R1 operand: it must contain zero
- // on return.
- // This is not needed in interrupts however, where R1 is handled specially
- // (only pushed/popped when needed).
- RetOps.push_back(DAG.getRegister(AVR::R1, MVT::i8));
+ // The return instruction has an implicit zero register operand: it must
+ // contain zero on return.
+ // This is not needed in interrupts however, where the zero register is
+ // handled specially (only pushed/popped when needed).
+ RetOps.push_back(DAG.getRegister(Subtarget.getZeroRegister(), MVT::i8));
}
unsigned RetOpc =
unsigned Opc;
const TargetRegisterClass *RC;
bool HasRepeatedOperand = false;
+ bool HasZeroOperand = false;
MachineFunction *F = BB->getParent();
MachineRegisterInfo &RI = F->getRegInfo();
const TargetInstrInfo &TII = *Subtarget.getInstrInfo();
case AVR::Rol8:
Opc = AVR::ROLBRd;
RC = &AVR::GPR8RegClass;
+ HasZeroOperand = true;
break;
case AVR::Rol16:
Opc = AVR::ROLWRd;
auto ShiftMI = BuildMI(LoopBB, dl, TII.get(Opc), ShiftReg2).addReg(ShiftReg);
if (HasRepeatedOperand)
ShiftMI.addReg(ShiftReg);
+ if (HasZeroOperand)
+ ShiftMI.addReg(Subtarget.getZeroRegister());
// CheckBB:
// ShiftReg = phi [%SrcReg, BB], [%ShiftReg2, LoopBB]
return BB;
}
-// Insert a read from R1, which almost always contains the value 0.
+// Insert a read from the zero register.
MachineBasicBlock *
-AVRTargetLowering::insertCopyR1(MachineInstr &MI, MachineBasicBlock *BB) const {
+AVRTargetLowering::insertCopyZero(MachineInstr &MI,
+ MachineBasicBlock *BB) const {
const TargetInstrInfo &TII = *Subtarget.getInstrInfo();
MachineBasicBlock::iterator I(MI);
BuildMI(*BB, I, MI.getDebugLoc(), TII.get(AVR::COPY))
.add(MI.getOperand(0))
- .addReg(AVR::R1);
+ .addReg(Subtarget.getZeroRegister());
MI.eraseFromParent();
return BB;
}
MachineRegisterInfo &MRI = BB->getParent()->getRegInfo();
const TargetInstrInfo &TII = *Subtarget.getInstrInfo();
MachineBasicBlock::iterator I(MI);
- const Register SCRATCH_REGISTER = AVR::R0;
DebugLoc dl = MI.getDebugLoc();
// Example instruction sequence, for an atomic 8-bit add:
unsigned StoreOpcode = (Width == 8) ? AVR::STPtrRr : AVR::STWPtrRr;
// Disable interrupts.
- BuildMI(*BB, I, dl, TII.get(AVR::INRdA), SCRATCH_REGISTER)
+ BuildMI(*BB, I, dl, TII.get(AVR::INRdA), Subtarget.getTmpRegister())
.addImm(Subtarget.getIORegSREG());
BuildMI(*BB, I, dl, TII.get(AVR::BCLRs)).addImm(7);
// Restore interrupts.
BuildMI(*BB, I, dl, TII.get(AVR::OUTARr))
.addImm(Subtarget.getIORegSREG())
- .addReg(SCRATCH_REGISTER);
+ .addReg(Subtarget.getTmpRegister());
// Remove the pseudo instruction.
MI.eraseFromParent();
case AVR::MULRdRr:
case AVR::MULSRdRr:
return insertMul(MI, MBB);
- case AVR::CopyR1:
- return insertCopyR1(MI, MBB);
+ case AVR::CopyZero:
+ return insertCopyZero(MI, MBB);
case AVR::AtomicLoadAdd8:
return insertAtomicArithmeticOp(MI, MBB, AVR::ADDRdRr, 8);
case AVR::AtomicLoadAdd16:
break;
case 't': // Temporary register: r0.
if (VT == MVT::i8)
- return std::make_pair(unsigned(AVR::R0), &AVR::GPR8RegClass);
+ return std::make_pair(unsigned(Subtarget.getTmpRegister()),
+ &AVR::GPR8RegClass);
break;
case 'w': // Special upper register pairs: r24, r26, r28, r30.
if (VT == MVT::i8 || VT == MVT::i16)
private:
MachineBasicBlock *insertShift(MachineInstr &MI, MachineBasicBlock *BB) const;
MachineBasicBlock *insertMul(MachineInstr &MI, MachineBasicBlock *BB) const;
- MachineBasicBlock *insertCopyR1(MachineInstr &MI,
- MachineBasicBlock *BB) const;
+ MachineBasicBlock *insertCopyZero(MachineInstr &MI,
+ MachineBasicBlock *BB) const;
MachineBasicBlock *insertAtomicArithmeticOp(MachineInstr &MI,
MachineBasicBlock *BB,
unsigned Opcode, int Width) const;
// neg Rd+1
// neg Rd
// sbc Rd+1, r1
- let Uses = [R1] in
- def NEGWRd : Pseudo<(outs DREGS
- : $rd),
- (ins DREGS
- : $src),
+ let hasSideEffects=0 in
+ def NEGWRd : Pseudo<(outs DREGS:$rd),
+ (ins DREGS:$src, GPR8:$zero),
"negw\t$rd",
- [(set i16
- : $rd, (ineg i16
- : $src)),
- (implicit SREG)]>;
+ []>;
}
// TST Rd
def ASRWLoRd : Pseudo<(outs DREGS:$rd), (ins DREGS:$src), "asrwlo\t$rd",
[(set i16:$rd, (AVRasrlo i16:$src)), (implicit SREG)]>;
- let Uses = [R1] in
+ let hasSideEffects=0 in
def ROLBRd : Pseudo<(outs GPR8
: $rd),
- (ins GPR8
- : $src),
+ (ins GPR8:$src, GPR8:$zero),
"rolb\t$rd",
- [(set i8
- : $rd, (AVRrol i8
- : $src)),
- (implicit SREG)]>;
+ []>;
def RORBRd : Pseudo<(outs GPR8
: $rd),
: $src, i8
: $cnt))]>;
-// lowered to a copy from R1, which contains the value zero.
+// lowered to a copy from the zero register.
let usesCustomInserter=1 in
-def CopyR1 : Pseudo<(outs GPR8:$rd), (ins), "clrz\t$rd", [(set i8:$rd, 0)]>;
+def CopyZero : Pseudo<(outs GPR8:$rd), (ins), "clrz\t$rd", [(set i8:$rd, 0)]>;
//===----------------------------------------------------------------------===//
// Non-Instruction Patterns
: $src1, (imm8_neg_XFORM imm
: $src2))>;
+// Emit NEGWRd with an extra zero register operand.
+def : Pat<(ineg i16:$src),
+ (NEGWRd i16:$src, (CopyZero))>;
+
// Calls.
let Predicates = [HasJMPCALL] in {
def : Pat<(AVRcall(i16 tglobaladdr:$dst)), (CALLk tglobaladdr:$dst)>;
// a compare and branch, invalidating the contents of SREG set by the
// compare instruction because of the add/sub pairs. Conservatively save and
// restore SREG before and after each add/sub pair.
- BuildMI(MBB, II, dl, TII.get(AVR::INRdA), AVR::R0)
+ BuildMI(MBB, II, dl, TII.get(AVR::INRdA), STI.getTmpRegister())
.addImm(STI.getIORegSREG());
MachineInstr *New = BuildMI(MBB, II, dl, TII.get(AddOpc), AVR::R29R28)
// Restore SREG.
BuildMI(MBB, std::next(II), dl, TII.get(AVR::OUTARr))
.addImm(STI.getIORegSREG())
- .addReg(AVR::R0, RegState::Kill);
+ .addReg(STI.getTmpRegister(), RegState::Kill);
// No need to set SREG as dead here otherwise if the next instruction is a
// cond branch it will be using a dead register.
#include "AVRISelLowering.h"
#include "AVRInstrInfo.h"
#include "AVRSelectionDAGInfo.h"
+#include "MCTargetDesc/AVRMCTargetDesc.h"
#define GET_SUBTARGETINFO_HEADER
#include "AVRGenSubtargetInfo.inc"
int getRegTmpIndex() const { return hasTinyEncoding() ? 16 : 0; }
int getRegZeroIndex() const { return hasTinyEncoding() ? 17 : 1; }
+ Register getTmpRegister() const {
+ return hasTinyEncoding() ? AVR::R16 : AVR::R0;
+ }
+ Register getZeroRegister() const {
+ return hasTinyEncoding() ? AVR::R17 : AVR::R1;
+ }
+
private:
/// The ELF e_flags architecture.
unsigned ELFArch;
-; RUN: llc -mattr=avrtiny -O0 < %s -march=avr | FileCheck %s
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
+; RUN: llc -mcpu=atmega328p -O0 < %s -mtriple=avr | FileCheck --check-prefix=CHECK-MEGA %s
+; RUN: llc -mattr=avrtiny -O0 < %s -mtriple=avr | FileCheck %s
define i16 @reg_copy16(i16, i16 %a) {
-; CHECK-LABEL: reg_copy16
-; CHECK: mov r24, r22
-; CHECK: mov r25, r23
-
+; CHECK-MEGA-LABEL: reg_copy16:
+; CHECK-MEGA: ; %bb.0:
+; CHECK-MEGA-NEXT: movw r24, r22
+; CHECK-MEGA-NEXT: ret
+;
+; CHECK-LABEL: reg_copy16:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r24, r22
+; CHECK-NEXT: mov r25, r23
+; CHECK-NEXT: ret
ret i16 %a
}
+
+define i8 @return_zero() {
+; CHECK-MEGA-LABEL: return_zero:
+; CHECK-MEGA: ; %bb.0:
+; CHECK-MEGA-NEXT: mov r24, r1
+; CHECK-MEGA-NEXT: ret
+;
+; CHECK-LABEL: return_zero:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r24, r17
+; CHECK-NEXT: ret
+ ret i8 0
+}
+
+define i8 @atomic_load8(i8* %foo) {
+; CHECK-MEGA-LABEL: atomic_load8:
+; CHECK-MEGA: ; %bb.0:
+; CHECK-MEGA-NEXT: movw r26, r24
+; CHECK-MEGA-NEXT: in r0, 63
+; CHECK-MEGA-NEXT: cli
+; CHECK-MEGA-NEXT: ld r24, X
+; CHECK-MEGA-NEXT: out 63, r0
+; CHECK-MEGA-NEXT: ret
+;
+; CHECK-LABEL: atomic_load8:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: mov r26, r24
+; CHECK-NEXT: mov r27, r25
+; CHECK-NEXT: in r16, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: ld r24, X
+; CHECK-NEXT: out 63, r16
+; CHECK-NEXT: ret
+ %val = load atomic i8, i8* %foo unordered, align 1
+ ret i8 %val
+}
+
+define avr_signalcc void @signal_handler_with_asm() {
+; CHECK-MEGA-LABEL: signal_handler_with_asm:
+; CHECK-MEGA: ; %bb.0:
+; CHECK-MEGA-NEXT: push r0
+; CHECK-MEGA-NEXT: in r0, 63
+; CHECK-MEGA-NEXT: push r0
+; CHECK-MEGA-NEXT: push r1
+; CHECK-MEGA-NEXT: clr r1
+; CHECK-MEGA-NEXT: push r24
+; CHECK-MEGA-NEXT: ldi r24, 3
+; CHECK-MEGA-NEXT: ;APP
+; CHECK-MEGA-NEXT: mov r24, r24
+; CHECK-MEGA-NEXT: ;NO_APP
+; CHECK-MEGA-NEXT: pop r24
+; CHECK-MEGA-NEXT: pop r1
+; CHECK-MEGA-NEXT: pop r0
+; CHECK-MEGA-NEXT: out 63, r0
+; CHECK-MEGA-NEXT: pop r0
+; CHECK-MEGA-NEXT: reti
+;
+; CHECK-LABEL: signal_handler_with_asm:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: push r16
+; CHECK-NEXT: in r16, 63
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: clr r17
+; CHECK-NEXT: push r24
+; CHECK-NEXT: ldi r24, 3
+; CHECK-NEXT: ;APP
+; CHECK-NEXT: mov r24, r24
+; CHECK-NEXT: ;NO_APP
+; CHECK-NEXT: pop r24
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: out 63, r16
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: reti
+ call i8 asm sideeffect "mov $0, $1", "=r,r"(i8 3) nounwind
+ ret void
+}
+
+declare void @foo()
+
+define avr_signalcc void @signal_handler_with_call() {
+; CHECK-MEGA-LABEL: signal_handler_with_call:
+; CHECK-MEGA: ; %bb.0:
+; CHECK-MEGA-NEXT: push r0
+; CHECK-MEGA-NEXT: in r0, 63
+; CHECK-MEGA-NEXT: push r0
+; CHECK-MEGA-NEXT: push r1
+; CHECK-MEGA-NEXT: clr r1
+; CHECK-MEGA-NEXT: push r18
+; CHECK-MEGA-NEXT: push r19
+; CHECK-MEGA-NEXT: push r20
+; CHECK-MEGA-NEXT: push r21
+; CHECK-MEGA-NEXT: push r22
+; CHECK-MEGA-NEXT: push r23
+; CHECK-MEGA-NEXT: push r24
+; CHECK-MEGA-NEXT: push r25
+; CHECK-MEGA-NEXT: push r26
+; CHECK-MEGA-NEXT: push r27
+; CHECK-MEGA-NEXT: push r30
+; CHECK-MEGA-NEXT: push r31
+; CHECK-MEGA-NEXT: call foo
+; CHECK-MEGA-NEXT: pop r31
+; CHECK-MEGA-NEXT: pop r30
+; CHECK-MEGA-NEXT: pop r27
+; CHECK-MEGA-NEXT: pop r26
+; CHECK-MEGA-NEXT: pop r25
+; CHECK-MEGA-NEXT: pop r24
+; CHECK-MEGA-NEXT: pop r23
+; CHECK-MEGA-NEXT: pop r22
+; CHECK-MEGA-NEXT: pop r21
+; CHECK-MEGA-NEXT: pop r20
+; CHECK-MEGA-NEXT: pop r19
+; CHECK-MEGA-NEXT: pop r18
+; CHECK-MEGA-NEXT: pop r1
+; CHECK-MEGA-NEXT: pop r0
+; CHECK-MEGA-NEXT: out 63, r0
+; CHECK-MEGA-NEXT: pop r0
+; CHECK-MEGA-NEXT: reti
+;
+; CHECK-LABEL: signal_handler_with_call:
+; CHECK: ; %bb.0:
+; CHECK-NEXT: push r16
+; CHECK-NEXT: in r16, 63
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: clr r17
+; CHECK-NEXT: push r20
+; CHECK-NEXT: push r21
+; CHECK-NEXT: push r22
+; CHECK-NEXT: push r23
+; CHECK-NEXT: push r24
+; CHECK-NEXT: push r25
+; CHECK-NEXT: push r26
+; CHECK-NEXT: push r27
+; CHECK-NEXT: push r30
+; CHECK-NEXT: push r31
+; CHECK-NEXT: rcall foo
+; CHECK-NEXT: pop r31
+; CHECK-NEXT: pop r30
+; CHECK-NEXT: pop r27
+; CHECK-NEXT: pop r26
+; CHECK-NEXT: pop r25
+; CHECK-NEXT: pop r24
+; CHECK-NEXT: pop r23
+; CHECK-NEXT: pop r22
+; CHECK-NEXT: pop r21
+; CHECK-NEXT: pop r20
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: out 63, r16
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: reti
+ call void @foo()
+ ret void
+}
; CHECK: $r15 = NEGRd killed $r15, implicit-def dead $sreg
; CHECK-NEXT: $r14 = NEGRd $r14
; CHECK-NEXT: $r15 = SBCRdRr $r15, $r1, implicit-def $sreg, implicit killed $sreg
+ $r15r14 = NEGWRd $r15r14, $r1, implicit-def $sreg
- $r15r14 = NEGWRd $r15r14, implicit-def $sreg, implicit $r1
+ ; avrtiny variant
+ ; CHECK: $r15 = NEGRd killed $r15, implicit-def dead $sreg
+ ; CHECK-NEXT: $r14 = NEGRd $r14
+ ; CHECK-NEXT: $r15 = SBCRdRr $r15, $r17, implicit-def $sreg, implicit killed $sreg
+ $r15r14 = NEGWRd $r15r14, $r17, implicit-def $sreg
...
; CHECK: $r14 = ADDRdRr killed $r14, killed $r14, implicit-def $sreg
; CHECK-NEXT: $r14 = ADCRdRr $r14, $r1, implicit-def dead $sreg, implicit killed $sreg
+ $r14 = ROLBRd $r14, $r1, implicit-def $sreg
- $r14 = ROLBRd $r14, implicit-def $sreg, implicit $r1
+ ; avrtiny variant
+ ; CHECK: $r14 = ADDRdRr killed $r14, killed $r14, implicit-def $sreg
+ ; CHECK-NEXT: $r14 = ADCRdRr $r14, $r17, implicit-def dead $sreg, implicit killed $sreg
+ $r14 = ROLBRd $r14, $r17, implicit-def $sreg
...
; TINY-LABEL: interrupt_handler:
; TINY: ; %bb.0:
; TINY-NEXT: sei
-; TINY-NEXT: push r0
-; TINY-NEXT: in r0, 63
-; TINY-NEXT: push r0
-; TINY-NEXT: pop r0
-; TINY-NEXT: out 63, r0
-; TINY-NEXT: pop r0
+; TINY-NEXT: push r16
+; TINY-NEXT: in r16, 63
+; TINY-NEXT: push r16
+; TINY-NEXT: pop r16
+; TINY-NEXT: out 63, r16
+; TINY-NEXT: pop r16
; TINY-NEXT: reti
ret void
}
;
; TINY-LABEL: signal_handler:
; TINY: ; %bb.0:
-; TINY-NEXT: push r0
-; TINY-NEXT: in r0, 63
-; TINY-NEXT: push r0
-; TINY-NEXT: pop r0
-; TINY-NEXT: out 63, r0
-; TINY-NEXT: pop r0
+; TINY-NEXT: push r16
+; TINY-NEXT: in r16, 63
+; TINY-NEXT: push r16
+; TINY-NEXT: pop r16
+; TINY-NEXT: out 63, r16
+; TINY-NEXT: pop r16
; TINY-NEXT: reti
ret void
}