https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/211804
>From e50a09ba77934bd3d932160fdcc36480c23772fa Mon Sep 17 00:00:00 2001 From: Joseph Huber <[email protected]> Date: Fri, 24 Jul 2026 09:10:47 -0500 Subject: [PATCH] [IR][IPO] Don't rewrite the signature of optnone functions Summary: This PR unifies the behavior of attributes the prevent signature requires, like `naked`, with `optnone`. Currently, `optnone` functions are not restricted from taking plcae in IPO passes by design. However, this means that in places the signature will still be rewritten, which is likely not what users who make use of `optnone` desire. This PR will allow `optnone` functions to still take place in IPO optimizations but will disallow signatures being rewritten. This ensures the funciton itself does not change beteen `-O0` and `-O2`. --- .../amdgpu-enqueue-kernel-linking.cl | 4 +-- llvm/docs/LangRef.md | 1 + llvm/docs/ReleaseNotes.md | 4 +++ llvm/include/llvm/IR/Function.h | 7 ++++ llvm/lib/Transforms/IPO/ArgumentPromotion.cpp | 8 ++--- llvm/lib/Transforms/IPO/Attributor.cpp | 6 ++++ .../IPO/DeadArgumentElimination.cpp | 24 ++++++------- llvm/lib/Transforms/IPO/GlobalOpt.cpp | 3 ++ .../Transforms/ArgumentPromotion/optnone.ll | 36 +++++++++++++++++++ llvm/test/Transforms/DeadArgElim/optnone.ll | 30 ++++++++++++++++ llvm/test/Transforms/GlobalOpt/optnone.ll | 18 ++++++++++ 11 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 llvm/test/Transforms/ArgumentPromotion/optnone.ll create mode 100644 llvm/test/Transforms/DeadArgElim/optnone.ll create mode 100644 llvm/test/Transforms/GlobalOpt/optnone.ll diff --git a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl index bdb5918b00814..97446c61f9ec7 100644 --- a/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl +++ b/clang/test/CodeGenOpenCL/amdgpu-enqueue-kernel-linking.cl @@ -30,7 +30,7 @@ // CHECK-LABEL: define dso_local amdgpu_kernel void @test_kernel_first( -// CHECK-LABEL: define internal fastcc void @static_invoker(ptr addrspace(1) noundef %outptr, ptr addrspace(1) noundef %argptr) +// CHECK-LABEL: define internal void @static_invoker(ptr addrspace(1) noundef %outptr, ptr addrspace(1) noundef %argptr) // CHECK: call i32 @__enqueue_kernel_basic(ptr addrspace(1) %{{[0-9]+}}, i32 %{{[0-9]+}}, ptr addrspace(5) %tmp, ptr addrspacecast (ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle to ptr), ptr %{{.+}}) // CHECK: declare i32 @__enqueue_kernel_basic(ptr addrspace(1), i32, ptr addrspace(5), ptr, ptr) local_unnamed_addr @@ -48,7 +48,7 @@ // CHECK-LABEL: define dso_local amdgpu_kernel void @test_kernel_second(ptr addrspace(1) noundef align 4 %outptr, ptr addrspace(1) noundef align 4 %argptr, ptr addrspace(1) noundef align 4 %difference) -// CHECK-LABEL: define internal fastcc void @static_invoker.5(ptr addrspace(1) noundef %outptr, ptr addrspace(1) noundef %argptr) unnamed_addr #{{[0-9]+}} { +// CHECK-LABEL: define internal void @static_invoker.5(ptr addrspace(1) noundef %outptr, ptr addrspace(1) noundef %argptr) unnamed_addr #{{[0-9]+}} { // CHECK: call i32 @__enqueue_kernel_basic(ptr addrspace(1) %{{[0-9]+}}, i32 %{{[0-9]+}}, ptr addrspace(5) %tmp, ptr addrspacecast (ptr addrspace(1) @__static_invoker_block_invoke_kernel.runtime.handle.3 to ptr), ptr %{{.+}}) diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index d57f619042f22..431bebd067e3c 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -2551,6 +2551,7 @@ fn -> other_fn -> other_fn ; fn is norecurse `optnone` : This function attribute indicates that most optimization passes will skip this function, with the exception of interprocedural optimization passes. + Interprocedural passes may will analyze this function but will not alter it. Code generation defaults to the "fast" instruction selector. This attribute cannot be used together with the `alwaysinline` attribute; this attribute is also incompatible diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index f8fce847b62f3..abe3ab42e3bb3 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -64,6 +64,10 @@ Makes programs 10x faster by doing Special New Thing. ### Changes to Interprocedural Optimizations +- Interprocedural passes no longer rewrite the signature, calling convention, or + parameter and return attributes of functions marked `optnone`. Interprocedural + analysis of such functions is unaffected. + - The IR Outliner has been removed, due to lack of a maintainer and the presence of correctness issues. diff --git a/llvm/include/llvm/IR/Function.h b/llvm/include/llvm/IR/Function.h index 0238c9b352f5f..c1830c4b2f8d9 100644 --- a/llvm/include/llvm/IR/Function.h +++ b/llvm/include/llvm/IR/Function.h @@ -684,6 +684,13 @@ class LLVM_ABI Function : public GlobalObject, public ilist_node<Function> { /// Do not optimize this function (-O0). bool hasOptNone() const { return hasFnAttribute(Attribute::OptimizeNone); } + /// Determine whether interprocedural transforms may rewrite this function's + /// signature. + bool canChangeSignature() const { + return !hasFnAttribute(Attribute::Naked) && + !hasFnAttribute(Attribute::NoIPA) && !hasOptNone(); + } + /// Optimize this function for minimum size (-Oz). bool hasMinSize() const { return hasFnAttribute(Attribute::MinSize); } diff --git a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp index 9dfcd0926e4c7..51821dd7f23bb 100644 --- a/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/llvm/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -807,10 +807,10 @@ static bool areTypesABICompatible(ArrayRef<Type *> Types, const Function &F, /// calls the DoPromotion method. static Function *promoteArguments(Function *F, FunctionAnalysisManager &FAM, unsigned MaxElements, bool IsRecursive) { - // Don't perform argument promotion for naked functions; otherwise we can end - // up removing parameters that are seemingly 'not used' as they are referred - // to in the assembly. - if (F->hasFnAttribute(Attribute::Naked)) + // Don't rewrite the signature of functions whose ABI must be preserved. For + // naked functions we can end up removing parameters that are seemingly 'not + // used' as they are referred to in the assembly. + if (!F->canChangeSignature()) return nullptr; // Make sure that it is local to this module. diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp index 07d2435108ac7..a8addc8c65e2c 100644 --- a/llvm/lib/Transforms/IPO/Attributor.cpp +++ b/llvm/lib/Transforms/IPO/Attributor.cpp @@ -2921,6 +2921,12 @@ bool Attributor::isValidFunctionSignatureRewrite( return false; Function *Fn = Arg.getParent(); + if (!Fn->canChangeSignature()) { + LLVM_DEBUG(dbgs() << "[Attributor] Cannot rewrite the signature of a " + "function whose ABI must be preserved\n"); + return false; + } + auto CallSiteCanBeChanged = [Fn](AbstractCallSite ACS) { // Forbid the call site to cast the function return type. If we need to // rewrite these functions we need to re-create a cast for the new call site diff --git a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp index dcbf60ab55c72..277c7a11d7eea 100644 --- a/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp +++ b/llvm/lib/Transforms/IPO/DeadArgumentElimination.cpp @@ -104,10 +104,10 @@ bool DeadArgumentEliminationPass::deleteDeadVarargs(Function &F) { if (F.hasAddressTaken()) return false; - // Don't touch naked functions. The assembly might be using an argument, or - // otherwise rely on the frame layout in a way that this analysis will not - // see. - if (F.hasFnAttribute(Attribute::Naked)) { + // Don't touch functions whose ABI must be preserved. For naked functions the + // assembly might be using an argument, or otherwise rely on the frame layout + // in a way that this analysis will not see. + if (!F.canChangeSignature()) { return false; } @@ -252,10 +252,10 @@ bool DeadArgumentEliminationPass::removeDeadArgumentsFromCallers(Function &F) { !F.getFunctionType()->isVarArg()) return false; - // Don't touch naked functions. The assembly might be using an argument, or - // otherwise rely on the frame layout in a way that this analysis will not - // see. - if (F.hasFnAttribute(Attribute::Naked)) + // Don't touch functions whose ABI must be preserved. For naked functions the + // assembly might be using an argument, or otherwise rely on the frame layout + // in a way that this analysis will not see. + if (!F.canChangeSignature()) return false; if (F.use_empty()) @@ -474,10 +474,10 @@ void DeadArgumentEliminationPass::surveyFunction(const Function &F) { return; } - // Don't touch naked functions. The assembly might be using an argument, or - // otherwise rely on the frame layout in a way that this analysis will not - // see. - if (F.hasFnAttribute(Attribute::Naked)) { + // Don't touch functions whose ABI must be preserved. For naked functions the + // assembly might be using an argument, or otherwise rely on the frame layout + // in a way that this analysis will not see. + if (!F.canChangeSignature()) { markFrozen(F); return; } diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp index 0618073387d97..f78b7169a6a26 100644 --- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp +++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp @@ -1719,6 +1719,9 @@ static bool hasChangeableCCImpl(Function *F) { if (CC != CallingConv::C && CC != CallingConv::X86_ThisCall) return false; + if (!F->canChangeSignature()) + return false; + if (F->isVarArg()) return false; diff --git a/llvm/test/Transforms/ArgumentPromotion/optnone.ll b/llvm/test/Transforms/ArgumentPromotion/optnone.ll new file mode 100644 index 0000000000000..36e97f402991c --- /dev/null +++ b/llvm/test/Transforms/ArgumentPromotion/optnone.ll @@ -0,0 +1,36 @@ +; RUN: opt -passes=argpromotion -S < %s | FileCheck %s + +declare void @sink(i32) + +; CHECK-LABEL: define internal void @optnone_promote(ptr %X) +; CHECK-NOT: DW_CC_nocall +define internal void @optnone_promote(ptr %X) optnone noinline !dbg !4 { + %v = load i32, ptr %X, align 4 + call void @sink(i32 %v) + ret void +} + +; CHECK-LABEL: define internal void @promote(i32 %X.0.val) +define internal void @promote(ptr %X) { + %v = load i32, ptr %X, align 4 + call void @sink(i32 %v) + ret void +} + +define void @caller(ptr %Y, ptr %Z) { + call void @optnone_promote(ptr %Y) + call void @promote(ptr %Z) + ret void +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +!1 = !DIFile(filename: "optnone.c", directory: "/") +!2 = !DISubroutineType(types: !5) +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "optnone_promote", scope: !1, file: !1, line: 1, type: !2, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0) +!5 = !{null, !6} +!6 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 64) +!7 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) diff --git a/llvm/test/Transforms/DeadArgElim/optnone.ll b/llvm/test/Transforms/DeadArgElim/optnone.ll new file mode 100644 index 0000000000000..ae09a56c24d98 --- /dev/null +++ b/llvm/test/Transforms/DeadArgElim/optnone.ll @@ -0,0 +1,30 @@ +; RUN: opt -passes=deadargelim -S < %s | FileCheck %s + +; CHECK-LABEL: define internal i32 @optnone_dead_arg(i32 %live, i32 %dead) +; CHECK-NOT: DW_CC_nocall +define internal i32 @optnone_dead_arg(i32 %live, i32 %dead) optnone noinline !dbg !4 { + ret i32 %live +} + +; CHECK-LABEL: define internal i32 @dead_arg(i32 %live) +define internal i32 @dead_arg(i32 %live, i32 %dead) { + ret i32 %live +} + +define i32 @caller() { + %a = call i32 @optnone_dead_arg(i32 1, i32 2) + %b = call i32 @dead_arg(i32 3, i32 4) + %c = add i32 %a, %b + ret i32 %c +} + +!llvm.dbg.cu = !{!0} +!llvm.module.flags = !{!3} + +!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug) +!1 = !DIFile(filename: "optnone.c", directory: "/") +!2 = !DISubroutineType(types: !7) +!3 = !{i32 2, !"Debug Info Version", i32 3} +!4 = distinct !DISubprogram(name: "optnone_dead_arg", scope: !1, file: !1, line: 1, type: !2, scopeLine: 1, spFlags: DISPFlagDefinition, unit: !0) +!7 = !{!8, !8, !8} +!8 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) diff --git a/llvm/test/Transforms/GlobalOpt/optnone.ll b/llvm/test/Transforms/GlobalOpt/optnone.ll new file mode 100644 index 0000000000000..550a3313bf89a --- /dev/null +++ b/llvm/test/Transforms/GlobalOpt/optnone.ll @@ -0,0 +1,18 @@ +; RUN: opt -passes=globalopt -S < %s | FileCheck %s + +; CHECK: define internal fastcc i32 @foo( +define internal i32 @foo(i32 %x) noinline { + ret i32 %x +} + +; CHECK: define internal i32 @foo_optnone( +define internal i32 @foo_optnone(i32 %x) optnone noinline { + ret i32 %x +} + +define i32 @bar() { + %r = call i32 @foo(i32 5) + %s = call i32 @foo_optnone(i32 5) + %res = add i32 %r, %s + ret i32 %res +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
