diff --git a/clang/test/Driver/Inputs/dummy-bc.bc b/clang/test/Driver/Inputs/dummy-bc.bc new file mode 100644 index 00000000000000..11129ed391af3c Binary files /dev/null and b/clang/test/Driver/Inputs/dummy-bc.bc differ diff --git a/clang/test/Driver/Inputs/dummy-elf.o b/clang/test/Driver/Inputs/dummy-elf.o new file mode 100644 index 00000000000000..48161b2aad0734 Binary files /dev/null and b/clang/test/Driver/Inputs/dummy-elf.o differ diff --git a/clang/test/Driver/linker-wrapper-image.c b/clang/test/Driver/linker-wrapper-image.c new file mode 100644 index 00000000000000..dbe32f3070318c --- /dev/null +++ b/clang/test/Driver/linker-wrapper-image.c @@ -0,0 +1,27 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang -cc1 %s -emit-obj -o %t.o \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,nvptx64-nvida-cuda,sm_70 +// RUN: clang-linker-wrapper --print-wrapped-module --dry-run -linker-path /usr/bin/ld \ +// RUN: -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=OPENMP + +// OPENMP: @__start_omp_offloading_entries = external hidden constant %__tgt_offload_entry +// OPENMP-NEXT: @__stop_omp_offloading_entries = external hidden constant %__tgt_offload_entry +// OPENMP-NEXT: @__dummy.omp_offloading.entry = hidden constant [0 x %__tgt_offload_entry] zeroinitializer, section "omp_offloading_entries" +// OPENMP-NEXT: @.omp_offloading.device_image = internal unnamed_addr constant [0 x i8] zeroinitializer +// OPENMP-NEXT: @.omp_offloading.device_images = internal unnamed_addr constant [1 x %__tgt_device_image] [%__tgt_device_image { i8* getelementptr inbounds ([0 x i8], [0 x i8]* @.omp_offloading.device_image, i64 0, i64 0), i8* getelementptr inbounds ([0 x i8], [0 x i8]* @.omp_offloading.device_image, i64 0, i64 0), %__tgt_offload_entry* @__start_omp_offloading_entries, %__tgt_offload_entry* @__stop_omp_offloading_entries }] +// OPENMP-NEXT: @.omp_offloading.descriptor = internal constant %__tgt_bin_desc { i32 1, %__tgt_device_image* getelementptr inbounds ([1 x %__tgt_device_image], [1 x %__tgt_device_image]* @.omp_offloading.device_images, i64 0, i64 0), %__tgt_offload_entry* @__start_omp_offloading_entries, %__tgt_offload_entry* @__stop_omp_offloading_entries } +// OPENMP-NEXT: @llvm.global_ctors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 1, void ()* @.omp_offloading.descriptor_reg, i8* null }] +// OPENMP-NEXT: @llvm.global_dtors = appending global [1 x { i32, void ()*, i8* }] [{ i32, void ()*, i8* } { i32 1, void ()* @.omp_offloading.descriptor_unreg, i8* null }] + +// OPENMP: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" { +// OPENMP-NEXT: entry: +// OPENMP-NEXT: call void @__tgt_register_lib(%__tgt_bin_desc* @.omp_offloading.descriptor) +// OPENMP-NEXT: ret void +// OPENMP-NEXT: } + +// OPENMP: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" { +// OPENMP-NEXT: entry: +// OPENMP-NEXT: call void @__tgt_unregister_lib(%__tgt_bin_desc* @.omp_offloading.descriptor) +// OPENMP-NEXT: ret void +// OPENMP-NEXT: } diff --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c new file mode 100644 index 00000000000000..0f9220bd2a951e --- /dev/null +++ b/clang/test/Driver/linker-wrapper.c @@ -0,0 +1,40 @@ +// REQUIRES: x86-registered-target + +// RUN: %clang -cc1 %s -emit-obj -o %t.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_70 +// RUN: clang-linker-wrapper --dry-run -linker-path /usr/bin/ld -- %t.o -o a.out \ +// RUN: 2>&1 | FileCheck %s --check-prefix=NVPTX_LINK + +// NVPTX_LINK: nvlink{{.*}}-m64 -o {{.*}}.out -arch sm_70 {{.*}}.o {{.*}}.o + +// RUN: %clang -cc1 %s -emit-obj -o %t.o \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,amdgcn-amd-amdhsam,gfx908 \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,amdgcn-amd-amdhsam,gfx908 +// RUN: clang-linker-wrapper --dry-run -linker-path /usr/bin/ld -- %t.o -o a.out \ +// RUN: 2>&1 | FileCheck %s --check-prefix=AMDGPU_LINK + +// AMDGPU_LINK: lld{{.*}}-flavor gnu --no-undefined -shared -o {{.*}}.out {{.*}}.o {{.*}}.o + +// RUN: %clang -cc1 %s -emit-obj -o %t.o \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,x86_64-unknown-linux-gnu, \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-elf.o,openmp,x86_64-unknown-linux-gnu, +// RUN: clang-linker-wrapper --dry-run -linker-path /usr/bin/ld.lld -- %t.o -o a.out \ +// RUN: 2>&1 | FileCheck %s --check-prefix=CPU_LINK + +// CPU_LINK: ld.lld{{.*}}-m elf_x86_64 -shared -Bsymbolic -o {{.*}}.out {{.*}}.o {{.*}}.o + +// RUN: %clang -cc1 %s -emit-obj -o %t.o +// RUN: clang-linker-wrapper --dry-run -linker-path /usr/bin/ld.lld -- -a -b -c %t.o -o a.out \ +// RUN: 2>&1 | FileCheck %s --check-prefix=HOST_LINK + +// HOST_LINK: ld.lld{{.*}}-a -b -c {{.*}}.o -o a.out + +// RUN: %clang -cc1 %s -emit-obj -o %t.o \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-bc.bc,openmp,nvptx64-nvida-cuda,sm_70 \ +// RUN: -fembed-offload-object=%S/Inputs/dummy-bc.bc,openmp,nvptx64-nvida-cuda,sm_70 +// RUN: clang-linker-wrapper --dry-run -linker-path /usr/bin/ld -- %t.o -o a.out \ +// RUN: 2>&1 | FileCheck %s --check-prefix=LTO + +// LTO: ptxas{{.*}}-m64 -o {{.*}}.cubin -O2 --gpu-name sm_70 -c {{.*}}.s +// LTO: nvlink{{.*}}-m64 -o {{.*}}.out -arch sm_70 {{.*}}.cubin diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py index afc119cd4166d2..b7557952b935cb 100644 --- a/clang/test/lit.cfg.py +++ b/clang/test/lit.cfg.py @@ -64,7 +64,7 @@ tools = [ 'apinotes-test', 'c-index-test', 'clang-diff', 'clang-format', 'clang-repl', - 'clang-tblgen', 'clang-scan-deps', 'opt', 'llvm-ifs', 'yaml2obj', + 'clang-tblgen', 'clang-scan-deps', 'opt', 'llvm-ifs', 'yaml2obj', 'clang-linker-wrapper', ToolSubst('%clang_extdef_map', command=FindTool( 'clang-extdef-mapping'), unresolved='ignore'), ToolSubst('%clang_dxc', command=config.clang, diff --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp index e187e57cd22c85..cc5582d74a6ff1 100644 --- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp +++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp @@ -92,6 +92,16 @@ static cl::opt EmbedBitcode( cl::desc("Embed linked bitcode instead of an executable device image"), cl::init(false), cl::cat(ClangLinkerWrapperCategory)); +static cl::opt DryRun( + "dry-run", cl::ZeroOrMore, + cl::desc("List the linker commands to be run without executing them"), + cl::init(false), cl::cat(ClangLinkerWrapperCategory)); + +static cl::opt + PrintWrappedModule("print-wrapped-module", cl::ZeroOrMore, + cl::desc("Print the wrapped module's IR for testing"), + cl::init(false), cl::cat(ClangLinkerWrapperCategory)); + static cl::opt HostTriple("host-triple", cl::ZeroOrMore, cl::desc("Triple to use for the host compilation"), @@ -233,17 +243,40 @@ Error createOutputFile(const Twine &Prefix, StringRef Extension, return Error::success(); } +/// Execute the command \p ExecutablePath with the arguments \p Args. +Error executeCommands(StringRef ExecutablePath, ArrayRef Args) { + if (Verbose || DryRun) + printCommands(Args); + + if (!DryRun) + if (sys::ExecuteAndWait(ExecutablePath, Args)) + return createStringError(inconvertibleErrorCode(), + "'" + sys::path::filename(ExecutablePath) + "'" + + " failed"); + return Error::success(); +} + +Expected findProgram(StringRef Name, ArrayRef Paths) { + + ErrorOr Path = sys::findProgramByName(Name, Paths); + if (!Path) + Path = sys::findProgramByName(Name); + if (!Path && DryRun) + return Name.str(); + if (!Path) + return createStringError(Path.getError(), + "Unable to find '" + Name + "' in path"); + return *Path; +} + Error runLinker(std::string &LinkerPath, SmallVectorImpl &Args) { std::vector LinkerArgs; LinkerArgs.push_back(LinkerPath); for (auto &Arg : Args) LinkerArgs.push_back(Arg); - if (Verbose) - printCommands(LinkerArgs); - - if (sys::ExecuteAndWait(LinkerPath, LinkerArgs)) - return createStringError(inconvertibleErrorCode(), "'linker' failed"); + if (Error Err = executeCommands(LinkerPath, LinkerArgs)) + return Err; return Error::success(); } @@ -379,12 +412,10 @@ extractFromBinary(const ObjectFile &Obj, // We will use llvm-strip to remove the now unneeded section containing the // offloading code. - ErrorOr StripPath = - sys::findProgramByName("llvm-strip", {getMainExecutable("llvm-strip")}); + Expected StripPath = + findProgram("llvm-strip", {getMainExecutable("llvm-strip")}); if (!StripPath) - StripPath = sys::findProgramByName("llvm-strip"); - if (!StripPath) - return None; + return StripPath.takeError(); SmallString<128> TempFile; if (Error Err = createOutputFile(Prefix + "-host", Extension, TempFile)) @@ -401,11 +432,8 @@ extractFromBinary(const ObjectFile &Obj, StripArgs.push_back("-o"); StripArgs.push_back(TempFile); - if (Verbose) - printCommands(StripArgs); - - if (sys::ExecuteAndWait(*StripPath, StripArgs)) - return createStringError(inconvertibleErrorCode(), "'llvm-strip' failed"); + if (Error Err = executeCommands(*StripPath, StripArgs)) + return Err; return static_cast(TempFile); } @@ -569,13 +597,9 @@ namespace nvptx { Expected assemble(StringRef InputFile, Triple TheTriple, StringRef Arch) { // NVPTX uses the ptxas binary to create device object files. - ErrorOr PtxasPath = - sys::findProgramByName("ptxas", {CudaBinaryPath}); - if (!PtxasPath) - PtxasPath = sys::findProgramByName("ptxas"); + Expected PtxasPath = findProgram("ptxas", {CudaBinaryPath}); if (!PtxasPath) - return createStringError(PtxasPath.getError(), - "Unable to find 'ptxas' in path"); + return PtxasPath.takeError(); // Create a new file to write the linked device image to. SmallString<128> TempFile; @@ -609,8 +633,8 @@ Expected assemble(StringRef InputFile, Triple TheTriple, if (Verbose) printCommands(CmdArgs); - if (sys::ExecuteAndWait(*PtxasPath, CmdArgs)) - return createStringError(inconvertibleErrorCode(), "'ptxas' failed"); + if (Error Err = executeCommands(*PtxasPath, CmdArgs)) + return Err; return static_cast(TempFile); } @@ -618,13 +642,9 @@ Expected assemble(StringRef InputFile, Triple TheTriple, Expected link(ArrayRef InputFiles, Triple TheTriple, StringRef Arch) { // NVPTX uses the nvlink binary to link device object files. - ErrorOr NvlinkPath = - sys::findProgramByName("nvlink", {CudaBinaryPath}); + Expected NvlinkPath = findProgram("nvlink", {CudaBinaryPath}); if (!NvlinkPath) - NvlinkPath = sys::findProgramByName("nvlink"); - if (!NvlinkPath) - return createStringError(NvlinkPath.getError(), - "Unable to find 'nvlink' in path"); + return NvlinkPath.takeError(); // Create a new file to write the linked device image to. SmallString<128> TempFile; @@ -653,8 +673,8 @@ Expected link(ArrayRef InputFiles, Triple TheTriple, if (Verbose) printCommands(CmdArgs); - if (sys::ExecuteAndWait(*NvlinkPath, CmdArgs)) - return createStringError(inconvertibleErrorCode(), "'nvlink' failed"); + if (Error Err = executeCommands(*NvlinkPath, CmdArgs)) + return Err; return static_cast(TempFile); } @@ -663,13 +683,9 @@ namespace amdgcn { Expected link(ArrayRef InputFiles, Triple TheTriple, StringRef Arch) { // AMDGPU uses lld to link device object files. - ErrorOr LLDPath = - sys::findProgramByName("lld", {getMainExecutable("lld")}); - if (!LLDPath) - LLDPath = sys::findProgramByName("lld"); + Expected LLDPath = findProgram("lld", {CudaBinaryPath}); if (!LLDPath) - return createStringError(LLDPath.getError(), - "Unable to find 'lld' in path"); + return LLDPath.takeError(); // Create a new file to write the linked device image to. SmallString<128> TempFile; @@ -694,8 +710,8 @@ Expected link(ArrayRef InputFiles, Triple TheTriple, if (Verbose) printCommands(CmdArgs); - if (sys::ExecuteAndWait(*LLDPath, CmdArgs)) - return createStringError(inconvertibleErrorCode(), "'lld' failed"); + if (Error Err = executeCommands(*LLDPath, CmdArgs)) + return Err; return static_cast(TempFile); } @@ -774,8 +790,8 @@ Expected link(ArrayRef InputFiles, Triple TheTriple, if (Verbose) printCommands(CmdArgs); - if (sys::ExecuteAndWait(LinkerUserPath, CmdArgs)) - return createStringError(inconvertibleErrorCode(), "'linker' failed"); + if (Error Err = executeCommands(LinkerUserPath, CmdArgs)) + return Err; return static_cast(TempFile); } @@ -1185,6 +1201,9 @@ Expected wrapDeviceImages(ArrayRef Images) { if (Error Err = wrapBinaries(M, ImagesToWrap)) return std::move(Err); + if (PrintWrappedModule) + llvm::errs() << M; + return compileModule(M); }