[llvm] [CodeGen] Parse frame-pointer attribute once when creating MachineFunction (PR #191974)

Cullen Rhodes via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 17 03:57:15 PDT 2026


https://github.com/c-rhodes updated https://github.com/llvm/llvm-project/pull/191974

>From 056e2230eca86d1362452178012fb33aeb806362 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Mon, 13 Apr 2026 10:13:19 +0000
Subject: [PATCH 1/3] [CodeGen] Parse frame-pointer attribute once when
 creating MachineFunction

TargetOptions::DisableFramePointerElim is hot and showing up in
compile-time profiles via AArch64FrameLowering::hasFPImpl on
aarch64-O0-g builds. Repeatedly looking up the function attribute is
expensive. Parsing it once at MachineFunction initialisation and storing
as FramePointerKind on MachineFrameInfo is a -0.21% geomean improvement
on CTMark stage1-aarch64-O0-g. Also helps debug builds on other targets.

https://llvm-compile-time-tracker.com/compare.php?from=215f35eb8f1c313ac135ad47db1cc0b99b3ae694&to=51f6617517177bea1cc49baeab3acaf62d5e9df9&stat=instructions%3Au
---
 llvm/include/llvm/CodeGen/MachineFrameInfo.h | 10 ++++++
 llvm/lib/CodeGen/MachineFunction.cpp         | 17 +++++++++
 llvm/lib/CodeGen/TargetOptionsImpl.cpp       | 37 ++++++++++----------
 3 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/llvm/include/llvm/CodeGen/MachineFrameInfo.h b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
index c4852f84be352..22729e736fbd3 100644
--- a/llvm/include/llvm/CodeGen/MachineFrameInfo.h
+++ b/llvm/include/llvm/CodeGen/MachineFrameInfo.h
@@ -17,6 +17,7 @@
 #include "llvm/CodeGen/Register.h"
 #include "llvm/CodeGen/TargetFrameLowering.h"
 #include "llvm/Support/Alignment.h"
+#include "llvm/Support/CodeGen.h"
 #include "llvm/Support/Compiler.h"
 #include <cassert>
 #include <vector>
@@ -279,6 +280,10 @@ class MachineFrameInfo {
   /// Set to true if this function has any function calls.
   bool HasCalls = false;
 
+  /// Frame-pointer policy for this function to avoid repeated attribute
+  /// lookups in hot paths.
+  FramePointerKind FramePointerPolicy = FramePointerKind::None;
+
   /// The frame index for the stack protector.
   int StackProtectorIdx = -1;
 
@@ -641,6 +646,11 @@ class MachineFrameInfo {
   bool hasCalls() const { return HasCalls; }
   void setHasCalls(bool V) { HasCalls = V; }
 
+  FramePointerKind getFramePointerPolicy() const { return FramePointerPolicy; }
+  void setFramePointerPolicy(FramePointerKind Kind) {
+    FramePointerPolicy = Kind;
+  }
+
   /// Returns true if the function contains opaque dynamic stack adjustments.
   bool hasOpaqueSPAdjustment() const { return HasOpaqueSPAdjustment; }
   void setHasOpaqueSPAdjustment(bool B) { HasOpaqueSPAdjustment = B; }
diff --git a/llvm/lib/CodeGen/MachineFunction.cpp b/llvm/lib/CodeGen/MachineFunction.cpp
index a2dd9c425370c..71179ed9f743d 100644
--- a/llvm/lib/CodeGen/MachineFunction.cpp
+++ b/llvm/lib/CodeGen/MachineFunction.cpp
@@ -20,6 +20,7 @@
 #include "llvm/ADT/SmallString.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
 #include "llvm/ADT/Twine.h"
 #include "llvm/Analysis/ConstantFolding.h"
 #include "llvm/Analysis/ProfileSummaryInfo.h"
@@ -161,6 +162,21 @@ static inline Align getFnStackAlignment(const TargetSubtargetInfo &STI,
   return STI.getFrameLowering()->getStackAlign();
 }
 
+static FramePointerKind getFramePointerPolicy(const Function &F) {
+  Attribute FPAttr = F.getFnAttribute("frame-pointer");
+  if (!FPAttr.isValid())
+    return FramePointerKind::None;
+
+  StringRef FP = FPAttr.getValueAsString();
+  return StringSwitch<FramePointerKind>(FP)
+      .Case("all", FramePointerKind::All)
+      .Case("non-leaf", FramePointerKind::NonLeaf)
+      .Case("non-leaf-no-reserve", FramePointerKind::NonLeafNoReserve)
+      .Case("reserved", FramePointerKind::Reserved)
+      .Case("none", FramePointerKind::None)
+      .Default(FramePointerKind::None);
+}
+
 MachineFunction::MachineFunction(Function &F, const TargetMachine &Target,
                                  const TargetSubtargetInfo &STI, MCContext &Ctx,
                                  unsigned FunctionNum)
@@ -202,6 +218,7 @@ void MachineFunction::init() {
   FrameInfo = new (Allocator) MachineFrameInfo(
       getFnStackAlignment(STI, F), /*StackRealignable=*/CanRealignSP,
       /*ForcedRealign=*/ForceRealignSP && CanRealignSP);
+  FrameInfo->setFramePointerPolicy(getFramePointerPolicy(F));
 
   setUnsafeStackSize(F, *FrameInfo);
 
diff --git a/llvm/lib/CodeGen/TargetOptionsImpl.cpp b/llvm/lib/CodeGen/TargetOptionsImpl.cpp
index 16d86b42db4a3..2dbde46ac62e3 100644
--- a/llvm/lib/CodeGen/TargetOptionsImpl.cpp
+++ b/llvm/lib/CodeGen/TargetOptionsImpl.cpp
@@ -10,7 +10,6 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "llvm/ADT/StringSwitch.h"
 #include "llvm/CodeGen/MachineFrameInfo.h"
 #include "llvm/CodeGen/MachineFunction.h"
 #include "llvm/CodeGen/TargetFrameLowering.h"
@@ -22,31 +21,33 @@ using namespace llvm;
 /// DisableFramePointerElim - This returns true if frame pointer elimination
 /// optimization should be disabled for the given machine function.
 bool TargetOptions::DisableFramePointerElim(const MachineFunction &MF) const {
-  const Function &F = MF.getFunction();
-
-  Attribute FPAttr = F.getFnAttribute("frame-pointer");
-  if (!FPAttr.isValid())
-    return false;
-  StringRef FP = FPAttr.getValueAsString();
-  if (FP == "all")
+  FramePointerKind FP = MF.getFrameInfo().getFramePointerPolicy();
+  switch (FP) {
+  case FramePointerKind::All:
     return true;
-  if (FP == "non-leaf" || FP == "non-leaf-no-reserve")
+  case FramePointerKind::NonLeaf:
+  case FramePointerKind::NonLeafNoReserve:
     return MF.getFrameInfo().hasCalls();
-  if (FP == "none" || FP == "reserved")
+  case FramePointerKind::None:
+  case FramePointerKind::Reserved:
     return false;
+  }
   llvm_unreachable("unknown frame pointer flag");
 }
 
 bool TargetOptions::FramePointerIsReserved(const MachineFunction &MF) const {
-  const Function &F = MF.getFunction();
-  Attribute FPAttr = F.getFnAttribute("frame-pointer");
-  if (!FPAttr.isValid())
+  FramePointerKind FP = MF.getFrameInfo().getFramePointerPolicy();
+  switch (FP) {
+  case FramePointerKind::All:
+  case FramePointerKind::NonLeaf:
+  case FramePointerKind::Reserved:
+    return true;
+  case FramePointerKind::NonLeafNoReserve:
+    return MF.getFrameInfo().hasCalls();
+  case FramePointerKind::None:
     return false;
-
-  return StringSwitch<bool>(FPAttr.getValueAsString())
-      .Cases({"all", "non-leaf", "reserved"}, true)
-      .Case(("non-leaf-no-reserve"), MF.getFrameInfo().hasCalls())
-      .Case("none", false);
+  }
+  llvm_unreachable("unknown frame pointer flag");
 }
 
 /// HonorSignDependentRoundingFPMath - Return true if the codegen must assume

>From 975f2aea805b3affca3fd8a3c0349d96c65d71e9 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Tue, 14 Apr 2026 15:05:08 +0000
Subject: [PATCH 2/3] address comments

---
 llvm/include/llvm/CodeGen/MIRYamlMapping.h         | 14 ++++++++++++++
 llvm/lib/CodeGen/MIRParser/MIRParser.cpp           |  2 ++
 llvm/lib/CodeGen/MIRPrinter.cpp                    |  1 +
 llvm/test/CodeGen/MIR/Generic/frame-info.mir       |  4 +++-
 .../tools/llvm-reduce/mir/preserve-frame-info.mir  |  2 ++
 5 files changed, 22 insertions(+), 1 deletion(-)

diff --git a/llvm/include/llvm/CodeGen/MIRYamlMapping.h b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
index f3e1ff91f453c..295ee296f78ab 100644
--- a/llvm/include/llvm/CodeGen/MIRYamlMapping.h
+++ b/llvm/include/llvm/CodeGen/MIRYamlMapping.h
@@ -17,6 +17,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/CodeGen/MachineJumpTableInfo.h"
 #include "llvm/CodeGen/TargetFrameLowering.h"
+#include "llvm/Support/CodeGen.h"
 #include "llvm/Support/SMLoc.h"
 #include "llvm/Support/YAMLTraits.h"
 #include "llvm/Support/raw_ostream.h"
@@ -144,6 +145,16 @@ template <> struct ScalarEnumerationTraits<MachineJumpTableInfo::JTEntryKind> {
   }
 };
 
+template <> struct ScalarEnumerationTraits<FramePointerKind> {
+  static void enumeration(IO &IO, FramePointerKind &FP) {
+    IO.enumCase(FP, "none", FramePointerKind::None);
+    IO.enumCase(FP, "non-leaf", FramePointerKind::NonLeaf);
+    IO.enumCase(FP, "all", FramePointerKind::All);
+    IO.enumCase(FP, "reserved", FramePointerKind::Reserved);
+    IO.enumCase(FP, "non-leaf-no-reserve", FramePointerKind::NonLeafNoReserve);
+  }
+};
+
 template <> struct ScalarTraits<MaybeAlign> {
   static void output(const MaybeAlign &Alignment, void *,
                      llvm::raw_ostream &out) {
@@ -702,6 +713,7 @@ struct MachineFrameInfo {
   unsigned MaxAlignment = 0;
   bool AdjustsStack = false;
   bool HasCalls = false;
+  FramePointerKind FramePointerPolicy = FramePointerKind::None;
   StringValue StackProtector;
   StringValue FunctionContext;
   unsigned MaxCallFrameSize = ~0u; ///< ~0u means: not computed yet.
@@ -724,6 +736,7 @@ struct MachineFrameInfo {
            OffsetAdjustment == Other.OffsetAdjustment &&
            MaxAlignment == Other.MaxAlignment &&
            AdjustsStack == Other.AdjustsStack && HasCalls == Other.HasCalls &&
+           FramePointerPolicy == Other.FramePointerPolicy &&
            StackProtector == Other.StackProtector &&
            FunctionContext == Other.FunctionContext &&
            MaxCallFrameSize == Other.MaxCallFrameSize &&
@@ -751,6 +764,7 @@ template <> struct MappingTraits<MachineFrameInfo> {
     YamlIO.mapOptional("maxAlignment", MFI.MaxAlignment, (unsigned)0);
     YamlIO.mapOptional("adjustsStack", MFI.AdjustsStack, false);
     YamlIO.mapOptional("hasCalls", MFI.HasCalls, false);
+    YamlIO.mapOptional("framePointerPolicy", MFI.FramePointerPolicy);
     YamlIO.mapOptional("stackProtector", MFI.StackProtector,
                        StringValue()); // Don't print it out when it's empty.
     YamlIO.mapOptional("functionContext", MFI.FunctionContext,
diff --git a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
index db5bc67776c7d..00967e1de43cc 100644
--- a/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
+++ b/llvm/lib/CodeGen/MIRParser/MIRParser.cpp
@@ -900,6 +900,8 @@ bool MIRParserImpl::initializeFrameInfo(PerFunctionMIParsingState &PFS,
     MFI.ensureMaxAlignment(Align(YamlMFI.MaxAlignment));
   MFI.setAdjustsStack(YamlMFI.AdjustsStack);
   MFI.setHasCalls(YamlMFI.HasCalls);
+  if (YamlMFI.FramePointerPolicy != FramePointerKind::None)
+    MFI.setFramePointerPolicy(YamlMFI.FramePointerPolicy);
   if (YamlMFI.MaxCallFrameSize != ~0u)
     MFI.setMaxCallFrameSize(YamlMFI.MaxCallFrameSize);
   MFI.setCVBytesOfCalleeSavedRegisters(YamlMFI.CVBytesOfCalleeSavedRegisters);
diff --git a/llvm/lib/CodeGen/MIRPrinter.cpp b/llvm/lib/CodeGen/MIRPrinter.cpp
index 250b40863c9f3..60202efcd7af5 100644
--- a/llvm/lib/CodeGen/MIRPrinter.cpp
+++ b/llvm/lib/CodeGen/MIRPrinter.cpp
@@ -364,6 +364,7 @@ static void convertMFI(ModuleSlotTracker &MST, yaml::MachineFrameInfo &YamlMFI,
   YamlMFI.MaxAlignment = MFI.getMaxAlign().value();
   YamlMFI.AdjustsStack = MFI.adjustsStack();
   YamlMFI.HasCalls = MFI.hasCalls();
+  YamlMFI.FramePointerPolicy = MFI.getFramePointerPolicy();
   YamlMFI.MaxCallFrameSize = MFI.isMaxCallFrameSizeComputed()
     ? MFI.getMaxCallFrameSize() : ~0u;
   YamlMFI.CVBytesOfCalleeSavedRegisters =
diff --git a/llvm/test/CodeGen/MIR/Generic/frame-info.mir b/llvm/test/CodeGen/MIR/Generic/frame-info.mir
index 32396b7486fe5..8992e9f51f8ef 100644
--- a/llvm/test/CodeGen/MIR/Generic/frame-info.mir
+++ b/llvm/test/CodeGen/MIR/Generic/frame-info.mir
@@ -36,6 +36,7 @@ tracksRegLiveness: true
 # CHECK-NEXT: maxAlignment:
 # CHECK-NEXT: adjustsStack: false
 # CHECK-NEXT: hasCalls: false
+# CHECK-NEXT: framePointerPolicy: none
 # CHECK-NEXT: stackProtector:  ''
 # CHECK-NEXT: functionContext:  ''
 # CHECK-NEXT: maxCallFrameSize:
@@ -70,6 +71,7 @@ stack:
 # CHECK-NEXT: maxAlignment:
 # CHECK-NEXT: adjustsStack: true
 # CHECK-NEXT: hasCalls: true
+# CHECK-NEXT: framePointerPolicy: non-leaf
 # CHECK-NEXT: stackProtector:  ''
 # CHECK-NEXT: functionContext:  '%stack.0'
 # CHECK-NEXT: maxCallFrameSize: 4
@@ -89,6 +91,7 @@ frameInfo:
   maxAlignment:    4
   adjustsStack:    true
   hasCalls:        true
+  framePointerPolicy: non-leaf
   functionContext: '%stack.0'
   maxCallFrameSize: 4
   cvBytesOfCalleeSavedRegisters: 8
@@ -100,4 +103,3 @@ frameInfo:
 body: |
   bb.0.entry:
 ...
-
diff --git a/llvm/test/tools/llvm-reduce/mir/preserve-frame-info.mir b/llvm/test/tools/llvm-reduce/mir/preserve-frame-info.mir
index 9af4ac3863405..eb01fbef2cb14 100644
--- a/llvm/test/tools/llvm-reduce/mir/preserve-frame-info.mir
+++ b/llvm/test/tools/llvm-reduce/mir/preserve-frame-info.mir
@@ -13,6 +13,7 @@
 # RESULT-NEXT: maxAlignment:    64
 # RESULT-NEXT: adjustsStack:    true
 # RESULT-NEXT: hasCalls:        true
+# RESULT-NEXT: framePointerPolicy: none
 # RESULT-NEXT: stackProtector: '%stack.9.guard'
 # RESULT-NEXT: maxCallFrameSize: 420
 # RESULT-NEXT: cvBytesOfCalleeSavedRegisters: 48
@@ -110,6 +111,7 @@ frameInfo:
   maxAlignment:    1
   adjustsStack:    true
   hasCalls:        true
+  framePointerPolicy: none
   stackProtector:  '%stack.9'
   maxCallFrameSize: 420
   cvBytesOfCalleeSavedRegisters: 48

>From b05b64c6a6e137ca722dc62861c3d0e6304f8d57 Mon Sep 17 00:00:00 2001
From: Cullen Rhodes <cullen.rhodes at arm.com>
Date: Fri, 17 Apr 2026 10:55:56 +0000
Subject: [PATCH 3/3] address comments

---
 llvm/test/CodeGen/MIR/Generic/frame-info.mir | 98 +++++++++++++++++++-
 1 file changed, 96 insertions(+), 2 deletions(-)

diff --git a/llvm/test/CodeGen/MIR/Generic/frame-info.mir b/llvm/test/CodeGen/MIR/Generic/frame-info.mir
index 8992e9f51f8ef..daf87feb2c712 100644
--- a/llvm/test/CodeGen/MIR/Generic/frame-info.mir
+++ b/llvm/test/CodeGen/MIR/Generic/frame-info.mir
@@ -20,6 +20,46 @@
     ret i32 %c
   }
 
+  define i32 @test_framePointerPolicy_none(i32 %a) {
+  entry:
+    %b = alloca i32
+    store i32 %a, ptr %b
+    %c = load i32, ptr %b
+    ret i32 %c
+  }
+
+  define i32 @test_framePointerPolicy_non_leaf(i32 %a) {
+  entry:
+    %b = alloca i32
+    store i32 %a, ptr %b
+    %c = load i32, ptr %b
+    ret i32 %c
+  }
+
+  define i32 @test_framePointerPolicy_all(i32 %a) {
+  entry:
+    %b = alloca i32
+    store i32 %a, ptr %b
+    %c = load i32, ptr %b
+    ret i32 %c
+  }
+
+  define i32 @test_framePointerPolicy_reserved(i32 %a) {
+  entry:
+    %b = alloca i32
+    store i32 %a, ptr %b
+    %c = load i32, ptr %b
+    ret i32 %c
+  }
+
+  define i32 @test_framePointerPolicy_non_leaf_no_reserve(i32 %a) {
+  entry:
+    %b = alloca i32
+    store i32 %a, ptr %b
+    %c = load i32, ptr %b
+    ret i32 %c
+  }
+
 ...
 ---
 name:            test
@@ -71,7 +111,7 @@ stack:
 # CHECK-NEXT: maxAlignment:
 # CHECK-NEXT: adjustsStack: true
 # CHECK-NEXT: hasCalls: true
-# CHECK-NEXT: framePointerPolicy: non-leaf
+# CHECK-NEXT: framePointerPolicy: none
 # CHECK-NEXT: stackProtector:  ''
 # CHECK-NEXT: functionContext:  '%stack.0'
 # CHECK-NEXT: maxCallFrameSize: 4
@@ -91,7 +131,6 @@ frameInfo:
   maxAlignment:    4
   adjustsStack:    true
   hasCalls:        true
-  framePointerPolicy: non-leaf
   functionContext: '%stack.0'
   maxCallFrameSize: 4
   cvBytesOfCalleeSavedRegisters: 8
@@ -103,3 +142,58 @@ frameInfo:
 body: |
   bb.0.entry:
 ...
+---
+name:            test_framePointerPolicy_none
+
+# CHECK: test_framePointerPolicy_none
+# CHECK: frameInfo:
+# CHECK: framePointerPolicy: none
+frameInfo:
+  framePointerPolicy: none
+body: |
+  bb.0.entry:
+...
+---
+name:            test_framePointerPolicy_non_leaf
+
+# CHECK: test_framePointerPolicy_non_leaf
+# CHECK: frameInfo:
+# CHECK: framePointerPolicy: non-leaf
+frameInfo:
+  framePointerPolicy: non-leaf
+body: |
+  bb.0.entry:
+...
+---
+name:            test_framePointerPolicy_all
+
+# CHECK: test_framePointerPolicy_all
+# CHECK: frameInfo:
+# CHECK: framePointerPolicy: all
+frameInfo:
+  framePointerPolicy: all
+body: |
+  bb.0.entry:
+...
+---
+name:            test_framePointerPolicy_reserved
+
+# CHECK: test_framePointerPolicy_reserved
+# CHECK: frameInfo:
+# CHECK: framePointerPolicy: reserved
+frameInfo:
+  framePointerPolicy: reserved
+body: |
+  bb.0.entry:
+...
+---
+name:            test_framePointerPolicy_non_leaf_no_reserve
+
+# CHECK: test_framePointerPolicy_non_leaf_no_reserve
+# CHECK: frameInfo:
+# CHECK: framePointerPolicy: non-leaf-no-reserve
+frameInfo:
+  framePointerPolicy: non-leaf-no-reserve
+body: |
+  bb.0.entry:
+...



More information about the llvm-commits mailing list