xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg //===-- clang-offload-wrapper/ClangOffloadWrapper.cpp -----------*- C++ -*-===//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg ///
97330f729Sjoerg /// \file
107330f729Sjoerg /// Implementation of the offload wrapper tool. It takes offload target binaries
117330f729Sjoerg /// as input and creates wrapper bitcode file containing target binaries
127330f729Sjoerg /// packaged as data. Wrapper bitcode also includes initialization code which
137330f729Sjoerg /// registers target binaries in offloading runtime at program startup.
147330f729Sjoerg ///
157330f729Sjoerg //===----------------------------------------------------------------------===//
167330f729Sjoerg 
177330f729Sjoerg #include "clang/Basic/Version.h"
187330f729Sjoerg #include "llvm/ADT/ArrayRef.h"
197330f729Sjoerg #include "llvm/ADT/Triple.h"
207330f729Sjoerg #include "llvm/Bitcode/BitcodeWriter.h"
217330f729Sjoerg #include "llvm/IR/Constants.h"
227330f729Sjoerg #include "llvm/IR/GlobalVariable.h"
237330f729Sjoerg #include "llvm/IR/IRBuilder.h"
247330f729Sjoerg #include "llvm/IR/LLVMContext.h"
257330f729Sjoerg #include "llvm/IR/Module.h"
267330f729Sjoerg #include "llvm/Support/CommandLine.h"
277330f729Sjoerg #include "llvm/Support/Errc.h"
287330f729Sjoerg #include "llvm/Support/Error.h"
297330f729Sjoerg #include "llvm/Support/ErrorOr.h"
30*e038c9c4Sjoerg #include "llvm/Support/FileSystem.h"
317330f729Sjoerg #include "llvm/Support/MemoryBuffer.h"
327330f729Sjoerg #include "llvm/Support/Signals.h"
337330f729Sjoerg #include "llvm/Support/ToolOutputFile.h"
347330f729Sjoerg #include "llvm/Support/WithColor.h"
357330f729Sjoerg #include "llvm/Support/raw_ostream.h"
367330f729Sjoerg #include "llvm/Transforms/Utils/ModuleUtils.h"
377330f729Sjoerg #include <cassert>
387330f729Sjoerg #include <cstdint>
397330f729Sjoerg 
407330f729Sjoerg using namespace llvm;
417330f729Sjoerg 
427330f729Sjoerg static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
437330f729Sjoerg 
447330f729Sjoerg // Mark all our options with this category, everything else (except for -version
457330f729Sjoerg // and -help) will be hidden.
467330f729Sjoerg static cl::OptionCategory
477330f729Sjoerg     ClangOffloadWrapperCategory("clang-offload-wrapper options");
487330f729Sjoerg 
497330f729Sjoerg static cl::opt<std::string> Output("o", cl::Required,
507330f729Sjoerg                                    cl::desc("Output filename"),
517330f729Sjoerg                                    cl::value_desc("filename"),
527330f729Sjoerg                                    cl::cat(ClangOffloadWrapperCategory));
537330f729Sjoerg 
547330f729Sjoerg static cl::list<std::string> Inputs(cl::Positional, cl::OneOrMore,
557330f729Sjoerg                                     cl::desc("<input files>"),
567330f729Sjoerg                                     cl::cat(ClangOffloadWrapperCategory));
577330f729Sjoerg 
587330f729Sjoerg static cl::opt<std::string>
597330f729Sjoerg     Target("target", cl::Required,
607330f729Sjoerg            cl::desc("Target triple for the output module"),
617330f729Sjoerg            cl::value_desc("triple"), cl::cat(ClangOffloadWrapperCategory));
627330f729Sjoerg 
637330f729Sjoerg namespace {
647330f729Sjoerg 
657330f729Sjoerg class BinaryWrapper {
667330f729Sjoerg   LLVMContext C;
677330f729Sjoerg   Module M;
687330f729Sjoerg 
697330f729Sjoerg   StructType *EntryTy = nullptr;
707330f729Sjoerg   StructType *ImageTy = nullptr;
717330f729Sjoerg   StructType *DescTy = nullptr;
727330f729Sjoerg 
737330f729Sjoerg private:
getSizeTTy()747330f729Sjoerg   IntegerType *getSizeTTy() {
757330f729Sjoerg     switch (M.getDataLayout().getPointerTypeSize(Type::getInt8PtrTy(C))) {
767330f729Sjoerg     case 4u:
777330f729Sjoerg       return Type::getInt32Ty(C);
787330f729Sjoerg     case 8u:
797330f729Sjoerg       return Type::getInt64Ty(C);
807330f729Sjoerg     }
817330f729Sjoerg     llvm_unreachable("unsupported pointer type size");
827330f729Sjoerg   }
837330f729Sjoerg 
847330f729Sjoerg   // struct __tgt_offload_entry {
857330f729Sjoerg   //   void *addr;
867330f729Sjoerg   //   char *name;
877330f729Sjoerg   //   size_t size;
887330f729Sjoerg   //   int32_t flags;
897330f729Sjoerg   //   int32_t reserved;
907330f729Sjoerg   // };
getEntryTy()917330f729Sjoerg   StructType *getEntryTy() {
927330f729Sjoerg     if (!EntryTy)
937330f729Sjoerg       EntryTy = StructType::create("__tgt_offload_entry", Type::getInt8PtrTy(C),
947330f729Sjoerg                                    Type::getInt8PtrTy(C), getSizeTTy(),
957330f729Sjoerg                                    Type::getInt32Ty(C), Type::getInt32Ty(C));
967330f729Sjoerg     return EntryTy;
977330f729Sjoerg   }
987330f729Sjoerg 
getEntryPtrTy()997330f729Sjoerg   PointerType *getEntryPtrTy() { return PointerType::getUnqual(getEntryTy()); }
1007330f729Sjoerg 
1017330f729Sjoerg   // struct __tgt_device_image {
1027330f729Sjoerg   //   void *ImageStart;
1037330f729Sjoerg   //   void *ImageEnd;
1047330f729Sjoerg   //   __tgt_offload_entry *EntriesBegin;
1057330f729Sjoerg   //   __tgt_offload_entry *EntriesEnd;
1067330f729Sjoerg   // };
getDeviceImageTy()1077330f729Sjoerg   StructType *getDeviceImageTy() {
1087330f729Sjoerg     if (!ImageTy)
1097330f729Sjoerg       ImageTy = StructType::create("__tgt_device_image", Type::getInt8PtrTy(C),
1107330f729Sjoerg                                    Type::getInt8PtrTy(C), getEntryPtrTy(),
1117330f729Sjoerg                                    getEntryPtrTy());
1127330f729Sjoerg     return ImageTy;
1137330f729Sjoerg   }
1147330f729Sjoerg 
getDeviceImagePtrTy()1157330f729Sjoerg   PointerType *getDeviceImagePtrTy() {
1167330f729Sjoerg     return PointerType::getUnqual(getDeviceImageTy());
1177330f729Sjoerg   }
1187330f729Sjoerg 
1197330f729Sjoerg   // struct __tgt_bin_desc {
1207330f729Sjoerg   //   int32_t NumDeviceImages;
1217330f729Sjoerg   //   __tgt_device_image *DeviceImages;
1227330f729Sjoerg   //   __tgt_offload_entry *HostEntriesBegin;
1237330f729Sjoerg   //   __tgt_offload_entry *HostEntriesEnd;
1247330f729Sjoerg   // };
getBinDescTy()1257330f729Sjoerg   StructType *getBinDescTy() {
1267330f729Sjoerg     if (!DescTy)
1277330f729Sjoerg       DescTy = StructType::create("__tgt_bin_desc", Type::getInt32Ty(C),
1287330f729Sjoerg                                   getDeviceImagePtrTy(), getEntryPtrTy(),
1297330f729Sjoerg                                   getEntryPtrTy());
1307330f729Sjoerg     return DescTy;
1317330f729Sjoerg   }
1327330f729Sjoerg 
getBinDescPtrTy()1337330f729Sjoerg   PointerType *getBinDescPtrTy() {
1347330f729Sjoerg     return PointerType::getUnqual(getBinDescTy());
1357330f729Sjoerg   }
1367330f729Sjoerg 
1377330f729Sjoerg   /// Creates binary descriptor for the given device images. Binary descriptor
1387330f729Sjoerg   /// is an object that is passed to the offloading runtime at program startup
1397330f729Sjoerg   /// and it describes all device images available in the executable or shared
1407330f729Sjoerg   /// library. It is defined as follows
1417330f729Sjoerg   ///
1427330f729Sjoerg   /// __attribute__((visibility("hidden")))
1437330f729Sjoerg   /// extern __tgt_offload_entry *__start_omp_offloading_entries;
1447330f729Sjoerg   /// __attribute__((visibility("hidden")))
1457330f729Sjoerg   /// extern __tgt_offload_entry *__stop_omp_offloading_entries;
1467330f729Sjoerg   ///
1477330f729Sjoerg   /// static const char Image0[] = { <Bufs.front() contents> };
1487330f729Sjoerg   ///  ...
1497330f729Sjoerg   /// static const char ImageN[] = { <Bufs.back() contents> };
1507330f729Sjoerg   ///
1517330f729Sjoerg   /// static const __tgt_device_image Images[] = {
1527330f729Sjoerg   ///   {
1537330f729Sjoerg   ///     Image0,                            /*ImageStart*/
1547330f729Sjoerg   ///     Image0 + sizeof(Image0),           /*ImageEnd*/
1557330f729Sjoerg   ///     __start_omp_offloading_entries,    /*EntriesBegin*/
1567330f729Sjoerg   ///     __stop_omp_offloading_entries      /*EntriesEnd*/
1577330f729Sjoerg   ///   },
1587330f729Sjoerg   ///   ...
1597330f729Sjoerg   ///   {
1607330f729Sjoerg   ///     ImageN,                            /*ImageStart*/
1617330f729Sjoerg   ///     ImageN + sizeof(ImageN),           /*ImageEnd*/
1627330f729Sjoerg   ///     __start_omp_offloading_entries,    /*EntriesBegin*/
1637330f729Sjoerg   ///     __stop_omp_offloading_entries      /*EntriesEnd*/
1647330f729Sjoerg   ///   }
1657330f729Sjoerg   /// };
1667330f729Sjoerg   ///
1677330f729Sjoerg   /// static const __tgt_bin_desc BinDesc = {
1687330f729Sjoerg   ///   sizeof(Images) / sizeof(Images[0]),  /*NumDeviceImages*/
1697330f729Sjoerg   ///   Images,                              /*DeviceImages*/
1707330f729Sjoerg   ///   __start_omp_offloading_entries,      /*HostEntriesBegin*/
1717330f729Sjoerg   ///   __stop_omp_offloading_entries        /*HostEntriesEnd*/
1727330f729Sjoerg   /// };
1737330f729Sjoerg   ///
1747330f729Sjoerg   /// Global variable that represents BinDesc is returned.
createBinDesc(ArrayRef<ArrayRef<char>> Bufs)1757330f729Sjoerg   GlobalVariable *createBinDesc(ArrayRef<ArrayRef<char>> Bufs) {
1767330f729Sjoerg     // Create external begin/end symbols for the offload entries table.
1777330f729Sjoerg     auto *EntriesB = new GlobalVariable(
1787330f729Sjoerg         M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage,
1797330f729Sjoerg         /*Initializer*/ nullptr, "__start_omp_offloading_entries");
1807330f729Sjoerg     EntriesB->setVisibility(GlobalValue::HiddenVisibility);
1817330f729Sjoerg     auto *EntriesE = new GlobalVariable(
1827330f729Sjoerg         M, getEntryTy(), /*isConstant*/ true, GlobalValue::ExternalLinkage,
1837330f729Sjoerg         /*Initializer*/ nullptr, "__stop_omp_offloading_entries");
1847330f729Sjoerg     EntriesE->setVisibility(GlobalValue::HiddenVisibility);
1857330f729Sjoerg 
1867330f729Sjoerg     // We assume that external begin/end symbols that we have created above will
1877330f729Sjoerg     // be defined by the linker. But linker will do that only if linker inputs
1887330f729Sjoerg     // have section with "omp_offloading_entries" name which is not guaranteed.
1897330f729Sjoerg     // So, we just create dummy zero sized object in the offload entries section
1907330f729Sjoerg     // to force linker to define those symbols.
1917330f729Sjoerg     auto *DummyInit =
1927330f729Sjoerg         ConstantAggregateZero::get(ArrayType::get(getEntryTy(), 0u));
1937330f729Sjoerg     auto *DummyEntry = new GlobalVariable(
1947330f729Sjoerg         M, DummyInit->getType(), true, GlobalVariable::ExternalLinkage,
1957330f729Sjoerg         DummyInit, "__dummy.omp_offloading.entry");
1967330f729Sjoerg     DummyEntry->setSection("omp_offloading_entries");
1977330f729Sjoerg     DummyEntry->setVisibility(GlobalValue::HiddenVisibility);
1987330f729Sjoerg 
1997330f729Sjoerg     auto *Zero = ConstantInt::get(getSizeTTy(), 0u);
2007330f729Sjoerg     Constant *ZeroZero[] = {Zero, Zero};
2017330f729Sjoerg 
2027330f729Sjoerg     // Create initializer for the images array.
2037330f729Sjoerg     SmallVector<Constant *, 4u> ImagesInits;
2047330f729Sjoerg     ImagesInits.reserve(Bufs.size());
2057330f729Sjoerg     for (ArrayRef<char> Buf : Bufs) {
2067330f729Sjoerg       auto *Data = ConstantDataArray::get(C, Buf);
2077330f729Sjoerg       auto *Image = new GlobalVariable(M, Data->getType(), /*isConstant*/ true,
2087330f729Sjoerg                                        GlobalVariable::InternalLinkage, Data,
2097330f729Sjoerg                                        ".omp_offloading.device_image");
2107330f729Sjoerg       Image->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
2117330f729Sjoerg 
2127330f729Sjoerg       auto *Size = ConstantInt::get(getSizeTTy(), Buf.size());
2137330f729Sjoerg       Constant *ZeroSize[] = {Zero, Size};
2147330f729Sjoerg 
2157330f729Sjoerg       auto *ImageB = ConstantExpr::getGetElementPtr(Image->getValueType(),
2167330f729Sjoerg                                                     Image, ZeroZero);
2177330f729Sjoerg       auto *ImageE = ConstantExpr::getGetElementPtr(Image->getValueType(),
2187330f729Sjoerg                                                     Image, ZeroSize);
2197330f729Sjoerg 
2207330f729Sjoerg       ImagesInits.push_back(ConstantStruct::get(getDeviceImageTy(), ImageB,
2217330f729Sjoerg                                                 ImageE, EntriesB, EntriesE));
2227330f729Sjoerg     }
2237330f729Sjoerg 
2247330f729Sjoerg     // Then create images array.
2257330f729Sjoerg     auto *ImagesData = ConstantArray::get(
2267330f729Sjoerg         ArrayType::get(getDeviceImageTy(), ImagesInits.size()), ImagesInits);
2277330f729Sjoerg 
2287330f729Sjoerg     auto *Images =
2297330f729Sjoerg         new GlobalVariable(M, ImagesData->getType(), /*isConstant*/ true,
2307330f729Sjoerg                            GlobalValue::InternalLinkage, ImagesData,
2317330f729Sjoerg                            ".omp_offloading.device_images");
2327330f729Sjoerg     Images->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
2337330f729Sjoerg 
2347330f729Sjoerg     auto *ImagesB = ConstantExpr::getGetElementPtr(Images->getValueType(),
2357330f729Sjoerg                                                    Images, ZeroZero);
2367330f729Sjoerg 
2377330f729Sjoerg     // And finally create the binary descriptor object.
2387330f729Sjoerg     auto *DescInit = ConstantStruct::get(
2397330f729Sjoerg         getBinDescTy(),
2407330f729Sjoerg         ConstantInt::get(Type::getInt32Ty(C), ImagesInits.size()), ImagesB,
2417330f729Sjoerg         EntriesB, EntriesE);
2427330f729Sjoerg 
2437330f729Sjoerg     return new GlobalVariable(M, DescInit->getType(), /*isConstant*/ true,
2447330f729Sjoerg                               GlobalValue::InternalLinkage, DescInit,
2457330f729Sjoerg                               ".omp_offloading.descriptor");
2467330f729Sjoerg   }
2477330f729Sjoerg 
createRegisterFunction(GlobalVariable * BinDesc)2487330f729Sjoerg   void createRegisterFunction(GlobalVariable *BinDesc) {
2497330f729Sjoerg     auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
2507330f729Sjoerg     auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
2517330f729Sjoerg                                   ".omp_offloading.descriptor_reg", &M);
2527330f729Sjoerg     Func->setSection(".text.startup");
2537330f729Sjoerg 
2547330f729Sjoerg     // Get __tgt_register_lib function declaration.
2557330f729Sjoerg     auto *RegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(),
2567330f729Sjoerg                                         /*isVarArg*/ false);
2577330f729Sjoerg     FunctionCallee RegFuncC =
2587330f729Sjoerg         M.getOrInsertFunction("__tgt_register_lib", RegFuncTy);
2597330f729Sjoerg 
2607330f729Sjoerg     // Construct function body
2617330f729Sjoerg     IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
2627330f729Sjoerg     Builder.CreateCall(RegFuncC, BinDesc);
2637330f729Sjoerg     Builder.CreateRetVoid();
2647330f729Sjoerg 
2657330f729Sjoerg     // Add this function to constructors.
266*e038c9c4Sjoerg     // Set priority to 1 so that __tgt_register_lib is executed AFTER
267*e038c9c4Sjoerg     // __tgt_register_requires (we want to know what requirements have been
268*e038c9c4Sjoerg     // asked for before we load a libomptarget plugin so that by the time the
269*e038c9c4Sjoerg     // plugin is loaded it can report how many devices there are which can
270*e038c9c4Sjoerg     // satisfy these requirements).
271*e038c9c4Sjoerg     appendToGlobalCtors(M, Func, /*Priority*/ 1);
2727330f729Sjoerg   }
2737330f729Sjoerg 
createUnregisterFunction(GlobalVariable * BinDesc)2747330f729Sjoerg   void createUnregisterFunction(GlobalVariable *BinDesc) {
2757330f729Sjoerg     auto *FuncTy = FunctionType::get(Type::getVoidTy(C), /*isVarArg*/ false);
2767330f729Sjoerg     auto *Func = Function::Create(FuncTy, GlobalValue::InternalLinkage,
2777330f729Sjoerg                                   ".omp_offloading.descriptor_unreg", &M);
2787330f729Sjoerg     Func->setSection(".text.startup");
2797330f729Sjoerg 
2807330f729Sjoerg     // Get __tgt_unregister_lib function declaration.
2817330f729Sjoerg     auto *UnRegFuncTy = FunctionType::get(Type::getVoidTy(C), getBinDescPtrTy(),
2827330f729Sjoerg                                           /*isVarArg*/ false);
2837330f729Sjoerg     FunctionCallee UnRegFuncC =
2847330f729Sjoerg         M.getOrInsertFunction("__tgt_unregister_lib", UnRegFuncTy);
2857330f729Sjoerg 
2867330f729Sjoerg     // Construct function body
2877330f729Sjoerg     IRBuilder<> Builder(BasicBlock::Create(C, "entry", Func));
2887330f729Sjoerg     Builder.CreateCall(UnRegFuncC, BinDesc);
2897330f729Sjoerg     Builder.CreateRetVoid();
2907330f729Sjoerg 
2917330f729Sjoerg     // Add this function to global destructors.
292*e038c9c4Sjoerg     // Match priority of __tgt_register_lib
293*e038c9c4Sjoerg     appendToGlobalDtors(M, Func, /*Priority*/ 1);
2947330f729Sjoerg   }
2957330f729Sjoerg 
2967330f729Sjoerg public:
BinaryWrapper(StringRef Target)2977330f729Sjoerg   BinaryWrapper(StringRef Target) : M("offload.wrapper.object", C) {
2987330f729Sjoerg     M.setTargetTriple(Target);
2997330f729Sjoerg   }
3007330f729Sjoerg 
wrapBinaries(ArrayRef<ArrayRef<char>> Binaries)3017330f729Sjoerg   const Module &wrapBinaries(ArrayRef<ArrayRef<char>> Binaries) {
3027330f729Sjoerg     GlobalVariable *Desc = createBinDesc(Binaries);
3037330f729Sjoerg     assert(Desc && "no binary descriptor");
3047330f729Sjoerg     createRegisterFunction(Desc);
3057330f729Sjoerg     createUnregisterFunction(Desc);
3067330f729Sjoerg     return M;
3077330f729Sjoerg   }
3087330f729Sjoerg };
3097330f729Sjoerg 
3107330f729Sjoerg } // anonymous namespace
3117330f729Sjoerg 
main(int argc,const char ** argv)3127330f729Sjoerg int main(int argc, const char **argv) {
3137330f729Sjoerg   sys::PrintStackTraceOnErrorSignal(argv[0]);
3147330f729Sjoerg 
3157330f729Sjoerg   cl::HideUnrelatedOptions(ClangOffloadWrapperCategory);
3167330f729Sjoerg   cl::SetVersionPrinter([](raw_ostream &OS) {
3177330f729Sjoerg     OS << clang::getClangToolFullVersion("clang-offload-wrapper") << '\n';
3187330f729Sjoerg   });
3197330f729Sjoerg   cl::ParseCommandLineOptions(
3207330f729Sjoerg       argc, argv,
3217330f729Sjoerg       "A tool to create a wrapper bitcode for offload target binaries. Takes "
3227330f729Sjoerg       "offload\ntarget binaries as input and produces bitcode file containing "
3237330f729Sjoerg       "target binaries packaged\nas data and initialization code which "
3247330f729Sjoerg       "registers target binaries in offload runtime.\n");
3257330f729Sjoerg 
3267330f729Sjoerg   if (Help) {
3277330f729Sjoerg     cl::PrintHelpMessage();
3287330f729Sjoerg     return 0;
3297330f729Sjoerg   }
3307330f729Sjoerg 
3317330f729Sjoerg   auto reportError = [argv](Error E) {
3327330f729Sjoerg     logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
3337330f729Sjoerg   };
3347330f729Sjoerg 
3357330f729Sjoerg   if (Triple(Target).getArch() == Triple::UnknownArch) {
3367330f729Sjoerg     reportError(createStringError(
3377330f729Sjoerg         errc::invalid_argument, "'" + Target + "': unsupported target triple"));
3387330f729Sjoerg     return 1;
3397330f729Sjoerg   }
3407330f729Sjoerg 
3417330f729Sjoerg   // Read device binaries.
3427330f729Sjoerg   SmallVector<std::unique_ptr<MemoryBuffer>, 4u> Buffers;
3437330f729Sjoerg   SmallVector<ArrayRef<char>, 4u> Images;
3447330f729Sjoerg   Buffers.reserve(Inputs.size());
3457330f729Sjoerg   Images.reserve(Inputs.size());
3467330f729Sjoerg   for (const std::string &File : Inputs) {
3477330f729Sjoerg     ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr =
3487330f729Sjoerg         MemoryBuffer::getFileOrSTDIN(File);
3497330f729Sjoerg     if (!BufOrErr) {
3507330f729Sjoerg       reportError(createFileError(File, BufOrErr.getError()));
3517330f729Sjoerg       return 1;
3527330f729Sjoerg     }
3537330f729Sjoerg     const std::unique_ptr<MemoryBuffer> &Buf =
3547330f729Sjoerg         Buffers.emplace_back(std::move(*BufOrErr));
3557330f729Sjoerg     Images.emplace_back(Buf->getBufferStart(), Buf->getBufferSize());
3567330f729Sjoerg   }
3577330f729Sjoerg 
3587330f729Sjoerg   // Create the output file to write the resulting bitcode to.
3597330f729Sjoerg   std::error_code EC;
3607330f729Sjoerg   ToolOutputFile Out(Output, EC, sys::fs::OF_None);
3617330f729Sjoerg   if (EC) {
3627330f729Sjoerg     reportError(createFileError(Output, EC));
3637330f729Sjoerg     return 1;
3647330f729Sjoerg   }
3657330f729Sjoerg 
3667330f729Sjoerg   // Create a wrapper for device binaries and write its bitcode to the file.
3677330f729Sjoerg   WriteBitcodeToFile(BinaryWrapper(Target).wrapBinaries(
3687330f729Sjoerg                          makeArrayRef(Images.data(), Images.size())),
3697330f729Sjoerg                      Out.os());
3707330f729Sjoerg   if (Out.os().has_error()) {
3717330f729Sjoerg     reportError(createFileError(Output, Out.os().error()));
3727330f729Sjoerg     return 1;
3737330f729Sjoerg   }
3747330f729Sjoerg 
3757330f729Sjoerg   // Success.
3767330f729Sjoerg   Out.keep();
3777330f729Sjoerg   return 0;
3787330f729Sjoerg }
379