diff --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c --- a/clang/test/Driver/linker-wrapper.c +++ b/clang/test/Driver/linker-wrapper.c @@ -48,3 +48,15 @@ // RUN: /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA_OMP_LINK // CUDA_OMP_LINK: nvlink{{.*}}-m64 -o {{.*}}.out -arch sm_70 {{.*}}.o {{.*}}.o + +// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-lib.o \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,nvptx64-nvida-cuda,sm_70 \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,nvptx64-nvida-cuda,sm_52 +// RUN: llvm-ar rcs %t.a %t-lib.o +// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-obj.o \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,nvptx64-nvida-cuda,sm_70 +// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -linker-path \ +// RUN: /usr/bin/ld -- %t.a %t-obj.o -o a.out 2>&1 | FileCheck %s --check-prefix=STATIC-LIBRARY + +// STATIC-LIBRARY: nvlink{{.*}} -arch sm_70 +// STATIC-LIBRARY-NOT: nvlink{{.*}} -arch sm_50 diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -167,13 +167,15 @@ /// Information for a device offloading file extracted from the host. struct DeviceFile { DeviceFile(StringRef Kind, StringRef TheTriple, StringRef Arch, - StringRef Filename) - : Kind(Kind), TheTriple(TheTriple), Arch(Arch), Filename(Filename) {} + StringRef Filename, bool IsLibrary = false) + : Kind(Kind), TheTriple(TheTriple), Arch(Arch), Filename(Filename), + IsLibrary(IsLibrary) {} std::string Kind; std::string TheTriple; std::string Arch; std::string Filename; + bool IsLibrary; }; namespace llvm { @@ -208,7 +210,8 @@ Expected> extractFromBuffer(std::unique_ptr Buffer, - SmallVectorImpl &DeviceFiles); + SmallVectorImpl &DeviceFiles, + bool IsLibrary = false); void printCommands(ArrayRef CmdArgs) { if (CmdArgs.empty()) @@ -324,7 +327,8 @@ /// buffer \p Contents. The buffer is expected to contain a valid offloading /// binary format. Error extractOffloadFiles(StringRef Contents, StringRef Prefix, - SmallVectorImpl &DeviceFiles) { + SmallVectorImpl &DeviceFiles, + bool IsLibrary = false) { uint64_t Offset = 0; // There could be multiple offloading binaries stored at this section. while (Offset < Contents.size()) { @@ -361,7 +365,7 @@ return E; DeviceFiles.emplace_back(Kind, Binary.getTriple(), Binary.getArch(), - TempFile); + TempFile, IsLibrary); Offset += Binary.getSize(); } @@ -371,7 +375,8 @@ Expected> extractFromBinary(const ObjectFile &Obj, - SmallVectorImpl &DeviceFiles) { + SmallVectorImpl &DeviceFiles, + bool IsLibrary = false) { StringRef Extension = sys::path::extension(Obj.getFileName()).drop_front(); StringRef Prefix = sys::path::stem(Obj.getFileName()); SmallVector ToBeStripped; @@ -386,7 +391,8 @@ if (!Contents) return Contents.takeError(); - if (Error Err = extractOffloadFiles(*Contents, Prefix, DeviceFiles)) + if (Error Err = + extractOffloadFiles(*Contents, Prefix, DeviceFiles, IsLibrary)) return std::move(Err); ToBeStripped.push_back(*Name); @@ -447,7 +453,8 @@ Expected> extractFromBitcode(std::unique_ptr Buffer, - SmallVectorImpl &DeviceFiles) { + SmallVectorImpl &DeviceFiles, + bool IsLibrary = false) { LLVMContext Context; SMDiagnostic Err; std::unique_ptr M = getLazyIRModule(std::move(Buffer), Err, Context); @@ -473,7 +480,8 @@ StringRef Contents = CDS->getAsString(); - if (Error Err = extractOffloadFiles(Contents, Prefix, DeviceFiles)) + if (Error Err = + extractOffloadFiles(Contents, Prefix, DeviceFiles, IsLibrary)) return std::move(Err); ToBeDeleted.push_back(&GV); @@ -521,7 +529,8 @@ std::unique_ptr ChildBuffer = MemoryBuffer::getMemBuffer(*ChildBufferRefOrErr, false); - auto FileOrErr = extractFromBuffer(std::move(ChildBuffer), DeviceFiles); + auto FileOrErr = extractFromBuffer(std::move(ChildBuffer), DeviceFiles, + /*IsLibrary*/ true); if (!FileOrErr) return FileOrErr.takeError(); @@ -573,11 +582,11 @@ /// device code stripped from the buffer will be returned. Expected> extractFromBuffer(std::unique_ptr Buffer, - SmallVectorImpl &DeviceFiles) { + SmallVectorImpl &DeviceFiles, bool IsLibrary) { file_magic Type = identify_magic(Buffer->getBuffer()); switch (Type) { case file_magic::bitcode: - return extractFromBitcode(std::move(Buffer), DeviceFiles); + return extractFromBitcode(std::move(Buffer), DeviceFiles, IsLibrary); case file_magic::elf_relocatable: case file_magic::macho_object: case file_magic::coff_object: { @@ -585,7 +594,7 @@ ObjectFile::createObjectFile(*Buffer, Type); if (!ObjFile) return ObjFile.takeError(); - return extractFromBinary(*ObjFile->get(), DeviceFiles); + return extractFromBinary(*ObjFile->get(), DeviceFiles, IsLibrary); } case file_magic::archive: { Expected> LibFile = @@ -1127,8 +1136,22 @@ SmallVectorImpl &LinkedImages) { // Get the list of inputs for a specific device. DenseMap> LinkerInputMap; - for (auto &File : DeviceFiles) - LinkerInputMap[File].push_back(File.Filename); + SmallVector LibraryFiles; + for (auto &File : DeviceFiles) { + if (File.IsLibrary) + LibraryFiles.push_back(File); + else + LinkerInputMap[File].push_back(File.Filename); + } + + // Static libraries are loaded lazily as-needed, only add them if other files + // are present. + // TODO: We need to check the symbols as well, static libraries are only + // loaded if they contain symbols that are currently undefined or common + // in the symbol table. + for (auto &File : LibraryFiles) + if (LinkerInputMap.count(File)) + LinkerInputMap[File].push_back(File.Filename); // Try to link each device toolchain. for (auto &LinkerInput : LinkerInputMap) {