Skip to content

Commit e5c1afa

Browse files
committed
Add clang-offload-deps tool for generating dependence files for offload targets
This tool is intended to be used by the clang driver for offload linking with static offload libraries. It takes linked host image as input and produces bitcode files (one per offload target) containing references to symbols that must be defined in the target images. Each dependence bitcode file then is expected to be compiled to an object by the driver using appropriate target toolchain, and dependence object added to the target linker as input together with the other inputs. References to the symbols in dependence object should ensure that target linker pulls in necessary symbol definitions from the input static libraries. Signed-off-by: Sergey Dmitriev <[email protected]>
1 parent 0bd6bb1 commit e5c1afa

File tree

6 files changed

+316
-0
lines changed

6 files changed

+316
-0
lines changed

clang/test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ list(APPEND CLANG_TEST_DEPS
6565
clang-format
6666
clang-tblgen
6767
clang-offload-bundler
68+
clang-offload-deps
6869
clang-import-test
6970
clang-rename
7071
clang-refactor
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// REQUIRES: x86-registered-target
2+
3+
//
4+
// Check help message.
5+
//
6+
// RUN: clang-offload-deps --help | FileCheck %s --check-prefix CHECK-HELP
7+
// CHECK-HELP: {{.*}}OVERVIEW: A tool for creating dependence bitcode files for offload targets. Takes
8+
// CHECK-HELP: {{.*}}host image as input and produces bitcode files, one per offload target, with
9+
// CHECK-HELP: {{.*}}references to symbols that must be defined in target images.
10+
// CHECK-HELP: {{.*}}USAGE: clang-offload-deps [options] <input file>
11+
// CHECK-HELP: {{.*}} --outputs=<string> - [<output file>,...]
12+
// CHECK-HELP: {{.*}} --targets=<string> - [<offload kind>-<target triple>,...]
13+
14+
//
15+
// Create source image for reading dependencies from.
16+
//
17+
// RUN: %clang -target %itanium_abi_triple -c %s -o %t.host
18+
// RUN: %clang -target x86_64-pc-linux-gnu -c %s -o %t.x86_64
19+
// RUN: %clang -target spir64 -emit-llvm -c %s -o %t.spir64
20+
// RUN: clang-offload-bundler -type=o -targets=host-%itanium_abi_triple,openmp-x86_64-pc-linux-gnu,sycl-spir64 -inputs=%t.host,%t.x86_64,%t.spir64 -outputs=%t.fat
21+
22+
//
23+
// Generate dependencies for targets and check contents of the output bitcode files.
24+
//
25+
// RUN: clang-offload-deps -targets=openmp-x86_64-pc-linux-gnu,sycl-spir64 -outputs=%t.deps.x86_64,%t.deps.spir64 %t.fat
26+
// RUN: llvm-dis -o - %t.deps.x86_64 | FileCheck %s --check-prefixes=CHECK-DEPS-X86_64
27+
// RUN: llvm-dis -o - %t.deps.spir64 | FileCheck %s --check-prefixes=CHECK-DEPS-SPIR64
28+
29+
// CHECK-DEPS-X86_64: target triple = "x86_64-pc-linux-gnu"
30+
// CHECK-DEPS-X86_64: @bar = external global i8*
31+
// CHECK-DEPS-X86_64: @foo = external global i8*
32+
// CHECK-DEPS-X86_64: @offload.symbols = hidden local_unnamed_addr global [2 x i8*] [i8* bitcast (i8** @bar to i8*), i8* bitcast (i8** @foo to i8*)]
33+
34+
// CHECK-DEPS-SPIR64: target triple = "spir64"
35+
// CHECK-DEPS-SPIR64: @bar = external global i8*
36+
// CHECK-DEPS-SPIR64: @foo = external global i8*
37+
// CHECK-DEPS-SPIR64: @llvm.used = appending global [2 x i8*] [i8* bitcast (i8** @bar to i8*), i8* bitcast (i8** @foo to i8*)], section "llvm.metadata"
38+
39+
void foo(void) {}
40+
void bar(void) {}

clang/tools/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ add_clang_subdirectory(clang-format-vs)
99
add_clang_subdirectory(clang-fuzzer)
1010
add_clang_subdirectory(clang-import-test)
1111
add_clang_subdirectory(clang-offload-bundler)
12+
add_clang_subdirectory(clang-offload-deps)
1213
add_clang_subdirectory(clang-offload-wrapper)
1314
add_clang_subdirectory(clang-scan-deps)
1415

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
set(LLVM_LINK_COMPONENTS BitWriter Core Object Support)
2+
3+
add_clang_tool(clang-offload-deps
4+
ClangOffloadDeps.cpp
5+
6+
DEPENDS
7+
intrinsics_gen
8+
)
9+
10+
set(CLANG_OFFLOAD_DEPS_LIB_DEPS
11+
clangBasic
12+
)
13+
14+
add_dependencies(clang clang-offload-deps)
15+
16+
clang_target_link_libraries(clang-offload-deps
17+
PRIVATE
18+
${CLANG_OFFLOAD_DEPS_LIB_DEPS}
19+
)
Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
//===----------- clang-offload-deps/ClangOffloadDeps.cpp ------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
///
9+
/// \file
10+
/// Implementation of the clang-offload-deps tool. This tool is intended to be
11+
/// used by the clang driver for offload linking with static offload libraries.
12+
/// It takes linked host image as input and produces bitcode files, one per
13+
/// offload target, containing references to symbols that must be defined in the
14+
/// target images. Dependence bitcode file is then expected to be compiled to an
15+
/// object by the driver using the appropriate offload target toolchain, and
16+
/// dependence object added to the target linker as input together with the
17+
/// other inputs. References to the symbols in dependence object should ensure
18+
/// that target linker pulls in necessary symbol definitions from the input
19+
/// static libraries.
20+
///
21+
//===----------------------------------------------------------------------===//
22+
23+
#include "clang/Basic/Version.h"
24+
#include "llvm/ADT/ArrayRef.h"
25+
#include "llvm/ADT/Triple.h"
26+
#include "llvm/Bitcode/BitcodeWriter.h"
27+
#include "llvm/IR/GlobalVariable.h"
28+
#include "llvm/IR/LLVMContext.h"
29+
#include "llvm/IR/Module.h"
30+
#ifndef NDEBUG
31+
#include "llvm/IR/Verifier.h"
32+
#endif // NDEBUG
33+
#include "llvm/Object/ObjectFile.h"
34+
#include "llvm/Support/CommandLine.h"
35+
#include "llvm/Support/Errc.h"
36+
#include "llvm/Support/ErrorOr.h"
37+
#include "llvm/Support/Signals.h"
38+
#include "llvm/Support/ToolOutputFile.h"
39+
#include "llvm/Support/WithColor.h"
40+
#include "llvm/Support/raw_ostream.h"
41+
42+
#define SYMBOLS_SECTION_NAME ".tgtsym"
43+
44+
using namespace llvm;
45+
using namespace llvm::object;
46+
47+
static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
48+
49+
// Mark all our options with this category, everything else (except for -version
50+
// and -help) will be hidden.
51+
static cl::OptionCategory
52+
ClangOffloadDepsCategory("clang-offload-deps options");
53+
54+
static cl::list<std::string> Outputs("outputs", cl::CommaSeparated,
55+
cl::OneOrMore,
56+
cl::desc("[<output file>,...]"),
57+
cl::cat(ClangOffloadDepsCategory));
58+
static cl::list<std::string>
59+
Targets("targets", cl::CommaSeparated, cl::OneOrMore,
60+
cl::desc("[<offload kind>-<target triple>,...]"),
61+
cl::cat(ClangOffloadDepsCategory));
62+
63+
static cl::opt<std::string> Input(cl::Positional, cl::Required,
64+
cl::desc("<input file>"),
65+
cl::cat(ClangOffloadDepsCategory));
66+
67+
/// Path to the current binary.
68+
static std::string ToolPath;
69+
70+
static void reportError(Error E) {
71+
logAllUnhandledErrors(std::move(E), WithColor::error(errs(), ToolPath));
72+
}
73+
74+
int main(int argc, const char **argv) {
75+
sys::PrintStackTraceOnErrorSignal(argv[0]);
76+
ToolPath = sys::fs::getMainExecutable(argv[0], &ToolPath);
77+
78+
cl::HideUnrelatedOptions(ClangOffloadDepsCategory);
79+
cl::SetVersionPrinter([](raw_ostream &OS) {
80+
OS << clang::getClangToolFullVersion("clang-offload-deps") << '\n';
81+
});
82+
cl::ParseCommandLineOptions(
83+
argc, argv,
84+
"A tool for creating dependence bitcode files for offload targets. "
85+
"Takes\nhost image as input and produces bitcode files, one per offload "
86+
"target, with\nreferences to symbols that must be defined in target "
87+
"images.\n");
88+
89+
if (Help) {
90+
cl::PrintHelpMessage();
91+
return 0;
92+
}
93+
94+
// The number of output files and targets should match.
95+
if (Targets.size() != Outputs.size()) {
96+
reportError(
97+
createStringError(errc::invalid_argument,
98+
"number of output files and targets should match"));
99+
return 1;
100+
}
101+
102+
// Verify that given targets are valid. Each target string is expected to have
103+
// the following format
104+
// <kind>-<triple>
105+
// where <kind> is host, openmp, hip, sycl or fpga,
106+
// and <triple> is an offload target triple.
107+
SmallVector<StringRef, 8u> Triples(Targets.size());
108+
for (unsigned I = 0; I < Targets.size(); ++I) {
109+
StringRef Kind;
110+
std::tie(Kind, Triples[I]) = StringRef(Targets[I]).split('-');
111+
112+
bool KindIsValid = !Kind.empty() && StringSwitch<bool>(Kind)
113+
.Case("host", true)
114+
.Case("openmp", true)
115+
.Case("hip", true)
116+
.Case("sycl", true)
117+
.Case("fpga", true)
118+
.Default(false);
119+
120+
bool TripleIsValid = !Triples[I].empty() &&
121+
Triple(Triples[I]).getArch() != Triple::UnknownArch;
122+
123+
if (!KindIsValid || !TripleIsValid) {
124+
SmallVector<char, 128u> Buf;
125+
raw_svector_ostream Msg(Buf);
126+
Msg << "invalid target '" << Targets[I] << "'";
127+
if (!KindIsValid)
128+
Msg << ", unknown offloading kind '" << Kind << "'";
129+
if (!TripleIsValid)
130+
Msg << ", unknown target triple '" << Triples[I] << "'";
131+
reportError(createStringError(errc::invalid_argument, Msg.str()));
132+
return 1;
133+
}
134+
}
135+
136+
// Read input file. It should have one of the supported object file formats.
137+
Expected<OwningBinary<ObjectFile>> ObjectOrErr =
138+
ObjectFile::createObjectFile(Input);
139+
if (!ObjectOrErr) {
140+
reportError(ObjectOrErr.takeError());
141+
return 1;
142+
}
143+
144+
// Then try to find a section in the input binary which contains offload
145+
// symbol names and parse section contents.
146+
DenseMap<StringRef, SmallDenseSet<StringRef>> Target2Symbols;
147+
for (SectionRef Section : ObjectOrErr->getBinary()->sections()) {
148+
// Look for the .tgtsym section in the binary.
149+
Expected<StringRef> NameOrErr = Section.getName();
150+
if (!NameOrErr) {
151+
reportError(NameOrErr.takeError());
152+
return 1;
153+
}
154+
if (*NameOrErr != SYMBOLS_SECTION_NAME)
155+
continue;
156+
157+
// This is the section we are looking for, read symbol names from it.
158+
Expected<StringRef> DataOrErr = Section.getContents();
159+
if (!DataOrErr) {
160+
reportError(DataOrErr.takeError());
161+
return 1;
162+
}
163+
164+
// Symbol names are prefixed by a target, and prefixed names are separated
165+
// by '\0' characters from each other. Find the names matching our list of
166+
// offload targets and insert them into the map.
167+
for (StringRef Symbol = DataOrErr.get(); !Symbol.empty();) {
168+
unsigned Len = strlen(Symbol.data());
169+
170+
for (const std::string &Target : Targets) {
171+
std::string Prefix = Target + ".";
172+
if (Symbol.startswith(Prefix))
173+
Target2Symbols[Target].insert(
174+
Symbol.substr(Prefix.size(), Len - Prefix.size()));
175+
}
176+
177+
Symbol = Symbol.drop_front(Len + 1u);
178+
}
179+
180+
// Binary should not have more than one .tgtsym section.
181+
break;
182+
}
183+
184+
LLVMContext Context;
185+
Type *Int8PtrTy = Type::getInt8PtrTy(Context);
186+
187+
// Create bitcode file with the symbol names for each target and write it to
188+
// the output file.
189+
SmallVector<std::unique_ptr<ToolOutputFile>, 8u> Files;
190+
Files.reserve(Outputs.size());
191+
for (unsigned I = 0; I < Outputs.size(); ++I) {
192+
StringRef FileName = Outputs[I];
193+
194+
Module Mod{"offload-deps", Context};
195+
Mod.setTargetTriple(Triples[I]);
196+
197+
SmallVector<Constant *, 8u> Used;
198+
Used.reserve(Target2Symbols[Targets[I]].size());
199+
for (StringRef Symbol : Target2Symbols[Targets[I]])
200+
Used.push_back(ConstantExpr::getPointerBitCastOrAddrSpaceCast(
201+
Mod.getOrInsertGlobal(Symbol, Int8PtrTy), Int8PtrTy));
202+
203+
if (!Used.empty()) {
204+
ArrayType *ArrayTy = ArrayType::get(Int8PtrTy, Used.size());
205+
206+
// For SPIRV linking is done on LLVM IR inputs, so we can use special
207+
// global variable llvm.used to represent a reference to a symbol. But for
208+
// other targets we have to create a real reference since llvm.used may
209+
// not be representable in the object file.
210+
if (Triple(Triples[I]).isSPIR()) {
211+
auto *GV = new GlobalVariable(
212+
Mod, ArrayTy, false, GlobalValue::AppendingLinkage,
213+
ConstantArray::get(ArrayTy, Used), "llvm.used");
214+
GV->setSection("llvm.metadata");
215+
} else {
216+
auto *GV = new GlobalVariable(
217+
Mod, ArrayTy, false, GlobalValue::ExternalLinkage,
218+
ConstantArray::get(ArrayTy, Used), "offload.symbols");
219+
GV->setUnnamedAddr(GlobalValue::UnnamedAddr::Local);
220+
GV->setVisibility(GlobalValue::HiddenVisibility);
221+
}
222+
}
223+
224+
#ifndef NDEBUG
225+
if (verifyModule(Mod, &errs())) {
226+
reportError(createStringError(inconvertibleErrorCode(),
227+
"module verification error"));
228+
return 1;
229+
}
230+
#endif // NDEBUG
231+
232+
// Open output file.
233+
std::error_code EC;
234+
const auto &File = Files.emplace_back(
235+
std::make_unique<ToolOutputFile>(FileName, EC, sys::fs::OF_None));
236+
if (EC) {
237+
reportError(createFileError(FileName, EC));
238+
return 1;
239+
}
240+
241+
// Write deps module to the output.
242+
WriteBitcodeToFile(Mod, File->os());
243+
if (File->os().has_error()) {
244+
reportError(createFileError(FileName, File->os().error()));
245+
return 1;
246+
}
247+
}
248+
249+
// Everything is done, keep the output files.
250+
for (const auto &File : Files)
251+
File->keep();
252+
253+
return 0;
254+
}

sycl/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ add_custom_target( sycl-toolchain
280280
clang
281281
clang-offload-wrapper
282282
clang-offload-bundler
283+
clang-offload-deps
283284
file-table-tform
284285
llc
285286
llvm-ar

0 commit comments

Comments
 (0)