[clang] [llvm] [clang] Add clang::nooutline Attribute (PR #163493)
Sam Elliott via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 14 21:51:35 PDT 2025
https://github.com/lenary created https://github.com/llvm/llvm-project/pull/163493
This PR:
- Adds a `[[clang::nooutline]]` function attribute for C and C++. There
is no equivalent GNU syntax for this attribute, so no `__attribute__`
syntax.
- Uses the presence of `[[clang::nooutline]]` to add the `nooutline`
attribute to IR function definitions.
- Turns the `"nooutline"` attribute into an enum attribute (without
quotes), and adds an auto-upgrader for bitcode to make that same
change to existing IR.
- Adds test for the above.
The attribute is capable of disabling both the Machine Outliner (enabled
at Oz for some targets), and the IR Outliner (disabled by default).
>From 19afc83278b346629053b52d4655049938332342 Mon Sep 17 00:00:00 2001
From: Sam Elliott <aelliott at qti.qualcomm.com>
Date: Tue, 14 Oct 2025 21:11:24 -0700
Subject: [PATCH] [clang] Add clang::nooutline Attribute
This PR:
- Adds a `[[clang::nooutline]]` function attribute for C and C++. There
is no equivalent GNU syntax for this attribute, so no `__attribute__`
syntax.
- Uses the presence of `[[clang::nooutline]]` to add the `nooutline`
attribute to IR function definitions.
- Turns the `"nooutline"` attribute into an enum attribute (without
quotes), and adds an auto-upgrader for bitcode to make that same
change to existing IR.
- Adds test for the above.
The attribute is capable of disabling both the Machine Outliner (enabled
at Oz for some targets), and the IR Outliner (disabled by default).
---
clang/include/clang/Basic/Attr.td | 7 +++++++
clang/lib/CodeGen/CodeGenModule.cpp | 3 +++
clang/test/CodeGen/attr-nooutline.c | 16 ++++++++++++++++
clang/test/Sema/attr-nooutline.c | 8 ++++++++
clang/test/Sema/attr-nooutline.cpp | 7 +++++++
llvm/docs/LangRef.rst | 2 +-
llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 +
llvm/include/llvm/IR/Attributes.td | 3 +++
llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 ++
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 ++
llvm/lib/CodeGen/MachineOutliner.cpp | 2 +-
llvm/lib/IR/AutoUpgrade.cpp | 6 ++++++
llvm/lib/Transforms/IPO/IROutliner.cpp | 2 +-
llvm/test/Bitcode/upgrade-nooutline.ll | 8 ++++++++
.../machine-outliner-mapper-debug-output.mir | 2 +-
.../Transforms/IROutliner/nooutline-attribute.ll | 4 ++--
16 files changed, 69 insertions(+), 6 deletions(-)
create mode 100644 clang/test/CodeGen/attr-nooutline.c
create mode 100644 clang/test/Sema/attr-nooutline.c
create mode 100644 clang/test/Sema/attr-nooutline.cpp
create mode 100644 llvm/test/Bitcode/upgrade-nooutline.ll
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 22e60aa9fe312..b8a61ba4cbac9 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2355,6 +2355,13 @@ def NoInline : DeclOrStmtAttr {
let SimpleHandler = 1;
}
+def NoOutline : DeclOrStmtAttr {
+ let Spellings = [CXX11<"clang", "nooutline">, C23<"clang", "nooutline">];
+ let Subjects = SubjectList<[Function], ErrorDiag>;
+ let Documentation = [Undocumented];
+ let SimpleHandler = 1;
+}
+
def NoMips16 : InheritableAttr, TargetSpecificAttr<TargetMips32> {
let Spellings = [GCC<"nomips16">];
let Subjects = SubjectList<[Function], ErrorDiag>;
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 8d019d4b2da25..ab267236ed579 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2820,6 +2820,9 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
B.addAttribute(llvm::Attribute::MinSize);
}
+ if (D->hasAttr<NoOutlineAttr>())
+ B.addAttribute(llvm::Attribute::NoOutline);
+
F->addFnAttrs(B);
unsigned alignment = D->getMaxAlignment() / Context.getCharWidth();
diff --git a/clang/test/CodeGen/attr-nooutline.c b/clang/test/CodeGen/attr-nooutline.c
new file mode 100644
index 0000000000000..b9f175da24cb5
--- /dev/null
+++ b/clang/test/CodeGen/attr-nooutline.c
@@ -0,0 +1,16 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --version 6
+// RUN: %clang_cc1 -emit-llvm %s -triple x86_64-unknown-linux-gnu -disable-O0-optnone -o - | FileCheck %s
+
+
+// CHECK: Function Attrs: noinline nooutline nounwind
+// CHECK-LABEL: define dso_local i32 @t1(
+// CHECK-SAME: i32 noundef [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+// CHECK-NEXT: [[ENTRY:.*:]]
+// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// CHECK-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// CHECK-NEXT: ret i32 [[TMP0]]
+//
+[[clang::nooutline]] int t1(int x) {
+ return x;
+}
diff --git a/clang/test/Sema/attr-nooutline.c b/clang/test/Sema/attr-nooutline.c
new file mode 100644
index 0000000000000..05ff644cfdb73
--- /dev/null
+++ b/clang/test/Sema/attr-nooutline.c
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 %s -verify -fsyntax-only
+
+[[clang::nooutline]] int a; // expected-error {{'clang::nooutline' attribute only applies to functions}}
+
+[[clang::nooutline]] void t1(void);
+
+[[clang::nooutline(2)]] void t2(void); // expected-error {{'clang::nooutline' attribute takes no arguments}}
+
diff --git a/clang/test/Sema/attr-nooutline.cpp b/clang/test/Sema/attr-nooutline.cpp
new file mode 100644
index 0000000000000..b6c9b3995081a
--- /dev/null
+++ b/clang/test/Sema/attr-nooutline.cpp
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -verify -fsyntax-only %s -Wno-c++17-extensions
+
+[[clang::nooutline]] int a; // expected-error {{'clang::nooutline' attribute only applies to functions}}
+
+[[clang::nooutline]] void t1(void);
+
+[[clang::nooutline(2)]] void t2(void); // expected-error {{'clang::nooutline' attribute takes no arguments}}
diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst
index 4884e2dcbbe00..73887d1039488 100644
--- a/llvm/docs/LangRef.rst
+++ b/llvm/docs/LangRef.rst
@@ -2738,7 +2738,7 @@ For example:
to signify an unbounded maximum. The syntax `vscale_range(<val>)` can be
used to set both `min` and `max` to the same value. Functions that don't
include this attribute make no assumptions about the value of `vscale`.
-``"nooutline"``
+``nooutline``
This attribute indicates that outlining passes should not modify the
function.
diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
index 464f475098ec5..95596273aad69 100644
--- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h
+++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h
@@ -801,6 +801,7 @@ enum AttributeKindCodes {
ATTR_KIND_CAPTURES = 102,
ATTR_KIND_DEAD_ON_RETURN = 103,
ATTR_KIND_SANITIZE_ALLOC_TOKEN = 104,
+ ATTR_KIND_NOOUTLINE = 105,
};
enum ComdatSelectionKindCodes {
diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td
index 8e7d9dcebfe2a..46a77ec121039 100644
--- a/llvm/include/llvm/IR/Attributes.td
+++ b/llvm/include/llvm/IR/Attributes.td
@@ -207,6 +207,9 @@ def NoImplicitFloat : EnumAttr<"noimplicitfloat", IntersectPreserve, [FnAttr]>;
/// inline=never.
def NoInline : EnumAttr<"noinline", IntersectPreserve, [FnAttr]>;
+/// nooutline
+def NoOutline : EnumAttr<"nooutline", IntersectPreserve, [FnAttr]>;
+
/// Function is called early and/or often, so lazy binding isn't worthwhile.
def NonLazyBind : EnumAttr<"nonlazybind", IntersectPreserve, [FnAttr]>;
diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
index aaee1f0a7687c..ab80da376fdf8 100644
--- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
+++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp
@@ -2257,6 +2257,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::Captures;
case bitc::ATTR_KIND_DEAD_ON_RETURN:
return Attribute::DeadOnReturn;
+ case bitc::ATTR_KIND_NOOUTLINE:
+ return Attribute::NoOutline;
}
}
diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
index 54e916e2dcfe1..0efe7e030e0dc 100644
--- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
+++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
@@ -956,6 +956,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_CAPTURES;
case Attribute::DeadOnReturn:
return bitc::ATTR_KIND_DEAD_ON_RETURN;
+ case Attribute::NoOutline:
+ return bitc::ATTR_KIND_NOOUTLINE;
case Attribute::EndAttrKinds:
llvm_unreachable("Can not encode end-attribute kinds marker.");
case Attribute::None:
diff --git a/llvm/lib/CodeGen/MachineOutliner.cpp b/llvm/lib/CodeGen/MachineOutliner.cpp
index 9feb9740de126..d6f7305278f38 100644
--- a/llvm/lib/CodeGen/MachineOutliner.cpp
+++ b/llvm/lib/CodeGen/MachineOutliner.cpp
@@ -1257,7 +1257,7 @@ void MachineOutliner::populateMapper(InstructionMapper &Mapper, Module &M) {
for (Function &F : M) {
LLVM_DEBUG(dbgs() << "MAPPING FUNCTION: " << F.getName() << "\n");
- if (F.hasFnAttribute("nooutline")) {
+ if (F.hasFnAttribute(Attribute::NoOutline)) {
LLVM_DEBUG(dbgs() << "SKIP: Function has nooutline attribute\n");
continue;
}
diff --git a/llvm/lib/IR/AutoUpgrade.cpp b/llvm/lib/IR/AutoUpgrade.cpp
index f28b98957cae4..f3b164e5d0603 100644
--- a/llvm/lib/IR/AutoUpgrade.cpp
+++ b/llvm/lib/IR/AutoUpgrade.cpp
@@ -5956,6 +5956,12 @@ void llvm::UpgradeFunctionAttributes(Function &F) {
F.removeFnAttr("implicit-section-name");
}
+ if (Attribute A = F.getFnAttribute("nooutline");
+ A.isValid() && A.isStringAttribute()) {
+ F.removeFnAttr("nooutline");
+ F.addFnAttr(Attribute::NoOutline);
+ }
+
if (!F.empty()) {
// For some reason this is called twice, and the first time is before any
// instructions are loaded into the body.
diff --git a/llvm/lib/Transforms/IPO/IROutliner.cpp b/llvm/lib/Transforms/IPO/IROutliner.cpp
index fdf0c3ac8007d..cdcff8fb19236 100644
--- a/llvm/lib/Transforms/IPO/IROutliner.cpp
+++ b/llvm/lib/Transforms/IPO/IROutliner.cpp
@@ -2419,7 +2419,7 @@ void IROutliner::pruneIncompatibleRegions(
if (FnForCurrCand.hasOptNone())
continue;
- if (FnForCurrCand.hasFnAttribute("nooutline")) {
+ if (FnForCurrCand.hasFnAttribute(Attribute::NoOutline)) {
LLVM_DEBUG({
dbgs() << "... Skipping function with nooutline attribute: "
<< FnForCurrCand.getName() << "\n";
diff --git a/llvm/test/Bitcode/upgrade-nooutline.ll b/llvm/test/Bitcode/upgrade-nooutline.ll
new file mode 100644
index 0000000000000..ac0168812bc42
--- /dev/null
+++ b/llvm/test/Bitcode/upgrade-nooutline.ll
@@ -0,0 +1,8 @@
+; RUN: llvm-as < %s | llvm-dis - | FileCheck %s --implicit-check-not=\"nooutline\"
+
+; CHECK: define void @f() [[ATTR:#[0-9]+]]
+; CHECK: attributes [[ATTR]] = { nooutline }
+
+define void @f() "nooutline" {
+ ret void
+}
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-mapper-debug-output.mir b/llvm/test/CodeGen/AArch64/machine-outliner-mapper-debug-output.mir
index 826157e68d75c..056c5f18d492c 100644
--- a/llvm/test/CodeGen/AArch64/machine-outliner-mapper-debug-output.mir
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-mapper-debug-output.mir
@@ -17,7 +17,7 @@
--- |
define void @block_too_small() noredzone { unreachable }
- define void @no_outline() noredzone "nooutline" { unreachable }
+ define void @no_outline() noredzone nooutline { unreachable }
define void @redzone() { unreachable }
declare void @no_mf()
define void @block_addr_fn() noredzone {
diff --git a/llvm/test/Transforms/IROutliner/nooutline-attribute.ll b/llvm/test/Transforms/IROutliner/nooutline-attribute.ll
index eaf3afa3b15a1..a61a1614a3115 100644
--- a/llvm/test/Transforms/IROutliner/nooutline-attribute.ll
+++ b/llvm/test/Transforms/IROutliner/nooutline-attribute.ll
@@ -8,7 +8,7 @@
define void @outlinable() { ret void }
-define i8 @nooutline1(ptr noalias %s, ptr noalias %d, i64 %len) "nooutline" {
+define i8 @nooutline1(ptr noalias %s, ptr noalias %d, i64 %len) nooutline {
%a = load i8, ptr %s
%b = load i8, ptr %d
call void @llvm.memcpy.p0.p0.i64(ptr %d, ptr %s, i64 %len, i1 false)
@@ -17,7 +17,7 @@ define i8 @nooutline1(ptr noalias %s, ptr noalias %d, i64 %len) "nooutline" {
ret i8 %ret
}
-define i8 @nooutline2(ptr noalias %s, ptr noalias %d, i64 %len) "nooutline" {
+define i8 @nooutline2(ptr noalias %s, ptr noalias %d, i64 %len) nooutline {
%a = load i8, ptr %s
%b = load i8, ptr %d
call void @llvm.memcpy.p0.p0.i64(ptr %d, ptr %s, i64 %len, i1 false)
More information about the cfe-commits
mailing list