9
9
#include " HIPUtility.h"
10
10
#include " CommonArgs.h"
11
11
#include " clang/Driver/Compilation.h"
12
+ #include " clang/Driver/Options.h"
13
+ #include " llvm/ADT/StringExtras.h"
12
14
#include " llvm/ADT/StringRef.h"
15
+ #include " llvm/Object/Archive.h"
16
+ #include " llvm/Object/ObjectFile.h"
17
+ #include " llvm/Support/MD5.h"
18
+ #include " llvm/Support/MemoryBuffer.h"
13
19
#include " llvm/Support/Path.h"
20
+ #include " llvm/Support/raw_ostream.h"
14
21
#include " llvm/TargetParser/Triple.h"
22
+ #include < deque>
23
+ #include < set>
15
24
25
+ using namespace clang ;
16
26
using namespace clang ::driver;
17
27
using namespace clang ::driver::tools;
18
28
using namespace llvm ::opt;
29
+ using llvm::dyn_cast;
19
30
20
31
#if defined(_WIN32) || defined(_WIN64)
21
32
#define NULL_FILE " nul"
@@ -36,6 +47,146 @@ static std::string normalizeForBundler(const llvm::Triple &T,
36
47
: T.normalize ();
37
48
}
38
49
50
+ // Collect undefined __hip_fatbin* and __hip_gpubin_handle* symbols from all
51
+ // input object or archive files.
52
+ class HIPUndefinedFatBinSymbols {
53
+ public:
54
+ HIPUndefinedFatBinSymbols (const Compilation &C)
55
+ : C(C), DiagID(C.getDriver().getDiags().getCustomDiagID(
56
+ DiagnosticsEngine::Error,
57
+ " Error collecting HIP undefined fatbin symbols: %0" )),
58
+ Quiet (C.getArgs().hasArg(options::OPT__HASH_HASH_HASH)),
59
+ Verbose(C.getArgs().hasArg(options::OPT_v)) {
60
+ populateSymbols ();
61
+ if (Verbose) {
62
+ for (auto Name : FatBinSymbols)
63
+ llvm::errs () << " Found undefined HIP fatbin symbol: " << Name << " \n " ;
64
+ for (auto Name : GPUBinHandleSymbols)
65
+ llvm::errs () << " Found undefined HIP gpubin handle symbol: " << Name
66
+ << " \n " ;
67
+ }
68
+ }
69
+
70
+ const std::set<std::string> &getFatBinSymbols () const {
71
+ return FatBinSymbols;
72
+ }
73
+
74
+ const std::set<std::string> &getGPUBinHandleSymbols () const {
75
+ return GPUBinHandleSymbols;
76
+ }
77
+
78
+ private:
79
+ const Compilation &C;
80
+ unsigned DiagID;
81
+ bool Quiet;
82
+ bool Verbose;
83
+ std::set<std::string> FatBinSymbols;
84
+ std::set<std::string> GPUBinHandleSymbols;
85
+ const std::string FatBinPrefix = " __hip_fatbin" ;
86
+ const std::string GPUBinHandlePrefix = " __hip_gpubin_handle" ;
87
+
88
+ void populateSymbols () {
89
+ std::deque<const Action *> WorkList;
90
+ std::set<const Action *> Visited;
91
+
92
+ for (const auto &Action : C.getActions ()) {
93
+ WorkList.push_back (Action);
94
+ }
95
+
96
+ while (!WorkList.empty ()) {
97
+ const Action *CurrentAction = WorkList.front ();
98
+ WorkList.pop_front ();
99
+
100
+ if (!CurrentAction || !Visited.insert (CurrentAction).second )
101
+ continue ;
102
+
103
+ if (const auto *IA = dyn_cast<InputAction>(CurrentAction)) {
104
+ std::string ID = IA->getId ().str ();
105
+ if (!ID.empty ()) {
106
+ ID = llvm::utohexstr (llvm::MD5Hash (ID), /* LowerCase=*/ true );
107
+ FatBinSymbols.insert (Twine (FatBinPrefix + " _" + ID).str ());
108
+ GPUBinHandleSymbols.insert (
109
+ Twine (GPUBinHandlePrefix + " _" + ID).str ());
110
+ continue ;
111
+ }
112
+ const char *Filename = IA->getInputArg ().getValue ();
113
+ auto BufferOrErr = llvm::MemoryBuffer::getFile (Filename);
114
+ // Input action could be options to linker, therefore ignore it
115
+ // if cannot read it.
116
+ if (!BufferOrErr)
117
+ continue ;
118
+
119
+ processInput (BufferOrErr.get ()->getMemBufferRef ());
120
+ } else
121
+ WorkList.insert (WorkList.end (), CurrentAction->getInputs ().begin (),
122
+ CurrentAction->getInputs ().end ());
123
+ }
124
+ }
125
+
126
+ void processInput (const llvm::MemoryBufferRef &Buffer) {
127
+ // Try processing as object file first.
128
+ auto ObjFileOrErr = llvm::object::ObjectFile::createObjectFile (Buffer);
129
+ if (ObjFileOrErr) {
130
+ processSymbols (**ObjFileOrErr);
131
+ return ;
132
+ }
133
+
134
+ // Then try processing as archive files.
135
+ llvm::consumeError (ObjFileOrErr.takeError ());
136
+ auto ArchiveOrErr = llvm::object::Archive::create (Buffer);
137
+ if (ArchiveOrErr) {
138
+ llvm::Error Err = llvm::Error::success ();
139
+ llvm::object::Archive &Archive = *ArchiveOrErr.get ();
140
+ for (auto &Child : Archive.children (Err)) {
141
+ auto ChildBufOrErr = Child.getMemoryBufferRef ();
142
+ if (ChildBufOrErr)
143
+ processInput (*ChildBufOrErr);
144
+ else
145
+ errorHandler (ChildBufOrErr.takeError ());
146
+ }
147
+
148
+ if (Err)
149
+ errorHandler (std::move (Err));
150
+ return ;
151
+ }
152
+
153
+ // Ignore other files.
154
+ llvm::consumeError (ArchiveOrErr.takeError ());
155
+ }
156
+ void processSymbols (const llvm::object::ObjectFile &Obj) {
157
+ for (const auto &Symbol : Obj.symbols ()) {
158
+ auto FlagOrErr = Symbol.getFlags ();
159
+ if (!FlagOrErr) {
160
+ errorHandler (FlagOrErr.takeError ());
161
+ continue ;
162
+ }
163
+
164
+ // Filter only undefined symbols
165
+ if (!(FlagOrErr.get () & llvm::object::SymbolRef::SF_Undefined)) {
166
+ continue ;
167
+ }
168
+
169
+ auto NameOrErr = Symbol.getName ();
170
+ if (!NameOrErr) {
171
+ errorHandler (NameOrErr.takeError ());
172
+ continue ;
173
+ }
174
+ llvm::StringRef Name = *NameOrErr;
175
+
176
+ if (Name.starts_with (FatBinPrefix))
177
+ FatBinSymbols.insert (Name.str ());
178
+ else if (Name.starts_with (GPUBinHandlePrefix))
179
+ GPUBinHandleSymbols.insert (Name.str ());
180
+ }
181
+ }
182
+
183
+ void errorHandler (llvm::Error Err) {
184
+ if (Quiet)
185
+ return ;
186
+ C.getDriver ().Diag (DiagID) << llvm::toString (std::move (Err));
187
+ }
188
+ };
189
+
39
190
// Construct a clang-offload-bundler command to bundle code objects for
40
191
// different devices into a HIP fat binary.
41
192
void HIP::constructHIPFatbinCommand (Compilation &C, const JobAction &JA,
@@ -130,26 +281,86 @@ void HIP::constructGenerateObjFileFromHIPFatBinary(
130
281
auto HostTriple =
131
282
C.getSingleOffloadToolChain <Action::OFK_Host>()->getTriple ();
132
283
284
+ HIPUndefinedFatBinSymbols Symbols (C);
285
+
286
+ std::string PrimaryHipFatbinSymbol;
287
+ std::string PrimaryGpuBinHandleSymbol;
288
+ bool FoundPrimaryHipFatbinSymbol = false ;
289
+ bool FoundPrimaryGpuBinHandleSymbol = false ;
290
+
291
+ std::vector<std::string> AliasHipFatbinSymbols;
292
+ std::vector<std::string> AliasGpuBinHandleSymbols;
293
+
294
+ // Iterate through symbols to find the primary ones and collect others for
295
+ // aliasing
296
+ for (const auto &Symbol : Symbols.getFatBinSymbols ()) {
297
+ if (!FoundPrimaryHipFatbinSymbol) {
298
+ PrimaryHipFatbinSymbol = Symbol;
299
+ FoundPrimaryHipFatbinSymbol = true ;
300
+ } else {
301
+ AliasHipFatbinSymbols.push_back (Symbol);
302
+ }
303
+ }
304
+
305
+ for (const auto &Symbol : Symbols.getGPUBinHandleSymbols ()) {
306
+ if (!FoundPrimaryGpuBinHandleSymbol) {
307
+ PrimaryGpuBinHandleSymbol = Symbol;
308
+ FoundPrimaryGpuBinHandleSymbol = true ;
309
+ } else {
310
+ AliasGpuBinHandleSymbols.push_back (Symbol);
311
+ }
312
+ }
313
+
133
314
// Add MC directives to embed target binaries. We ensure that each
134
315
// section and image is 16-byte aligned. This is not mandatory, but
135
316
// increases the likelihood of data to be aligned with a cache block
136
317
// in several main host machines.
137
318
ObjStream << " # HIP Object Generator\n " ;
138
319
ObjStream << " # *** Automatically generated by Clang ***\n " ;
139
- if (HostTriple.isWindowsMSVCEnvironment ()) {
140
- ObjStream << " .section .hip_fatbin, \" dw\"\n " ;
141
- } else {
142
- ObjStream << " .protected __hip_fatbin\n " ;
143
- ObjStream << " .type __hip_fatbin,@object\n " ;
144
- ObjStream << " .section .hip_fatbin,\" a\" ,@progbits\n " ;
320
+ if (FoundPrimaryGpuBinHandleSymbol) {
321
+ // Define the first gpubin handle symbol
322
+ if (HostTriple.isWindowsMSVCEnvironment ()) {
323
+ ObjStream << " .section .hip_gpubin_handle,\" dw\"\n " ;
324
+ } else {
325
+ ObjStream << " .protected " << PrimaryGpuBinHandleSymbol << " \n " ;
326
+ ObjStream << " .type " << PrimaryGpuBinHandleSymbol << " ,@object\n " ;
327
+ ObjStream << " .section .hip_gpubin_handle,\" aw\"\n " ;
328
+ }
329
+ ObjStream << " .globl " << PrimaryGpuBinHandleSymbol << " \n " ;
330
+ ObjStream << " .p2align 3\n " ; // Align 8
331
+ ObjStream << PrimaryGpuBinHandleSymbol << " :\n " ;
332
+ ObjStream << " .zero 8\n " ; // Size 8
333
+
334
+ // Generate alias directives for other gpubin handle symbols
335
+ for (const auto &AliasSymbol : AliasGpuBinHandleSymbols) {
336
+ ObjStream << " .globl " << AliasSymbol << " \n " ;
337
+ ObjStream << " .set " << AliasSymbol << " ," << PrimaryGpuBinHandleSymbol
338
+ << " \n " ;
339
+ }
340
+ }
341
+ if (FoundPrimaryHipFatbinSymbol) {
342
+ // Define the first fatbin symbol
343
+ if (HostTriple.isWindowsMSVCEnvironment ()) {
344
+ ObjStream << " .section .hip_fatbin,\" dw\"\n " ;
345
+ } else {
346
+ ObjStream << " .protected " << PrimaryHipFatbinSymbol << " \n " ;
347
+ ObjStream << " .type " << PrimaryHipFatbinSymbol << " ,@object\n " ;
348
+ ObjStream << " .section .hip_fatbin,\" a\" ,@progbits\n " ;
349
+ }
350
+ ObjStream << " .globl " << PrimaryHipFatbinSymbol << " \n " ;
351
+ ObjStream << " .p2align " << llvm::Log2 (llvm::Align (HIPCodeObjectAlign))
352
+ << " \n " ;
353
+ // Generate alias directives for other fatbin symbols
354
+ for (const auto &AliasSymbol : AliasHipFatbinSymbols) {
355
+ ObjStream << " .globl " << AliasSymbol << " \n " ;
356
+ ObjStream << " .set " << AliasSymbol << " ," << PrimaryHipFatbinSymbol
357
+ << " \n " ;
358
+ }
359
+ ObjStream << PrimaryHipFatbinSymbol << " :\n " ;
360
+ ObjStream << " .incbin " ;
361
+ llvm::sys::printArg (ObjStream, BundleFile, /* Quote=*/ true );
362
+ ObjStream << " \n " ;
145
363
}
146
- ObjStream << " .globl __hip_fatbin\n " ;
147
- ObjStream << " .p2align " << llvm::Log2 (llvm::Align (HIPCodeObjectAlign))
148
- << " \n " ;
149
- ObjStream << " __hip_fatbin:\n " ;
150
- ObjStream << " .incbin " ;
151
- llvm::sys::printArg (ObjStream, BundleFile, /* Quote=*/ true );
152
- ObjStream << " \n " ;
153
364
if (HostTriple.isOSLinux () && HostTriple.isOSBinFormatELF ())
154
365
ObjStream << " .section .note.GNU-stack, \"\" , @progbits\n " ;
155
366
ObjStream.flush ();
0 commit comments