Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ clang/lib/CodeGen/CGCall.cpp @@ -4866,6 +4866,18 @@ Callee.getAbstractInfo(), Attrs, CallingConv, /*AttrOnCallSite=*/true); + // Calling a function that aliases to self through an AsmLabel is a pattern + // used by glibc for fortified functions. Mark these as NoInline so that + // they're not considered recursive. + if (const FunctionDecl *FD = dyn_cast_or_null(TargetDecl)) { + if (AsmLabelAttr *Attr = FD->getAttr()) { + if (CurFn->getName() == Attr->getLabel()) + Attrs = Attrs.addAttribute(getLLVMContext(), + llvm::AttributeList::FunctionIndex, + llvm::Attribute::NoInline); + } + } + if (const FunctionDecl *FD = dyn_cast_or_null(CurFuncDecl)) if (FD->hasAttr()) // All calls within a strictfp function are marked strictfp Index: clang/lib/CodeGen/CodeGenModule.h =================================================================== --- clang/lib/CodeGen/CodeGenModule.h +++ clang/lib/CodeGen/CodeGenModule.h @@ -506,7 +506,7 @@ void createOpenMPRuntime(); void createCUDARuntime(); - bool isTriviallyRecursive(const FunctionDecl *F); + bool isTriviallyForwardingToSelf(const FunctionDecl *F); bool shouldEmitFunction(GlobalDecl GD); bool shouldOpportunisticallyEmitVTables(); /// Map used to be sure we don't emit the same CompoundLiteral twice. Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -2855,38 +2855,37 @@ } namespace { - struct FunctionIsDirectlyRecursive - : public ConstStmtVisitor { - const StringRef Name; - const Builtin::Context &BI; - FunctionIsDirectlyRecursive(StringRef N, const Builtin::Context &C) - : Name(N), BI(C) {} - - bool VisitCallExpr(const CallExpr *E) { - const FunctionDecl *FD = E->getDirectCallee(); - if (!FD) - return false; - AsmLabelAttr *Attr = FD->getAttr(); - if (Attr && Name == Attr->getLabel()) - return true; - unsigned BuiltinID = FD->getBuiltinID(); - if (!BuiltinID || !BI.isLibFunction(BuiltinID)) - return false; - StringRef BuiltinName = BI.getName(BuiltinID); - if (BuiltinName.startswith("__builtin_") && - Name == BuiltinName.slice(strlen("__builtin_"), StringRef::npos)) { - return true; - } +struct StmtIsDirectlyAlwaysRecursive + : public ConstStmtVisitor { + const StringRef Name; + const Builtin::Context &BI; + StmtIsDirectlyAlwaysRecursive(StringRef N, const Builtin::Context &C) + : Name(N), BI(C) {} + + bool VisitCallExpr(const CallExpr *E) { + const FunctionDecl *FD = E->getDirectCallee(); + if (!FD) return false; - } - - bool VisitStmt(const Stmt *S) { - for (const Stmt *Child : S->children()) - if (Child && this->Visit(Child)) - return true; + AsmLabelAttr *Attr = FD->getAttr(); + if (Attr && Name == Attr->getLabel()) + return true; + unsigned BuiltinID = FD->getBuiltinID(); + if (!BuiltinID || !BI.isLibFunction(BuiltinID)) return false; + StringRef BuiltinName = BI.getName(BuiltinID); + if (BuiltinName.startswith("__builtin_") && + Name == BuiltinName.slice(strlen("__builtin_"), StringRef::npos)) { + return true; } - }; + return false; + } + + bool VisitStmt(const Stmt *S) { + return llvm::any_of(S->children(), [this](const Stmt *Child) { + return Child && this->Visit(Child); + }); + } +}; // Make sure we're not referencing non-imported vars or functions. struct DLLImportFunctionVisitor @@ -2952,11 +2951,10 @@ }; } -// isTriviallyRecursive - Check if this function calls another +// isTriviallyForwardingToSelf - Check if this function always calls another // decl that, because of the asm attribute or the other decl being a builtin, // ends up pointing to itself. -bool -CodeGenModule::isTriviallyRecursive(const FunctionDecl *FD) { +bool CodeGenModule::isTriviallyForwardingToSelf(const FunctionDecl *FD) { StringRef Name; if (getCXXABI().getMangleContext().shouldMangleDeclName(FD)) { // asm labels are a special kind of mangling we have to support. @@ -2968,9 +2966,16 @@ Name = FD->getName(); } - FunctionIsDirectlyRecursive Walker(Name, Context.BuiltinInfo); - const Stmt *Body = FD->getBody(); - return Body ? Walker.Visit(Body) : false; + Stmt *Body = FD->getBody(); + if (CompoundStmt *CS = dyn_cast_or_null(Body)) { + if (CS->size() != 1) + return false; + if (ReturnStmt *RS = dyn_cast(CS->body_front())) { + StmtIsDirectlyAlwaysRecursive Walker(Name, Context.BuiltinInfo); + return Walker.Visit(RS); + } + } + return false; } bool CodeGenModule::shouldEmitFunction(GlobalDecl GD) { @@ -3005,7 +3010,7 @@ // but a function that calls itself through asm label/`__builtin_` trickery is // clearly not equivalent to the real implementation. // This happens in glibc's btowc and in some configure checks. - return !isTriviallyRecursive(F); + return !isTriviallyForwardingToSelf(F); } bool CodeGenModule::shouldOpportunisticallyEmitVTables() { Index: clang/test/CodeGen/memmove-always-inline-definition-used.c =================================================================== --- /dev/null +++ clang/test/CodeGen/memmove-always-inline-definition-used.c @@ -0,0 +1,32 @@ +// Verifies that even at -O0, the inline definition of memmove has precedence over the builtin +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -disable-llvm-passes -S -emit-llvm -o - %s | FileCheck %s + +#define AVAILABLE_EXTERNALLY extern inline __attribute__((always_inline)) \ +__attribute__((gnu_inline)) + +typedef unsigned long size_t; + +extern void *memmove_alias(void *a, const void *b, size_t c) __asm__("memmove"); + +// Check that foo is calling the actual memmove function and not the builtin. +// CHECK-LABEL: define void @foo +// CHECK: call i8* @memmove +// +// Check that the recursive call to memmove through memmove_alias is marked as +// noinline. +// CHECK-LABEL: define available_externally i8* @memmove +// CHECK: call void @llvm.memcpy +// CHECK: call i8* @memmove({{.*}}) #[[ATTR:[0-9]+]] +// CHECK: attributes #[[ATTR]] = { noinline } +AVAILABLE_EXTERNALLY void *memmove(void *a, const void *b, size_t c) { + if (c == 1 && a != b) { + return __builtin_memcpy(a, b, c); + } else { + return memmove_alias(a, b, c); + } +} + +void foo(void *a, const void *b, size_t c) { + memmove(a, b, c); +} + Index: llvm/lib/Analysis/InlineCost.cpp =================================================================== --- llvm/lib/Analysis/InlineCost.cpp +++ llvm/lib/Analysis/InlineCost.cpp @@ -2449,9 +2449,9 @@ if (!Call) continue; - // Disallow recursive calls. + // Disallow recursive calls, unless marked as no-inline Function *Callee = Call->getCalledFunction(); - if (&F == Callee) + if (!Call->hasFnAttr(Attribute::NoInline) && &F == Callee) return InlineResult::failure("recursive call"); // Disallow calls which expose returns-twice to a function not previously