[llvm] [dyndbg][IPSCCP] Add 'no-func-spec' function attribute (PR #194846)
Orlando Cazalet-Hyams via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 29 05:52:51 PDT 2026
https://github.com/OCHyams created https://github.com/llvm/llvm-project/pull/194846
Add 'no-func-spec' function attribute to disable function specialization.
Dynamic debugging introduces unoptimized versions of functions into an external module, to which a debugger may divert control flow by patching a jump in the optimized version.
Function specialization creates duplicates specialized for specific call sites, which it can do safely by creating internal-linkage copies of external-linkage functions.
Routing control to a non-specialized unoptimized function from an optimized specialization would be incorrect (the argument set up may be wrong), and there's no version of the specialized function in the unoptimized module to route to instead. To avoid cases where it's not possible to step into unoptimized code we'd prefer to disable function specialization.
---
'noduplicte' was raised as an alternative but isn't quite right as that limits inlining and is generally more restrictive than we need. The LangRef says “this attribute indicates that calls to the function cannot be duplicated” but we have no such requirement for calls in dynamic debugging.
We could have Clang disable the optimization in the pass builder (see IPSCCPOptions) when dynamic debugging is enabled instead of using an attribute, which may be a reasonable alternative.
I chose an attribute for the initial version as it's easier to test and less invasive. Another advantage is an attribute can be used by other passes, e.g. the machine outliner.
>From 1ac2a3c7c1dfaf3d38ced7ce4597e246767b1a26 Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Thu, 2 Apr 2026 15:32:41 +0100
Subject: [PATCH 1/2] [dyndbg][IPSCCP] Add 'no-func-spec' function attribute
Add 'no-func-spec' function attribute to disable function specialization.
Dynamic debugging introduces unoptimized versions of functions into an external
module, to which a debugger may divert control flow by patching a jump in the
optimized version.
Function specialization creates duplicates specialized for specific call sites,
which it can do safely by creating internal-linkage copies of external-linkage
functions.
Routing control to a non-specialized unoptimized function from an optimized
specialization would be incorrect (the argument set up may be wrong), and
there's no version of the specialized function in the unoptimized module to
route to instead. To avoid cases where it's not possible to step into
unoptimized code we'd prefer to disable function specialization.
---
We could have Clang disable the optimization in the pass builder (see
IPSCCPOptions) when dynamic debugging is enabled instead of using an attribute,
which may be a reasonable alternative.
I chose an attribute for the initial version as it's easier to test and less
invasive. Another advantage is an attribute can be used by other passes,
e.g. the machine outliner.
---
.../Transforms/IPO/FunctionSpecialization.cpp | 3 ++
.../attr-no-func-spec.ll | 37 +++++++++++++++++++
2 files changed, 40 insertions(+)
create mode 100644 llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll
diff --git a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
index 1367940ad8ef8..2ca6df3b37a01 100644
--- a/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
+++ b/llvm/lib/Transforms/IPO/FunctionSpecialization.cpp
@@ -1047,6 +1047,9 @@ bool FunctionSpecializer::isCandidateFunction(Function *F) {
if (F->hasOptSize())
return false;
+ if (F->hasFnAttribute("no-func-spec"))
+ return false;
+
// Do not specialize the cloned function again.
if (Specializations.contains(F))
return false;
diff --git a/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll b/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll
new file mode 100644
index 0000000000000..6c5c9bd568bca
--- /dev/null
+++ b/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll
@@ -0,0 +1,37 @@
+; RUN: opt -passes="ipsccp<func-spec>" -funcspec-min-function-size=3 -S < %s \
+; RUN: | FileCheck %s --implicit-check-not=specialized
+
+; Check the attribute "no-func-spec" blocks function specialization. 'do_spec'
+; gets specialized but 'no_spec' which is identical except for the attribute
+; shouldn't (--implicit-check-not) .
+
+; CHECK: %call = tail call i32 @do_spec.specialized.1(i32 noundef 0)
+; CHECK: define internal i32 @do_spec.specialized.1(i32 noundef %a)
+
+declare i32 @ext(i32 noundef)
+
+define hidden i32 @do_spec(i32 noundef %a) {
+entry:
+ %call = tail call i32 @ext(i32 noundef %a)
+ ret i32 %call
+}
+
+define hidden void @a() {
+entry:
+ %call = tail call i32 @do_spec(i32 noundef 0)
+ ret void
+}
+
+define hidden i32 @no_spec(i32 noundef %a) #0 {
+entry:
+ %call = tail call i32 @ext(i32 noundef %a)
+ ret i32 %call
+}
+
+define hidden void @b() {
+entry:
+ %call = tail call i32 @no_spec(i32 noundef 0)
+ ret void
+}
+
+attributes #0 = { "no-func-spec" }
>From 38f5fd439bb318dfe980a6d6f9e20b8fb8eac36a Mon Sep 17 00:00:00 2001
From: Orlando Cazalet-Hyams <orlando.hyams at sony.com>
Date: Wed, 29 Apr 2026 13:47:45 +0100
Subject: [PATCH 2/2] whitespace
---
.../test/Transforms/FunctionSpecialization/attr-no-func-spec.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll b/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll
index 6c5c9bd568bca..7da896077881c 100644
--- a/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll
+++ b/llvm/test/Transforms/FunctionSpecialization/attr-no-func-spec.ll
@@ -3,7 +3,7 @@
; Check the attribute "no-func-spec" blocks function specialization. 'do_spec'
; gets specialized but 'no_spec' which is identical except for the attribute
-; shouldn't (--implicit-check-not) .
+; shouldn't (--implicit-check-not).
; CHECK: %call = tail call i32 @do_spec.specialized.1(i32 noundef 0)
; CHECK: define internal i32 @do_spec.specialized.1(i32 noundef %a)
More information about the llvm-commits
mailing list