forked from OSchip/llvm-project
Fix inline builtin handling in case of redefinition
Basically, inline builtin definition are shadowed by externally visible redefinition. This matches GCC behavior. The implementation has to workaround the fact that: 1. inline builtin are renamed at callsite during codegen, but 2. they may be shadowed by a later external definition As a consequence, during codegen, we need to walk redecls and eventually rewrite some call sites, which is totally inelegant. Differential Revision: https://reviews.llvm.org/D112059
This commit is contained in:
parent
48677f58b0
commit
6bfc85c217
|
@ -1299,18 +1299,40 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
|
|||
// When generating code for a builtin with an inline declaration, use a
|
||||
// mangled name to hold the actual body, while keeping an external definition
|
||||
// in case the function pointer is referenced somewhere.
|
||||
if (FD->isInlineBuiltinDeclaration() && Fn) {
|
||||
std::string FDInlineName = (Fn->getName() + ".inline").str();
|
||||
llvm::Module *M = Fn->getParent();
|
||||
llvm::Function *Clone = M->getFunction(FDInlineName);
|
||||
if (!Clone) {
|
||||
Clone = llvm::Function::Create(Fn->getFunctionType(),
|
||||
llvm::GlobalValue::InternalLinkage,
|
||||
Fn->getAddressSpace(), FDInlineName, M);
|
||||
Clone->addFnAttr(llvm::Attribute::AlwaysInline);
|
||||
if (Fn) {
|
||||
if (FD->isInlineBuiltinDeclaration()) {
|
||||
std::string FDInlineName = (Fn->getName() + ".inline").str();
|
||||
llvm::Module *M = Fn->getParent();
|
||||
llvm::Function *Clone = M->getFunction(FDInlineName);
|
||||
if (!Clone) {
|
||||
Clone = llvm::Function::Create(Fn->getFunctionType(),
|
||||
llvm::GlobalValue::InternalLinkage,
|
||||
Fn->getAddressSpace(), FDInlineName, M);
|
||||
Clone->addFnAttr(llvm::Attribute::AlwaysInline);
|
||||
}
|
||||
Fn->setLinkage(llvm::GlobalValue::ExternalLinkage);
|
||||
Fn = Clone;
|
||||
}
|
||||
|
||||
// Detect the unusual situation where an inline version is shadowed by a
|
||||
// non-inline version. In that case we should pick the external one
|
||||
// everywhere. That's GCC behavior too. Unfortunately, I cannot find a way
|
||||
// to detect that situation before we reach codegen, so do some late
|
||||
// replacement.
|
||||
else {
|
||||
for (const FunctionDecl *PD = FD->getPreviousDecl(); PD;
|
||||
PD = PD->getPreviousDecl()) {
|
||||
if (LLVM_UNLIKELY(PD->isInlineBuiltinDeclaration())) {
|
||||
std::string FDInlineName = (Fn->getName() + ".inline").str();
|
||||
llvm::Module *M = Fn->getParent();
|
||||
if (llvm::Function *Clone = M->getFunction(FDInlineName)) {
|
||||
Clone->replaceAllUsesWith(Fn);
|
||||
Clone->eraseFromParent();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Fn->setLinkage(llvm::GlobalValue::ExternalLinkage);
|
||||
Fn = Clone;
|
||||
}
|
||||
|
||||
// Check if we should generate debug info for this function.
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
|
||||
//
|
||||
// Verifies that clang-generated *.inline are removed when shadowed by an external definition
|
||||
|
||||
// CHECK-NOT: strlen.inline
|
||||
|
||||
unsigned long strnlen(const char *, unsigned long);
|
||||
void fortify_panic(const char *);
|
||||
|
||||
extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) unsigned long strlen(const char *p) {
|
||||
return 1;
|
||||
}
|
||||
unsigned long mystrlen(char const *s) {
|
||||
return strlen(s);
|
||||
}
|
||||
unsigned long strlen(const char *s) {
|
||||
return 2;
|
||||
}
|
||||
unsigned long yourstrlen(char const *s) {
|
||||
return strlen(s);
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -O1 -o - %s | FileCheck %s
|
||||
//
|
||||
// Verifies that the gnu_inline version is ignored in favor of the redecl
|
||||
|
||||
extern inline __attribute__((gnu_inline)) unsigned long some_size(int c) {
|
||||
return 1;
|
||||
}
|
||||
unsigned long mycall(int s) {
|
||||
// CHECK-LABEL: i64 @mycall
|
||||
// CHECK: ret i64 2
|
||||
return some_size(s);
|
||||
}
|
||||
unsigned long some_size(int c) {
|
||||
return 2;
|
||||
}
|
||||
unsigned long yourcall(int s) {
|
||||
// CHECK-LABEL: i64 @yourcall
|
||||
// CHECK: ret i64 2
|
||||
return some_size(s);
|
||||
}
|
Loading…
Reference in New Issue