This is an archive of the discontinued LLVM Phabricator instance.

Update inline builtin handling to honor gnu inline attribute
ClosedPublic

Authored by serge-sans-paille on Oct 3 2021, 4:56 AM.

Details

Summary

Per the GCC info page:

If the function is declared 'extern', then this definition of the
function is used only for inlining.  In no case is the function
compiled as a standalone function, not even if you take its address
explicitly.  Such an address becomes an external reference, as if
you had only declared the function, and had not defined it.

Respect that behavior for inline builtins: keep the original definition, and
generate a copy of the declaration suffixed by '.inline' that's only referenced
in direct call.

Diff Detail

Event Timeline

serge-sans-paille requested review of this revision.Oct 3 2021, 4:56 AM
serge-sans-paille created this revision.
Herald added a project: Restricted Project. · View Herald TranscriptOct 3 2021, 4:56 AM
Herald added a subscriber: cfe-commits. · View Herald Transcript

hmm...this seems to cause a bunch of ICEs building the kernel:

Global is external, but doesn't have external or weak linkage!
i64 (i8*)* @strlen
fatal error: error in backend: Broken module found, compilation aborted!
Global is external, but doesn't have external or weak linkage!
i8* (i8*, i8*, i64)* @strncpy
Global is external, but doesn't have external or weak linkage!
i8* (i8*, i8*)* @strcpy
Global is external, but doesn't have external or weak linkage!
i64 (i8*)* @strlen
Global is external, but doesn't have external or weak linkage!
i64 (i8*, i8*, i64)* @strlcpy
Global is external, but doesn't have external or weak linkage!
i8* (i8*, i32, i64)* @memchr
clang/lib/CodeGen/CGExpr.cpp
4895

s/Replaceable builtin/Replaceable builtins/

4915

When building this patch (with Clang, via cmake vars -DCMAKE_C_COMPILER= and -DCMAKE_CXX_COMPILER=), I observe the following warning:

[99/110] Building CXX object tools/clang/lib/CodeGen/CMakeFiles/obj.clangCodeGen.dir/CGExpr.cpp.o
/android0/llvm-project/clang/lib/CodeGen/CGExpr.cpp:4898:30: warning: object backing the pointer will be destroyed at the end of the full-expression [-Wdangling-gsl]
    StringRef FDInlineName = (FD->getName() + ".inline").str();
                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This leads to crashes when using the resulting image built with assertions enabled.

diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp
index afed918c5c7f..cb6469d58b4c 100644
--- a/clang/lib/CodeGen/CGExpr.cpp
+++ b/clang/lib/CodeGen/CGExpr.cpp
@@ -4895,9 +4895,9 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
     // Replaceable builtin provide their own implementation of a builtin. If we
     // are in an inline builtin implementation, avoid trivial infinite
     // recursion.
-    StringRef FDInlineName = (FD->getName() + ".inline").str();
+    Twine FDInlineName = FD->getName() + ".inline";
     if (!FD->isInlineBuiltinDeclaration() ||
-        CGF.CurFn->getName() == FDInlineName) {
+        CGF.CurFn->getName() == FDInlineName.str()) {
       return CGCallee::forBuiltin(builtinID, FD);
     }
     // When directing calling an inline builtin, call it through it's mangled
@@ -4906,7 +4906,7 @@ static CGCallee EmitDirectCallee(CodeGenFunction &CGF, GlobalDecl GD) {
       llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD);
       llvm::Function *Fn = llvm::cast<llvm::Function>(CalleePtr);
       llvm::Module &M = CGF.CGM.getModule();
-      llvm::Function *Clone = M.getFunction(FDInlineName);
+      llvm::Function *Clone = M.getFunction(FDInlineName.str());
       if (!Clone) {
         Clone = llvm::Function::Create(Fn->getFunctionType(), Fn->getLinkage(),
                                        Fn->getAddressSpace(),
4916

I think if you flip the order of the branches by inverting the logic in the conditional, then you could avoid needing braces for the single statement branch. ie.

if (FD->isInlineBuiltinDeclaration() && CGF.CurFn->getName() != FDInlineName) {
  llvm::Constant *CalleePtr = EmitFunctionDeclPointer(CGF.CGM, GD);
  ...
} else
  return CGCallee::forBuiltin(builtinID, FD);
4923

If you have a handle to a Function, you can get a pointer to it's module via Function::getParent().

4928

Can we reuse FDInlineName here for the 4th param?

clang/lib/CodeGen/CodeGenFunction.cpp
1302

could use Fn->getParent() here, too.

1307

Fn->getName() + ".inline" appears twice. Want to save it in a local variable then reuse it, similar to what you do above in clang/lib/CodeGen/CGExpr.cpp?

nickdesaulniers added a comment.EditedOct 3 2021, 11:58 AM

Note: kernel tests need to have CONFIG_FORTIFY_SOURCE=y enabled in the .config to use the fortified routines; this isn't enabled by default in an x86_64 defconfig build.

clang/lib/CodeGen/CGExpr.cpp
4926

given the reported ICE, I wonder if we shouldn't be reusing the Fn's linkage, but explicitly setting extern? Same with below.

Here's a reduced test case from the kernel. Let's add a unit test for it.

const void *con_unify_unimap_p1;
extern inline __attribute__((__always_inline__)) __attribute__((gnu_inline))
int memcmp(const void *p, const void *q, unsigned long size) {
  __builtin_memcmp(p, q, size);
}
void con_unify_unimap_q1(void) {
  memcmp(con_unify_unimap_p1, con_unify_unimap_q1, sizeof(int));
}

+ extra test case
+ avoid Playing with Twines
+ fix storage of external declaration of inline builtins
+ minor nits

With Diff 376790, I'm seeing a similar failure to https://github.com/ClangBuiltLinux/linux/issues/1466#issue-1011472964, but from different TUs.

ld.lld: error: duplicate symbol: memcpy.inline
>>> defined at file.c
>>>            fs/efivarfs/file.o:(memcpy.inline)
>>> defined at super.c
>>>            fs/efivarfs/super.o:(.text+0x800)

let me see if I can put together another reproducer.

+ fix linkage of generated function

With Diff 376790

inline __attribute__((__always_inline__)) __attribute__((gnu_inline)) void*
memcpy() {}

clang -O2 is producing undefined references to memcpy.inline. Please add that to the new unit tests added here.

diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp
index a8ae5f5fc0de..132834516473 100644
--- a/clang/lib/CodeGen/CodeGenFunction.cpp
+++ b/clang/lib/CodeGen/CodeGenFunction.cpp
@@ -1303,7 +1303,7 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
     llvm::Module *M = Fn->getParent();
     llvm::Function *Clone = M->getFunction(FDInlineName);
     if (!Clone) {
-      Clone = llvm::Function::Create(Fn->getFunctionType(), Fn->getLinkage(),
+      Clone = llvm::Function::Create(Fn->getFunctionType(), llvm::GlobalValue::AvailableExternallyLinkage,
                                      Fn->getAddressSpace(), FDInlineName, M);
       Clone->addFnAttr(llvm::Attribute::AlwaysInline);
     }

fixes that, but I don't think that's the right visibility.

00000000000002a0 T memcpy.inline

should be t not T I think? Because now the failure looks like

ld.lld: error: duplicate symbol: memcpy.inline
>>> defined at file.c
>>>            fs/efivarfs/file.o:(memcpy.inline)
>>> defined at super.c
>>>            fs/efivarfs/super.o:(.text+0x800)

Perhaps llvm::GlobalValue::InternalLinkage?

Diff 376794 builds! Let's still add a test for

inline __attribute__((__always_inline__)) __attribute__((gnu_inline)) void*
memcpy() {}

though.

+ extra test case
+ use internal linkage instead of available_externally linkage for generated clone

nickdesaulniers accepted this revision.Oct 4 2021, 9:17 AM
nickdesaulniers added inline comments.
clang/lib/CodeGen/CGExpr.cpp
4901

llvm::Module *M = Fn->getParent();

This revision is now accepted and ready to land.Oct 4 2021, 9:17 AM

I am noticing a clang crash with ToT after this change.

  • testcase --
long a;
char b, d;
extern inline __attribute__((always_inline))
__attribute__((gnu_inline)) unsigned long
strlen() {
  return a;
}
c(void) {
  strlen(&b);
  return 0;
}
unsigned long strlen() { return d; }
bin/clang -x c foo.c -Wno-error
foo.c:8:1: warning: type specifier missing, defaults to 'int' [-Wimplicit-int]
c(void) {
^
Global is external, but doesn't have external or weak linkage!
i64 (i8*)* @strlen.inline
fatal error: error in backend: Broken module found, compilation aborted!
clang version 14.0.0 (https://github.com/llvm/llvm-project.git 3129aa5caf1f9b5c48ab708f43cb3fc5173dd021)

See also: https://github.com/ClangBuiltLinux/linux/issues/1477. Surprising that this didn't show up in newer kernels, just older (but still supported) kernel versions. Sorry I missed that in my tests.