diff --git a/lib/Transforms/IPO/ArgumentPromotion.cpp b/lib/Transforms/IPO/ArgumentPromotion.cpp index d3a7b0e76fe..461c4e12cbd 100644 --- a/lib/Transforms/IPO/ArgumentPromotion.cpp +++ b/lib/Transforms/IPO/ArgumentPromotion.cpp @@ -817,6 +817,12 @@ promoteArguments(Function *F, function_ref AARGetter, unsigned MaxElements, Optional> ReplaceCallSite) { + // 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)) + return nullptr; + // Make sure that it is local to this module. if (!F->hasLocalLinkage()) return nullptr; diff --git a/lib/Transforms/IPO/FunctionAttrs.cpp b/lib/Transforms/IPO/FunctionAttrs.cpp index 5352e32479b..353daece48e 100644 --- a/lib/Transforms/IPO/FunctionAttrs.cpp +++ b/lib/Transforms/IPO/FunctionAttrs.cpp @@ -1136,7 +1136,8 @@ PreservedAnalyses PostOrderFunctionAttrsPass::run(LazyCallGraph::SCC &C, bool HasUnknownCall = false; for (LazyCallGraph::Node &N : C) { Function &F = N.getFunction(); - if (F.hasFnAttribute(Attribute::OptimizeNone)) { + if (F.hasFnAttribute(Attribute::OptimizeNone) || + F.hasFnAttribute(Attribute::Naked)) { // Treat any function we're trying not to optimize as if it were an // indirect call and omit it from the node set used below. HasUnknownCall = true; @@ -1221,7 +1222,8 @@ static bool runImpl(CallGraphSCC &SCC, AARGetterT AARGetter) { bool ExternalNode = false; for (CallGraphNode *I : SCC) { Function *F = I->getFunction(); - if (!F || F->hasFnAttribute(Attribute::OptimizeNone)) { + if (!F || F->hasFnAttribute(Attribute::OptimizeNone) || + F->hasFnAttribute(Attribute::Naked)) { // External node or function we're trying not to optimize - we both avoid // transform them and avoid leveraging information they provide. ExternalNode = true; diff --git a/lib/Transforms/IPO/GlobalOpt.cpp b/lib/Transforms/IPO/GlobalOpt.cpp index 9dfb61d3d73..274ecf0a099 100644 --- a/lib/Transforms/IPO/GlobalOpt.cpp +++ b/lib/Transforms/IPO/GlobalOpt.cpp @@ -2221,6 +2221,11 @@ OptimizeFunctions(Module &M, TargetLibraryInfo *TLI, for (Module::iterator FI = M.begin(), E = M.end(); FI != E; ) { Function *F = &*FI++; + // Don't perform global opt pass on naked functions; we don't want fast + // calling conventions for naked functions. + if (F->hasFnAttribute(Attribute::Naked)) + continue; + // Functions without names cannot be referenced outside this module. if (!F->hasName() && !F->isDeclaration() && !F->hasLocalLinkage()) F->setLinkage(GlobalValue::InternalLinkage); diff --git a/test/Transforms/ArgumentPromotion/naked_functions.ll b/test/Transforms/ArgumentPromotion/naked_functions.ll new file mode 100644 index 00000000000..70a63f4d02e --- /dev/null +++ b/test/Transforms/ArgumentPromotion/naked_functions.ll @@ -0,0 +1,23 @@ +; RUN: opt < %s -argpromotion -S | FileCheck %s + +; Don't promote paramaters of/arguments to naked functions + +@g = common global i32 0, align 4 + +define i32 @bar() { +entry: + %call = call i32 @foo(i32* @g) +; CHECK: %call = call i32 @foo(i32* @g) + ret i32 %call +} + +define internal i32 @foo(i32*) #0 { +entry: + %retval = alloca i32, align 4 + call void asm sideeffect "ldr r0, [r0] \0Abx lr \0A", ""() + unreachable +} + +; CHECK: define internal i32 @foo(i32*) + +attributes #0 = { naked } diff --git a/test/Transforms/FunctionAttrs/naked_functions.ll b/test/Transforms/FunctionAttrs/naked_functions.ll new file mode 100644 index 00000000000..d34dc0c20d9 --- /dev/null +++ b/test/Transforms/FunctionAttrs/naked_functions.ll @@ -0,0 +1,25 @@ +; RUN: opt -S -functionattrs %s | FileCheck %s +; RUN: opt -S -passes='function-attrs' %s | FileCheck %s + +; Don't change the attributes of parameters of naked functions, in particular +; don't mark them as readnone + +@g = common global i32 0, align 4 + +define i32 @bar() { +entry: + %call = call i32 @foo(i32* @g) +; CHECK: %call = call i32 @foo(i32* @g) + ret i32 %call +} + +define internal i32 @foo(i32*) #0 { +entry: + %retval = alloca i32, align 4 + call void asm sideeffect "ldr r0, [r0] \0Abx lr \0A", ""() + unreachable +} + +; CHECK: define internal i32 @foo(i32*) + +attributes #0 = { naked } diff --git a/test/Transforms/GlobalOpt/naked_functions.ll b/test/Transforms/GlobalOpt/naked_functions.ll new file mode 100644 index 00000000000..80c3aa8c3b2 --- /dev/null +++ b/test/Transforms/GlobalOpt/naked_functions.ll @@ -0,0 +1,23 @@ +; RUN: opt < %s -globalopt -S | FileCheck %s + +; Check that naked functions don't get marked with fast calling conventions + +@g = common global i32 0, align 4 + +define i32 @bar() { +entry: + %call = call i32 @foo(i32* @g) +; CHECK: %call = call i32 @foo(i32* @g) + ret i32 %call +} + +define internal i32 @foo(i32*) #0 { +entry: + %retval = alloca i32, align 4 + call void asm sideeffect "ldr r0, [r0] \0Abx lr \0A", ""() + unreachable +} + +; CHECK: define internal i32 @foo(i32*) + +attributes #0 = { naked }