diff --git a/clang/test/CodeGen/thinlto-inline-asm2.c b/clang/test/CodeGen/thinlto-inline-asm2.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/thinlto-inline-asm2.c @@ -0,0 +1,29 @@ +// REQUIRES: x86-registered-target + +// RUN: split-file %s %t +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -flto=thin -emit-llvm-bc %t/a.c -o %t/a.bc +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -flto=thin -emit-llvm-bc %t/b.c -o %t/b.bc +// RUN: llvm-nm %t/a.bc | FileCheck %s --check-prefix=NM + +// RUN: llvm-lto2 run %t/a.bc %t/b.bc -o %t/out -save-temps -r=%t/a.bc,ref,plx -r=%t/b.bc,ff_h264_cabac_tables,pl +// RUN: llvm-dis < %t/out.2.2.internalize.bc | FileCheck %s + +//--- a.c +/// IR symtab does not track inline asm symbols, so we don't know +/// ff_h264_cabac_tables is undefined. +// NM-NOT: {{.}} +// NM: ---------------- T ref +// NM-NOT: {{.}} +const char *ref() { + const char *ret; + asm("lea ff_h264_cabac_tables(%%rip), %0" : "=r"(ret)); + return ret; +} + +//--- b.c +/// ff_h264_cabac_tables has __attribute__((used)) in the source code, which means +/// its definition must be retained because there can be references the compiler +/// cannot see (inline asm reference). Test we don't internalize it. +// CHECK: @ff_h264_cabac_tables = dso_local constant [1 x i8] c"\09" +__attribute__((used)) +const char ff_h264_cabac_tables[1] = "\x09"; diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -563,8 +563,8 @@ // Set the partition to external if we know it is re-defined by the linker // with -defsym or -wrap options, used elsewhere, e.g. it is visible to a - // regular object, is referenced from llvm.compiler_used, or was already - // recorded as being referenced from a different partition. + // regular object, is referenced from llvm.compiler.used/llvm.used, or was + // already recorded as being referenced from a different partition. if (Res.LinkerRedefined || Res.VisibleToRegularObj || Sym.isUsed() || (GlobalRes.Partition != GlobalResolution::Unknown && GlobalRes.Partition != Partition)) { diff --git a/llvm/lib/Object/IRSymtab.cpp b/llvm/lib/Object/IRSymtab.cpp --- a/llvm/lib/Object/IRSymtab.cpp +++ b/llvm/lib/Object/IRSymtab.cpp @@ -119,8 +119,19 @@ return make_error("input module has no datalayout", inconvertibleErrorCode()); + // Symbols in the llvm.used list will get the FB_Used bit and will not be + // internalized. We do this for llvm.compiler.used as well: + // + // IR symbol table tracks module-level asm symbol references but not inline + // asm, so we may not know that a symbol defined in another translation unit + // is referenced. That definition may have __attribute__((used)) (which lowers + // to llvm.compiler.used on ELF targets) to communicate to the compiler that + // it may be used by inline asm. The usage is perfectly fine, so we treat + // llvm.compiler.used conservatively as llvm.used to work around our own + // limitation. SmallVector UsedV; collectUsedGlobalVariables(*M, UsedV, /*CompilerUsed=*/false); + collectUsedGlobalVariables(*M, UsedV, /*CompilerUsed=*/true); SmallPtrSet Used(UsedV.begin(), UsedV.end()); ModuleSymbolTable Msymtab; diff --git a/llvm/test/ThinLTO/X86/asm.ll b/llvm/test/ThinLTO/X86/asm.ll new file mode 100644 --- /dev/null +++ b/llvm/test/ThinLTO/X86/asm.ll @@ -0,0 +1,34 @@ +; RUN: split-file %s %t +; RUN: opt -module-summary %t/a.s -o %t/a.bc +; RUN: opt -module-summary %t/b.s -o %t/b.bc +; RUN: llvm-nm %t/a.bc | FileCheck %s --check-prefix=NM + +; RUN: llvm-lto2 run %t/a.bc %t/b.bc -o %t/out -save-temps -r=%t/a.bc,ref,plx -r=%t/b.bc,ff_h264_cabac_tables,pl +; RUN: llvm-dis < %t/out.2.2.internalize.bc | FileCheck %s + +;--- a.s +;; IR symtab does not track inline asm symbols, so we don't know +;; ff_h264_cabac_tables is undefined. +; NM-NOT: {{.}} +; NM: ---------------- T ref +; NM-NOT: {{.}} +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define i8* @ref() { +entry: + %0 = tail call i8* asm sideeffect "lea ff_h264_cabac_tables(%rip), $0", "=&r,~{dirflag},~{fpsr},~{flags}"() + ret i8* %0 +} + +;--- b.s +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +;; ff_h264_cabac_tables has __attribute__((used)) in the source code, which means +;; its definition must be retained because there can be references the compiler +;; cannot see (inline asm reference). Test we don't internalize it. +; CHECK: @ff_h264_cabac_tables = dso_local constant [1 x i8] c"\09" +@ff_h264_cabac_tables = dso_local constant [1 x i8] c"\09" + +@llvm.compiler.used = appending global [1 x i8*] [i8* bitcast ([1 x i8]* @ff_h264_cabac_tables to i8*)], section "llvm.metadata" diff --git a/llvm/test/tools/gold/X86/emit-llvm.ll b/llvm/test/tools/gold/X86/emit-llvm.ll --- a/llvm/test/tools/gold/X86/emit-llvm.ll +++ b/llvm/test/tools/gold/X86/emit-llvm.ll @@ -54,6 +54,7 @@ ret void } +;; f2 is not internalized because it is in the llvm.used list. ; CHECK-DAG: define hidden void @f2() ; OPT-DAG: define hidden void @f2() define hidden void @f2() { @@ -97,6 +98,15 @@ ret i32* @g8 } +;; f9 is not internalized because it is in the llvm.compiler.used list. +; CHECK-DAG: define hidden void @f9() +; OPT-DAG: define hidden void @f9() +define hidden void @f9() { + ret void +} + +@llvm.compiler.used = appending global [1 x i8*] [ i8* bitcast (void ()* @f9 to i8*)] + ; RES: .o,f1,pl{{$}} ; RES: .o,f2,pl{{$}} ; RES: .o,f3,px{{$}} @@ -105,6 +115,7 @@ ; RES: .o,f6,p{{$}} ; RES: .o,f7,px{{$}} ; RES: .o,f8,px{{$}} +; RES: .o,f9,pl{{$}} ; RES: .o,g1,px{{$}} ; RES: .o,g2,p{{$}} ; RES: .o,g3,p{{$}}