Skip to content

Conversation

fpetrogalli
Copy link
Member

@fpetrogalli fpetrogalli commented Aug 18, 2025

[clang][driver][arm][macho] Default to -mframe-pointer=non-leaf.

The commit in [1] changes the behavior of the Arm backend for the
attribute frame-pointer=all. Before [1], leaf functions marked with
frame-pointer=all were not emitting the frame-pointer.

After [1], frame-pointer=all started generating frame pointer for all
functions, including leaf functions.

However, the default behavior for the driver in clang is to emit the
command line option -mframe-pointer=all on Arm, if no options for
handling the frame pointer is specified at command line. This causes
observable regressions.

This patch addresses these regressions by configuring the driver so
to emit -mframe-pointer=non-leaf when targeting Arm.

Codegen tests dealing with frame pointer generation have been extended
to handle functions with a tail call, since this configuration was
missing.

[1] 4a2bd78f5b0d0661c23dff9c4b93a393a49dbf9aWIP

@llvmbot llvmbot added clang Clang issues not falling into any other category backend:ARM clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' labels Aug 18, 2025
@llvmbot
Copy link
Member

llvmbot commented Aug 18, 2025

@llvm/pr-subscribers-clang-driver

@llvm/pr-subscribers-clang

Author: Francesco Petrogalli (fpetrogalli)

Changes

The change in [1] uncovered a bug in the emission of the frame pointer.

Specifically, before the fix in [1], non-leaf functions with a tail call were not emitting the frame pointer even if the default behavior emitted by clang was "frame-pointer=all".

After the fix in [1], we had a set of internal build failures, caused by the fact that suddenly all non-leaf functions with tail calls started needing extra 4/8/16 bytes because of the extra push/pop instructions emitted to handle the frame pointer. Because of this, the size constraints we had on the firmware binaries were not met anymore.

These projects are built with -Oz, therefore we have changed the default behavior of the Driver to emit -mframe-pointer=non-leaf when targeting anything that cares about size on MachO for armv7 (-Oz and -Os).

The default behavior is also checked against
-mno-omit-leaf-frame-pointer.

An LLVM codegen test has also been added to make sure the test case we have in our firmware does not regress.

[1] #109628


Full diff: https://github.com/llvm/llvm-project/pull/154216.diff

4 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/CommonArgs.cpp (+13-2)
  • (modified) clang/test/Driver/frame-pointer-elim.c (+26)
  • (modified) clang/test/Driver/frame-pointer.c (+14)
  • (added) llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll (+31)
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index eb88f7bd4b469..73f005b66bad4 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -151,11 +151,22 @@ static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
   return true;
 }
 
-static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
+static bool
+useLeafFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
+                                      const llvm::Triple &Triple) {
   if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
       (Triple.isAndroid() && !Triple.isARM()))
     return false;
 
+  if (Triple.isARM() && Triple.isOSBinFormatMachO()) {
+    if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
+      if (A->getOption().matches(options::OPT_O)) {
+        StringRef S(A->getValue());
+        if (S == "s" || S == "z")
+          return false;
+      }
+    }
+  }
   return true;
 }
 
@@ -252,7 +263,7 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
                    clang::driver::options::OPT_fomit_frame_pointer, DefaultFP);
 
   bool DefaultLeafFP =
-      useLeafFramePointerForTargetByDefault(Triple) ||
+      useLeafFramePointerForTargetByDefault(Args, Triple) ||
       (EnableFP && framePointerImpliesLeafFramePointer(Args, Triple));
   bool EnableLeafFP = Args.hasFlag(
       clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
diff --git a/clang/test/Driver/frame-pointer-elim.c b/clang/test/Driver/frame-pointer-elim.c
index 6e21671f43775..c7206986a18ad 100644
--- a/clang/test/Driver/frame-pointer-elim.c
+++ b/clang/test/Driver/frame-pointer-elim.c
@@ -221,5 +221,31 @@
 // RUN: %clang -### --target=aarch64-pc-windows-msvc -S -fomit-frame-pointer %s 2>&1 |  \
 // RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
 
+// At all optimization levels, -fno-omit-frame-pointer gives the default behavior.
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NON-LEAF %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NON-LEAF %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+
+// All all optimization levels, the frame pointer is "none" if -fomit-frame-pointer is specified.
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+
+// At -Oz/-Os, -fno-omit-frame-pointer + -mno-omit-leaf-frame-pointer give frame-pointer=all
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+
+// At -O0/1/2/3, -fomit-frame-pointer wins over -mno-omit-leaf-frame-pointer
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+
 void f0() {}
 void f1() { f0(); }
diff --git a/clang/test/Driver/frame-pointer.c b/clang/test/Driver/frame-pointer.c
index 2015fa520c2a2..5a9eec5a9ae4a 100644
--- a/clang/test/Driver/frame-pointer.c
+++ b/clang/test/Driver/frame-pointer.c
@@ -80,12 +80,26 @@
 // RUN: %clang --target=loongarch64 -### -S -O3 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK3-64 %s
 // RUN: %clang --target=loongarch64 -### -S -Os %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKs-64 %s
 
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKs-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKz-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK0-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK1-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK2-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK3-32-MACHO %s
+
 // CHECK0-32: -mframe-pointer=all
 // CHECK1-32-NOT: -mframe-pointer=all
 // CHECK2-32-NOT: -mframe-pointer=all
 // CHECK3-32-NOT: -mframe-pointer=all
 // CHECKs-32-NOT: -mframe-pointer=all
 
+// CHECKs-32-MACHO: -mframe-pointer=non-leaf
+// CHECKz-32-MACHO: -mframe-pointer=non-leaf
+// CHECK0-32-MACHO: -mframe-pointer=all
+// CHECK1-32-MACHO: -mframe-pointer=all
+// CHECK2-32-MACHO: -mframe-pointer=all
+// CHECK3-32-MACHO: -mframe-pointer=all
+
 // CHECK0-64: -mframe-pointer=all
 // CHECK1-64-NOT: -mframe-pointer=all
 // CHECK2-64-NOT: -mframe-pointer=all
diff --git a/llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll b/llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll
new file mode 100644
index 0000000000000..5bb19825a7655
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc < %s | FileCheck %s
+
+target triple = "thumbv7-apple-macho"
+
+define void @foo(ptr %0) "frame-pointer"="all" {
+; CHECK-LABEL: foo:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    push {r7, lr}
+; CHECK-NEXT:    mov r7, sp
+; CHECK-NEXT:    ldrh r0, [r0]
+; CHECK-NEXT:    pop.w {r7, lr}
+; CHECK-NEXT:    b bar
+  %2 = load i16, ptr %0, align 2
+  tail call void @bar(i16 %2)
+  ret void
+}
+
+declare void @bar(i16) "frame-pointer"="all"
+
+define void @foo_02(ptr %0) "frame-pointer"="non-leaf" {
+; CHECK-LABEL: foo_02:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    ldrh r0, [r0]
+; CHECK-NEXT:    b bar_02
+  %2 = load i16, ptr %0, align 2
+  tail call void @bar_02(i16 %2)
+  ret void
+}
+
+declare void @bar_02(i16) "frame-pointer"="non-leaf"

@llvmbot
Copy link
Member

llvmbot commented Aug 18, 2025

@llvm/pr-subscribers-backend-arm

Author: Francesco Petrogalli (fpetrogalli)

Changes

The change in [1] uncovered a bug in the emission of the frame pointer.

Specifically, before the fix in [1], non-leaf functions with a tail call were not emitting the frame pointer even if the default behavior emitted by clang was "frame-pointer=all".

After the fix in [1], we had a set of internal build failures, caused by the fact that suddenly all non-leaf functions with tail calls started needing extra 4/8/16 bytes because of the extra push/pop instructions emitted to handle the frame pointer. Because of this, the size constraints we had on the firmware binaries were not met anymore.

These projects are built with -Oz, therefore we have changed the default behavior of the Driver to emit -mframe-pointer=non-leaf when targeting anything that cares about size on MachO for armv7 (-Oz and -Os).

The default behavior is also checked against
-mno-omit-leaf-frame-pointer.

An LLVM codegen test has also been added to make sure the test case we have in our firmware does not regress.

[1] #109628


Full diff: https://github.com/llvm/llvm-project/pull/154216.diff

4 Files Affected:

  • (modified) clang/lib/Driver/ToolChains/CommonArgs.cpp (+13-2)
  • (modified) clang/test/Driver/frame-pointer-elim.c (+26)
  • (modified) clang/test/Driver/frame-pointer.c (+14)
  • (added) llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll (+31)
diff --git a/clang/lib/Driver/ToolChains/CommonArgs.cpp b/clang/lib/Driver/ToolChains/CommonArgs.cpp
index eb88f7bd4b469..73f005b66bad4 100644
--- a/clang/lib/Driver/ToolChains/CommonArgs.cpp
+++ b/clang/lib/Driver/ToolChains/CommonArgs.cpp
@@ -151,11 +151,22 @@ static bool useFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
   return true;
 }
 
-static bool useLeafFramePointerForTargetByDefault(const llvm::Triple &Triple) {
+static bool
+useLeafFramePointerForTargetByDefault(const llvm::opt::ArgList &Args,
+                                      const llvm::Triple &Triple) {
   if (Triple.isAArch64() || Triple.isPS() || Triple.isVE() ||
       (Triple.isAndroid() && !Triple.isARM()))
     return false;
 
+  if (Triple.isARM() && Triple.isOSBinFormatMachO()) {
+    if (Arg *A = Args.getLastArg(options::OPT_O_Group)) {
+      if (A->getOption().matches(options::OPT_O)) {
+        StringRef S(A->getValue());
+        if (S == "s" || S == "z")
+          return false;
+      }
+    }
+  }
   return true;
 }
 
@@ -252,7 +263,7 @@ getFramePointerKind(const llvm::opt::ArgList &Args,
                    clang::driver::options::OPT_fomit_frame_pointer, DefaultFP);
 
   bool DefaultLeafFP =
-      useLeafFramePointerForTargetByDefault(Triple) ||
+      useLeafFramePointerForTargetByDefault(Args, Triple) ||
       (EnableFP && framePointerImpliesLeafFramePointer(Args, Triple));
   bool EnableLeafFP = Args.hasFlag(
       clang::driver::options::OPT_mno_omit_leaf_frame_pointer,
diff --git a/clang/test/Driver/frame-pointer-elim.c b/clang/test/Driver/frame-pointer-elim.c
index 6e21671f43775..c7206986a18ad 100644
--- a/clang/test/Driver/frame-pointer-elim.c
+++ b/clang/test/Driver/frame-pointer-elim.c
@@ -221,5 +221,31 @@
 // RUN: %clang -### --target=aarch64-pc-windows-msvc -S -fomit-frame-pointer %s 2>&1 |  \
 // RUN:   FileCheck --check-prefix=KEEP-RESERVED %s
 
+// At all optimization levels, -fno-omit-frame-pointer gives the default behavior.
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NON-LEAF %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NON-LEAF %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s -fno-omit-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+
+// All all optimization levels, the frame pointer is "none" if -fomit-frame-pointer is specified.
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s -fomit-frame-pointer    2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+
+// At -Oz/-Os, -fno-omit-frame-pointer + -mno-omit-leaf-frame-pointer give frame-pointer=all
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s -fno-omit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-ALL %s
+
+// At -O0/1/2/3, -fomit-frame-pointer wins over -mno-omit-leaf-frame-pointer
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s -fomit-frame-pointer -mno-omit-leaf-frame-pointer 2>&1 | FileCheck -check-prefix=KEEP-NONE %s
+
 void f0() {}
 void f1() { f0(); }
diff --git a/clang/test/Driver/frame-pointer.c b/clang/test/Driver/frame-pointer.c
index 2015fa520c2a2..5a9eec5a9ae4a 100644
--- a/clang/test/Driver/frame-pointer.c
+++ b/clang/test/Driver/frame-pointer.c
@@ -80,12 +80,26 @@
 // RUN: %clang --target=loongarch64 -### -S -O3 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK3-64 %s
 // RUN: %clang --target=loongarch64 -### -S -Os %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKs-64 %s
 
+// RUN: %clang --target=armv7-apple-macho -### -S -Os %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKs-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -Oz %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECKz-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O0 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK0-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O1 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK1-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O2 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK2-32-MACHO %s
+// RUN: %clang --target=armv7-apple-macho -### -S -O3 %s -o %t.s 2>&1 | FileCheck -check-prefix=CHECK3-32-MACHO %s
+
 // CHECK0-32: -mframe-pointer=all
 // CHECK1-32-NOT: -mframe-pointer=all
 // CHECK2-32-NOT: -mframe-pointer=all
 // CHECK3-32-NOT: -mframe-pointer=all
 // CHECKs-32-NOT: -mframe-pointer=all
 
+// CHECKs-32-MACHO: -mframe-pointer=non-leaf
+// CHECKz-32-MACHO: -mframe-pointer=non-leaf
+// CHECK0-32-MACHO: -mframe-pointer=all
+// CHECK1-32-MACHO: -mframe-pointer=all
+// CHECK2-32-MACHO: -mframe-pointer=all
+// CHECK3-32-MACHO: -mframe-pointer=all
+
 // CHECK0-64: -mframe-pointer=all
 // CHECK1-64-NOT: -mframe-pointer=all
 // CHECK2-64-NOT: -mframe-pointer=all
diff --git a/llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll b/llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll
new file mode 100644
index 0000000000000..5bb19825a7655
--- /dev/null
+++ b/llvm/test/CodeGen/ARM/macho-leaf-functions-fp.ll
@@ -0,0 +1,31 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
+; RUN: llc < %s | FileCheck %s
+
+target triple = "thumbv7-apple-macho"
+
+define void @foo(ptr %0) "frame-pointer"="all" {
+; CHECK-LABEL: foo:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    push {r7, lr}
+; CHECK-NEXT:    mov r7, sp
+; CHECK-NEXT:    ldrh r0, [r0]
+; CHECK-NEXT:    pop.w {r7, lr}
+; CHECK-NEXT:    b bar
+  %2 = load i16, ptr %0, align 2
+  tail call void @bar(i16 %2)
+  ret void
+}
+
+declare void @bar(i16) "frame-pointer"="all"
+
+define void @foo_02(ptr %0) "frame-pointer"="non-leaf" {
+; CHECK-LABEL: foo_02:
+; CHECK:       @ %bb.0:
+; CHECK-NEXT:    ldrh r0, [r0]
+; CHECK-NEXT:    b bar_02
+  %2 = load i16, ptr %0, align 2
+  tail call void @bar_02(i16 %2)
+  ret void
+}
+
+declare void @bar_02(i16) "frame-pointer"="non-leaf"

@fpetrogalli fpetrogalli changed the title [arm][macho] Force -mframe-pointer=non-leaf for -Oz and -Os [arm][macho] Default -mframe-pointer=non-leaf. Aug 25, 2025
@fpetrogalli fpetrogalli changed the title [arm][macho] Default -mframe-pointer=non-leaf. [arm][macho] Default to -mframe-pointer=non-leaf. Aug 25, 2025
@fpetrogalli fpetrogalli changed the title [arm][macho] Default to -mframe-pointer=non-leaf. [clang][driver][arm][macho] Default to -mframe-pointer=non-leaf. Aug 25, 2025
@fpetrogalli fpetrogalli force-pushed the armv7-macho-Oz-frame-pointer branch from 7d021d6 to ece21d3 Compare August 25, 2025 23:16
The commit in [1] changes the behavior of the Arm backend for the
attribute frame-pointer=all. Before [1], leaf functions marked with
frame-pointer=all were not emitting the frame-pointer.

After [1], frame-pointer=all started generating frame pointer for all
functions, including leaf functions.

However, the default behavior for the driver in clang is to emit the
command line option `-mframe-pointer=all` on Arm, if no options for
handling the frame pointer is specified at command line. This causes
observable regressions.

This patch addresses these regressions by configuring the driver so
to emit `-mframe-pointer=non-leaf` when targeting Arm.

Codegen tests dealing with frame pointer generation have been extended
to handle functions with a tail call, since this configuration was
missing.

[1] 4a2bd78
@fpetrogalli fpetrogalli force-pushed the armv7-macho-Oz-frame-pointer branch from ece21d3 to ab60c69 Compare August 26, 2025 23:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend:ARM clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants