Skip to content

Commit f6bf1d7

Browse files
jhuber6lamb-j
authored andcommitted
[Clang] Allow bitcode linking when the input is LLVM-IR
Clang provides the `-mlink-bitcode-file` and `-mlink-builtin-bitcode` options to insert LLVM-IR into the current TU. These are usefuly primarily for including LLVM-IR files that require special handling to be correct and cannot be linked normally, such as GPU vendor libraries like `libdevice.10.bc`. Currently these options can only be used if the source input goes through the AST consumer path. This patch makes the changes necessary to also support this when the input is LLVM-IR. This will allow the following operation: ``` clang in.bc -Xclang -mlink-builtin-bitcode -Xclang libdevice.10.bc ``` Reviewed By: yaxunl Differential Revision: https://reviews.llvm.org/D152391 Change-Id: I1f2e553fe1b5e3bd16508ad0728323e444b5cc81
1 parent 5a92673 commit f6bf1d7

File tree

6 files changed

+197
-39
lines changed

6 files changed

+197
-39
lines changed

clang/include/clang/CodeGen/CodeGenAction.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ class CodeGenAction : public ASTFrontendAction {
5353

5454
std::unique_ptr<llvm::Module> loadModule(llvm::MemoryBufferRef MBRef);
5555

56+
/// Load bitcode modules to link into our module from the options.
57+
bool loadLinkModules(CompilerInstance &CI);
58+
5659
protected:
5760
/// Create a new code generation action. If the optional \p _VMContext
5861
/// parameter is supplied, the action uses it without taking ownership,

clang/lib/CodeGen/CGCall.cpp

Lines changed: 68 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1847,8 +1847,9 @@ addMergableDefaultFunctionAttributes(const CodeGenOptions &CodeGenOpts,
18471847
FuncAttrs);
18481848
}
18491849

1850-
void CodeGenModule::getTrivialDefaultFunctionAttributes(
1851-
StringRef Name, bool HasOptnone, bool AttrOnCallSite,
1850+
static void getTrivialDefaultFunctionAttributes(
1851+
StringRef Name, bool HasOptnone, const CodeGenOptions &CodeGenOpts,
1852+
const LangOptions &LangOpts, bool AttrOnCallSite,
18521853
llvm::AttrBuilder &FuncAttrs) {
18531854
// OptimizeNoneAttr takes precedence over -Os or -Oz. No warning needed.
18541855
if (!HasOptnone) {
@@ -1969,7 +1970,7 @@ void CodeGenModule::getTrivialDefaultFunctionAttributes(
19691970
}
19701971
}
19711972

1972-
if (getLangOpts().assumeFunctionsAreConvergent()) {
1973+
if (LangOpts.assumeFunctionsAreConvergent()) {
19731974
// Conservatively, mark all functions and calls in CUDA and OpenCL as
19741975
// convergent (meaning, they may call an intrinsically convergent op, such
19751976
// as __syncthreads() / barrier(), and so can't have certain optimizations
@@ -1980,8 +1981,8 @@ void CodeGenModule::getTrivialDefaultFunctionAttributes(
19801981

19811982
// TODO: NoUnwind attribute should be added for other GPU modes HIP,
19821983
// OpenMP offload. AFAIK, neither of them support exceptions in device code.
1983-
if ((getLangOpts().CUDA && getLangOpts().CUDAIsDevice) ||
1984-
getLangOpts().OpenCL || getLangOpts().SYCLIsDevice) {
1984+
if ((LangOpts.CUDA && LangOpts.CUDAIsDevice) || LangOpts.OpenCL ||
1985+
LangOpts.SYCLIsDevice) {
19851986
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);
19861987
}
19871988

@@ -1992,36 +1993,25 @@ void CodeGenModule::getTrivialDefaultFunctionAttributes(
19921993
}
19931994
}
19941995

1995-
void CodeGenModule::getDefaultFunctionAttributes(StringRef Name,
1996-
bool HasOptnone,
1997-
bool AttrOnCallSite,
1998-
llvm::AttrBuilder &FuncAttrs) {
1999-
getTrivialDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite,
2000-
FuncAttrs);
2001-
if (!AttrOnCallSite) {
2002-
// If we're just getting the default, get the default values for mergeable
2003-
// attributes.
2004-
addMergableDefaultFunctionAttributes(CodeGenOpts, FuncAttrs);
2005-
}
2006-
}
1996+
/// Adds attributes to \p F according to our \p CodeGenOpts and \p LangOpts, as
1997+
/// though we had emitted it ourselves. We remove any attributes on F that
1998+
/// conflict with the attributes we add here.
1999+
static void mergeDefaultFunctionDefinitionAttributes(
2000+
llvm::Function &F, const CodeGenOptions CodeGenOpts,
2001+
const LangOptions &LangOpts, const TargetOptions &TargetOpts,
2002+
bool WillInternalize) {
20072003

2008-
void CodeGenModule::addDefaultFunctionDefinitionAttributes(llvm::Function &F) {
20092004
llvm::AttrBuilder FuncAttrs(F.getContext());
2010-
getDefaultFunctionAttributes(F.getName(), F.hasOptNone(),
2011-
/* AttrOnCallSite = */ false, FuncAttrs);
2012-
// TODO: call GetCPUAndFeaturesAttributes?
2013-
F.addFnAttrs(FuncAttrs);
2014-
}
2005+
// Here we only extract the options that are relevant compared to the version
2006+
// from GetCPUAndFeaturesAttributes.
2007+
if (!TargetOpts.CPU.empty())
2008+
FuncAttrs.addAttribute("target-cpu", TargetOpts.CPU);
2009+
if (!TargetOpts.TuneCPU.empty())
2010+
FuncAttrs.addAttribute("tune-cpu", TargetOpts.TuneCPU);
20152011

2016-
/// Apply default attributes to \p F, accounting for merge semantics of
2017-
/// attributes that should not overwrite existing attributes.
2018-
void CodeGenModule::mergeDefaultFunctionDefinitionAttributes(
2019-
llvm::Function &F, bool WillInternalize) {
2020-
llvm::AttrBuilder FuncAttrs(F.getContext());
2021-
getTrivialDefaultFunctionAttributes(F.getName(), F.hasOptNone(),
2022-
/*AttrOnCallSite=*/false, FuncAttrs);
2023-
GetCPUAndFeaturesAttributes(GlobalDecl(), FuncAttrs,
2024-
/*AddTargetFeatures=*/false);
2012+
::getTrivialDefaultFunctionAttributes(F.getName(), F.hasOptNone(),
2013+
CodeGenOpts, LangOpts,
2014+
/*AttrOnCallSite=*/false, FuncAttrs);
20252015

20262016
if (!WillInternalize && F.isInterposable()) {
20272017
// Do not promote "dynamic" denormal-fp-math to this translation unit's
@@ -2066,6 +2056,52 @@ void CodeGenModule::mergeDefaultFunctionDefinitionAttributes(
20662056
F.addFnAttrs(FuncAttrs);
20672057
}
20682058

2059+
void clang::CodeGen::mergeDefaultFunctionDefinitionAttributes(
2060+
llvm::Function &F, const CodeGenOptions CodeGenOpts,
2061+
const LangOptions &LangOpts, const TargetOptions &TargetOpts,
2062+
bool WillInternalize) {
2063+
2064+
::mergeDefaultFunctionDefinitionAttributes(F, CodeGenOpts, LangOpts,
2065+
TargetOpts, WillInternalize);
2066+
}
2067+
2068+
void CodeGenModule::getTrivialDefaultFunctionAttributes(
2069+
StringRef Name, bool HasOptnone, bool AttrOnCallSite,
2070+
llvm::AttrBuilder &FuncAttrs) {
2071+
::getTrivialDefaultFunctionAttributes(Name, HasOptnone, getCodeGenOpts(),
2072+
getLangOpts(), AttrOnCallSite,
2073+
FuncAttrs);
2074+
}
2075+
2076+
void CodeGenModule::getDefaultFunctionAttributes(StringRef Name,
2077+
bool HasOptnone,
2078+
bool AttrOnCallSite,
2079+
llvm::AttrBuilder &FuncAttrs) {
2080+
getTrivialDefaultFunctionAttributes(Name, HasOptnone, AttrOnCallSite,
2081+
FuncAttrs);
2082+
// If we're just getting the default, get the default values for mergeable
2083+
// attributes.
2084+
if (!AttrOnCallSite)
2085+
addMergableDefaultFunctionAttributes(CodeGenOpts, FuncAttrs);
2086+
}
2087+
2088+
void CodeGenModule::addDefaultFunctionDefinitionAttributes(llvm::Function &F) {
2089+
llvm::AttrBuilder FuncAttrs(F.getContext());
2090+
getDefaultFunctionAttributes(F.getName(), F.hasOptNone(),
2091+
/* AttrOnCallSite = */ false, FuncAttrs);
2092+
// TODO: call GetCPUAndFeaturesAttributes?
2093+
F.addFnAttrs(FuncAttrs);
2094+
}
2095+
2096+
/// Apply default attributes to \p F, accounting for merge semantics of
2097+
/// attributes that should not overwrite existing attributes.
2098+
void CodeGenModule::mergeDefaultFunctionDefinitionAttributes(
2099+
llvm::Function &F, bool WillInternalize) {
2100+
::mergeDefaultFunctionDefinitionAttributes(F, getCodeGenOpts(), getLangOpts(),
2101+
getTarget().getTargetOpts(),
2102+
WillInternalize);
2103+
}
2104+
20692105
void CodeGenModule::addDefaultFunctionDefinitionAttributes(
20702106
llvm::AttrBuilder &attrs) {
20712107
getDefaultFunctionAttributes(/*function name*/ "", /*optnone*/ false,

clang/lib/CodeGen/CGCall.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class Value;
3030
namespace clang {
3131
class Decl;
3232
class FunctionDecl;
33+
class TargetOptions;
3334
class VarDecl;
3435

3536
namespace CodeGen {
@@ -374,6 +375,14 @@ class ReturnValueSlot {
374375
bool isExternallyDestructed() const { return IsExternallyDestructed; }
375376
};
376377

378+
/// Helper to add attributes to \p F according to the CodeGenOptions and
379+
/// LangOptions without requiring a CodeGenModule to be constructed.
380+
void mergeDefaultFunctionDefinitionAttributes(llvm::Function &F,
381+
const CodeGenOptions CodeGenOpts,
382+
const LangOptions &LangOpts,
383+
const TargetOptions &TargetOpts,
384+
bool WillInternalize);
385+
377386
} // end namespace CodeGen
378387
} // end namespace clang
379388

clang/lib/CodeGen/CodeGenAction.cpp

Lines changed: 59 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
//===----------------------------------------------------------------------===//
88

99
#include "clang/CodeGen/CodeGenAction.h"
10+
#include "CGCall.h"
1011
#include "CodeGenModule.h"
1112
#include "CoverageMappingGen.h"
1213
#include "MacroPPCallbacks.h"
@@ -263,7 +264,7 @@ namespace clang {
263264
}
264265

265266
// Links each entry in LinkModules into our module. Returns true on error.
266-
bool LinkInModules() {
267+
bool LinkInModules(llvm::Module *M) {
267268
for (auto &LM : LinkModules) {
268269
assert(LM.Module && "LinkModule does not actually have a module");
269270
if (LM.PropagateAttrs)
@@ -272,24 +273,23 @@ namespace clang {
272273
// in LLVM IR.
273274
if (F.isIntrinsic())
274275
continue;
275-
Gen->CGM().mergeDefaultFunctionDefinitionAttributes(F,
276-
LM.Internalize);
276+
CodeGen::mergeDefaultFunctionDefinitionAttributes(
277+
F, CodeGenOpts, LangOpts, TargetOpts, LM.Internalize);
277278
}
278279

279280
CurLinkModule = LM.Module.get();
280281

281282
bool Err;
282283
if (LM.Internalize) {
283284
Err = Linker::linkModules(
284-
*getModule(), std::move(LM.Module), LM.LinkFlags,
285+
*M, std::move(LM.Module), LM.LinkFlags,
285286
[](llvm::Module &M, const llvm::StringSet<> &GVS) {
286287
internalizeModule(M, [&GVS](const llvm::GlobalValue &GV) {
287288
return !GV.hasName() || (GVS.count(GV.getName()) == 0);
288289
});
289290
});
290291
} else {
291-
Err = Linker::linkModules(*getModule(), std::move(LM.Module),
292-
LM.LinkFlags);
292+
Err = Linker::linkModules(*M, std::move(LM.Module), LM.LinkFlags);
293293
}
294294

295295
if (Err)
@@ -358,7 +358,7 @@ namespace clang {
358358
}
359359

360360
// Link each LinkModule into our module.
361-
if (LinkInModules())
361+
if (LinkInModules(getModule()))
362362
return;
363363

364364
for (auto &F : getModule()->functions()) {
@@ -994,6 +994,36 @@ CodeGenAction::~CodeGenAction() {
994994
delete VMContext;
995995
}
996996

997+
bool CodeGenAction::loadLinkModules(CompilerInstance &CI) {
998+
if (!LinkModules.empty())
999+
return false;
1000+
1001+
for (const CodeGenOptions::BitcodeFileToLink &F :
1002+
CI.getCodeGenOpts().LinkBitcodeFiles) {
1003+
auto BCBuf = CI.getFileManager().getBufferForFile(F.Filename);
1004+
if (!BCBuf) {
1005+
CI.getDiagnostics().Report(diag::err_cannot_open_file)
1006+
<< F.Filename << BCBuf.getError().message();
1007+
LinkModules.clear();
1008+
return true;
1009+
}
1010+
1011+
Expected<std::unique_ptr<llvm::Module>> ModuleOrErr =
1012+
getOwningLazyBitcodeModule(std::move(*BCBuf), *VMContext);
1013+
if (!ModuleOrErr) {
1014+
handleAllErrors(ModuleOrErr.takeError(), [&](ErrorInfoBase &EIB) {
1015+
CI.getDiagnostics().Report(diag::err_cannot_open_file)
1016+
<< F.Filename << EIB.message();
1017+
});
1018+
LinkModules.clear();
1019+
return true;
1020+
}
1021+
LinkModules.push_back({std::move(ModuleOrErr.get()), F.PropagateAttrs,
1022+
F.Internalize, F.LinkFlags});
1023+
}
1024+
return false;
1025+
}
1026+
9971027
bool CodeGenAction::hasIRSupport() const { return true; }
9981028

9991029
void CodeGenAction::EndSourceFileAction() {
@@ -1048,6 +1078,16 @@ CodeGenAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
10481078
if (BA != Backend_EmitNothing && !OS)
10491079
return nullptr;
10501080

1081+
// TODO: Re-enable this function and remove the manual implementation below
1082+
// once support for linking archives is added to loadLinkModules(). This
1083+
// support should be added upstream by:
1084+
// https://github.com/llvm/llvm-project/pull/71978
1085+
#if LOAD_LINK_FIXED
1086+
if (loadLinkModules(CI))
1087+
return nullptr;
1088+
#endif
1089+
1090+
// TODO: Remove this redundant code once linkInModules() is re-enabled
10511091
// Load bitcode modules to link with, if we need to.
10521092
if (LinkModules.empty())
10531093
for (const CodeGenOptions::BitcodeFileToLink &F :
@@ -1187,6 +1227,13 @@ CodeGenAction::loadModule(MemoryBufferRef MBRef) {
11871227
return std::move(*MOrErr);
11881228
}
11891229

1230+
// TODO: See comment on other loadLinkModules() call
1231+
#if LOAD_LINK_FIXED
1232+
// Load bitcode modules to link with, if we need to.
1233+
if (loadLinkModules(CI))
1234+
return nullptr;
1235+
#endif
1236+
11901237
llvm::SMDiagnostic Err;
11911238
if (std::unique_ptr<llvm::Module> M = parseIR(MBRef, Err, *VMContext))
11921239
return M;
@@ -1266,6 +1313,11 @@ void CodeGenAction::ExecuteAction() {
12661313
CI.getCodeGenOpts(), CI.getTargetOpts(),
12671314
CI.getLangOpts(), TheModule.get(),
12681315
std::move(LinkModules), *VMContext, nullptr);
1316+
1317+
// Link in each pending link module.
1318+
if (Result.LinkInModules(&*TheModule))
1319+
return;
1320+
12691321
// PR44896: Force DiscardValueNames as false. DiscardValueNames cannot be
12701322
// true here because the valued names are needed for reading textual IR.
12711323
Ctx.setDiscardValueNames(false);

clang/test/CodeGen/link-bitcode-file.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
// XFAIL: *
2+
// Restore test once https://github.com/llvm/llvm-project/pull/71978 makes it to
3+
// amd-mainline-open
4+
15
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -DBITCODE -emit-llvm-bc -o %t.bc %s
26
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -DBITCODE2 -emit-llvm-bc -o %t-2.bc %s
37
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -mlink-bitcode-file %t.bc \
@@ -11,6 +15,14 @@
1115
// RUN: not %clang_cc1 -triple i386-pc-linux-gnu -mlink-bitcode-file no-such-file.bc \
1216
// RUN: -emit-llvm -o - %s 2>&1 | FileCheck -check-prefix=CHECK-NO-FILE %s
1317

18+
// Make sure we can perform the same options if the input is LLVM-IR
19+
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -emit-llvm-bc -o %t-in.bc %s
20+
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -mlink-bitcode-file %t.bc \
21+
// RUN: -O3 -emit-llvm -o - %t-in.bc | FileCheck -check-prefix=CHECK-NO-BC %s
22+
// RUN: %clang_cc1 -triple i386-pc-linux-gnu -O3 -emit-llvm -o - \
23+
// RUN: -mlink-bitcode-file %t.bc -mlink-bitcode-file %t-2.bc %t-in.bc \
24+
// RUN: | FileCheck -check-prefix=CHECK-NO-BC -check-prefix=CHECK-NO-BC2 %s
25+
1426
int f(void);
1527

1628
#ifdef BITCODE
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// XFAIL: *
2+
// Restore test once https://github.com/llvm/llvm-project/pull/71978 makes it to
3+
// amd-mainline-open
4+
5+
// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-attributes --check-globals --include-generated-funcs --version 2
6+
// RUN: %clang_cc1 -triple amdgcn-- -target-cpu gfx803 -DBITCODE -emit-llvm-bc -o %t-lib.bc %s
7+
// RUN: %clang_cc1 -triple amdgcn-- -target-cpu gfx90a -emit-llvm-bc -o %t.bc %s
8+
// RUN: %clang_cc1 -triple amdgcn-- -target-cpu gfx90a -emit-llvm \
9+
// RUN: -mlink-builtin-bitcode %t-lib.bc -o - %t.bc | FileCheck %s
10+
11+
#ifdef BITCODE
12+
int foo(void) { return 42; }
13+
int x = 12;
14+
#endif
15+
16+
extern int foo(void);
17+
extern int x;
18+
19+
int bar() { return foo() + x; }
20+
//.
21+
// CHECK: @x = internal addrspace(1) global i32 12, align 4
22+
//.
23+
// CHECK: Function Attrs: noinline nounwind optnone
24+
// CHECK-LABEL: define dso_local i32 @bar
25+
// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
26+
// CHECK-NEXT: entry:
27+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5)
28+
// CHECK-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr
29+
// CHECK-NEXT: [[CALL:%.*]] = call i32 @foo()
30+
// CHECK-NEXT: [[TMP0:%.*]] = load i32, ptr addrspacecast (ptr addrspace(1) @x to ptr), align 4
31+
// CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[CALL]], [[TMP0]]
32+
// CHECK-NEXT: ret i32 [[ADD]]
33+
//
34+
//
35+
// CHECK: Function Attrs: convergent noinline nounwind optnone
36+
// CHECK-LABEL: define internal i32 @foo
37+
// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
38+
// CHECK-NEXT: entry:
39+
// CHECK-NEXT: [[RETVAL:%.*]] = alloca i32, align 4, addrspace(5)
40+
// CHECK-NEXT: [[RETVAL_ASCAST:%.*]] = addrspacecast ptr addrspace(5) [[RETVAL]] to ptr
41+
// CHECK-NEXT: ret i32 42
42+
//
43+
//.
44+
// CHECK: attributes #0 = { noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx90a" "target-features"="+16-bit-insts,+atomic-buffer-global-pk-add-f16-insts,+atomic-fadd-rtn-insts,+ci-insts,+dl-insts,+dot1-insts,+dot10-insts,+dot2-insts,+dot3-insts,+dot4-insts,+dot5-insts,+dot6-insts,+dot7-insts,+dpp,+gfx8-insts,+gfx9-insts,+gfx90a-insts,+mai-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64" }
45+
// CHECK: attributes #1 = { convergent noinline nounwind optnone "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="gfx90a" "target-features"="+16-bit-insts,+ci-insts,+dpp,+gfx8-insts,+s-memrealtime,+s-memtime-inst,+wavefrontsize64" }
46+
//.

0 commit comments

Comments
 (0)