Skip to content

Commit 6bfc85c

Browse files
author
serge-sans-paille
committed
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
1 parent 48677f5 commit 6bfc85c

File tree

3 files changed

+74
-11
lines changed

3 files changed

+74
-11
lines changed

clang/lib/CodeGen/CodeGenFunction.cpp

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1299,18 +1299,40 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
12991299
// When generating code for a builtin with an inline declaration, use a
13001300
// mangled name to hold the actual body, while keeping an external definition
13011301
// in case the function pointer is referenced somewhere.
1302-
if (FD->isInlineBuiltinDeclaration() && Fn) {
1303-
std::string FDInlineName = (Fn->getName() + ".inline").str();
1304-
llvm::Module *M = Fn->getParent();
1305-
llvm::Function *Clone = M->getFunction(FDInlineName);
1306-
if (!Clone) {
1307-
Clone = llvm::Function::Create(Fn->getFunctionType(),
1308-
llvm::GlobalValue::InternalLinkage,
1309-
Fn->getAddressSpace(), FDInlineName, M);
1310-
Clone->addFnAttr(llvm::Attribute::AlwaysInline);
1302+
if (Fn) {
1303+
if (FD->isInlineBuiltinDeclaration()) {
1304+
std::string FDInlineName = (Fn->getName() + ".inline").str();
1305+
llvm::Module *M = Fn->getParent();
1306+
llvm::Function *Clone = M->getFunction(FDInlineName);
1307+
if (!Clone) {
1308+
Clone = llvm::Function::Create(Fn->getFunctionType(),
1309+
llvm::GlobalValue::InternalLinkage,
1310+
Fn->getAddressSpace(), FDInlineName, M);
1311+
Clone->addFnAttr(llvm::Attribute::AlwaysInline);
1312+
}
1313+
Fn->setLinkage(llvm::GlobalValue::ExternalLinkage);
1314+
Fn = Clone;
1315+
}
1316+
1317+
// Detect the unusual situation where an inline version is shadowed by a
1318+
// non-inline version. In that case we should pick the external one
1319+
// everywhere. That's GCC behavior too. Unfortunately, I cannot find a way
1320+
// to detect that situation before we reach codegen, so do some late
1321+
// replacement.
1322+
else {
1323+
for (const FunctionDecl *PD = FD->getPreviousDecl(); PD;
1324+
PD = PD->getPreviousDecl()) {
1325+
if (LLVM_UNLIKELY(PD->isInlineBuiltinDeclaration())) {
1326+
std::string FDInlineName = (Fn->getName() + ".inline").str();
1327+
llvm::Module *M = Fn->getParent();
1328+
if (llvm::Function *Clone = M->getFunction(FDInlineName)) {
1329+
Clone->replaceAllUsesWith(Fn);
1330+
Clone->eraseFromParent();
1331+
}
1332+
break;
1333+
}
1334+
}
13111335
}
1312-
Fn->setLinkage(llvm::GlobalValue::ExternalLinkage);
1313-
Fn = Clone;
13141336
}
13151337

13161338
// Check if we should generate debug info for this function.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -disable-llvm-passes -o - %s | FileCheck %s
2+
//
3+
// Verifies that clang-generated *.inline are removed when shadowed by an external definition
4+
5+
// CHECK-NOT: strlen.inline
6+
7+
unsigned long strnlen(const char *, unsigned long);
8+
void fortify_panic(const char *);
9+
10+
extern inline __attribute__((always_inline)) __attribute__((gnu_inline)) unsigned long strlen(const char *p) {
11+
return 1;
12+
}
13+
unsigned long mystrlen(char const *s) {
14+
return strlen(s);
15+
}
16+
unsigned long strlen(const char *s) {
17+
return 2;
18+
}
19+
unsigned long yourstrlen(char const *s) {
20+
return strlen(s);
21+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// RUN: %clang_cc1 -triple x86_64 -S -emit-llvm -O1 -o - %s | FileCheck %s
2+
//
3+
// Verifies that the gnu_inline version is ignored in favor of the redecl
4+
5+
extern inline __attribute__((gnu_inline)) unsigned long some_size(int c) {
6+
return 1;
7+
}
8+
unsigned long mycall(int s) {
9+
// CHECK-LABEL: i64 @mycall
10+
// CHECK: ret i64 2
11+
return some_size(s);
12+
}
13+
unsigned long some_size(int c) {
14+
return 2;
15+
}
16+
unsigned long yourcall(int s) {
17+
// CHECK-LABEL: i64 @yourcall
18+
// CHECK: ret i64 2
19+
return some_size(s);
20+
}

0 commit comments

Comments
 (0)