[llvm] [AVR] Support stack realignment (PR #187129)
Patryk Wychowaniec via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 8 13:47:40 PDT 2026
https://github.com/Patryk27 updated https://github.com/llvm/llvm-project/pull/187129
>From 1edbf2fb4296558b439bde6bb2610b094c5e1f35 Mon Sep 17 00:00:00 2001
From: Patryk Wychowaniec <pwychowaniec at pm.me>
Date: Tue, 23 Jun 2026 19:03:50 +0200
Subject: [PATCH] [AVR] Support stack realignment
---
llvm/include/llvm/CodeGen/MIRYamlMapping.h | 1 +
llvm/include/llvm/CodeGen/MachineFrameInfo.h | 8 +-
.../llvm/CodeGen/TargetFrameLowering.h | 1 +
llvm/lib/Target/AMDGPU/SIFrameLowering.cpp | 3 +-
llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp | 66 +++++
llvm/lib/Target/AVR/AVRFrameLowering.cpp | 70 ++---
llvm/lib/Target/AVR/AVRFrameLowering.h | 4 +
llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp | 257 +++++++++++++++++-
llvm/lib/Target/AVR/AVRInstrInfo.td | 22 +-
llvm/lib/Target/AVR/AVRMachineFunctionInfo.h | 8 +
llvm/lib/Target/AVR/AVRRegisterInfo.cpp | 98 +++----
llvm/lib/Target/RISCV/RISCVFrameLowering.cpp | 2 +
llvm/test/CodeGen/AVR/pseudo/FRMIDX.mir | 2 +-
llvm/test/CodeGen/AVR/stack-realignment.ll | 250 +++++++++++++++++
14 files changed, 685 insertions(+), 107 deletions(-)
create mode 100644 llvm/test/CodeGen/AVR/stack-realignment.ll
diff --git a/llvm/include/llvm/CodeGen/MIRYamlMapping.h b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
index 76f5e38a045ff..de61b8d3fec12 100644
--- a/llvm/include/llvm/CodeGen/MIRYamlMapping.h
+++ b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
@@ -407,6 +407,7 @@ struct ScalarEnumerationTraits<TargetStackID::Value> {
IO.enumCase(ID, "scalable-predicate-vector",
TargetStackID::ScalablePredicateVector);
IO.enumCase(ID, "wasm-local", TargetStackID::WasmLocal);
+ IO.enumCase(ID, "avr-align", TargetStackID::AvrAlign);
IO.enumCase(ID, "noalloc", TargetStackID::NoAlloc);
}
};
diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
index 9cafbf5a1ce6c..ecc60ddeccb8e 100644
--- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
@@ -623,11 +623,13 @@ class MachineFrameInfo {
/// Set the correction for frame offsets.
void setOffsetAdjustment(int64_t Adj) { OffsetAdjustment = Adj; }
- /// Return the alignment in bytes that this function must be aligned to,
- /// which is greater than the default stack alignment provided by the target.
+ /// Return alignment of this function's frame.
Align getMaxAlign() const { return MaxAlignment; }
- /// Make sure the function is at least Align bytes aligned.
+ /// Overwrite alignment of this function's frame.
+ void setMaxAlign(Align Alignment) { MaxAlignment = Alignment; }
+
+ /// Make sure the function's frame is at least Align bytes aligned.
LLVM_ABI void ensureMaxAlignment(Align Alignment);
/// Return true if stack realignment is forced by function attributes or if
diff --git a/llvm/include/llvm/CodeGen/TargetFrameLowering.h b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
index 66b1cc137e06c..40c5b7dda51e9 100644
--- a/llvm/include/llvm/CodeGen/TargetFrameLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetFrameLowering.h
@@ -34,6 +34,7 @@ enum Value {
ScalableVector = 2,
WasmLocal = 3,
ScalablePredicateVector = 4,
+ AvrAlign = 5,
NoAlloc = 255
};
}
diff --git a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
index a9620b40931b8..8137590cffb19 100644
--- a/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
+++ b/llvm/lib/Target/AMDGPU/SIFrameLowering.cpp
@@ -1101,8 +1101,9 @@ bool SIFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
case TargetStackID::SGPRSpill:
return true;
case TargetStackID::ScalableVector:
- case TargetStackID::ScalablePredicateVector:
case TargetStackID::WasmLocal:
+ case TargetStackID::ScalablePredicateVector:
+ case TargetStackID::AvrAlign:
return false;
}
llvm_unreachable("Invalid TargetStackID::Value");
diff --git a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
index 393b556c9b210..f9da7b3cfe7e9 100644
--- a/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
+++ b/llvm/lib/Target/AVR/AVRExpandPseudoInsts.cpp
@@ -14,6 +14,7 @@
#include "AVR.h"
#include "AVRInstrInfo.h"
+#include "AVRMachineFunctionInfo.h"
#include "AVRTargetMachine.h"
#include "MCTargetDesc/AVRMCTargetDesc.h"
@@ -293,6 +294,38 @@ bool AVRExpandPseudo::expand<AVR::ADCWRdRr>(Block &MBB, BlockIt MBBI) {
return expandArith(AVR::ADCRdRr, AVR::ADCRdRr, MBB, MBBI);
}
+template <>
+bool AVRExpandPseudo::expand<AVR::ADIWRdKP>(Block &MBB, BlockIt MBBI) {
+ const AVRSubtarget &STI = MBB.getParent()->getSubtarget<AVRSubtarget>();
+ MachineInstr &MI = *MBBI;
+ Register DstReg = MI.getOperand(0).getReg();
+ Register SrcReg = MI.getOperand(1).getReg();
+ int64_t Imm = MI.getOperand(2).getImm();
+ unsigned Opcode;
+
+ if (SrcReg != DstReg) {
+ TII->copyPhysReg(MBB, MI, MI.getDebugLoc(), DstReg, SrcReg, false, false,
+ false);
+ }
+
+ if (isUInt<6>(Imm) && STI.hasADDSUBIW() &&
+ AVR::IWREGSRegClass.contains(DstReg)) {
+ Opcode = AVR::ADIWRdK;
+ } else {
+ Opcode = AVR::SUBIWRdK;
+ Imm = -Imm;
+ }
+
+ buildMI(MBB, MI, Opcode)
+ .addReg(DstReg, RegState::Define)
+ .addReg(DstReg, RegState::Kill)
+ .addImm(Imm)
+ .setOperandDead(3); // implicit-def $sreg
+
+ MI.eraseFromParent();
+ return true;
+}
+
template <>
bool AVRExpandPseudo::expand<AVR::SUBWRdRr>(Block &MBB, BlockIt MBBI) {
return expandArith(AVR::SUBRdRr, AVR::SBCRdRr, MBB, MBBI);
@@ -2586,6 +2619,37 @@ bool AVRExpandPseudo::expand<AVR::SPWRITE>(Block &MBB, BlockIt MBBI) {
return true;
}
+template <> bool AVRExpandPseudo::expand<AVR::FRMSP>(Block &MBB, BlockIt MBBI) {
+ MachineInstr &MI = *MBBI;
+ MachineFunction &MF = *MI.getMF();
+ AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
+ DebugLoc DL;
+ Register ASOPointer = MI.getOperand(0).getReg();
+ int64_t ASOAlignment = MI.getOperand(1).getImm();
+
+ StackOffset ASOOffset =
+ MF.getSubtarget<AVRSubtarget>()
+ .getFrameLowering()
+ ->getFrameIndexReference(MF, AFI->AlignedStackObjectIdx, ASOPointer);
+
+ TII->copyPhysReg(MBB, MI, DL, ASOPointer, AVR::R29R28, false, false, false);
+
+ buildMI(MBB, MI, AVR::ADIWRdKP)
+ .addReg(ASOPointer, RegState::Define)
+ .addReg(ASOPointer, RegState::Kill)
+ .addImm(ASOOffset.getFixed() + ASOAlignment - 1)
+ .setOperandDead(3); // implicit-def $sreg
+
+ buildMI(MBB, MI, AVR::ANDIWRdK)
+ .addReg(ASOPointer, RegState::Define)
+ .addReg(ASOPointer, RegState::Kill)
+ .addImm(-ASOAlignment)
+ .setOperandDead(3); // implicit-def $sreg
+
+ MI.eraseFromParent();
+ return true;
+}
+
bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) {
MachineInstr &MI = *MBBI;
int Opcode = MBBI->getOpcode();
@@ -2597,6 +2661,7 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) {
switch (Opcode) {
EXPAND(AVR::ADDWRdRr);
EXPAND(AVR::ADCWRdRr);
+ EXPAND(AVR::ADIWRdKP);
EXPAND(AVR::SUBWRdRr);
EXPAND(AVR::SUBIWRdK);
EXPAND(AVR::SBCWRdRr);
@@ -2661,6 +2726,7 @@ bool AVRExpandPseudo::expandMI(Block &MBB, BlockIt MBBI) {
EXPAND(AVR::ZEXT);
EXPAND(AVR::SPREAD);
EXPAND(AVR::SPWRITE);
+ EXPAND(AVR::FRMSP);
}
#undef EXPAND
return false;
diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.cpp b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
index b919be3d4466d..5f16f04f7bc6c 100644
--- a/llvm/lib/Target/AVR/AVRFrameLowering.cpp
+++ b/llvm/lib/Target/AVR/AVRFrameLowering.cpp
@@ -23,6 +23,8 @@
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/Support/ErrorHandling.h"
namespace llvm {
@@ -197,22 +199,11 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
}
if (FrameSize) {
- unsigned Opcode;
-
- // Select the optimal opcode depending on how big it is.
- if (isUInt<6>(FrameSize) && STI.hasADDSUBIW()) {
- Opcode = AVR::ADIWRdK;
- } else {
- Opcode = AVR::SUBIWRdK;
- FrameSize = -FrameSize;
- }
-
// Restore the frame pointer by doing FP += <size>.
- MachineInstr *MI = BuildMI(MBB, MBBI, DL, TII.get(Opcode), AVR::R29R28)
- .addReg(AVR::R29R28, RegState::Kill)
- .addImm(FrameSize);
- // The SREG implicit def is dead.
- MI->getOperand(3).setIsDead();
+ BuildMI(MBB, MBBI, DL, TII.get(AVR::ADIWRdKP), AVR::R29R28)
+ .addReg(AVR::R29R28, RegState::Kill)
+ .addImm(FrameSize)
+ .setOperandDead(3); // implicit-def $sreg
}
// Write back R29R28 to SP and temporarily disable interrupts.
@@ -222,6 +213,32 @@ void AVRFrameLowering::emitEpilogue(MachineFunction &MF,
restoreStatusRegister(MF, MBB);
}
+StackOffset AVRFrameLowering::getFrameIndexReference(const MachineFunction &MF,
+ int FI,
+ Register &FrameReg) const {
+ int64_t Offset;
+ const MachineFrameInfo &MFI = MF.getFrameInfo();
+
+ switch (MFI.getStackID(FI)) {
+ case TargetStackID::Default:
+ Offset = MFI.getObjectOffset(FI) + MFI.getOffsetAdjustment() +
+ MFI.getStackSize() - getOffsetOfLocalArea() + 1;
+
+ assert(Offset > 0);
+ break;
+
+ case TargetStackID::AvrAlign:
+ Offset = MFI.getObjectOffset(FI);
+ assert(Offset >= 0);
+ break;
+
+ default:
+ llvm_unreachable("Unsupported stack!");
+ }
+
+ return StackOffset::getFixed(Offset);
+}
+
// Return true if the specified function should have a dedicated frame
// pointer register. This is true if the function meets any of the following
// conditions:
@@ -389,23 +406,12 @@ MachineBasicBlock::iterator AVRFrameLowering::eliminateCallFramePseudoInstr(
// with a few pop instructions instead of the 8-9 instructions now
// required.
- // Select the best opcode to adjust SP based on the offset size.
- unsigned AddOpcode;
-
- if (isUInt<6>(Amount) && STI.hasADDSUBIW()) {
- AddOpcode = AVR::ADIWRdK;
- } else {
- AddOpcode = AVR::SUBIWRdK;
- Amount = -Amount;
- }
-
- // Build the instruction sequence.
BuildMI(MBB, MI, DL, TII.get(AVR::SPREAD), AVR::R31R30).addReg(AVR::SP);
- MachineInstr *New = BuildMI(MBB, MI, DL, TII.get(AddOpcode), AVR::R31R30)
- .addReg(AVR::R31R30, RegState::Kill)
- .addImm(Amount);
- New->getOperand(3).setIsDead();
+ BuildMI(MBB, MI, DL, TII.get(AVR::ADIWRdKP), AVR::R31R30)
+ .addReg(AVR::R31R30, RegState::Kill)
+ .addImm(Amount)
+ .setOperandDead(3); // implicit-def $sreg
BuildMI(MBB, MI, DL, TII.get(AVR::SPWRITE), AVR::SP)
.addReg(AVR::R31R30, RegState::Kill);
@@ -425,6 +431,7 @@ void AVRFrameLowering::determineCalleeSaves(MachineFunction &MF,
SavedRegs.set(AVR::R28);
}
}
+
/// The frame analyzer pass.
///
/// Scans the function for allocas and used arguments
@@ -444,8 +451,7 @@ struct AVRFrameAnalyzer : public MachineFunctionPass {
// about fixed size allocas so do not give false positives if only
// variable sized allocas are present.
for (unsigned i = 0, e = MFI.getObjectIndexEnd(); i != e; ++i) {
- // Variable sized objects have size 0.
- if (MFI.getObjectSize(i)) {
+ if (!MFI.isVariableSizedObjectIndex(i) && !MFI.isDeadObjectIndex(i)) {
AFI->setHasAllocas(true);
break;
}
diff --git a/llvm/lib/Target/AVR/AVRFrameLowering.h b/llvm/lib/Target/AVR/AVRFrameLowering.h
index 7baa5e9d62f60..2d6b56579b3d2 100644
--- a/llvm/lib/Target/AVR/AVRFrameLowering.h
+++ b/llvm/lib/Target/AVR/AVRFrameLowering.h
@@ -21,6 +21,10 @@ class AVRFrameLowering : public TargetFrameLowering {
public:
void emitPrologue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
+
+ StackOffset getFrameIndexReference(const MachineFunction &MF, int FI,
+ Register &FrameReg) const override;
+
bool spillCalleeSavedRegisters(MachineBasicBlock &MBB,
MachineBasicBlock::iterator MI,
ArrayRef<CalleeSavedInfo> CSI,
diff --git a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp
index 8997d0fb07dc3..1c2ae2ed99ff5 100644
--- a/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp
+++ b/llvm/lib/Target/AVR/AVRISelDAGToDAG.cpp
@@ -11,12 +11,20 @@
//===----------------------------------------------------------------------===//
#include "AVR.h"
+#include "AVRMachineFunctionInfo.h"
+#include "AVRRegisterInfo.h"
#include "AVRTargetMachine.h"
#include "MCTargetDesc/AVRMCTargetDesc.h"
+#include "llvm/CodeGen/ISDOpcodes.h"
+#include "llvm/CodeGen/MachineFrameInfo.h"
+#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/SelectionDAGISel.h"
+#include "llvm/CodeGen/SelectionDAGNodes.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
#define DEBUG_TYPE "avr-isel"
@@ -36,9 +44,14 @@ class AVRDAGToDAGISel : public SelectionDAGISel {
bool runOnMachineFunction(MachineFunction &MF) override;
+ void PostprocessISelDAG() override;
+
bool SelectAddr(SDNode *Op, SDValue N, SDValue &Base, SDValue &Disp);
+ bool selectAlignedFrameLoad(SDNode *N);
+ bool selectAlignedFrameStore(SDNode *N);
bool selectIndexedLoad(SDNode *N);
+
unsigned selectIndexedProgMemLoad(const LoadSDNode *LD, MVT VT, int Bank);
bool SelectInlineAsmMemoryOperand(const SDValue &Op,
@@ -49,6 +62,8 @@ class AVRDAGToDAGISel : public SelectionDAGISel {
#include "AVRGenDAGISel.inc"
private:
+ bool isPostProcessDone;
+
void Select(SDNode *N) override;
bool trySelect(SDNode *N);
@@ -74,9 +89,74 @@ INITIALIZE_PASS(AVRDAGToDAGISelLegacy, DEBUG_TYPE, PASS_NAME, false, false)
bool AVRDAGToDAGISel::runOnMachineFunction(MachineFunction &MF) {
Subtarget = &MF.getSubtarget<AVRSubtarget>();
+ isPostProcessDone = false;
+
+ AVRMachineFunctionInfo *AFI = MF.getInfo<AVRMachineFunctionInfo>();
+
+ // If our function has aligned allocas (e.g. `alloca i16, align 16`), we will
+ // need an aligned stack register to address them.
+ //
+ // Since we don't know this information upfront, assume we *will* need aligned
+ // stack register and aligned stack space - if this proves false,
+ // post-processing below will undo these two:
+ AFI->AlignedStackReg =
+ MF.getRegInfo().createVirtualRegister(&AVR::DLDREGSRegClass);
+
+ AFI->AlignedStackObjectIdx =
+ MF.getFrameInfo().CreateStackObject(1, Align(1), false);
+
return SelectionDAGISel::runOnMachineFunction(MF);
}
+void AVRDAGToDAGISel::PostprocessISelDAG() {
+ if (isPostProcessDone) {
+ // Sometimes post-processing gets run multiple times on a single function -
+ // because our algorithm is not idempotent (we generate FRMSP instruction
+ // and reset function's alignment), it can only run once per function.
+ //
+ // This flags gets toggled on at the end of this function and it gets reset
+ // during `runOnMachineFunction()`.
+ return;
+ }
+
+ MachineFrameInfo &MFI = MF->getFrameInfo();
+ AVRMachineFunctionInfo *AFI = MF->getInfo<AVRMachineFunctionInfo>();
+
+ uint64_t Offset = 0;
+ uint64_t Alignment = 1;
+
+ // Move all aligned objects into their own stack, `AvrAlign`.
+ for (int FI = 0, MaxFI = MFI.getObjectIndexEnd(); FI != MaxFI; ++FI) {
+ if (MFI.getObjectAlign(FI).value() > 1) {
+ Offset = alignTo(Offset, MFI.getObjectAlign(FI));
+
+ MFI.setStackID(FI, TargetStackID::AvrAlign);
+
+ // Usually offsets are assigned by the prologue/epilogue inserter, but PEI
+ // doesn't touch objects that lay outside the default stack - so, seizing
+ // the day, let's assign the offset ourselves.
+ MFI.setObjectOffset(FI, Offset);
+
+ Offset += MFI.getObjectSize(FI);
+ Alignment = std::max(Alignment, MFI.getObjectAlign(FI).value());
+ }
+ }
+
+ // If we had any aligned objects, allocate the aligned stack register.
+ if (Offset > 0) {
+ BuildMI(&MF->front(), DebugLoc(), TII->get(AVR::FRMSP))
+ .addReg(AFI->AlignedStackReg, RegState::Define)
+ .addImm(Alignment);
+
+ MFI.setObjectSize(AFI->AlignedStackObjectIdx, Offset + Alignment - 1);
+ MFI.setMaxAlign(Align(1));
+ } else {
+ MFI.RemoveStackObject(AFI->AlignedStackObjectIdx);
+ }
+
+ isPostProcessDone = true;
+}
+
bool AVRDAGToDAGISel::SelectAddr(SDNode *Op, SDValue N, SDValue &Base,
SDValue &Disp) {
SDLoc dl(Op);
@@ -139,6 +219,154 @@ bool AVRDAGToDAGISel::SelectAddr(SDNode *Op, SDValue N, SDValue &Base,
return false;
}
+bool AVRDAGToDAGISel::selectAlignedFrameLoad(SDNode *N) {
+ LoadSDNode *LD = cast<LoadSDNode>(N);
+ const SDValue LDB = LD->getBasePtr();
+
+ // ---
+
+ int InIndex;
+ int InOffset;
+
+ switch (LDB->getOpcode()) {
+ case ISD::FrameIndex:
+ InIndex = cast<FrameIndexSDNode>(LDB)->getIndex();
+ InOffset = 0;
+ break;
+
+ case ISD::ADD:
+ case ISD::OR:
+ if (LDB->getOperand(0)->getOpcode() == ISD::FrameIndex &&
+ LDB->getOperand(1)->getOpcode() == ISD::Constant) {
+ InIndex = cast<FrameIndexSDNode>(LDB->getOperand(0))->getIndex();
+ InOffset = cast<ConstantSDNode>(LDB->getOperand(1))->getZExtValue();
+ } else {
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ // ---
+
+ uint64_t Alignment = MF->getFrameInfo().getObjectAlign(InIndex).value();
+
+ if (Alignment == 1) {
+ return false;
+ }
+
+ // ---
+
+ unsigned Opcode;
+
+ switch (LD->getMemoryVT().getSimpleVT().SimpleTy) {
+ case MVT::i8:
+ Opcode = AVR::LDRdPtr;
+ break;
+ case MVT::i16:
+ Opcode = AVR::LDWRdPtr;
+ break;
+ default:
+ return false;
+ }
+
+ // ---
+
+ MVT PointerTy = getTargetLowering()->getPointerTy(CurDAG->getDataLayout());
+ Register StackReg = MF->getInfo<AVRMachineFunctionInfo>()->AlignedStackReg;
+
+ SDValue Index = CurDAG->getTargetFrameIndex(InIndex, PointerTy);
+ SDValue Offset = CurDAG->getTargetConstant(InOffset, SDLoc(N), MVT::i16);
+
+ SDNode *ResNode =
+ CurDAG->getMachineNode(AVR::FRMIDX, SDLoc(N), PointerTy, Index, Offset,
+ CurDAG->getRegister(StackReg, PointerTy));
+
+ CurDAG->SelectNodeTo(N, Opcode, LD->getMemoryVT().getSimpleVT(), MVT::Other,
+ SDValue(ResNode, 0), LD->getChain());
+
+ return true;
+}
+
+bool AVRDAGToDAGISel::selectAlignedFrameStore(SDNode *N) {
+ StoreSDNode *ST = cast<StoreSDNode>(N);
+ const SDValue STB = ST->getBasePtr();
+
+ // ---
+
+ int InIndex;
+ int InOffset;
+
+ switch (STB->getOpcode()) {
+ case ISD::FrameIndex:
+ InIndex = cast<FrameIndexSDNode>(STB)->getIndex();
+ InOffset = 0;
+ break;
+
+ case ISD::ADD:
+ case ISD::OR:
+ if (STB->getOperand(0)->getOpcode() == ISD::FrameIndex &&
+ STB->getOperand(1)->getOpcode() == ISD::Constant) {
+ InIndex = cast<FrameIndexSDNode>(STB->getOperand(0))->getIndex();
+ InOffset = cast<ConstantSDNode>(STB->getOperand(1))->getZExtValue();
+ } else {
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+
+ // ---
+
+ uint64_t Alignment = MF->getFrameInfo().getObjectAlign(InIndex).value();
+
+ if (Alignment == 1) {
+ return false;
+ }
+
+ // ---
+
+ unsigned Opcode;
+
+ switch (ST->getMemoryVT().getSimpleVT().SimpleTy) {
+ case MVT::i8:
+ Opcode = AVR::STPtrRr;
+ break;
+ case MVT::i16:
+ Opcode = AVR::STWPtrRr;
+ break;
+ default:
+ return false;
+ }
+
+ // ---
+
+ MVT PointerTy = getTargetLowering()->getPointerTy(CurDAG->getDataLayout());
+ Register StackReg = MF->getInfo<AVRMachineFunctionInfo>()->AlignedStackReg;
+
+ SDValue Index = CurDAG->getTargetFrameIndex(InIndex, PointerTy);
+ SDValue Offset = CurDAG->getTargetConstant(InOffset, SDLoc(N), MVT::i16);
+
+ SDNode *AddrNode =
+ CurDAG->getMachineNode(AVR::FRMIDX, SDLoc(N), PointerTy, Index, Offset,
+ CurDAG->getRegister(StackReg, PointerTy));
+
+ SDNode *ResNode = CurDAG->getMachineNode(
+ Opcode, SDLoc(N), MVT::Other,
+ {SDValue(AddrNode, 0), ST->getValue(), ST->getChain()});
+
+ CurDAG->setNodeMemRefs(cast<MachineSDNode>(ResNode), {ST->getMemOperand()});
+
+ ReplaceUses(SDValue(N, 0), SDValue(ResNode, 0));
+ CurDAG->RemoveDeadNode(N);
+
+ return true;
+}
+
bool AVRDAGToDAGISel::selectIndexedLoad(SDNode *N) {
const LoadSDNode *LD = cast<LoadSDNode>(N);
ISD::MemIndexedMode AM = LD->getAddressingMode();
@@ -324,21 +552,35 @@ bool AVRDAGToDAGISel::SelectInlineAsmMemoryOperand(
return false;
}
+// Convert the frameindex into a temp instruction that will hold the effective
+// address of the final stack slot.
template <> bool AVRDAGToDAGISel::select<ISD::FrameIndex>(SDNode *N) {
auto DL = CurDAG->getDataLayout();
+ auto PointerTy = getTargetLowering()->getPointerTy(DL);
- // Convert the frameindex into a temp instruction that will hold the
- // effective address of the final stack slot.
int FI = cast<FrameIndexSDNode>(N)->getIndex();
- SDValue TFI =
- CurDAG->getTargetFrameIndex(FI, getTargetLowering()->getPointerTy(DL));
+ SDValue TFI = CurDAG->getTargetFrameIndex(FI, PointerTy);
+ SDValue Offset = CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16);
+ uint64_t Alignment = MF->getFrameInfo().getObjectAlign(FI).value();
+
+ if (Alignment == 1) {
+ CurDAG->SelectNodeTo(N, AVR::FRMIDX, PointerTy, TFI, Offset,
+ CurDAG->getRegister(AVR::R29R28, PointerTy));
+ } else {
+ auto StackReg = MF->getInfo<AVRMachineFunctionInfo>()->AlignedStackReg;
+
+ CurDAG->SelectNodeTo(N, AVR::FRMIDX, PointerTy, TFI, Offset,
+ CurDAG->getRegister(StackReg, PointerTy));
+ }
- CurDAG->SelectNodeTo(N, AVR::FRMIDX, getTargetLowering()->getPointerTy(DL),
- TFI, CurDAG->getTargetConstant(0, SDLoc(N), MVT::i16));
return true;
}
template <> bool AVRDAGToDAGISel::select<ISD::STORE>(SDNode *N) {
+ if (selectAlignedFrameStore(N)) {
+ return true;
+ }
+
// Use the STD{W}SPQRr pseudo instruction when passing arguments through
// the stack on function calls for further expansion during the PEI phase.
const StoreSDNode *ST = cast<StoreSDNode>(N);
@@ -378,8 +620,7 @@ template <> bool AVRDAGToDAGISel::select<ISD::STORE>(SDNode *N) {
template <> bool AVRDAGToDAGISel::select<ISD::LOAD>(SDNode *N) {
const LoadSDNode *LD = cast<LoadSDNode>(N);
if (!AVR::isProgramMemoryAccess(LD)) {
- // Check if the opcode can be converted into an indexed load.
- return selectIndexedLoad(N);
+ return selectAlignedFrameLoad(N) || selectIndexedLoad(N);
}
if (!Subtarget->hasLPM())
diff --git a/llvm/lib/Target/AVR/AVRInstrInfo.td b/llvm/lib/Target/AVR/AVRInstrInfo.td
index 1ad4776d749f4..4b5be413ba669 100644
--- a/llvm/lib/Target/AVR/AVRInstrInfo.td
+++ b/llvm/lib/Target/AVR/AVRInstrInfo.td
@@ -398,12 +398,22 @@ let hasSideEffects = 0, isCommutable = 1, Constraints = "$src = $rd",
"adcw\t$rd, $rr",
[(set i16:$rd, (adde i16:$src, i16:$rr))]>;
- // AIDW Rd, k
+ // ADIW Rd, k
// Adds an immediate 6-bit value K to Rd, placing the result in Rd.
def ADIWRdK : FWRdK<0b0, (outs IWREGS:$rd), (ins IWREGS :$src, imm_arith6:$k),
"adiw\t$rd, $k",
[(set i16:$rd, (add i16:$src, uimm6:$k))]>,
Requires<[HasADDSUBIW]>;
+
+ // ADIW Rd, k
+ // Adds an immediate 8-bit value K to Rd, placing the result in Rd.
+ //
+ // Expands to ADIW or SUBIW, depending on the size of `k` and the supported
+ // instruction set.
+ def ADIWRdKP : Pseudo<(outs DLDREGS:$rd),
+ (ins DLDREGS:$src, i16imm:$k),
+ "adiw\t$rd, $k",
+ [(set i16:$rd, (add i16:$src, i16:$k))]>;
}
//===----------------------------------------------------------------------===//
@@ -1525,10 +1535,16 @@ def SEXT : ExtensionPseudo<(outs DREGS:$dt), (ins GPR8:$src), "sext\t$dt, $src",
def ZEXT : ExtensionPseudo<(outs DREGS:$dt), (ins GPR8:$src), "zext\t$dt, $src",
[(set i16:$dt, (zext i8:$src))]>;
+let hasSideEffects = 0 in
+def FRMSP : Pseudo<(outs DLDREGS:$dst),
+ (ins i16imm:$off),
+ "frmsp\t$dst, $off", []>;
+
// This pseudo gets expanded into a movw+adiw thus it clobbers SREG.
let Defs = [SREG], hasSideEffects = 0 in
-def FRMIDX : Pseudo<(outs DLDREGS:$dst), (ins DLDREGS:$src, i16imm:$src2),
- "frmidx\t$dst, $src, $src2", []>;
+def FRMIDX : Pseudo<(outs DLDREGS:$dst),
+ (ins DLDREGS:$src, i16imm:$off, DLDREGS:$rel),
+ "frmidx\t$dst, $src, $off, $rel", []>;
// The instructions STDSPQRr and STDWSPQRr are used to store to the stack
// frame. The most accurate implementation would be to load the SP into
diff --git a/llvm/lib/Target/AVR/AVRMachineFunctionInfo.h b/llvm/lib/Target/AVR/AVRMachineFunctionInfo.h
index 45c367a4dcd49..cf65a4c38f158 100644
--- a/llvm/lib/Target/AVR/AVRMachineFunctionInfo.h
+++ b/llvm/lib/Target/AVR/AVRMachineFunctionInfo.h
@@ -14,6 +14,7 @@
#define LLVM_AVR_MACHINE_FUNCTION_INFO_H
#include "llvm/CodeGen/MachineFunction.h"
+#include "llvm/CodeGen/Register.h"
namespace llvm {
@@ -45,6 +46,9 @@ class AVRMachineFunctionInfo : public MachineFunctionInfo {
int VarArgsFrameIndex;
public:
+ Register AlignedStackReg;
+ unsigned AlignedStackObjectIdx;
+
AVRMachineFunctionInfo(const Function &F, const TargetSubtargetInfo *STI)
: HasSpills(false), HasAllocas(false), HasStackArgs(false),
CalleeSavedFrameSize(0), VarArgsFrameIndex(0) {
@@ -52,8 +56,12 @@ class AVRMachineFunctionInfo : public MachineFunctionInfo {
this->IsInterruptHandler =
CallConv == CallingConv::AVR_INTR || F.hasFnAttribute("interrupt");
+
this->IsSignalHandler =
CallConv == CallingConv::AVR_SIGNAL || F.hasFnAttribute("signal");
+
+ this->AlignedStackReg = 0;
+ this->AlignedStackObjectIdx = 0;
}
MachineFunctionInfo *
diff --git a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
index 18bea848baeab..880c91537ef6a 100644
--- a/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
+++ b/llvm/lib/Target/AVR/AVRRegisterInfo.cpp
@@ -149,79 +149,55 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
const MachineFunction &MF = *MBB.getParent();
const AVRTargetMachine &TM = (const AVRTargetMachine &)MF.getTarget();
const TargetInstrInfo &TII = *TM.getSubtargetImpl()->getInstrInfo();
- const MachineFrameInfo &MFI = MF.getFrameInfo();
const TargetFrameLowering *TFI = TM.getSubtargetImpl()->getFrameLowering();
const AVRSubtarget &STI = MF.getSubtarget<AVRSubtarget>();
+
int FrameIndex = MI.getOperand(FIOperandNum).getIndex();
- int Offset = MFI.getObjectOffset(FrameIndex);
+ int FrameOffset = MI.getOperand(FIOperandNum + 1).getImm();
+ Register FrameReg;
- // Add one to the offset because SP points to an empty slot.
- Offset += MFI.getStackSize() - TFI->getOffsetOfLocalArea() + 1;
- // Fold incoming offset.
- Offset += MI.getOperand(FIOperandNum + 1).getImm();
+ int Offset =
+ TFI->getFrameIndexReference(MF, FrameIndex, FrameReg).getFixed() +
+ FrameOffset;
// This is actually "load effective address" of the stack slot
// instruction. We have only two-address instructions, thus we need to
// expand it into move + add.
if (MI.getOpcode() == AVR::FRMIDX) {
Register DstReg = MI.getOperand(0).getReg();
- assert(DstReg != AVR::R29R28 && "Dest reg cannot be the frame pointer");
-
- // Copy the frame pointer.
- if (STI.hasMOVW()) {
- BuildMI(MBB, MI, dl, TII.get(AVR::MOVWRdRr), DstReg).addReg(AVR::R29R28);
- } else {
- Register DstLoReg, DstHiReg;
- splitReg(DstReg, DstLoReg, DstHiReg);
- BuildMI(MBB, MI, dl, TII.get(AVR::MOVRdRr), DstLoReg).addReg(AVR::R28);
- BuildMI(MBB, MI, dl, TII.get(AVR::MOVRdRr), DstHiReg).addReg(AVR::R29);
- }
- assert(Offset > 0 && "Invalid offset");
-
- // We need to materialize the offset via an add instruction.
- unsigned Opcode;
-
- II++; // Skip over the FRMIDX instruction.
-
- // Generally, to load a frame address two add instructions are emitted that
- // could get folded into a single one:
- // movw r31:r30, r29:r28
- // adiw r31:r30, 29
- // adiw r31:r30, 16
- // to:
- // movw r31:r30, r29:r28
- // adiw r31:r30, 45
- if (II != MBB.end())
- foldFrameOffset(II, Offset, DstReg);
-
- // Select the best opcode based on DstReg and the offset size.
- switch (DstReg) {
- case AVR::R25R24:
- case AVR::R27R26:
- case AVR::R31R30: {
- if (isUInt<6>(Offset) && STI.hasADDSUBIW()) {
- Opcode = AVR::ADIWRdK;
- break;
- }
- [[fallthrough]];
- }
- default: {
- // This opcode will get expanded into a pair of subi/sbci.
- Opcode = AVR::SUBIWRdK;
- Offset = -Offset;
- break;
- }
+ FrameReg = MI.getOperand(FIOperandNum + 2).getReg();
+
+ if (DstReg != FrameReg) {
+ TII.copyPhysReg(MBB, MI, dl, DstReg, FrameReg, false, false, false);
}
- MachineInstr *New = BuildMI(MBB, II, dl, TII.get(Opcode), DstReg)
- .addReg(DstReg, RegState::Kill)
- .addImm(Offset);
- New->getOperand(3).setIsDead();
+ if (Offset > 0) {
+ // Skip over the FRMIDX instruction.
+ II++;
+
+ // Generally, to load a frame address two add instructions are emitted
+ // that could get folded into a single one:
+ // movw r31:r30, r29:r28
+ // adiw r31:r30, 29
+ // adiw r31:r30, 16
+ // to:
+ // movw r31:r30, r29:r28
+ // adiw r31:r30, 45
+ if (II != MBB.end())
+ foldFrameOffset(II, Offset, DstReg);
+
+ BuildMI(MBB, II, dl, TII.get(AVR::ADIWRdKP), DstReg)
+ .addReg(DstReg, RegState::Kill)
+ .addImm(Offset)
+ .setOperandDead(3); // implicit-def $sreg
+ }
- MI.eraseFromParent(); // remove FRMIDX
+ // Remove FRMIDX.
+ MI.eraseFromParent();
- return false;
+ // Since we removed an instruction, we return true.
+ return true;
}
// On most AVRs, we can use an offset up to 62 for load/store with
@@ -256,6 +232,7 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
MachineInstr *New = BuildMI(MBB, II, dl, TII.get(AddOpc), AVR::R29R28)
.addReg(AVR::R29R28, RegState::Kill)
.addImm(AddOffset);
+
New->getOperand(3).setIsDead();
// Restore SREG.
@@ -272,9 +249,12 @@ bool AVRRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
Offset = MaxOffset;
}
- MI.getOperand(FIOperandNum).ChangeToRegister(AVR::R29R28, false);
assert(isUInt<6>(Offset) && "Offset is out of range");
+
+ MI.getOperand(FIOperandNum).ChangeToRegister(AVR::R29R28, false);
MI.getOperand(FIOperandNum + 1).ChangeToImmediate(Offset);
+
+ // Since we didn't remove an instruction, we return false.
return false;
}
diff --git a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
index 5c5d081007796..b423c7237239e 100644
--- a/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVFrameLowering.cpp
@@ -22,6 +22,7 @@
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/RegisterScavenging.h"
+#include "llvm/CodeGen/TargetFrameLowering.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/MC/MCDwarf.h"
#include "llvm/Support/LEB128.h"
@@ -2602,6 +2603,7 @@ bool RISCVFrameLowering::isSupportedStackID(TargetStackID::Value ID) const {
case TargetStackID::SGPRSpill:
case TargetStackID::WasmLocal:
case TargetStackID::ScalablePredicateVector:
+ case TargetStackID::AvrAlign:
return false;
}
llvm_unreachable("Invalid TargetStackID::Value");
diff --git a/llvm/test/CodeGen/AVR/pseudo/FRMIDX.mir b/llvm/test/CodeGen/AVR/pseudo/FRMIDX.mir
index f45eeb661dbbf..30be73d558232 100644
--- a/llvm/test/CodeGen/AVR/pseudo/FRMIDX.mir
+++ b/llvm/test/CodeGen/AVR/pseudo/FRMIDX.mir
@@ -22,5 +22,5 @@ body: |
; CHECK-LABEL: test
- $r29r28 = FRMIDX $r31r30, 0, implicit-def $sreg
+ $r29r28 = FRMIDX $r31r30, 0, $r31r30, implicit-def $sreg
...
diff --git a/llvm/test/CodeGen/AVR/stack-realignment.ll b/llvm/test/CodeGen/AVR/stack-realignment.ll
new file mode 100644
index 0000000000000..c93ef7c0d651d
--- /dev/null
+++ b/llvm/test/CodeGen/AVR/stack-realignment.ll
@@ -0,0 +1,250 @@
+; RUN: llc -mtriple=avr -mcpu=atmega328 -O1 -verify-machineinstrs < %s | FileCheck %s
+
+declare void @use(ptr %x);
+
+; Case: One 2-byte variable with alignment of 1.
+;
+; This shouldn't activate stack realignment - this function exists so that it's
+; easy to see the difference when stack realignment gets actually activated (see
+; the next test case).
+define i16 @no_alignment() {
+; CHECK-LABEL: no_alignment:
+; CHECK-NEXT: ; %bb.0:
+;
+;; prologue
+; CHECK-NEXT: push r28
+; CHECK-NEXT: push r29
+; CHECK-NEXT: in r28, 61
+; CHECK-NEXT: in r29, 62
+; CHECK-NEXT: sbiw r28, 2
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+;
+;; call void @use(ptr %1)
+; CHECK-NEXT: movw r24, r28
+; CHECK-NEXT: adiw r24, 1
+; CHECK-NEXT: call use
+;
+;; %2 = load i16, ptr %1, align 1
+; CHECK-NEXT: ldd r24, Y+1
+; CHECK-NEXT: ldd r25, Y+2
+;
+;; epilogue
+; CHECK-NEXT: adiw r28, 2
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+; CHECK-NEXT: pop r29
+; CHECK-NEXT: pop r28
+; CHECK-NEXT: ret
+
+ %1 = alloca i16, align 1
+ call void @use(ptr %1)
+ %2 = load i16, ptr %1, align 1
+
+ ret i16 %2
+}
+
+; Case: One 2-byte variable with alignment of 16.
+;
+; This reserves 2 + 16 - 1 = 17 bytes of stack space and allocates an extra
+; aligned stack pointer (in this case into r24:r25).
+define i16 @some_alignment() {
+; CHECK-LABEL: some_alignment:
+; CHECK-NEXT: %bb.0:
+;
+;; prologue
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: push r28
+; CHECK-NEXT: push r29
+; CHECK-NEXT: in r28, 61
+; CHECK-NEXT: in r29, 62
+; CHECK-NEXT: sbiw r28, 17
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+;
+;; SP allocation
+; CHECK-NEXT: movw r24, r28
+; CHECK-NEXT: adiw r24, 16
+; CHECK-NEXT: andi r24, 240
+;
+;; call void @use (ptr %1)
+; CHECK-NEXT: movw r16, r24
+; CHECK-NEXT: movw r24, r16
+; CHECK-NEXT: call use
+;
+;; %2 = load i16, ptr %1, align 16
+; CHECK-NEXT: movw r30, r16
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+;
+;; epilogue
+; CHECK-NEXT: adiw r28, 17
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+; CHECK-NEXT: pop r29
+; CHECK-NEXT: pop r28
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+
+ %1 = alloca i16, align 16
+ call void @use(ptr %1)
+ %2 = load i16, ptr %1, align 16
+
+ ret i16 %2
+}
+
+; Case: One variable with no alignment, another variable with alignment of 16.
+;
+; This creates two separate stack spaces - an unaligned one (r28:r29) and an
+; aligned one (r16:r17).
+define i16 @mixed_alignment() {
+; CHECK-LABEL: mixed_alignment:
+; CHECK-NEXT: ; %bb.0:
+;
+;; prologue
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: push r28
+; CHECK-NEXT: push r29
+; CHECK-NEXT: in r28, 61
+; CHECK-NEXT: in r29, 62
+; CHECK-NEXT: sbiw r28, 19
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+;
+;; SP allocation
+; CHECK-NEXT: movw r16, r28
+; CHECK-NEXT: subi r16, 238
+; CHECK-NEXT: sbci r17, 255
+; CHECK-NEXT: andi r16, 240
+;
+;; call void @use(ptr %1)
+; CHECK-NEXT: movw r24, r28
+; CHECK-NEXT: adiw r24, 1
+; CHECK-NEXT: call use
+;
+;; call void @use(ptr %2)
+; CHECK-NEXT: movw r24, r16
+; CHECK-NEXT: call use
+;
+;; %4 = load i16, ptr %2, align 16
+; CHECK-NEXT: movw r30, r16
+; CHECK-NEXT: ld r18, Z
+; CHECK-NEXT: ldd r19, Z+1
+;
+;; %3 = load i16, ptr %1, align 1
+; CHECK-NEXT: ldd r24, Y+1
+; CHECK-NEXT: ldd r25, Y+2
+;
+;; %5 = or i16 %3, %4
+; CHECK-NEXT: or r24, r18
+; CHECK-NEXT: or r25, r19
+;
+;; epilogue
+; CHECK-NEXT: adiw r28, 19
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+; CHECK-NEXT: pop r29
+; CHECK-NEXT: pop r28
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+
+ %1 = alloca i16, align 1
+ %2 = alloca i16, align 16
+
+ call void @use(ptr %1)
+ call void @use(ptr %2)
+
+ %3 = load i16, ptr %1, align 1
+ %4 = load i16, ptr %2, align 16
+ %5 = or i16 %3, %4
+
+ ret i16 %5
+}
+
+; Case: getelementptr referring to an aligned variable.
+define i16 @gep() {
+; CHECK-LABEL: gep:
+; CHECK-NEXT: ; %bb.0:
+;
+;; prologue
+; CHECK-NEXT: push r16
+; CHECK-NEXT: push r17
+; CHECK-NEXT: push r28
+; CHECK-NEXT: push r29
+; CHECK-NEXT: in r28, 61
+; CHECK-NEXT: in r29, 62
+; CHECK-NEXT: sbiw r28, 23
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+;
+;; SP allocation
+; CHECK-NEXT: movw r16, r28
+; CHECK-NEXT: subi r16, 248
+; CHECK-NEXT: sbci r17, 255
+; CHECK-NEXT: andi r16, 248
+;
+;; %2 = getelementptr inbounds nuw i8, ptr %1, i16 8
+; CHECK-NEXT: movw r24, r16
+; CHECK-NEXT: adiw r24, 8
+;
+;; call void @use(ptr %2)
+; CHECK-NEXT: call use
+;
+;; %3 = getelementptr inbounds nuw i8, ptr %1, i16 8
+; CHECK-NEXT: movw r24, r16
+; CHECK-NEXT: adiw r24, 8
+;
+;; %4 = load i16, ptr %3, align 4
+;; ret i16 %4
+; CHECK-NEXT: movw r30, r24
+; CHECK-NEXT: ld r24, Z
+; CHECK-NEXT: ldd r25, Z+1
+; CHECK-NEXT: adiw r28, 23
+;
+;; epilogue
+; CHECK-NEXT: in r0, 63
+; CHECK-NEXT: cli
+; CHECK-NEXT: out 62, r29
+; CHECK-NEXT: out 63, r0
+; CHECK-NEXT: out 61, r28
+; CHECK-NEXT: pop r29
+; CHECK-NEXT: pop r28
+; CHECK-NEXT: pop r17
+; CHECK-NEXT: pop r16
+; CHECK-NEXT: ret
+
+ %1 = alloca [8 x i16], align 8
+ %2 = getelementptr inbounds nuw i8, ptr %1, i16 8
+
+ call void @use(ptr %2)
+
+ %3 = getelementptr inbounds nuw i8, ptr %1, i16 8
+ %4 = load i16, ptr %3, align 4
+
+ ret i16 %4
+}
More information about the llvm-commits
mailing list