[clang] d2ead9e - [LinkerWrapper][NFC] Rework command line argument handling in the linker wrapper

Joseph Huber via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 8 08:18:51 PDT 2022


Author: Joseph Huber
Date: 2022-07-08T11:18:38-04:00
New Revision: d2ead9e324d4d268e8c0634849d6081e177c9dd7

URL: https://github.com/llvm/llvm-project/commit/d2ead9e324d4d268e8c0634849d6081e177c9dd7
DIFF: https://github.com/llvm/llvm-project/commit/d2ead9e324d4d268e8c0634849d6081e177c9dd7.diff

LOG: [LinkerWrapper][NFC] Rework command line argument handling in the linker wrapper

Summary:
This patch reworks the command line argument handling in the linker
wrapper from using the LLVM `cl` interface to using the `Option`
interface with TableGen. This has several benefits compared to the old
method.

We use arguments from the linker arguments in the linker
wrapper, such as the libraries and input files, this allows us to
properly parse these. Additionally we can now easily set up aliases to
the linker wrapper arguments and pass them in the linker input directly.
That is, pass an option like `cuda-path=` as `--offload-arg=cuda-path=`
in the linker's inputs. This will allow us to handle offloading
compilation in the linker itself some day. Finally, this is also a much
cleaner interface for passing arguments to the individual device linking
jobs.

Added: 
    clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td

Modified: 
    clang/lib/Driver/ToolChains/Clang.cpp
    clang/test/Driver/linker-wrapper-image.c
    clang/test/Driver/linker-wrapper.c
    clang/test/Driver/openmp-offload.c
    clang/tools/clang-linker-wrapper/CMakeLists.txt
    clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp
index bc437d66a03ce..c11806ff692a5 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -8393,7 +8393,7 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
 
     for (StringRef LibName : BCLibs)
       CmdArgs.push_back(Args.MakeArgString(
-          "-target-library=" + Action::GetOffloadKindName(Action::OFK_OpenMP) +
+          "--target-library=" + Action::GetOffloadKindName(Action::OFK_OpenMP) +
           "-" + TC->getTripleString() + "-" + Arch + "=" + LibName));
   }
 
@@ -8413,41 +8413,35 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
       } else if (A->getOption().matches(options::OPT_O0))
         OOpt = "0";
       if (!OOpt.empty())
-        CmdArgs.push_back(Args.MakeArgString(Twine("-opt-level=O") + OOpt));
+        CmdArgs.push_back(Args.MakeArgString(Twine("--opt-level=O") + OOpt));
     }
   }
 
-  CmdArgs.push_back("-host-triple");
-  CmdArgs.push_back(Args.MakeArgString(TheTriple.getTriple()));
+  CmdArgs.push_back(
+      Args.MakeArgString("--host-triple=" + TheTriple.getTriple()));
   if (Args.hasArg(options::OPT_v))
-    CmdArgs.push_back("-v");
+    CmdArgs.push_back("--verbose");
 
-  // Add debug information if present.
   if (const Arg *A = Args.getLastArg(options::OPT_g_Group)) {
-    const Option &Opt = A->getOption();
-    if (Opt.matches(options::OPT_gN_Group)) {
-      if (Opt.matches(options::OPT_gline_directives_only) ||
-          Opt.matches(options::OPT_gline_tables_only))
-        CmdArgs.push_back("-gline-directives-only");
-    } else
-      CmdArgs.push_back("-g");
+    if (!A->getOption().matches(options::OPT_g0))
+      CmdArgs.push_back("--device-debug");
   }
 
   for (const auto &A : Args.getAllArgValues(options::OPT_Xcuda_ptxas))
-    CmdArgs.push_back(Args.MakeArgString("-ptxas-args=" + A));
+    CmdArgs.push_back(Args.MakeArgString("--ptxas-args=" + A));
 
   // Forward remarks passes to the LLVM backend in the wrapper.
   if (const Arg *A = Args.getLastArg(options::OPT_Rpass_EQ))
     CmdArgs.push_back(
-        Args.MakeArgString(Twine("-pass-remarks=") + A->getValue()));
+        Args.MakeArgString(Twine("--pass-remarks=") + A->getValue()));
   if (const Arg *A = Args.getLastArg(options::OPT_Rpass_missed_EQ))
     CmdArgs.push_back(
-        Args.MakeArgString(Twine("-pass-remarks-missed=") + A->getValue()));
+        Args.MakeArgString(Twine("--pass-remarks-missed=") + A->getValue()));
   if (const Arg *A = Args.getLastArg(options::OPT_Rpass_analysis_EQ))
     CmdArgs.push_back(
-        Args.MakeArgString(Twine("-pass-remarks-analysis=") + A->getValue()));
+        Args.MakeArgString(Twine("--pass-remarks-analysis=") + A->getValue()));
   if (Args.getLastArg(options::OPT_save_temps_EQ))
-    CmdArgs.push_back("-save-temps");
+    CmdArgs.push_back("--save-temps");
 
   // Construct the link job so we can wrap around it.
   Linker->ConstructJob(C, JA, Output, Inputs, Args, LinkingOutput);
@@ -8458,18 +8452,18 @@ void LinkerWrapper::ConstructJob(Compilation &C, const JobAction &JA,
     StringRef Val = Arg->getValue(0);
     if (Val.empty())
       CmdArgs.push_back(
-          Args.MakeArgString(Twine("-device-linker=") + Arg->getValue(1)));
+          Args.MakeArgString(Twine("--device-linker=") + Arg->getValue(1)));
     else
       CmdArgs.push_back(Args.MakeArgString(
-          "-device-linker=" +
+          "--device-linker=" +
           ToolChain::getOpenMPTriple(Val.drop_front()).getTriple() + "=" +
           Arg->getValue(1)));
   }
   Args.ClaimAllArgs(options::OPT_Xoffload_linker);
 
   // Add the linker arguments to be forwarded by the wrapper.
-  CmdArgs.push_back("-linker-path");
-  CmdArgs.push_back(LinkCommand->getExecutable());
+  CmdArgs.push_back(Args.MakeArgString(Twine("--linker-path=") +
+                                       LinkCommand->getExecutable()));
   CmdArgs.push_back("--");
   for (const char *LinkArg : LinkCommand->getArguments())
     CmdArgs.push_back(LinkArg);

diff  --git a/clang/test/Driver/linker-wrapper-image.c b/clang/test/Driver/linker-wrapper-image.c
index fd4a9cc41bb95..51904e1839cdd 100644
--- a/clang/test/Driver/linker-wrapper-image.c
+++ b/clang/test/Driver/linker-wrapper-image.c
@@ -5,10 +5,10 @@
 // RUN: clang-offload-packager -o %t.out --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
 // RUN:   -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple x86_64-unknown-linux-gnu \
-// RUN:   -linker-path /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=OPENMP
+// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
+// RUN:   --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=OPENMP
 
-// OPENMP: @__start_omp_offloading_entries = external hidden constant %__tgt_offload_entry
+//      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
@@ -17,13 +17,13 @@
 // OPENMP-NEXT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.omp_offloading.descriptor_reg, ptr null }]
 // OPENMP-NEXT: @llvm.global_dtors = appending global [1 x { i32, ptr, ptr }] [{ i32, ptr, ptr } { i32 1, ptr @.omp_offloading.descriptor_unreg, ptr null }]
 
-// OPENMP: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" {
+//      OPENMP: define internal void @.omp_offloading.descriptor_reg() section ".text.startup" {
 // OPENMP-NEXT: entry:
 // OPENMP-NEXT:   call void @__tgt_register_lib(ptr @.omp_offloading.descriptor)
 // OPENMP-NEXT:   ret void
 // OPENMP-NEXT: }
 
-// OPENMP: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" {
+//      OPENMP: define internal void @.omp_offloading.descriptor_unreg() section ".text.startup" {
 // OPENMP-NEXT: entry:
 // OPENMP-NEXT:   call void @__tgt_unregister_lib(ptr @.omp_offloading.descriptor)
 // OPENMP-NEXT:   ret void
@@ -32,8 +32,8 @@
 // RUN: clang-offload-packager -o %t.out --image=file=%S/Inputs/dummy-elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
 // RUN:   -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple x86_64-unknown-linux-gnu \
-// RUN:   -linker-path /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA
+// RUN: clang-linker-wrapper --print-wrapped-module --dry-run --host-triple=x86_64-unknown-linux-gnu \
+// RUN:   --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA
 
 //      CUDA: @.fatbin_image = internal constant [0 x i8] zeroinitializer, section ".nv_fatbin"
 // CUDA-NEXT: @.fatbin_wrapper = internal constant %fatbin_wrapper { i32 1180844977, i32 1, ptr @.fatbin_image, ptr null }, section ".nvFatBinSegment", align 8

diff  --git a/clang/test/Driver/linker-wrapper.c b/clang/test/Driver/linker-wrapper.c
index c9855ec38abb9..7adf627cba178 100644
--- a/clang/test/Driver/linker-wrapper.c
+++ b/clang/test/Driver/linker-wrapper.c
@@ -6,17 +6,26 @@
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -linker-path \
-// RUN:   /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=NVPTX_LINK
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN:   --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=NVPTX_LINK
 
 // NVPTX_LINK: nvlink{{.*}}-m64 -o {{.*}}.out -arch sm_70 {{.*}}.o {{.*}}.o
 
+// RUN: clang-offload-packager -o %t.out \
+// RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
+// RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
+// RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run --device-debug \
+// RUN:   --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=NVPTX_LINK_DEBUG
+
+// NVPTX_LINK_DEBUG: nvlink{{.*}}-m64 -g -o {{.*}}.out -arch sm_70 {{.*}}.o {{.*}}.o
+
 // RUN: clang-offload-packager -o %t.out \
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908 \
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=amdgcn-amd-amdhsa,arch=gfx908
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -linker-path \
-// RUN:   /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU_LINK
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN:   --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=AMDGPU_LINK
 
 // AMDGPU_LINK: lld{{.*}}-flavor gnu --no-undefined -shared -plugin-opt=-amdgpu-internalize-symbols -plugin-opt=mcpu=gfx908 -o {{.*}}.out {{.*}}.o {{.*}}.o
 
@@ -24,14 +33,14 @@
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu \
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=x86_64-unknown-linux-gnu
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -linker-path \
-// RUN:   /usr/bin/ld.lld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CPU_LINK
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN:   --linker-path=/usr/bin/ld.lld -- %t.o -o a.out 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 -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o
-// RUN: clang-linker-wrapper --dry-run --host-triple x86_64-unknown-linux-gnu -linker-path \
-// RUN:  /usr/bin/ld.lld -- -a -b -c %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=HOST_LINK
+// RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu \
+// RUN:   --linker-path=/usr/bin/ld.lld -- -a -b -c %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=HOST_LINK
 
 // HOST_LINK: ld.lld{{.*}}-a -b -c {{.*}}.o -o a.out
 
@@ -39,8 +48,8 @@
 // RUN:   --image=file=%S/Inputs/dummy-bc.bc,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
 // RUN:   --image=file=%S/Inputs/dummy-bc.bc,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -linker-path \
-// RUN:   /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=LTO
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN:   --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=LTO
 
 // LTO: ptxas{{.*}}-m64 -o {{.*}}.cubin -O2 --gpu-name sm_70 {{.*}}.s
 // LTO-NOT: nvlink
@@ -49,8 +58,8 @@
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70 \
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -linker-path \
-// RUN:   /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA_OMP_LINK
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN:   --linker-path=/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
 
@@ -62,8 +71,8 @@
 // RUN: clang-offload-packager -o %t.out \
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-obj.o -fembed-offload-object=%t.out
-// 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
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run \
+// RUN:   --linker-path=/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
@@ -74,8 +83,8 @@
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=cuda,triple=nvptx64-nvidia-cuda,arch=sm_52
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
 // RUN:   -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --dry-run --host-triple x86_64-unknown-linux-gnu -linker-path \
-// RUN:   /usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA
+// RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu \
+// RUN: --linker-path=/usr/bin/ld -- %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=CUDA
 
 // CUDA: nvlink{{.*}}-m64 -o {{.*}}.out -arch sm_52 {{.*}}.o
 // CUDA: nvlink{{.*}}-m64 -o {{.*}}.out -arch sm_70 {{.*}}.o {{.*}}.o
@@ -86,8 +95,8 @@
 // RUN:   --image=file=%S/Inputs/dummy-elf.o,kind=openmp,triple=nvptx64-nvidia-cuda,arch=sm_70
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t.o \
 // RUN:   -fembed-offload-object=%t.out
-// RUN: clang-linker-wrapper --dry-run --host-triple x86_64-unknown-linux-gnu -linker-path \
-// RUN:   /usr/bin/ld --device-linker=a --device-linker=nvptx64-nvidia-cuda=b -- \
+// RUN: clang-linker-wrapper --dry-run --host-triple=x86_64-unknown-linux-gnu \
+// RUN:   --linker-path=/usr/bin/ld --device-linker=a --device-linker=nvptx64-nvidia-cuda=b -- \
 // RUN:   %t.o -o a.out 2>&1 | FileCheck %s --check-prefix=LINKER_ARGS
 
 // LINKER_ARGS: lld{{.*}}-flavor gnu --no-undefined -shared -plugin-opt=-amdgpu-internalize-symbols -plugin-opt=mcpu=gfx908 -o {{.*}}.out {{.*}}.o a
@@ -100,6 +109,6 @@
 // RUN: llvm-ar rcs %t.a %t.o
 // RUN: rm -f %t.o
 // RUN: %clang -cc1 %s -triple x86_64-unknown-linux-gnu -emit-obj -o %t-obj.o
-// RUN: clang-linker-wrapper --host-triple x86_64-unknown-linux-gnu --dry-run -save-temps \
-// RUN:   -linker-path /usr/bin/ld -- %t.a %t-obj.o -o a.out
+// RUN: clang-linker-wrapper --host-triple=x86_64-unknown-linux-gnu --dry-run -save-temps \
+// RUN:   --linker-path=/usr/bin/ld -- %t.a %t-obj.o -o a.out
 // RUN: not ls *-device-*

diff  --git a/clang/test/Driver/openmp-offload.c b/clang/test/Driver/openmp-offload.c
index 89247604d07cd..5a5da4c408ecf 100644
--- a/clang/test/Driver/openmp-offload.c
+++ b/clang/test/Driver/openmp-offload.c
@@ -661,4 +661,10 @@
 // RUN:   %clang -### --target=powerpc64le-linux -fopenmp=libomp -fopenmp-targets=powerpc64le-ibm-linux-gnu -fopenmp-new-driver %s 2>&1 \
 // RUN:   | FileCheck -check-prefix=CHK-NEW-DRIVER %s
 
-// CHK-NEW-DRIVER: clang-linker-wrapper{{.*}}"-host-triple" "powerpc64le-unknown-linux"{{.*}}--{{.*}}"-lomp"{{.*}}"-lomptarget"
+// CHK-NEW-DRIVER: clang-linker-wrapper{{.*}}"--host-triple=powerpc64le-unknown-linux"{{.*}}--{{.*}}"-lomp"{{.*}}"-lomptarget"
+
+/// Check arguments to the linker wrapper
+// RUN:   %clang -### --target=powerpc64le-linux -fopenmp=libomp -fopenmp-targets=powerpc64le-ibm-linux-gnu -g -fopenmp-new-driver %s 2>&1 \
+// RUN:   | FileCheck -check-prefix=CHK-NEW-DRIVER-DEBUG %s
+
+// CHK-NEW-DRIVER-DEBUG: clang-linker-wrapper{{.*}}"--device-debug

diff  --git a/clang/tools/clang-linker-wrapper/CMakeLists.txt b/clang/tools/clang-linker-wrapper/CMakeLists.txt
index 1614f40fb60ea..60392c7d901fe 100644
--- a/clang/tools/clang-linker-wrapper/CMakeLists.txt
+++ b/clang/tools/clang-linker-wrapper/CMakeLists.txt
@@ -10,12 +10,17 @@ set(LLVM_LINK_COMPONENTS
   Passes
   IRReader
   Object
+  Option
   Support
   CodeGen
   LTO)
 
+set(LLVM_TARGET_DEFINITIONS LinkerWrapperOpts.td)
+tablegen(LLVM LinkerWrapperOpts.inc -gen-opt-parser-defs)
+add_public_tablegen_target(LinkerWrapperOpts)
+
 if(NOT CLANG_BUILT_STANDALONE)
-  set(tablegen_deps intrinsics_gen)
+  set(tablegen_deps intrinsics_gen LinkerWrapperOpts)
 endif()
 
 add_clang_executable(clang-linker-wrapper

diff  --git a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
index 8f98bb8be08c1..4e4f85cbcd027 100644
--- a/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
+++ b/clang/tools/clang-linker-wrapper/ClangLinkerWrapper.cpp
@@ -32,6 +32,9 @@
 #include "llvm/Object/IRObjectFile.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Object/OffloadBinary.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/OptTable.h"
+#include "llvm/Option/Option.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Errc.h"
 #include "llvm/Support/FileOutputBuffer.h"
@@ -50,100 +53,33 @@
 #include "llvm/Target/TargetMachine.h"
 
 using namespace llvm;
+using namespace llvm::opt;
 using namespace llvm::object;
 
-static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
-
-enum DebugKind {
-  NoDebugInfo,
-  DirectivesOnly,
-  FullDebugInfo,
-};
-
-// Mark all our options with this category, everything else (except for -help)
-// will be hidden.
+/// TODO: We use the command line parser only to forward `-pass-remarks` options
+/// to the LTO backend. This should be replaced when there is a better way.
 static cl::OptionCategory
     ClangLinkerWrapperCategory("clang-linker-wrapper options");
-
-static cl::opt<std::string> LinkerUserPath("linker-path", cl::Required,
-                                           cl::desc("Path of linker binary"),
-                                           cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<std::string> OptLevel("opt-level",
-                                     cl::desc("Optimization level for LTO"),
-                                     cl::init("O2"),
-                                     cl::cat(ClangLinkerWrapperCategory));
-
+static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden,
+                          cl::cat(ClangLinkerWrapperCategory));
 static cl::list<std::string>
-    BitcodeLibraries("target-library",
-                     cl::desc("Path for the target bitcode library"),
-                     cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<bool> EmbedBitcode(
-    "target-embed-bc",
-    cl::desc("Embed linked bitcode instead of an executable device image"),
-    cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<bool> DryRun(
-    "dry-run",
-    cl::desc("List the linker commands to be run without executing them"),
-    cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<bool>
-    PrintWrappedModule("print-wrapped-module",
-                       cl::desc("Print the wrapped module's IR for testing"),
-                       cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<std::string>
-    HostTriple("host-triple",
-               cl::desc("Triple to use for the host compilation"),
-               cl::init(sys::getDefaultTargetTriple()),
-               cl::cat(ClangLinkerWrapperCategory));
-
-static cl::list<std::string>
-    PtxasArgs("ptxas-args",
-              cl::desc("Argument to pass to the ptxas invocation"),
-              cl::cat(ClangLinkerWrapperCategory));
-
-static cl::list<std::string>
-    LinkerArgs("device-linker",
-               cl::desc("Arguments to pass to the device linker invocation"),
-               cl::value_desc("<value> or <triple>=<value>"),
-               cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<bool> Verbose("v", cl::desc("Verbose output from tools"),
+    DummyArguments(cl::Sink, cl::Hidden, cl::cat(ClangLinkerWrapperCategory));
 
-                             cl::cat(ClangLinkerWrapperCategory));
-
-static cl::opt<DebugKind> DebugInfo(
-    cl::desc("Choose debugging level:"), cl::init(NoDebugInfo),
-    cl::values(clEnumValN(NoDebugInfo, "g0", "No debug information"),
-               clEnumValN(DirectivesOnly, "gline-directives-only",
-                          "Direction information"),
-               clEnumValN(FullDebugInfo, "g", "Full debugging support")));
+/// Path of the current binary.
+static const char *LinkerExecutable;
 
-static cl::opt<bool> SaveTemps("save-temps",
-                               cl::desc("Save intermediary results."),
-                               cl::cat(ClangLinkerWrapperCategory));
+/// Ssave intermediary results.
+static bool SaveTemps = false;
 
-static cl::opt<std::string> CudaPath("cuda-path",
-                                     cl::desc("Save intermediary results."),
-                                     cl::cat(ClangLinkerWrapperCategory));
+/// Print arguments without executing.
+static bool DryRun = false;
 
-// Do not parse linker options.
-static cl::list<std::string>
-    HostLinkerArgs(cl::Positional,
-                   cl::desc("<options to be passed to linker>..."));
-
-/// Path of the current binary.
-static const char *LinkerExecutable;
+/// Print verbose output.
+static bool Verbose = false;
 
 /// Filename of the executable being created.
 static StringRef ExecutableName;
 
-/// System root if passed in to the linker via. '--sysroot='.
-static StringRef Sysroot = "";
-
 /// Binary path for the CUDA installation.
 static std::string CudaBinaryPath;
 
@@ -196,6 +132,48 @@ template <> struct DenseMapInfo<OffloadKind> {
 
 namespace {
 
+/// Must not overlap with llvm::opt::DriverFlag.
+enum WrapperFlags {
+  WrapperOnlyOption = (1 << 4), // Options only used by the linker wrapper.
+  DeviceOnlyOption = (1 << 5),  // Options only used for device linking.
+};
+
+enum ID {
+  OPT_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  OPT_##ID,
+#include "LinkerWrapperOpts.inc"
+  LastOption
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
+#include "LinkerWrapperOpts.inc"
+#undef PREFIX
+
+static const OptTable::Info InfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM,  \
+               HELPTEXT, METAVAR, VALUES)                                      \
+  {PREFIX, NAME,  HELPTEXT,    METAVAR,     OPT_##ID,  Option::KIND##Class,    \
+   PARAM,  FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS, VALUES},
+#include "LinkerWrapperOpts.inc"
+#undef OPTION
+};
+
+class WrapperOptTable : public opt::OptTable {
+public:
+  WrapperOptTable() : OptTable(InfoTable) {}
+};
+
+const OptTable &getOptTable() {
+  static const WrapperOptTable *Table = []() {
+    auto Result = std::make_unique<WrapperOptTable>();
+    return Result.release();
+  }();
+  return *Table;
+}
+
 Error extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,
                         SmallVectorImpl<OffloadFile> &DeviceFiles);
 
@@ -208,17 +186,6 @@ void printCommands(ArrayRef<StringRef> CmdArgs) {
     llvm::errs() << *IC << (std::next(IC) != IE ? " " : "\n");
 }
 
-/// Forward user requested arguments to the device linking job.
-void renderXLinkerArgs(SmallVectorImpl<StringRef> &Args, StringRef Triple) {
-  for (StringRef Arg : LinkerArgs) {
-    auto TripleAndValue = Arg.split('=');
-    if (TripleAndValue.second.empty())
-      Args.push_back(TripleAndValue.first);
-    else if (TripleAndValue.first == Triple)
-      Args.push_back(TripleAndValue.second);
-  }
-}
-
 /// Create an extra user-specified \p OffloadFile.
 /// TODO: We should find a way to wrap these as libraries instead.
 Expected<OffloadFile> getInputBitcodeLibrary(StringRef Input) {
@@ -292,8 +259,7 @@ Expected<std::string> findProgram(StringRef Name, ArrayRef<StringRef> Paths) {
   return *Path;
 }
 
-Error runLinker(StringRef LinkerPath, ArrayRef<StringRef> LinkerArgs) {
-
+Error runLinker(StringRef LinkerPath, ArgStringList LinkerArgs) {
   SmallVector<StringRef> Args({LinkerPath});
   for (StringRef Arg : LinkerArgs)
     Args.push_back(Arg);
@@ -302,7 +268,7 @@ Error runLinker(StringRef LinkerPath, ArrayRef<StringRef> LinkerArgs) {
   return Error::success();
 }
 
-void PrintVersion(raw_ostream &OS) {
+void printVersion(raw_ostream &OS) {
   OS << clang::getClangToolFullVersion("clang-linker-wrapper") << '\n';
 }
 
@@ -349,7 +315,6 @@ Error extractFromBinary(const ObjectFile &Obj,
       return Buffer.takeError();
 
     MemoryBufferRef Contents(*Buffer, Obj.getFileName());
-
     if (Error Err = extractOffloadFiles(Contents, DeviceFiles))
       return Err;
   }
@@ -368,7 +333,7 @@ Error extractFromBitcode(std::unique_ptr<MemoryBuffer> Buffer,
 
   // Extract offloading data from globals referenced by the
   // `llvm.embedded.object` metadata with the `.llvm.offloading` section.
-  auto MD = M->getNamedMetadata("llvm.embedded.object");
+  auto *MD = M->getNamedMetadata("llvm.embedded.object");
   if (!MD)
     return Error::success();
 
@@ -390,7 +355,6 @@ Error extractFromBitcode(std::unique_ptr<MemoryBuffer> Buffer,
       continue;
 
     MemoryBufferRef Contents(CDS->getAsString(), M->getName());
-
     if (Error Err = extractOffloadFiles(Contents, DeviceFiles))
       return Err;
   }
@@ -453,39 +417,38 @@ Error extractFromBuffer(std::unique_ptr<MemoryBuffer> Buffer,
 }
 
 namespace nvptx {
-Expected<StringRef> assemble(StringRef InputFile, Triple TheTriple,
-                             StringRef Arch, bool RDC = true) {
+Expected<StringRef> assemble(StringRef InputFile, const ArgList &Args) {
   // NVPTX uses the ptxas binary to create device object files.
   Expected<std::string> PtxasPath = findProgram("ptxas", {CudaBinaryPath});
   if (!PtxasPath)
     return PtxasPath.takeError();
 
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+  StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
   // Create a new file to write the linked device image to.
   auto TempFileOrErr =
       createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
-                           TheTriple.getArchName() + "-" + Arch,
+                           Triple.getArchName() + "-" + Arch,
                        "cubin");
   if (!TempFileOrErr)
     return TempFileOrErr.takeError();
 
   SmallVector<StringRef, 16> CmdArgs;
-  std::string Opt = "-" + OptLevel;
+  StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
   CmdArgs.push_back(*PtxasPath);
-  CmdArgs.push_back(TheTriple.isArch64Bit() ? "-m64" : "-m32");
+  CmdArgs.push_back(Triple.isArch64Bit() ? "-m64" : "-m32");
   if (Verbose)
     CmdArgs.push_back("-v");
-  if (DebugInfo == DirectivesOnly && OptLevel[1] == '0')
-    CmdArgs.push_back("-lineinfo");
-  else if (DebugInfo == FullDebugInfo && OptLevel[1] == '0')
-    CmdArgs.push_back("-g");
-  for (auto &Arg : PtxasArgs)
-    CmdArgs.push_back(Arg);
+  for (StringRef Arg : Args.getAllArgValues(OPT_ptxas_arg))
+    CmdArgs.push_back(Args.MakeArgString(Arg));
   CmdArgs.push_back("-o");
   CmdArgs.push_back(*TempFileOrErr);
-  CmdArgs.push_back(Opt);
+  CmdArgs.push_back(Args.MakeArgString("-" + OptLevel));
   CmdArgs.push_back("--gpu-name");
   CmdArgs.push_back(Arch);
-  if (RDC)
+  if (Args.hasArg(OPT_debug))
+    CmdArgs.push_back("-g");
+  if (!Args.hasArg(OPT_whole_program))
     CmdArgs.push_back("-c");
 
   CmdArgs.push_back(InputFile);
@@ -496,28 +459,30 @@ Expected<StringRef> assemble(StringRef InputFile, Triple TheTriple,
   return *TempFileOrErr;
 }
 
-Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
-                         StringRef Arch) {
+Expected<StringRef> link(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
   // NVPTX uses the nvlink binary to link device object files.
   Expected<std::string> NvlinkPath = findProgram("nvlink", {CudaBinaryPath});
   if (!NvlinkPath)
     return NvlinkPath.takeError();
 
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+  StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
+
   // Create a new file to write the linked device image to.
   auto TempFileOrErr =
       createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
-                           TheTriple.getArchName() + "-" + Arch,
+                           Triple.getArchName() + "-" + Arch,
                        "out");
   if (!TempFileOrErr)
     return TempFileOrErr.takeError();
 
   SmallVector<StringRef, 16> CmdArgs;
   CmdArgs.push_back(*NvlinkPath);
-  CmdArgs.push_back(TheTriple.isArch64Bit() ? "-m64" : "-m32");
+  CmdArgs.push_back(Triple.isArch64Bit() ? "-m64" : "-m32");
+  if (Args.hasArg(OPT_debug))
+    CmdArgs.push_back("-g");
   if (Verbose)
     CmdArgs.push_back("-v");
-  if (DebugInfo != NoDebugInfo)
-    CmdArgs.push_back("-g");
   CmdArgs.push_back("-o");
   CmdArgs.push_back(*TempFileOrErr);
   CmdArgs.push_back("-arch");
@@ -527,7 +492,8 @@ Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
   for (StringRef Input : InputFiles)
     CmdArgs.push_back(Input);
 
-  renderXLinkerArgs(CmdArgs, TheTriple.getTriple());
+  for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
+    CmdArgs.push_back(Args.MakeArgString(Arg));
   if (Error Err = executeCommands(*NvlinkPath, CmdArgs))
     return std::move(Err);
 
@@ -536,18 +502,19 @@ Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
 
 Expected<StringRef>
 fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
-          Triple TheTriple) {
+          const ArgList &Args) {
   // NVPTX uses the fatbinary program to bundle the linked images.
   Expected<std::string> FatBinaryPath =
       findProgram("fatbinary", {CudaBinaryPath});
   if (!FatBinaryPath)
     return FatBinaryPath.takeError();
 
+  llvm::Triple Triple(
+      Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
+
   // Create a new file to write the linked device image to.
-  auto TempFileOrErr =
-      createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
-                           TheTriple.getArchName(),
-                       "fatbin");
+  auto TempFileOrErr = createOutputFile(
+      sys::path::filename(ExecutableName) + "-device", "fatbin");
   if (!TempFileOrErr)
     return TempFileOrErr.takeError();
 
@@ -556,7 +523,7 @@ fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
 
   SmallVector<StringRef, 16> CmdArgs;
   CmdArgs.push_back(*FatBinaryPath);
-  CmdArgs.push_back(TheTriple.isArch64Bit() ? "-64" : "-32");
+  CmdArgs.push_back(Triple.isArch64Bit() ? "-64" : "-32");
   CmdArgs.push_back("--create");
   CmdArgs.push_back(*TempFileOrErr);
   for (const auto &FileAndArch : InputFiles)
@@ -569,19 +536,22 @@ fatbinary(ArrayRef<std::pair<StringRef, StringRef>> InputFiles,
   return *TempFileOrErr;
 }
 } // namespace nvptx
+
 namespace amdgcn {
-Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
-                         StringRef Arch) {
+Expected<StringRef> link(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
   // AMDGPU uses lld to link device object files.
   Expected<std::string> LLDPath =
       findProgram("lld", {getMainExecutable("lld")});
   if (!LLDPath)
     return LLDPath.takeError();
 
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+  StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
+
   // Create a new file to write the linked device image to.
   auto TempFileOrErr =
       createOutputFile(sys::path::filename(ExecutableName) + "-" +
-                           TheTriple.getArchName() + "-" + Arch,
+                           Triple.getArchName() + "-" + Arch,
                        "out");
   if (!TempFileOrErr)
     return TempFileOrErr.takeError();
@@ -602,7 +572,8 @@ Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
   for (StringRef Input : InputFiles)
     CmdArgs.push_back(Input);
 
-  renderXLinkerArgs(CmdArgs, TheTriple.getTriple());
+  for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
+    CmdArgs.push_back(Args.MakeArgString(Arg));
   if (Error Err = executeCommands(*LLDPath, CmdArgs))
     return std::move(Err);
 
@@ -637,12 +608,14 @@ const char *getLDMOption(const llvm::Triple &T) {
   }
 }
 
-Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
-                         StringRef Arch) {
+Expected<StringRef> link(ArrayRef<StringRef> InputFiles, const ArgList &Args) {
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+  StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
+
   // Create a new file to write the linked device image to.
   auto TempFileOrErr =
       createOutputFile(sys::path::filename(ExecutableName) + "-" +
-                           TheTriple.getArchName() + "-" + Arch,
+                           Triple.getArchName() + "-" + Arch,
                        "out");
   if (!TempFileOrErr)
     return TempFileOrErr.takeError();
@@ -650,29 +623,22 @@ Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
   // Use the host linker to perform generic offloading. Use the same libraries
   // and paths as the host application does.
   SmallVector<StringRef, 16> CmdArgs;
-  CmdArgs.push_back(LinkerUserPath);
+  CmdArgs.push_back(Args.getLastArgValue(OPT_linker_path_EQ));
   CmdArgs.push_back("-m");
-  CmdArgs.push_back(getLDMOption(TheTriple));
+  CmdArgs.push_back(getLDMOption(Triple));
   CmdArgs.push_back("-shared");
-  for (auto AI = HostLinkerArgs.begin(), AE = HostLinkerArgs.end(); AI != AE;
-       ++AI) {
-    StringRef Arg = *AI;
-    if (Arg.startswith("-L"))
-      CmdArgs.push_back(Arg);
-    else if (Arg.startswith("-l"))
-      CmdArgs.push_back(Arg);
-    else if (Arg.startswith("--as-needed"))
-      CmdArgs.push_back(Arg);
-    else if (Arg.startswith("--no-as-needed"))
-      CmdArgs.push_back(Arg);
-    else if (Arg.startswith("-rpath")) {
-      CmdArgs.push_back(Arg);
-      CmdArgs.push_back(*std::next(AI));
-    } else if (Arg.startswith("-dynamic-linker")) {
-      CmdArgs.push_back(Arg);
-      CmdArgs.push_back(*std::next(AI));
-    }
+
+  ArgStringList LinkerArgs;
+  for (const opt::Arg *Arg : Args) {
+    auto Op = Arg->getOption();
+    if (Op.matches(OPT_library) || Op.matches(OPT_library_path) ||
+        Op.matches(OPT_as_needed) || Op.matches(OPT_no_as_needed) ||
+        Op.matches(OPT_rpath) || Op.matches(OPT_dynamic_linker))
+      Arg->render(Args, LinkerArgs);
   }
+  for (StringRef Arg : LinkerArgs)
+    CmdArgs.push_back(Arg);
+
   CmdArgs.push_back("-Bsymbolic");
   CmdArgs.push_back("-o");
   CmdArgs.push_back(*TempFileOrErr);
@@ -681,32 +647,35 @@ Expected<StringRef> link(ArrayRef<StringRef> InputFiles, Triple TheTriple,
   for (StringRef Input : InputFiles)
     CmdArgs.push_back(Input);
 
-  renderXLinkerArgs(CmdArgs, TheTriple.getTriple());
-  if (Error Err = executeCommands(LinkerUserPath, CmdArgs))
+  for (StringRef Arg : Args.getAllArgValues(OPT_linker_arg_EQ))
+    CmdArgs.push_back(Args.MakeArgString(Arg));
+  if (Error Err =
+          executeCommands(Args.getLastArgValue(OPT_linker_path_EQ), CmdArgs))
     return std::move(Err);
 
   return *TempFileOrErr;
 }
 } // namespace generic
 
-Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles, Triple TheTriple,
-                               StringRef Arch) {
-  switch (TheTriple.getArch()) {
+Expected<StringRef> linkDevice(ArrayRef<StringRef> InputFiles,
+                               const ArgList &Args) {
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+  switch (Triple.getArch()) {
   case Triple::nvptx:
   case Triple::nvptx64:
-    return nvptx::link(InputFiles, TheTriple, Arch);
+    return nvptx::link(InputFiles, Args);
   case Triple::amdgcn:
-    return amdgcn::link(InputFiles, TheTriple, Arch);
+    return amdgcn::link(InputFiles, Args);
   case Triple::x86:
   case Triple::x86_64:
   case Triple::aarch64:
   case Triple::aarch64_be:
   case Triple::ppc64:
   case Triple::ppc64le:
-    return generic::link(InputFiles, TheTriple, Arch);
+    return generic::link(InputFiles, Args);
   default:
     return createStringError(inconvertibleErrorCode(),
-                             TheTriple.getArchName() +
+                             Triple.getArchName() +
                                  " linking is not supported");
   }
 }
@@ -769,9 +738,10 @@ CodeGenOpt::Level getCGOptLevel(unsigned OptLevel) {
 
 template <typename ModuleHook = function_ref<bool(size_t, const Module &)>>
 std::unique_ptr<lto::LTO> createLTO(
-    const Triple &TheTriple, StringRef Arch, bool WholeProgram,
-    const std::vector<std::string> &Features,
+    const ArgList &Args, const std::vector<std::string> &Features,
     ModuleHook Hook = [](size_t, const Module &) { return true; }) {
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+  StringRef Arch = Args.getLastArgValue(OPT_arch_EQ);
   lto::Config Conf;
   lto::ThinBackend Backend;
   // TODO: Handle index-only thin-LTO
@@ -779,14 +749,15 @@ std::unique_ptr<lto::LTO> createLTO(
       lto::createInProcessThinBackend(llvm::heavyweight_hardware_concurrency());
 
   Conf.CPU = Arch.str();
-  Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(TheTriple);
+  Conf.Options = codegen::InitTargetOptionsFromCodeGenFlags(Triple);
 
+  StringRef OptLevel = Args.getLastArgValue(OPT_opt_level, "O2");
   Conf.MAttrs = Features;
   Conf.CGOptLevel = getCGOptLevel(OptLevel[1] - '0');
   Conf.OptLevel = OptLevel[1] - '0';
   if (Conf.OptLevel > 0)
     Conf.UseDefaultPipeline = true;
-  Conf.DefaultTriple = TheTriple.getTriple();
+  Conf.DefaultTriple = Triple.getTriple();
   Conf.DiagHandler = diagnosticHandler;
 
   Conf.PTO.LoopVectorization = Conf.OptLevel > 1;
@@ -801,7 +772,7 @@ std::unique_ptr<lto::LTO> createLTO(
     Conf.PostInternalizeModuleHook = [&, Arch](size_t, const Module &M) {
       auto TempFileOrErr =
           createOutputFile(sys::path::filename(ExecutableName) + "-" +
-                               TheTriple.getTriple() + "-" + Arch,
+                               Triple.getTriple() + "-" + Arch,
                            "bc");
       if (!TempFileOrErr)
         HandleError(TempFileOrErr.takeError());
@@ -815,13 +786,10 @@ std::unique_ptr<lto::LTO> createLTO(
     };
   }
   Conf.PostOptModuleHook = Hook;
-  if (TheTriple.isNVPTX())
-    Conf.CGFileType = CGFT_AssemblyFile;
-  else
-    Conf.CGFileType = CGFT_ObjectFile;
+  Conf.CGFileType = Triple.isNVPTX() ? CGFT_AssemblyFile : CGFT_ObjectFile;
 
   // TODO: Handle remark files
-  Conf.HasWholeProgramVisibility = WholeProgram;
+  Conf.HasWholeProgramVisibility = Args.hasArg(OPT_whole_program);
 
   return std::make_unique<lto::LTO>(std::move(Conf), Backend);
 }
@@ -836,7 +804,9 @@ bool isValidCIdentifier(StringRef S) {
 
 Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
                        SmallVectorImpl<StringRef> &OutputFiles,
-                       const Triple &TheTriple, StringRef Arch) {
+                       const ArgList &Args) {
+  const llvm::Triple Triple(Args.getLastArgValue(OPT_triple_EQ));
+
   SmallVector<OffloadFile, 4> BitcodeInputFiles;
   DenseSet<StringRef> UsedInRegularObj;
   DenseSet<StringRef> UsedInSharedLib;
@@ -903,7 +873,7 @@ Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
   SmallVector<StringRef, 4> BitcodeOutput;
   auto OutputBitcode = [&](size_t Task, const Module &M) {
     auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
-                                              "-jit-" + TheTriple.getTriple(),
+                                              "-jit-" + Triple.getTriple(),
                                           "bc");
     if (!TempFileOrErr)
       HandleError(TempFileOrErr.takeError());
@@ -920,10 +890,9 @@ Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
   // We assume visibility of the whole program if every input file was bitcode.
   auto Features = getTargetFeatures(BitcodeInputFiles);
   bool WholeProgram = InputFiles.empty();
-  auto LTOBackend =
-      (EmbedBitcode)
-          ? createLTO(TheTriple, Arch, WholeProgram, Features, OutputBitcode)
-          : createLTO(TheTriple, Arch, WholeProgram, Features);
+  auto LTOBackend = Args.hasArg(OPT_embed_bitcode)
+                        ? createLTO(Args, Features, OutputBitcode)
+                        : createLTO(Args, Features);
 
   // We need to resolve the symbols so the LTO backend knows which symbols need
   // to be kept or can be internalized. This is a simplified symbol resolution
@@ -990,11 +959,10 @@ Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
   auto AddStream = [&](size_t Task) -> std::unique_ptr<CachedFileStream> {
     int FD = -1;
     auto &TempFile = Files[Task];
-    StringRef Extension = (TheTriple.isNVPTX()) ? "s" : "o";
-    auto TempFileOrErr =
-        createOutputFile(sys::path::filename(ExecutableName) + "-device-" +
-                             TheTriple.getTriple(),
-                         Extension);
+    StringRef Extension = (Triple.isNVPTX()) ? "s" : "o";
+    auto TempFileOrErr = createOutputFile(sys::path::filename(ExecutableName) +
+                                              "-device-" + Triple.getTriple(),
+                                          Extension);
     if (!TempFileOrErr)
       HandleError(TempFileOrErr.takeError());
     TempFile = *TempFileOrErr;
@@ -1008,7 +976,7 @@ Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
     return Err;
 
   // If we are embedding bitcode we only need the intermediate output.
-  if (EmbedBitcode) {
+  if (Args.hasArg(OPT_embed_bitcode)) {
     if (BitcodeOutput.size() != 1 || !WholeProgram)
       return createStringError(inconvertibleErrorCode(),
                                "Cannot embed bitcode with multiple files.");
@@ -1017,9 +985,9 @@ Error linkBitcodeFiles(SmallVectorImpl<OffloadFile> &InputFiles,
   }
 
   // Is we are compiling for NVPTX we need to run the assembler first.
-  if (TheTriple.isNVPTX()) {
+  if (Triple.isNVPTX()) {
     for (StringRef &File : Files) {
-      auto FileOrErr = nvptx::assemble(File, TheTriple, Arch, !WholeProgram);
+      auto FileOrErr = nvptx::assemble(File, Args);
       if (!FileOrErr)
         return FileOrErr.takeError();
       File = *FileOrErr;
@@ -1070,8 +1038,9 @@ Expected<StringRef> compileModule(Module &M) {
       codegen::InitTargetOptionsFromCodeGenFlags(Triple(M.getTargetTriple()));
   StringRef CPU = "";
   StringRef Features = "";
-  std::unique_ptr<TargetMachine> TM(T->createTargetMachine(
-      HostTriple, CPU, Features, Options, Reloc::PIC_, M.getCodeModel()));
+  std::unique_ptr<TargetMachine> TM(
+      T->createTargetMachine(M.getTargetTriple(), CPU, Features, Options,
+                             Reloc::PIC_, M.getCodeModel()));
 
   if (M.getDataLayout().isDefault())
     M.setDataLayout(TM->createDataLayout());
@@ -1101,7 +1070,7 @@ Expected<StringRef> compileModule(Module &M) {
 /// registration code from the device images stored in \p Images.
 Expected<StringRef>
 wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
-                 OffloadKind Kind) {
+                 const ArgList &Args, OffloadKind Kind) {
   SmallVector<ArrayRef<char>, 4> BuffersToWrap;
   for (const auto &Buffer : Buffers)
     BuffersToWrap.emplace_back(
@@ -1109,7 +1078,8 @@ wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
 
   LLVMContext Context;
   Module M("offload.wrapper.module", Context);
-  M.setTargetTriple(HostTriple);
+  M.setTargetTriple(
+      Args.getLastArgValue(OPT_host_triple_EQ, sys::getDefaultTargetTriple()));
 
   switch (Kind) {
   case OFK_OpenMP:
@@ -1126,8 +1096,8 @@ wrapDeviceImages(ArrayRef<std::unique_ptr<MemoryBuffer>> Buffers,
                                  " wrapping is not supported");
   }
 
-  if (PrintWrappedModule)
-    llvm::errs() << M;
+  if (Args.hasArg(OPT_print_wrapped_module))
+    errs() << M;
 
   auto FileOrErr = compileModule(M);
   if (!FileOrErr)
@@ -1146,7 +1116,7 @@ bundleOpenMP(ArrayRef<OffloadingImage> Images) {
 }
 
 Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
-bundleCuda(ArrayRef<OffloadingImage> Images) {
+bundleCuda(ArrayRef<OffloadingImage> Images, const ArgList &Args) {
   SmallVector<std::unique_ptr<MemoryBuffer>> Buffers;
 
   SmallVector<std::pair<StringRef, StringRef>, 4> InputFiles;
@@ -1155,7 +1125,7 @@ bundleCuda(ArrayRef<OffloadingImage> Images) {
                                            Image.StringData.lookup("arch")));
 
   Triple TheTriple = Triple(Images.front().StringData.lookup("triple"));
-  auto FileOrErr = nvptx::fatbinary(InputFiles, TheTriple);
+  auto FileOrErr = nvptx::fatbinary(InputFiles, Args);
   if (!FileOrErr)
     return FileOrErr.takeError();
 
@@ -1171,12 +1141,13 @@ bundleCuda(ArrayRef<OffloadingImage> Images) {
 /// Transforms the input \p Images into the binary format the runtime expects
 /// for the given \p Kind.
 Expected<SmallVector<std::unique_ptr<MemoryBuffer>>>
-bundleLinkedOutput(ArrayRef<OffloadingImage> Images, OffloadKind Kind) {
+bundleLinkedOutput(ArrayRef<OffloadingImage> Images, const ArgList &Args,
+                   OffloadKind Kind) {
   switch (Kind) {
   case OFK_OpenMP:
     return bundleOpenMP(Images);
   case OFK_Cuda:
-    return bundleCuda(Images);
+    return bundleCuda(Images, Args);
   default:
     return createStringError(inconvertibleErrorCode(),
                              getOffloadKindName(Kind) +
@@ -1184,10 +1155,47 @@ bundleLinkedOutput(ArrayRef<OffloadingImage> Images, OffloadKind Kind) {
   }
 }
 
+/// Returns a new ArgList containg arguments used for the device linking phase.
+DerivedArgList getLinkerArgs(ArrayRef<OffloadFile> Input,
+                             const InputArgList &Args) {
+  DerivedArgList DAL = DerivedArgList(DerivedArgList(Args));
+  for (Arg *A : Args)
+    DAL.append(A);
+
+  // Set the subarchitecture and target triple for this compilation.
+  const OptTable &Tbl = getOptTable();
+  DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_arch_EQ),
+                   Args.MakeArgString(Input.front().getBinary()->getArch()));
+  DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_triple_EQ),
+                   Args.MakeArgString(Input.front().getBinary()->getTriple()));
+
+  // If every input file is bitcode we have whole program visibility as we do
+  // only support static linking with bitcode.
+  auto ContainsBitcode = [](const OffloadFile &F) {
+    return identify_magic(F.getBinary()->getImage()) == file_magic::bitcode;
+  };
+  if (llvm::all_of(Input, ContainsBitcode))
+    DAL.AddFlagArg(nullptr, Tbl.getOption(OPT_whole_program));
+
+  // Forward '-Xoffload-linker' options to the appropriate backend.
+  for (StringRef Arg : Args.getAllArgValues(OPT_device_linker_args_EQ)) {
+    auto TripleAndValue = Arg.split('=');
+    if (TripleAndValue.second.empty())
+      DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
+                       Args.MakeArgString(TripleAndValue.first));
+    else if (TripleAndValue.first == DAL.getLastArgValue(OPT_triple_EQ))
+      DAL.AddJoinedArg(nullptr, Tbl.getOption(OPT_linker_arg_EQ),
+                       Args.MakeArgString(TripleAndValue.second));
+  }
+
+  return DAL;
+}
+
 /// Transforms all the extracted offloading input files into an image that can
 /// be registered by the runtime.
 Expected<SmallVector<StringRef>>
-linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
+linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles,
+                       const InputArgList &Args) {
   DenseMap<OffloadFile::TargetID, SmallVector<OffloadFile, 4>> InputsForTarget;
   for (auto &File : LinkerInputFiles)
     InputsForTarget[File].emplace_back(std::move(File));
@@ -1198,9 +1206,7 @@ linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
   DenseMap<OffloadKind, SmallVector<OffloadingImage, 2>> Images;
   for (auto &InputForTarget : InputsForTarget) {
     SmallVector<OffloadFile, 4> &Input = InputForTarget.getSecond();
-    StringRef TripleStr = Saver.save(InputForTarget.getFirst().first);
-    StringRef Arch = Saver.save(InputForTarget.getFirst().second);
-    llvm::Triple Triple(TripleStr);
+    auto LinkerArgs = getLinkerArgs(Input, Args);
 
     DenseSet<OffloadKind> ActiveOffloadKinds;
     for (const auto &File : Input)
@@ -1208,7 +1214,7 @@ linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
 
     // First link and remove all the input files containing bitcode.
     SmallVector<StringRef> InputFiles;
-    if (Error Err = linkBitcodeFiles(Input, InputFiles, Triple, Arch))
+    if (Error Err = linkBitcodeFiles(Input, InputFiles, LinkerArgs))
       return std::move(Err);
 
     // Write any remaining device inputs to an output file for the linker job.
@@ -1220,9 +1226,10 @@ linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
     }
 
     // Link the remaining device files, if necessary, using the device linker.
-    bool RequiresLinking =
-        !Input.empty() || (!EmbedBitcode && !Triple.isNVPTX());
-    auto OutputOrErr = (RequiresLinking) ? linkDevice(InputFiles, Triple, Arch)
+    llvm::Triple Triple(LinkerArgs.getLastArgValue(OPT_triple_EQ));
+    bool RequiresLinking = !Input.empty() || (!Args.hasArg(OPT_embed_bitcode) &&
+                                              !Triple.isNVPTX());
+    auto OutputOrErr = (RequiresLinking) ? linkDevice(InputFiles, LinkerArgs)
                                          : InputFiles.front();
     if (!OutputOrErr)
       return OutputOrErr.takeError();
@@ -1237,7 +1244,9 @@ linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
       OffloadingImage TheImage{};
       TheImage.TheImageKind = IMG_Object;
       TheImage.TheOffloadKind = Kind;
-      TheImage.StringData = {{"triple", TripleStr}, {"arch", Arch}};
+      TheImage.StringData = {
+          {"triple", LinkerArgs.getLastArgValue(OPT_triple_EQ)},
+          {"arch", LinkerArgs.getLastArgValue(OPT_arch_EQ)}};
       TheImage.Image = std::move(*FileOrErr);
       Images[Kind].emplace_back(std::move(TheImage));
     }
@@ -1249,10 +1258,10 @@ linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
   for (const auto &KindAndImages : Images) {
     OffloadKind Kind = KindAndImages.first;
     auto BundledImagesOrErr =
-        bundleLinkedOutput(KindAndImages.second, KindAndImages.first);
+        bundleLinkedOutput(KindAndImages.second, Args, Kind);
     if (!BundledImagesOrErr)
       return BundledImagesOrErr.takeError();
-    auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Kind);
+    auto OutputOrErr = wrapDeviceImages(*BundledImagesOrErr, Args, Kind);
     if (!OutputOrErr)
       return OutputOrErr.takeError();
     WrappedOutput.push_back(*OutputOrErr);
@@ -1261,10 +1270,11 @@ linkAndWrapDeviceFiles(SmallVectorImpl<OffloadFile> &LinkerInputFiles) {
   return WrappedOutput;
 }
 
-Optional<std::string> findFile(StringRef Dir, const Twine &Name) {
+Optional<std::string> findFile(StringRef Dir, StringRef Root,
+                               const Twine &Name) {
   SmallString<128> Path;
   if (Dir.startswith("="))
-    sys::path::append(Path, Sysroot, Dir.substr(1), Name);
+    sys::path::append(Path, Root, Dir.substr(1), Name);
   else
     sys::path::append(Path, Dir, Name);
 
@@ -1273,20 +1283,20 @@ Optional<std::string> findFile(StringRef Dir, const Twine &Name) {
   return None;
 }
 
-Optional<std::string> findFromSearchPaths(StringRef Name,
+Optional<std::string> findFromSearchPaths(StringRef Name, StringRef Root,
                                           ArrayRef<StringRef> SearchPaths) {
   for (StringRef Dir : SearchPaths)
-    if (Optional<std::string> File = findFile(Dir, Name))
+    if (Optional<std::string> File = findFile(Dir, Root, Name))
       return File;
   return None;
 }
 
-Optional<std::string> searchLibraryBaseName(StringRef Name,
+Optional<std::string> searchLibraryBaseName(StringRef Name, StringRef Root,
                                             ArrayRef<StringRef> SearchPaths) {
   for (StringRef Dir : SearchPaths) {
-    if (Optional<std::string> File = findFile(Dir, "lib" + Name + ".so"))
+    if (Optional<std::string> File = findFile(Dir, Root, "lib" + Name + ".so"))
       return None;
-    if (Optional<std::string> File = findFile(Dir, "lib" + Name + ".a"))
+    if (Optional<std::string> File = findFile(Dir, Root, "lib" + Name + ".a"))
       return File;
   }
   return None;
@@ -1294,77 +1304,94 @@ Optional<std::string> searchLibraryBaseName(StringRef Name,
 
 /// Search for static libraries in the linker's library path given input like
 /// `-lfoo` or `-l:libfoo.a`.
-Optional<std::string> searchLibrary(StringRef Input,
+Optional<std::string> searchLibrary(StringRef Input, StringRef Root,
                                     ArrayRef<StringRef> SearchPaths) {
-  if (!Input.startswith("-l"))
-    return None;
-  StringRef Name = Input.drop_front(2);
-  if (Name.startswith(":"))
-    return findFromSearchPaths(Name.drop_front(), SearchPaths);
-  return searchLibraryBaseName(Name, SearchPaths);
+  if (Input.startswith(":"))
+    return findFromSearchPaths(Input.drop_front(), Root, SearchPaths);
+  return searchLibraryBaseName(Input, Root, SearchPaths);
 }
 
 } // namespace
 
-int main(int argc, const char **argv) {
-  InitLLVM X(argc, argv);
+int main(int Argc, char **Argv) {
+  InitLLVM X(Argc, Argv);
   InitializeAllTargetInfos();
   InitializeAllTargets();
   InitializeAllTargetMCs();
   InitializeAllAsmParsers();
   InitializeAllAsmPrinters();
 
-  LinkerExecutable = argv[0];
-  sys::PrintStackTraceOnErrorSignal(argv[0]);
-  cl::SetVersionPrinter(PrintVersion);
-  cl::HideUnrelatedOptions(ClangLinkerWrapperCategory);
-  cl::ParseCommandLineOptions(
-      argc, argv,
-      "A wrapper utility over the host linker. It scans the input files for\n"
-      "sections that require additional processing prior to linking. The tool\n"
-      "will then transparently pass all arguments and input to the specified\n"
-      "host linker to create the final binary.\n");
-
-  if (Help) {
-    cl::PrintHelpMessage();
-    return EXIT_SUCCESS;
-  }
-
-  auto reportError = [argv](Error E) {
-    logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
+  LinkerExecutable = Argv[0];
+  sys::PrintStackTraceOnErrorSignal(Argv[0]);
+  auto reportError = [Argv](Error E) {
+    logAllUnhandledErrors(std::move(E), WithColor::error(errs(), Argv[0]));
     return EXIT_FAILURE;
   };
 
-  if (!CudaPath.empty())
-    CudaBinaryPath = CudaPath + "/bin";
-
-  auto RootIt = llvm::find_if(HostLinkerArgs, [](StringRef Arg) {
-    return Arg.startswith("--sysroot=");
+  const OptTable &Tbl = getOptTable();
+  BumpPtrAllocator Alloc;
+  StringSaver Saver(Alloc);
+  auto Args = Tbl.parseArgs(Argc, Argv, OPT_INVALID, Saver, [&](StringRef Err) {
+    reportError(createStringError(inconvertibleErrorCode(), Err));
   });
-  if (RootIt != HostLinkerArgs.end())
-    Sysroot = StringRef(*RootIt).split('=').second;
-
-  ExecutableName = *std::next(llvm::find(HostLinkerArgs, "-o"));
-  SmallVector<StringRef, 16> LinkerArgs;
-  for (StringRef Arg : HostLinkerArgs)
-    LinkerArgs.push_back(Arg);
-
-  SmallVector<StringRef, 16> LibraryPaths;
-  for (StringRef Arg : LinkerArgs) {
-    if (Arg.startswith("-L"))
-      LibraryPaths.push_back(Arg.drop_front(2));
+
+  if (Args.hasArg(OPT_help) || Args.hasArg(OPT_help_hidden)) {
+    Tbl.printHelp(
+        outs(),
+        "clang-linker-wrapper [options] -- <options to passed to the linker>",
+        "\nA wrapper utility over the host linker. It scans the input files\n"
+        "for sections that require additional processing prior to linking.\n"
+        "The will then transparently pass all arguments and input to the\n"
+        "specified host linker to create the final binary.\n",
+        Args.hasArg(OPT_help_hidden), Args.hasArg(OPT_help_hidden));
+    return EXIT_SUCCESS;
+  }
+  if (Args.hasArg(OPT_v)) {
+    printVersion(outs());
+    return EXIT_SUCCESS;
   }
 
-  // Try to extract device code from the linker input.
-  SmallVector<OffloadFile, 4> InputFiles;
-  SmallVector<OffloadFile, 4> LazyInputFiles;
-  for (StringRef Arg : LinkerArgs) {
-    if (Arg == ExecutableName)
+  // This forwards '-pass-remarks=' to the LTO backend if present.
+  cl::HideUnrelatedOptions(ClangLinkerWrapperCategory);
+  cl::ParseCommandLineOptions(Argc, Argv);
+
+  Verbose = Args.hasArg(OPT_verbose);
+  DryRun = Args.hasArg(OPT_dry_run);
+  SaveTemps = Args.hasArg(OPT_save_temps);
+  ExecutableName = Args.getLastArgValue(OPT_o, "a.out");
+  CudaBinaryPath = Args.getLastArgValue(OPT_cuda_path_EQ).str();
+  if (!CudaBinaryPath.empty())
+    CudaBinaryPath = CudaBinaryPath + "/bin";
+
+  StringRef Root = Args.getLastArgValue(OPT_sysroot_EQ);
+
+  SmallVector<StringRef> LibraryPaths;
+  for (const opt::Arg *Arg : Args.filtered(OPT_library_path))
+    LibraryPaths.push_back(Arg->getValue());
+
+  // Try to extract device code from the linker input files.
+  SmallVector<OffloadFile> InputFiles;
+  SmallVector<OffloadFile> LazyInputFiles;
+  for (const opt::Arg *Arg : Args.filtered(OPT_INPUT)) {
+    StringRef Filename = Arg->getValue();
+    if (!sys::fs::exists(Filename) || sys::fs::is_directory(Filename))
       continue;
 
-    // Search the inpuot argument for embedded device files if it is a static
-    // library or regular input file.
-    if (Optional<std::string> Library = searchLibrary(Arg, LibraryPaths)) {
+    ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
+        MemoryBuffer::getFileOrSTDIN(Filename);
+    if (std::error_code EC = BufferOrErr.getError())
+      return reportError(createFileError(Filename, EC));
+
+    bool IsLazy =
+        identify_magic((*BufferOrErr)->getBuffer()) == file_magic::archive;
+    if (Error Err = extractFromBuffer(std::move(*BufferOrErr),
+                                      IsLazy ? LazyInputFiles : InputFiles))
+      return reportError(std::move(Err));
+  }
+
+  // Try to extract input from input libraries.
+  for (const opt::Arg *Arg : Args.filtered(OPT_library)) {
+    if (auto Library = searchLibrary(Arg->getValue(), Root, LibraryPaths)) {
       ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
           MemoryBuffer::getFileOrSTDIN(*Library);
       if (std::error_code EC = BufferOrErr.getError())
@@ -1373,24 +1400,10 @@ int main(int argc, const char **argv) {
       if (Error Err =
               extractFromBuffer(std::move(*BufferOrErr), LazyInputFiles))
         return reportError(std::move(Err));
-    } else if (sys::fs::exists(Arg) && !sys::fs::is_directory(Arg)) {
-      ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
-          MemoryBuffer::getFileOrSTDIN(Arg);
-      if (std::error_code EC = BufferOrErr.getError())
-        return reportError(createFileError(Arg, EC));
-
-      if (sys::path::extension(Arg).endswith(".a")) {
-        if (Error Err =
-                extractFromBuffer(std::move(*BufferOrErr), LazyInputFiles))
-          return reportError(std::move(Err));
-      } else {
-        if (Error Err = extractFromBuffer(std::move(*BufferOrErr), InputFiles))
-          return reportError(std::move(Err));
-      }
     }
   }
 
-  for (StringRef Library : BitcodeLibraries) {
+  for (StringRef Library : Args.getAllArgValues(OPT_bitcode_library_EQ)) {
     auto FileOrErr = getInputBitcodeLibrary(Library);
     if (!FileOrErr)
       return reportError(FileOrErr.takeError());
@@ -1408,22 +1421,25 @@ int main(int argc, const char **argv) {
   LazyInputFiles.clear();
 
   // Link and wrap the device images extracted from the linker input.
-  auto FilesOrErr = linkAndWrapDeviceFiles(InputFiles);
+  auto FilesOrErr = linkAndWrapDeviceFiles(InputFiles, Args);
   if (!FilesOrErr)
     return reportError(FilesOrErr.takeError());
 
-  // We need to insert the new files next to the old ones to make sure they're
-  // linked with the same libraries / arguments.
-  if (!FilesOrErr->empty()) {
-    auto *FirstInput = std::next(llvm::find_if(LinkerArgs, [](StringRef Str) {
-      return sys::fs::exists(Str) && !sys::fs::is_directory(Str) &&
-             Str != ExecutableName;
-    }));
-    LinkerArgs.insert(FirstInput, FilesOrErr->begin(), FilesOrErr->end());
+  // Render the linker arguments and add the newly created image. We add it
+  // after the output file to ensure it is linked with the correct libraries.
+  ArgStringList LinkerArgs;
+  for (const opt::Arg *Arg : Args) {
+    if (Arg->getOption().hasFlag(WrapperOnlyOption))
+      continue;
+    Arg->render(Args, LinkerArgs);
+    if (Arg->getOption().matches(OPT_o))
+      llvm::transform(*FilesOrErr, std::back_inserter(LinkerArgs),
+                      [&](StringRef Arg) { return Args.MakeArgString(Arg); });
   }
 
-  // Run the host linking job.
-  if (Error Err = runLinker(LinkerUserPath, LinkerArgs))
+  // Run the host linking job with the rendered arguments.
+  StringRef LinkerPath = Args.getLastArgValue(OPT_linker_path_EQ);
+  if (Error Err = runLinker(LinkerPath, LinkerArgs))
     return reportError(std::move(Err));
 
   // Remove the temporary files created.

diff  --git a/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
new file mode 100644
index 0000000000000..0b0b2c901361f
--- /dev/null
+++ b/clang/tools/clang-linker-wrapper/LinkerWrapperOpts.td
@@ -0,0 +1,101 @@
+include "llvm/Option/OptParser.td"
+
+def WrapperOnlyOption : OptionFlag;
+def DeviceOnlyOption : OptionFlag;
+
+def help : Flag<["--"], "help">,
+  HelpText<"Display available options (--help-hidden for more)">;
+
+def help_hidden : Flag<["--"], "help-hidden">,
+  HelpText<"Display all available options">;
+
+// Flags for the linker wrapper.
+def linker_path_EQ : Joined<["--"], "linker-path=">,
+  Flags<[WrapperOnlyOption]>, MetaVarName<"<path>">,
+  HelpText<"The linker executable to invoke">;
+def cuda_path_EQ : Joined<["--"], "cuda-path=">,
+  Flags<[WrapperOnlyOption]>, MetaVarName<"<dir>">,
+  HelpText<"Set the system CUDA path">;
+def host_triple_EQ : Joined<["--"], "host-triple=">,
+  Flags<[WrapperOnlyOption]>, MetaVarName<"<triple>">,
+  HelpText<"Triple to use for the host compilation">;
+def opt_level : Joined<["--"], "opt-level=">,
+  Flags<[WrapperOnlyOption]>, MetaVarName<"<O0, O1, O2, or O3>">,
+  HelpText<"Optimization level for LTO">;
+def bitcode_library_EQ : Joined<["--"], "bitcode-library=">,
+  Flags<[WrapperOnlyOption]>, MetaVarName<"<kind>-<triple>-<arch>=<path>">,
+  HelpText<"Extra bitcode library to link">;
+def device_linker_args_EQ : Joined<["--"], "device-linker=">,
+  Flags<[WrapperOnlyOption]>, MetaVarName<"<value> or <triple>=<value>">,
+  HelpText<"Arguments to pass to the device linker invocation">;
+def dry_run : Flag<["--"], "dry-run">,
+  Flags<[WrapperOnlyOption]>,
+  HelpText<"Print program arguments without running">;
+def verbose : Flag<["--"], "verbose">,
+  Flags<[WrapperOnlyOption]>, HelpText<"Verbose output from tools">;
+def embed_bitcode : Flag<["--"], "embed-bitcode">,
+  Flags<[WrapperOnlyOption]>, HelpText<"Embed linked bitcode in the module">;
+def debug : Flag<["--"], "device-debug">, Flags<[WrapperOnlyOption]>, 
+  HelpText<"Use debugging">;
+def ptxas_arg : Joined<["--"], "ptxas-arg=">,
+  Flags<[WrapperOnlyOption]>,
+  HelpText<"Argument to pass to the 'ptxas' invocation">;
+def pass_remarks_EQ : Joined<["--"], "pass-remarks=">,
+  Flags<[WrapperOnlyOption]>, HelpText<"Pass remarks for LTO">;
+def pass_remarks_missed_EQ : Joined<["--"], "pass-remarks-missed=">,
+  Flags<[WrapperOnlyOption]>, HelpText<"Pass remarks for LTO">;
+def pass_remarks_analysis_EQ : Joined<["--"], "pass-remarks-analysis=">,
+  Flags<[WrapperOnlyOption]>, HelpText<"Pass remarks for LTO">;
+def print_wrapped_module : Flag<["--"], "print-wrapped-module">,
+  Flags<[WrapperOnlyOption]>,
+  HelpText<"Print the wrapped module's IR for testing">;
+def save_temps : Flag<["--"], "save-temps">,
+  Flags<[WrapperOnlyOption]>, HelpText<"Save intermediate results">;
+
+// Flags passed to the device linker.
+def arch_EQ : Joined<["--"], "arch=">,
+  Flags<[DeviceOnlyOption, HelpHidden]>, MetaVarName<"<arch>">,
+  HelpText<"The device subarchitecture">;
+def triple_EQ : Joined<["--"], "triple=">,
+  Flags<[DeviceOnlyOption, HelpHidden]>, MetaVarName<"<triple>">,
+  HelpText<"The device target triple">;
+def whole_program : Flag<["--"], "whole-program">,
+  Flags<[DeviceOnlyOption, HelpHidden]>,
+  HelpText<"LTO has visibility of all input files">;
+def linker_arg_EQ : Joined<["--"], "linker-arg=">,
+  Flags<[DeviceOnlyOption, HelpHidden]>,
+  HelpText<"An extra argument to be passed to the linker">;
+
+// Separator between the linker wrapper and host linker flags.
+def separator : Flag<["--"], "">, Flags<[WrapperOnlyOption]>,
+  HelpText<"The separator for the wrapped linker arguments">;
+
+// Standard linker flags also used by the linker wrapper.
+def sysroot_EQ : Joined<["--"], "sysroot">, HelpText<"Set the system root">;
+
+def o : JoinedOrSeparate<["-"], "o">, MetaVarName<"<path>">,
+  HelpText<"Path to file to write output">;
+def output_EQ : Joined<["--"], "output=">, Alias<o>, HelpText<"Alias for -o">;
+def output : Separate<["--"], "output">, Alias<o>, HelpText<"Alias for -o">;
+
+def library_path : JoinedOrSeparate<["-"], "L">, MetaVarName<"<dir>">,
+  HelpText<"Add <dir> to the library search path">;
+def library_path_S : Separate<["--", "-"], "library-path">, Alias<library_path>;
+def library_path_EQ : Joined<["--", "-"], "library-path=">, Alias<library_path>;
+
+def library : JoinedOrSeparate<["-"], "l">, MetaVarName<"<libname>">,
+  HelpText<"Search for library <libname>">;
+def library_S : Separate<["--", "-"], "library">, Alias<library_path>;
+def library_EQ : Joined<["--", "-"], "library=">, Alias<library_path>;
+
+def as_needed : Flag<["--", "-"], "as-needed">;
+def no_as_needed : Flag<["--", "-"], "no-as-needed">;
+
+def rpath : Separate<["--", "-"], "rpath">;
+def rpath_EQ : Joined<["--", "-"], "rpath=">, Alias<rpath>;
+
+def dynamic_linker : Separate<["--", "-"], "dynamic-linker">;
+def dynamic_linker_EQ : Joined<["--", "-"], "dynamic-linker=">, Alias<dynamic_linker>;
+
+def v : Flag<["--", "-"], "v">, HelpText<"Display the version number and exit">;
+def version : Flag<["--", "-"], "version">, Alias<v>;


        


More information about the cfe-commits mailing list