[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