Index: clang/lib/CodeGen/CodeGenModule.cpp =================================================================== --- clang/lib/CodeGen/CodeGenModule.cpp +++ clang/lib/CodeGen/CodeGenModule.cpp @@ -2880,11 +2880,16 @@ return false; } + bool VisitIfStmt(const IfStmt *S) { + bool Then = S->getThen() && this->Visit(S->getThen()); + bool Else = S->getElse() && this->Visit(S->getElse()); + return Then && Else; + } + bool VisitStmt(const Stmt *S) { - for (const Stmt *Child : S->children()) - if (Child && this->Visit(Child)) - return true; - return false; + return llvm::any_of(S->children(), [this](const Stmt *Child) { + return Child && this->Visit(Child); + }); } }; @@ -2952,7 +2957,7 @@ }; } -// isTriviallyRecursive - Check if this function calls another +// isTriviallyRecursive - 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 Index: clang/test/CodeGen/memmove-always-inline-definition-used.c =================================================================== --- /dev/null +++ clang/test/CodeGen/memmove-always-inline-definition-used.c @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-unknown -S -emit-llvm -o - %s | FileCheck %s +// +// Verifies that even at O0, the inline definition of memmove has precedence +// over the builtin + +typedef unsigned long size_t; + +#define AVAILABLE_EXTERNALLY extern inline __attribute__((always_inline)) \ +__attribute__((gnu_inline)) + +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 __builtin_memmove(a, b, c); +} + +// CHECK-LABEL: define void @foo +void foo(void *a, const void *b, size_t c) { + // Clang should use the inline definition of memmove here. + // CHECK: call void @llvm.memcpy + // CHECK: call void @llvm.memmove + memmove(a, b, c); +} + +// CHECK-NOT: nobuiltin Index: llvm/include/llvm/Analysis/InlineCost.h =================================================================== --- llvm/include/llvm/Analysis/InlineCost.h +++ llvm/include/llvm/Analysis/InlineCost.h @@ -272,6 +272,8 @@ /// Minimal filter to detect invalid constructs for inlining. InlineResult isInlineViable(Function &Callee); +/// AlwaysInline allows inlining of recursive function +InlineResult isAlwaysInlineViable(Function &Callee); // This pass is used to annotate instructions during the inline process for // debugging and analysis. The main purpose of the pass is to see and test Index: llvm/lib/Analysis/InlineCost.cpp =================================================================== --- llvm/lib/Analysis/InlineCost.cpp +++ llvm/lib/Analysis/InlineCost.cpp @@ -2430,7 +2430,7 @@ return llvm::InlineCost::get(CA.getCost(), CA.getThreshold()); } -InlineResult llvm::isInlineViable(Function &F) { +static InlineResult isInlineViableImpl(Function &F, bool RecursiveIsViable) { bool ReturnsTwice = F.hasFnAttribute(Attribute::ReturnsTwice); for (BasicBlock &BB : F) { // Disallow inlining of functions which contain indirect branches. @@ -2449,8 +2449,8 @@ if (!Call) continue; - // Disallow recursive calls. Function *Callee = Call->getCalledFunction(); + // Disallow recursive calls. if (&F == Callee) return InlineResult::failure("recursive call"); @@ -2486,6 +2486,14 @@ return InlineResult::success(); } +InlineResult llvm::isInlineViable(Function &F) { + return isInlineViableImpl(F, false /* disallow recursive call */); +} + +InlineResult llvm::isAlwaysInlineViable(Function &F) { + return isInlineViableImpl(F, true /* allow recursive call */); +} + // APIs to create InlineParams based on command line flags and/or other // parameters. Index: llvm/lib/Transforms/IPO/AlwaysInliner.cpp =================================================================== --- llvm/lib/Transforms/IPO/AlwaysInliner.cpp +++ llvm/lib/Transforms/IPO/AlwaysInliner.cpp @@ -189,7 +189,7 @@ if (!CB.hasFnAttr(Attribute::AlwaysInline)) return InlineCost::getNever("no alwaysinline attribute"); - auto IsViable = isInlineViable(*Callee); + auto IsViable = isAlwaysInlineViable(*Callee); if (!IsViable.isSuccess()) return InlineCost::getNever(IsViable.getFailureReason());