Index: clang/lib/Driver/Driver.cpp =================================================================== --- clang/lib/Driver/Driver.cpp +++ clang/lib/Driver/Driver.cpp @@ -2903,12 +2903,16 @@ std::string FileName = IA->getInputArg().getAsString(Args); // Check if the type of the file is the same as the action. Do not // unbundle it if it is not. Do not unbundle .so files, for example, - // which are not object files. + // which are not object files. Files with extension ".lib" is classified + // as TY_Object but they are actually archives, therefore should not be + // unbundled here as objects. They will be handled at other places. + const StringRef LibFileExt = ".lib"; if (IA->getType() == types::TY_Object && (!llvm::sys::path::has_extension(FileName) || types::lookupTypeForExtension( llvm::sys::path::extension(FileName).drop_front()) != - types::TY_Object)) + types::TY_Object || + llvm::sys::path::extension(FileName) == LibFileExt)) return ABRT_Inactive; for (auto Arch : GpuArchList) { Index: clang/lib/Driver/OffloadBundler.cpp =================================================================== --- clang/lib/Driver/OffloadBundler.cpp +++ clang/lib/Driver/OffloadBundler.cpp @@ -1139,8 +1139,24 @@ ArchiveBuffers.push_back(std::move(*BufOrErr)); Expected> LibOrErr = Archive::create(ArchiveBuffers.back()->getMemBufferRef()); - if (!LibOrErr) - return LibOrErr.takeError(); + // If it is not an archive, create an empty archive for each target if missing + // bundles are allowed. + if (Error E = LibOrErr.takeError()) { + if (!BundlerConfig.AllowMissingBundles) + return E; + // Silence the error. + (void)toString(std::move(E)); + for (auto &Target : BundlerConfig.TargetNames) { + StringRef FileName = TargetOutputFileNameMap[Target]; + std::vector EmptyArchive; + EmptyArchive.clear(); + if (Error WriteErr = writeArchive(FileName, EmptyArchive, true, + getDefaultArchiveKindForHost(), true, + false, nullptr)) + return WriteErr; + } + return Error::success(); + } auto Archive = std::move(*LibOrErr); Index: clang/lib/Driver/ToolChains/CommonArgs.cpp =================================================================== --- clang/lib/Driver/ToolChains/CommonArgs.cpp +++ clang/lib/Driver/ToolChains/CommonArgs.cpp @@ -1812,85 +1812,94 @@ return false; bool FoundAOB = false; - SmallVector AOBFileNames; std::string ArchiveOfBundles; - for (auto LPath : LibraryPaths) { - ArchiveOfBundles.clear(); - - llvm::Triple Triple(D.getTargetTriple()); - bool IsMSVC = Triple.isWindowsMSVCEnvironment(); - for (auto Prefix : {"/libdevice/", "/"}) { - if (IsMSVC) - AOBFileNames.push_back(Twine(LPath + Prefix + Lib + ".lib").str()); - AOBFileNames.push_back(Twine(LPath + Prefix + "lib" + Lib + ".a").str()); - } - for (auto AOB : AOBFileNames) { - if (llvm::sys::fs::exists(AOB)) { - ArchiveOfBundles = AOB; - FoundAOB = true; - break; + llvm::Triple Triple(D.getTargetTriple()); + bool IsMSVC = Triple.isWindowsMSVCEnvironment(); + auto Ext = IsMSVC ? ".lib" : ".a"; + if (!Lib.startswith(":") && llvm::sys::fs::exists(Lib)) { + ArchiveOfBundles = Lib; + FoundAOB = true; + } else { + for (auto LPath : LibraryPaths) { + ArchiveOfBundles.clear(); + SmallVector AOBFileNames; + auto LibFile = + (Lib.startswith(":") ? Lib.drop_front() + : IsMSVC ? Lib + Ext : "lib" + Lib + Ext) + .str(); + for (auto Prefix : {"/libdevice/", "/"}) { + auto AOB = Twine(LPath + Prefix + LibFile).str(); + if (llvm::sys::fs::exists(AOB)) { + ArchiveOfBundles = AOB; + FoundAOB = true; + break; + } } + if (FoundAOB) + break; } + } - if (!FoundAOB) - continue; - - StringRef Prefix = isBitCodeSDL ? "libbc-" : "lib"; - std::string OutputLib = D.GetTemporaryPath( - Twine(Prefix + Lib + "-" + Arch + "-" + Target).str(), "a"); - - C.addTempFile(C.getArgs().MakeArgString(OutputLib)); + if (!FoundAOB) + return false; - ArgStringList CmdArgs; - SmallString<128> DeviceTriple; - DeviceTriple += Action::GetOffloadKindName(JA.getOffloadingDeviceKind()); - DeviceTriple += '-'; - std::string NormalizedTriple = T.getToolChain().getTriple().normalize(); - DeviceTriple += NormalizedTriple; + StringRef Prefix = isBitCodeSDL ? "libbc-" : "lib"; + std::string OutputLib = + D.GetTemporaryPath(Twine(Prefix + llvm::sys::path::filename(Lib) + "-" + + Arch + "-" + Target) + .str(), + "a"); + + C.addTempFile(C.getArgs().MakeArgString(OutputLib)); + + ArgStringList CmdArgs; + SmallString<128> DeviceTriple; + DeviceTriple += Action::GetOffloadKindName(JA.getOffloadingDeviceKind()); + DeviceTriple += '-'; + std::string NormalizedTriple = T.getToolChain().getTriple().normalize(); + DeviceTriple += NormalizedTriple; if (!Target.empty()) { - DeviceTriple += '-'; + DeviceTriple += '-'; DeviceTriple += Target; - } + } - std::string UnbundleArg("-unbundle"); - std::string TypeArg("-type=a"); - std::string InputArg("-input=" + ArchiveOfBundles); - std::string OffloadArg("-targets=" + std::string(DeviceTriple)); - std::string OutputArg("-output=" + OutputLib); - - const char *UBProgram = DriverArgs.MakeArgString( - T.getToolChain().GetProgramPath("clang-offload-bundler")); - - ArgStringList UBArgs; - UBArgs.push_back(C.getArgs().MakeArgString(UnbundleArg)); - UBArgs.push_back(C.getArgs().MakeArgString(TypeArg)); - UBArgs.push_back(C.getArgs().MakeArgString(InputArg)); - UBArgs.push_back(C.getArgs().MakeArgString(OffloadArg)); - UBArgs.push_back(C.getArgs().MakeArgString(OutputArg)); - - // Add this flag to not exit from clang-offload-bundler if no compatible - // code object is found in heterogenous archive library. - std::string AdditionalArgs("-allow-missing-bundles"); - UBArgs.push_back(C.getArgs().MakeArgString(AdditionalArgs)); - - // Add this flag to treat hip and hipv4 offload kinds as compatible with - // openmp offload kind while extracting code objects from a heterogenous - // archive library. Vice versa is also considered compatible. - std::string HipCompatibleArgs("-hip-openmp-compatible"); - UBArgs.push_back(C.getArgs().MakeArgString(HipCompatibleArgs)); - - C.addCommand(std::make_unique( - JA, T, ResponseFileSupport::AtFileCurCP(), UBProgram, UBArgs, Inputs, - InputInfo(&JA, C.getArgs().MakeArgString(OutputLib)))); - if (postClangLink) - CC1Args.push_back("-mlink-builtin-bitcode"); + std::string UnbundleArg("-unbundle"); + std::string TypeArg("-type=a"); + std::string InputArg("-input=" + ArchiveOfBundles); + std::string OffloadArg("-targets=" + std::string(DeviceTriple)); + std::string OutputArg("-output=" + OutputLib); - CC1Args.push_back(DriverArgs.MakeArgString(OutputLib)); - break; - } + const char *UBProgram = DriverArgs.MakeArgString( + T.getToolChain().GetProgramPath("clang-offload-bundler")); + + ArgStringList UBArgs; + UBArgs.push_back(C.getArgs().MakeArgString(UnbundleArg)); + UBArgs.push_back(C.getArgs().MakeArgString(TypeArg)); + UBArgs.push_back(C.getArgs().MakeArgString(InputArg)); + UBArgs.push_back(C.getArgs().MakeArgString(OffloadArg)); + UBArgs.push_back(C.getArgs().MakeArgString(OutputArg)); + + // Add this flag to not exit from clang-offload-bundler if no compatible + // code object is found in heterogenous archive library. + std::string AdditionalArgs("-allow-missing-bundles"); + UBArgs.push_back(C.getArgs().MakeArgString(AdditionalArgs)); + + // Add this flag to treat hip and hipv4 offload kinds as compatible with + // openmp offload kind while extracting code objects from a heterogenous + // archive library. Vice versa is also considered compatible. + std::string HipCompatibleArgs("-hip-openmp-compatible"); + UBArgs.push_back(C.getArgs().MakeArgString(HipCompatibleArgs)); - return FoundAOB; + C.addCommand(std::make_unique( + JA, T, ResponseFileSupport::AtFileCurCP(), UBProgram, UBArgs, Inputs, + InputInfo(&JA, C.getArgs().MakeArgString(OutputLib)))); + if (postClangLink) + CC1Args.push_back("-mlink-builtin-bitcode"); + + CC1Args.push_back(DriverArgs.MakeArgString(OutputLib)); + + return true; } // Wrapper function used by driver for adding SDLs during link phase. @@ -1980,6 +1989,22 @@ } } + for (auto Input : DriverArgs.getAllArgValues(options::OPT_INPUT)) { + auto FileName = StringRef(Input); + // Clang treats any unknown file types as archives and passes them to the + // linker. Files with extension 'lib' are classified as TY_Object by clang + // but they are usually archives. It is OK if the file is not really an + // archive since in that case the unbundler just extracts an empty bitcode + // to link with. + const StringRef LibFileExt = ".lib"; + if (!llvm::sys::path::has_extension(FileName) || + types::lookupTypeForExtension( + llvm::sys::path::extension(FileName).drop_front()) == + types::TY_INVALID || + llvm::sys::path::extension(FileName) == LibFileExt) + SDLNames.insert(Input); + } + // The search stops as soon as an SDL file is found. The driver then provides // the full filename of the SDL to the llvm-link or clang-nvlink-wrapper // command. If no SDL is found after searching each LINKPATH with Index: clang/test/Driver/clang-offload-bundler.c =================================================================== --- clang/test/Driver/clang-offload-bundler.c +++ clang/test/Driver/clang-offload-bundler.c @@ -407,6 +407,19 @@ // HIP-AR-906-DAG: hip_bundle1-hip-amdgcn-amd-amdhsa--gfx906 // HIP-AR-906-DAG: hip_bundle2-hip-amdgcn-amd-amdhsa--gfx906 +// Check clang-offload-bundler extracts empty archives if the input file +// is not an archive when --allow-missing-bundles is specified, otherwise +// report an error. +// +// RUN: echo 'This is not an archive file.' > %t.non-archive +// RUN: clang-offload-bundler -unbundle -type=a -targets=hip-amdgcn-amd-amdhsa--gfx900 \ +// RUN: -output=%t.res.a -input=%t.non-archive --allow-missing-bundles +// RUN: llvm-ar t %t.res.a >%t.res.list 2>&1 +// RUN: diff %t.res.list %t.empty +// RUN: not clang-offload-bundler -unbundle -type=a -targets=hip-amdgcn-amd-amdhsa--gfx900 \ +// RUN: -output=%t.res.a -input=%t.non-archive 2>&1 | FileCheck %s -check-prefix=INVARCHIVE +// INVARCHIVE: error: file too small to be an archive + // // Check bundling without host target is allowed for HIP. // Index: clang/test/Driver/hip-link-bundle-archive.hip =================================================================== --- clang/test/Driver/hip-link-bundle-archive.hip +++ clang/test/Driver/hip-link-bundle-archive.hip @@ -8,7 +8,23 @@ // RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ // RUN: --target=x86_64-unknown-linux-gnu \ // RUN: -nogpulib %s -fgpu-rdc -L%t -lhipBundled \ -// RUN: 2>&1 | FileCheck -check-prefix=GNU %s +// RUN: 2>&1 | FileCheck -check-prefixes=GNU,GNU1,GNU-L %s + +// RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ +// RUN: --target=x86_64-unknown-linux-gnu \ +// RUN: -nogpulib %s -fgpu-rdc -L%t -l:libhipBundled.a \ +// RUN: 2>&1 | FileCheck -check-prefixes=GNU,GNU1,GNU-LA %s + +// RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ +// RUN: --target=x86_64-unknown-linux-gnu \ +// RUN: -nogpulib %s -fgpu-rdc %t/libhipBundled.a \ +// RUN: 2>&1 | FileCheck -check-prefixes=GNU,GNU1,GNU-A %s + +// RUN: touch %t/libhipBundled.a.5.2 +// RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ +// RUN: --target=x86_64-unknown-linux-gnu \ +// RUN: -nogpulib %s -fgpu-rdc %t/libhipBundled.a.5.2 \ +// RUN: 2>&1 | FileCheck -check-prefixes=GNU,GNU2,GNU-A %s // RUN: touch %t/hipBundled2.lib // RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ @@ -16,14 +32,27 @@ // RUN: -nogpulib %s -fgpu-rdc -L%t -lhipBundled2 \ // RUN: 2>&1 | FileCheck -check-prefix=MSVC %s -// GNU: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}libhipBundled.a" "-targets=hip-amdgcn-amd-amdhsa-gfx1030" "-output=[[A1030:.*\.a]]" "-allow-missing-bundles" +// RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ +// RUN: --target=x86_64-pc-windows-msvc \ +// RUN: -nogpulib %s -fgpu-rdc -L%t -l:hipBundled2.lib \ +// RUN: 2>&1 | FileCheck -check-prefix=MSVC %s + +// RUN: %clang -### --offload-arch=gfx906 --offload-arch=gfx1030 \ +// RUN: --target=x86_64-pc-windows-msvc \ +// RUN: -nogpulib %s -fgpu-rdc %t/hipBundled2.lib \ +// RUN: 2>&1 | FileCheck -check-prefix=MSVC %s + +// GNU1: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}[[LIB:libhipBundled\.a]]" "-targets=hip-amdgcn-amd-amdhsa-gfx1030" "-output=[[A1030:.*\.a]]" "-allow-missing-bundles" +// GNU2: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}[[LIB:libhipBundled\.a\.5\.2]]" "-targets=hip-amdgcn-amd-amdhsa-gfx1030" "-output=[[A1030:.*\.a]]" "-allow-missing-bundles" // GNU: "{{.*}}lld{{.*}}" {{.*}}"-plugin-opt=mcpu=gfx1030" {{.*}} "[[A1030]]" -// GNU: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}libhipBundled.a" "-targets=hip-amdgcn-amd-amdhsa-gfx906" "-output=[[A906:.*\.a]]" "-allow-missing-bundles" +// GNU: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}[[LIB]]" "-targets=hip-amdgcn-amd-amdhsa-gfx906" "-output=[[A906:.*\.a]]" "-allow-missing-bundles" // GNU: "{{.*}}lld{{.*}}" {{.*}}"-plugin-opt=mcpu=gfx906" {{.*}} "[[A906]]" -// GNU: "{{.*}}ld{{.*}}" {{.*}}"-o" "a.out" {{.*}}"-lhipBundled" +// GNU-L: "{{.*}}ld{{.*}}" {{.*}}"-o" "a.out" {{.*}}"-lhipBundled" +// GNU-LA: "{{.*}}ld{{.*}}" {{.*}}"-o" "a.out" {{.*}}"-l:libhipBundled.a" +// GNU-A: "{{.*}}ld{{.*}}" {{.*}}"-o" "a.out" "{{.*}}[[LIB]]" // MSVC: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}hipBundled2.lib" "-targets=hip-amdgcn-amd-amdhsa-gfx1030" "-output=[[A1030:.*\.a]]" "-allow-missing-bundles" // MSVC: "{{.*}}lld{{.*}}" {{.*}}"-plugin-opt=mcpu=gfx1030" {{.*}} "[[A1030]]" // MSVC: "{{.*}}clang-offload-bundler" "-unbundle" "-type=a" "-input={{.*}}hipBundled2.lib" "-targets=hip-amdgcn-amd-amdhsa-gfx906" "-output=[[A906:.*\.a]]" "-allow-missing-bundles" // MSVC: "{{.*}}lld{{.*}}" {{.*}}"-plugin-opt=mcpu=gfx906" {{.*}} "[[A906]]" -// MSVC: "{{.*}}link{{.*}}" {{.*}}"-out:a.exe" {{.*}}"hipBundled2.lib" +// MSVC: "{{.*}}link{{.*}}" {{.*}}"-out:a.exe" {{.*}}hipBundled2.lib"