Skip to content

Commit ad05778

Browse files
authored
[clang-offload-bundler] Add new unbundling mode 'a' for archives (#2840)
This patch adds support for unbundling archives into archives under a new file type 'a'. Input file for this mode is expected to be an archive with fat object files and outputs (one per offload target) will be archives with extracted device specific parts from the input's objects. Signed-off-by: Sergey Dmitriev <serguei.n.dmitriev@intel.com>
1 parent 012078c commit ad05778

File tree

2 files changed

+89
-16
lines changed

2 files changed

+89
-16
lines changed

clang/test/Driver/clang-offload-bundler.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
// CK-HELP: {{.*}}o {{.*}}- object
4747
// CK-HELP: {{.*}}gch {{.*}}- precompiled-header
4848
// CK-HELP: {{.*}}ast {{.*}}- clang AST file
49+
// CK-HELP: {{.*}}a {{.*}}- archive of objects
4950
// CK-HELP: {{.*}}ao {{.*}}- archive with one object; output is an unbundled object
5051
// CK-HELP: {{.*}}aoo {{.*}}- archive; output file is a list of unbundled objects
5152
// CK-HELP: {{.*}}-unbundle {{.*}}- Unbundle bundled file into several output files.
@@ -341,6 +342,17 @@
341342
// RUN: diff %t.tgt1 %t.res.tgt1
342343
// RUN: diff %t.tgt2 %t.res.tgt2
343344

345+
// Check archive mode.
346+
// RUN: clang-offload-bundler -type=a -targets=host-%itanium_abi_triple,openmp-powerpc64le-ibm-linux-gnu,openmp-x86_64-pc-linux-gnu -outputs=%t.host.a,%t.tgt1.a,%t.tgt2.a -inputs=%t.a -unbundle
347+
// RUN: cmp %t.host.a %t.a
348+
// RUN: llvm-ar t %t.tgt1.a | FileCheck %s --check-prefix=CHECK-AR-TGT1-LIST
349+
// RUN: llvm-ar t %t.tgt2.a | FileCheck %s --check-prefix=CHECK-AR-TGT2-LIST
350+
351+
// CHECK-AR-TGT1-LIST: openmp-powerpc64le-ibm-linux-gnu.{{.+}}.bundle3.o
352+
// CHECK-AR-TGT1-LIST: openmp-powerpc64le-ibm-linux-gnu.{{.+}}.bundle4.o
353+
// CHECK-AR-TGT2-LIST: openmp-x86_64-pc-linux-gnu.{{.+}}.bundle3.o
354+
// CHECK-AR-TGT2-LIST: openmp-x86_64-pc-linux-gnu.{{.+}}.bundle4.o
355+
344356
// Some code so that we can create a binary out of this file.
345357
int A = 0;
346358
void test_func(void) {

clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp

Lines changed: 77 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "llvm/ADT/StringSwitch.h"
2525
#include "llvm/ADT/Triple.h"
2626
#include "llvm/Object/Archive.h"
27+
#include "llvm/Object/ArchiveWriter.h"
2728
#include "llvm/Object/Binary.h"
2829
#include "llvm/Object/ObjectFile.h"
2930
#include "llvm/Support/Casting.h"
@@ -88,6 +89,7 @@ static cl::opt<std::string> FilesType(
8889
" oo - object; output file is a list of unbundled objects\n"
8990
" gch - precompiled-header\n"
9091
" ast - clang AST file\n"
92+
" a - archive of objects\n"
9193
" ao - archive with one object; output is an unbundled object\n"
9294
" aoo - archive; output file is a list of unbundled objects\n"),
9395
cl::cat(ClangOffloadBundlerCategory));
@@ -161,7 +163,7 @@ class FileHandler {
161163
virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
162164

163165
/// Read the current bundle and write the result into the stream \a OS.
164-
virtual Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
166+
virtual Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) = 0;
165167

166168
/// Write the header of the bundled file to \a OS based on the information
167169
/// gathered from \a Inputs.
@@ -342,7 +344,7 @@ class BinaryFileHandler final : public FileHandler {
342344
return Error::success();
343345
}
344346

345-
Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
347+
Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
346348
assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
347349
StringRef FC = Input.getBuffer();
348350
OS.write(FC.data() + CurBundleInfo->second.Offset,
@@ -624,7 +626,7 @@ class ObjectFileHandler final : public FileHandler {
624626

625627
Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
626628

627-
Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
629+
Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
628630
assert(CurBundle != TripleToBundleInfo.end() &&
629631
"all bundles have been read already");
630632

@@ -899,7 +901,7 @@ class TextFileHandler final : public FileHandler {
899901
return Error::success();
900902
}
901903

902-
Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
904+
Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) final {
903905
StringRef FC = Input.getBuffer();
904906
size_t BundleStart = ReadChars;
905907

@@ -955,11 +957,26 @@ class ArchiveFileHandler final : public FileHandler {
955957
StringMap<unsigned>::iterator CurrBundle = Bundles.end();
956958
StringMap<unsigned>::iterator NextBundle = Bundles.end();
957959

960+
/// Output mode for the archive unbundler.
961+
enum class OutputType {
962+
Unknown,
963+
FileList, // Output is a list file with extracted object file names
964+
Object, // Output is a single object file
965+
Archive // Output is an archive with extracted objects
966+
};
967+
const OutputType Mode = StringSwitch<OutputType>(FilesType)
968+
.Case("aoo", OutputType::FileList)
969+
.Case("ao", OutputType::Object)
970+
.Case("a", OutputType::Archive)
971+
.Default(OutputType::Unknown);
972+
958973
public:
959974
ArchiveFileHandler() = default;
960975
~ArchiveFileHandler() = default;
961976

962977
Error ReadHeader(MemoryBuffer &Input) override {
978+
assert(Mode != OutputType::Unknown && "unknown output mode");
979+
963980
// Create archive instance for the given input.
964981
auto ArOrErr = Archive::create(Input);
965982
if (!ArOrErr)
@@ -1014,18 +1031,27 @@ class ArchiveFileHandler final : public FileHandler {
10141031

10151032
Error ReadBundleEnd(MemoryBuffer &Input) override { return Error::success(); }
10161033

1017-
Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) override {
1034+
Error ReadBundle(raw_ostream &OS, MemoryBuffer &Input) override {
10181035
assert(CurrBundle->second && "attempt to extract nonexistent bundle");
10191036

1020-
bool FileListMode = FilesType == "aoo";
1021-
10221037
// In single-file mode we do not expect to see bundle more than once.
1023-
if (!FileListMode && CurrBundle->second > 1)
1038+
if (Mode == OutputType::Object && CurrBundle->second > 1)
10241039
return createStringError(
10251040
errc::invalid_argument,
10261041
"'ao' file type is requested, but the archive contains multiple "
10271042
"device objects; use 'aoo' instead");
10281043

1044+
// For 'host' archive bundle just copy input data to the output stream.
1045+
if (Mode == OutputType::Archive && hasHostKind(CurrBundle->first())) {
1046+
OS << Input.getBuffer();
1047+
return Error::success();
1048+
}
1049+
1050+
// Extracted objects data for archive mode.
1051+
SmallVector<std::string, 8u> ArNames;
1052+
SmallVector<SmallVector<char, 0u>, 8u> ArData;
1053+
SmallVector<NewArchiveMember, 8u> ArMembers;
1054+
10291055
// Read all children.
10301056
Error Err = Error::success();
10311057
for (auto &C : Ar->children(Err)) {
@@ -1043,6 +1069,10 @@ class ArchiveFileHandler final : public FileHandler {
10431069
auto Obj = std::unique_ptr<ObjectFile>(cast<ObjectFile>(Bin.release()));
10441070
auto Buf = MemoryBuffer::getMemBuffer(Obj->getMemoryBufferRef(), false);
10451071

1072+
auto ChildNameOrErr = C.getName();
1073+
if (!ChildNameOrErr)
1074+
return ChildNameOrErr.takeError();
1075+
10461076
ObjectFileHandler OFH(std::move(Obj));
10471077
if (Error Err = OFH.ReadHeader(*Buf))
10481078
return Err;
@@ -1052,10 +1082,9 @@ class ArchiveFileHandler final : public FileHandler {
10521082
while (*NameOrErr) {
10531083
auto TT = **NameOrErr;
10541084
if (TT == CurrBundle->first()) {
1055-
// This is the bundle we are looking for. Create temporary file where
1056-
// the device part will be extracted if we are in the file-list mode,
1057-
// or write directly to the output file otherwise.
1058-
if (FileListMode) {
1085+
// This is the bundle we are looking for.
1086+
if (Mode == OutputType::FileList) {
1087+
// Create temporary file where the device part will be extracted to.
10591088
SmallString<128u> ChildFileName;
10601089
auto EC = sys::fs::createTemporaryFile(TempFileNameBase, "o",
10611090
ChildFileName);
@@ -1075,8 +1104,23 @@ class ArchiveFileHandler final : public FileHandler {
10751104
// Add temporary file name with the device part to the output file
10761105
// list.
10771106
OS << ChildFileName << "\n";
1078-
} else if (Error Err = OFH.ReadBundle(OS, *Buf))
1079-
return Err;
1107+
} else if (Mode == OutputType::Object) {
1108+
// Extract the bundle to the output file in single file mode.
1109+
if (Error Err = OFH.ReadBundle(OS, *Buf))
1110+
return Err;
1111+
} else if (Mode == OutputType::Archive) {
1112+
auto &Name =
1113+
ArNames.emplace_back((TT + "." + *ChildNameOrErr).str());
1114+
auto &Data = ArData.emplace_back();
1115+
raw_svector_ostream ChildOS{Data};
1116+
1117+
// Extract the bundle.
1118+
if (Error Err = OFH.ReadBundle(ChildOS, *Buf))
1119+
return Err;
1120+
1121+
ArMembers.emplace_back(
1122+
MemoryBufferRef{StringRef(Data.data(), Data.size()), Name});
1123+
}
10801124
if (Error Err = OFH.ReadBundleEnd(*Buf))
10811125
return Err;
10821126
}
@@ -1087,6 +1131,23 @@ class ArchiveFileHandler final : public FileHandler {
10871131
}
10881132
if (Err)
10891133
return Err;
1134+
1135+
if (Mode == OutputType::Archive) {
1136+
// Determine archive kind for the offload target.
1137+
StringRef TargetKind;
1138+
StringRef TargetTriple;
1139+
getOffloadKindAndTriple(CurrBundle->first(), TargetKind, TargetTriple);
1140+
auto ArKind = Triple(TargetTriple).isOSDarwin() ? Archive::K_DARWIN
1141+
: Archive::K_GNU;
1142+
1143+
// And write archive to the output.
1144+
Expected<std::unique_ptr<MemoryBuffer>> NewAr =
1145+
writeArchiveToBuffer(ArMembers, /*WriteSymtab=*/true, ArKind,
1146+
/*Deterministic=*/true, /*Thin=*/false);
1147+
if (!NewAr)
1148+
return NewAr.takeError();
1149+
OS << NewAr.get()->getBuffer();
1150+
}
10901151
return Error::success();
10911152
}
10921153

@@ -1152,7 +1213,7 @@ CreateFileHandler(MemoryBuffer &FirstInput) {
11521213
return std::make_unique<BinaryFileHandler>();
11531214
if (FilesType == "ast")
11541215
return std::make_unique<BinaryFileHandler>();
1155-
if (FilesType == "ao" || FilesType == "aoo")
1216+
if (FilesType == "a" || FilesType == "ao" || FilesType == "aoo")
11561217
return std::make_unique<ArchiveFileHandler>();
11571218

11581219
return createStringError(errc::invalid_argument,
@@ -1163,7 +1224,7 @@ CreateFileHandler(MemoryBuffer &FirstInput) {
11631224
static Error BundleFiles() {
11641225
std::error_code EC;
11651226

1166-
if (FilesType == "ao" || FilesType == "aoo")
1227+
if (FilesType == "a" || FilesType == "ao" || FilesType == "aoo")
11671228
return createStringError(errc::invalid_argument,
11681229
"bundling is not supported for archives");
11691230

0 commit comments

Comments
 (0)