Add Statically Linked Libraries

Add GNU Static Lib Tool, which supports the --emit-static-lib
flag. For HIP, a static library archive will be created and
consist of HIP Fat Binary host object with the device images embedded.
Using llvm-ar to create the static archive. Also, delete existing
output file to ensure a new archive is created each time.

Reviewers: yaxunl, tra, rjmccall, echristo

Subscribers: echristo, JonChesterfield, scchan, msearles

Differential Revision: https://reviews.llvm.org/D78759
This commit is contained in:
Aaron En Ye Shi 2020-05-27 18:49:25 +00:00
parent 77df5a8283
commit 4bafb0adcf
15 changed files with 254 additions and 2 deletions

View File

@ -73,9 +73,10 @@ public:
OffloadBundlingJobClass,
OffloadUnbundlingJobClass,
OffloadWrapperJobClass,
StaticLibJobClass,
JobClassFirst = PreprocessJobClass,
JobClassLast = OffloadWrapperJobClass
JobClassLast = StaticLibJobClass
};
// The offloading kind determines if this action is binded to a particular
@ -637,6 +638,17 @@ public:
}
};
class StaticLibJobAction : public JobAction {
void anchor() override;
public:
StaticLibJobAction(ActionList &Inputs, types::ID Type);
static bool classof(const Action *A) {
return A->getKind() == StaticLibJobClass;
}
};
} // namespace driver
} // namespace clang

View File

@ -548,6 +548,9 @@ public:
/// handle this action.
bool ShouldUseFlangCompiler(const JobAction &JA) const;
/// ShouldEmitStaticLibrary - Should the linker emit a static library.
bool ShouldEmitStaticLibrary(const llvm::opt::ArgList &Args) const;
/// Returns true if we are performing any kind of LTO.
bool isUsingLTO() const { return LTOMode != LTOK_None; }

View File

@ -605,6 +605,8 @@ def hip_link : Flag<["--"], "hip-link">,
def no_offload_arch_EQ : Joined<["--"], "no-offload-arch=">, Flags<[DriverOption]>,
HelpText<"Remove CUDA/HIP offloading device architecture (e.g. sm_35, gfx906) from the list of devices to compile for. "
"'all' resets the list to its default value.">;
def emit_static_lib : Flag<["--"], "emit-static-lib">,
HelpText<"Enable linker job to emit a static library.">;
def no_cuda_gpu_arch_EQ : Joined<["--"], "no-cuda-gpu-arch=">, Flags<[DriverOption]>,
Alias<no_offload_arch_EQ>;
def cuda_noopt_device_debug : Flag<["--"], "cuda-noopt-device-debug">,

View File

@ -139,6 +139,7 @@ private:
mutable std::unique_ptr<Tool> Flang;
mutable std::unique_ptr<Tool> Assemble;
mutable std::unique_ptr<Tool> Link;
mutable std::unique_ptr<Tool> StaticLibTool;
mutable std::unique_ptr<Tool> IfsMerge;
mutable std::unique_ptr<Tool> OffloadBundler;
mutable std::unique_ptr<Tool> OffloadWrapper;
@ -147,6 +148,7 @@ private:
Tool *getFlang() const;
Tool *getAssemble() const;
Tool *getLink() const;
Tool *getStaticLibTool() const;
Tool *getIfsMerge() const;
Tool *getClangAs() const;
Tool *getOffloadBundler() const;
@ -174,6 +176,7 @@ protected:
virtual Tool *buildAssembler() const;
virtual Tool *buildLinker() const;
virtual Tool *buildStaticLibTool() const;
virtual Tool *getTool(Action::ActionClass AC) const;
/// \name Utilities for implementing subclasses.
@ -326,6 +329,9 @@ public:
/// the linker suffix or name.
std::string GetLinkerPath() const;
/// Returns the linker path for emitting a static library.
std::string GetStaticLibToolPath() const;
/// Dispatch to the specific toolchain for verbose printing.
///
/// This is used when handling the verbose option to print detailed,

View File

@ -43,6 +43,8 @@ const char *Action::getClassName(ActionClass AC) {
return "clang-offload-unbundler";
case OffloadWrapperJobClass:
return "clang-offload-wrapper";
case StaticLibJobClass:
return "static-lib-linker";
}
llvm_unreachable("invalid class");
@ -415,3 +417,8 @@ void OffloadWrapperJobAction::anchor() {}
OffloadWrapperJobAction::OffloadWrapperJobAction(ActionList &Inputs,
types::ID Type)
: JobAction(OffloadWrapperJobClass, Inputs, Type) {}
void StaticLibJobAction::anchor() {}
StaticLibJobAction::StaticLibJobAction(ActionList &Inputs, types::ID Type)
: JobAction(StaticLibJobClass, Inputs, Type) {}

View File

@ -3542,7 +3542,13 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
if (!LinkerInputs.empty()) {
if (Action *Wrapper = OffloadBuilder.makeHostLinkAction())
LinkerInputs.push_back(Wrapper);
Action *LA = C.MakeAction<LinkJobAction>(LinkerInputs, types::TY_Image);
Action *LA;
// Check if this Linker Job should emit a static library.
if (ShouldEmitStaticLibrary(Args)) {
LA = C.MakeAction<StaticLibJobAction>(LinkerInputs, types::TY_Image);
} else {
LA = C.MakeAction<LinkJobAction>(LinkerInputs, types::TY_Image);
}
LA = OffloadBuilder.processHostLinkAction(LA);
Actions.push_back(LA);
}
@ -5044,6 +5050,13 @@ bool Driver::ShouldUseFlangCompiler(const JobAction &JA) const {
return true;
}
bool Driver::ShouldEmitStaticLibrary(const ArgList &Args) const {
// Only emit static library if the flag is set explicitly.
if (Args.hasArg(options::OPT_emit_static_lib))
return true;
return false;
}
/// GetReleaseVersion - Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
/// grouped values as integers. Numbers which are not provided are set to 0.
///

View File

@ -275,6 +275,10 @@ Tool *ToolChain::buildLinker() const {
llvm_unreachable("Linking is not supported by this toolchain");
}
Tool *ToolChain::buildStaticLibTool() const {
llvm_unreachable("Creating static lib is not supported by this toolchain");
}
Tool *ToolChain::getAssemble() const {
if (!Assemble)
Assemble.reset(buildAssembler());
@ -293,6 +297,12 @@ Tool *ToolChain::getLink() const {
return Link.get();
}
Tool *ToolChain::getStaticLibTool() const {
if (!StaticLibTool)
StaticLibTool.reset(buildStaticLibTool());
return StaticLibTool.get();
}
Tool *ToolChain::getIfsMerge() const {
if (!IfsMerge)
IfsMerge.reset(new tools::ifstool::Merger(*this));
@ -322,6 +332,9 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
case Action::LinkJobClass:
return getLink();
case Action::StaticLibJobClass:
return getStaticLibTool();
case Action::InputClass:
case Action::BindArchClass:
case Action::OffloadClass:
@ -565,6 +578,11 @@ std::string ToolChain::GetLinkerPath() const {
return GetProgramPath(getDefaultLinker());
}
std::string ToolChain::GetStaticLibToolPath() const {
// TODO: Add support for static lib archiving on Windows
return GetProgramPath("llvm-ar");
}
types::ID ToolChain::LookupTypeForExtension(StringRef Ext) const {
types::ID id = types::lookupTypeForExtension(Ext);

View File

@ -341,6 +341,43 @@ static bool getStatic(const ArgList &Args) {
!Args.hasArg(options::OPT_static_pie);
}
void tools::gnutools::StaticLibTool::ConstructJob(
Compilation &C, const JobAction &JA, const InputInfo &Output,
const InputInfoList &Inputs, const ArgList &Args,
const char *LinkingOutput) const {
const Driver &D = getToolChain().getDriver();
// Silence warning for "clang -g foo.o -o foo"
Args.ClaimAllArgs(options::OPT_g_Group);
// and "clang -emit-llvm foo.o -o foo"
Args.ClaimAllArgs(options::OPT_emit_llvm);
// and for "clang -w foo.o -o foo". Other warning options are already
// handled somewhere else.
Args.ClaimAllArgs(options::OPT_w);
// Silence warnings when linking C code with a C++ '-stdlib' argument.
Args.ClaimAllArgs(options::OPT_stdlib_EQ);
// GNU ar tool command "ar <options> <output_file> <input_files>".
ArgStringList CmdArgs;
// Create and insert file members with a deterministic index.
CmdArgs.push_back("rcsD");
CmdArgs.push_back(Output.getFilename());
AddLinkerInputs(getToolChain(), Inputs, Args, CmdArgs, JA);
// Delete old output archive file if it already exists before generating a new
// archive file.
auto OutputFileName = Output.getFilename();
if (Output.isFilename() && llvm::sys::fs::exists(OutputFileName)) {
if (std::error_code EC = llvm::sys::fs::remove(OutputFileName)) {
D.Diag(diag::err_drv_unable_to_remove_file) << EC.message();
return;
}
}
const char *Exec = Args.MakeArgString(getToolChain().GetStaticLibToolPath());
C.addCommand(std::make_unique<Command>(JA, *this, Exec, CmdArgs, Inputs));
}
void tools::gnutools::Linker::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,

View File

@ -72,6 +72,20 @@ public:
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const override;
};
class LLVM_LIBRARY_VISIBILITY StaticLibTool : public GnuTool {
public:
StaticLibTool(const ToolChain &TC)
: GnuTool("GNU::StaticLibTool", "static-lib-linker", TC) {}
bool hasIntegratedCPP() const override { return false; }
bool isLinkJob() const override { return true; }
void ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output, const InputInfoList &Inputs,
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const override;
};
} // end namespace gnutools
/// gcc - Generic GCC tool implementations.

View File

@ -363,6 +363,10 @@ bool Linux::HasNativeLLVMSupport() const { return true; }
Tool *Linux::buildLinker() const { return new tools::gnutools::Linker(*this); }
Tool *Linux::buildStaticLibTool() const {
return new tools::gnutools::StaticLibTool(*this);
}
Tool *Linux::buildAssembler() const {
return new tools::gnutools::Assembler(*this);
}

View File

@ -57,6 +57,7 @@ public:
protected:
Tool *buildAssembler() const override;
Tool *buildLinker() const override;
Tool *buildStaticLibTool() const override;
std::string getMultiarchTriple(const Driver &D,
const llvm::Triple &TargetTriple,

View File

@ -23,3 +23,7 @@
// CHECK14: "clang", inputs: ["{{.*}}bindings.c"], output: "{{.*}}.s"
// CHECK14: "darwin::Assembler", inputs: ["{{.*}}.s"], output: "{{.*}}.o"
// CHECK14: "darwin::Linker", inputs: ["{{.*}}.o"], output: "a.out"
// GNU StaticLibTool binding
// RUN: %clang -target x86_64-linux-gnu -ccc-print-bindings --emit-static-lib %s 2>&1 | FileCheck %s --check-prefix=CHECK15
// CHECK15: "x86_64-unknown-linux-gnu" - "GNU::StaticLibTool", inputs: ["{{.*}}.o"], output: "a.out"

View File

@ -18,6 +18,24 @@
// RUN: --offload-arch=gfx906 %T/obj1.o %T/obj2.o 2>&1 | \
// RUN: FileCheck -check-prefixes=CHECK,NOUT %s
// -fgpu-rdc link with output and --emit-static-lib
// RUN: touch %T/obj1.o
// RUN: touch %T/obj2.o
// RUN: %clang -### -target x86_64-linux-gnu -nogpulib -save-temps \
// RUN: --hip-link -o libTest.a -fgpu-rdc --cuda-gpu-arch=gfx900 \
// RUN: --emit-static-lib \
// RUN: --offload-arch=gfx906 %T/obj1.o %T/obj2.o 2>&1 | \
// RUN: FileCheck -check-prefixes=CHECK,SLO %s
// -fgpu-rdc link without output and --emit-static-lib
// RUN: touch %T/obj1.o
// RUN: touch %T/obj2.o
// RUN: %clang -### -target x86_64-linux-gnu -nogpulib -save-temps \
// RUN: --hip-link -fgpu-rdc --cuda-gpu-arch=gfx900 \
// RUN: --emit-static-lib \
// RUN: --offload-arch=gfx906 %T/obj1.o %T/obj2.o 2>&1 | \
// RUN: FileCheck -check-prefixes=CHECK,SLNO %s
// CHECK: "{{.*clang-offload-bundler.*}}" {{.*}} "-outputs=obj1-host-x86_64-unknown-linux-gnu.o,obj1-hip-amdgcn-amd-amdhsa-gfx900.o,obj1-hip-amdgcn-amd-amdhsa-gfx906.o" "-unbundle"
// CHECK: "{{.*clang-offload-bundler.*}}" {{.*}} "-outputs=obj2-host-x86_64-unknown-linux-gnu.o,obj2-hip-amdgcn-amd-amdhsa-gfx900.o,obj2-hip-amdgcn-amd-amdhsa-gfx906.o" "-unbundle"
// CHECK-NOT: llvm-link
@ -31,3 +49,5 @@
// CHECK-SAME: "[[OBJBUNDLE:.*.o]]" "{{.*}}.mcin" "--filetype=obj"
// OUT: "{{.*ld.*}}" {{.*}} "-o" "executable" {{.*}} "[[OBJBUNDLE]]"
// NOUT: "{{.*ld.*}}" {{.*}} "-o" "a.out" {{.*}} "[[OBJBUNDLE]]"
// SLO: "{{.*llvm-ar.*}}" "rcsD" "libTest.a" {{.*}} "[[OBJBUNDLE]]"
// SLNO: "{{.*llvm-ar.*}}" "rcsD" "a.out" {{.*}} "[[OBJBUNDLE]]"

View File

@ -0,0 +1,27 @@
// REQUIRES: clang-driver
// REQUIRES: x86-registered-target
// REQUIRES: amdgpu-registered-target
// RUN: touch %t.o
// RUN: %clang --hip-link -ccc-print-bindings -target x86_64-linux-gnu \
// RUN: --emit-static-lib \
// RUN: --cuda-gpu-arch=gfx803 --cuda-gpu-arch=gfx900 -fgpu-rdc %t.o\
// RUN: 2>&1 | FileCheck %s
// CHECK: # "x86_64-unknown-linux-gnu" - "offload bundler", inputs: ["[[IN:.*o]]"], outputs: ["[[HOSTOBJ:.*o]]", "{{.*o}}", "{{.*o}}"]
// CHECK: # "amdgcn-amd-amdhsa" - "offload bundler", inputs: ["[[IN]]"], outputs: ["{{.*o}}", "[[DOBJ1:.*o]]", "[[DOBJ2:.*o]]"]
// CHECK: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[DOBJ1]]"], output: "[[IMG1:.*out]]"
// CHECK-NOT: offload bundler
// CHECK: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[DOBJ2]]"], output: "[[IMG2:.*out]]"
// CHECK-NOT: offload bundler
// CHECK: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[IMG1]]", "[[IMG2]]"], output: "[[FATBINOBJ:.*o]]"
// CHECK-NOT: offload bundler
// CHECK: # "x86_64-unknown-linux-gnu" - "GNU::StaticLibTool", inputs: ["[[HOSTOBJ]]", "[[FATBINOBJ]]"], output: "a.out"
// RUN: %clang --hip-link -ccc-print-bindings -target x86_64-linux-gnu \
// RUN: --emit-static-lib \
// RUN: --cuda-gpu-arch=gfx803 --cuda-gpu-arch=gfx900 %t.o\
// RUN: 2>&1 | FileCheck -check-prefix=NORDC %s
// NORDC-NOT: offload bundler
// NORDC: # "x86_64-unknown-linux-gnu" - "GNU::StaticLibTool", inputs: ["{{.*o}}"], output: "a.out"

View File

@ -0,0 +1,84 @@
// REQUIRES: clang-driver
// REQUIRES: x86-registered-target
// REQUIRES: amdgpu-registered-target
// RUN: %clang -### -target x86_64-linux-gnu \
// RUN: -x hip --cuda-gpu-arch=gfx803 --cuda-gpu-arch=gfx900 \
// RUN: --emit-static-lib -nogpulib \
// RUN: -fuse-ld=lld -fgpu-rdc -nogpuinc \
// RUN: %S/Inputs/hip_multiple_inputs/a.cu \
// RUN: %S/Inputs/hip_multiple_inputs/b.hip \
// RUN: 2>&1 | FileCheck %s
// emit objects for host side path
// CHECK: [[CLANG:".*clang.*"]] "-cc1" "-triple" "x86_64-unknown-linux-gnu"
// CHECK-SAME: "-aux-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-emit-obj"
// CHECK-SAME: {{.*}} "-main-file-name" "a.cu"
// CHECK-SAME: {{.*}} "-o" [[A_OBJ_HOST:".*o"]] "-x" "hip"
// CHECK-SAME: {{.*}} [[A_SRC:".*a.cu"]]
// CHECK: [[CLANG]] "-cc1" "-triple" "x86_64-unknown-linux-gnu"
// CHECK-SAME: "-aux-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-emit-obj"
// CHECK-SAME: {{.*}} "-main-file-name" "b.hip"
// CHECK-SAME: {{.*}} "-o" [[B_OBJ_HOST:".*o"]] "-x" "hip"
// CHECK-SAME: {{.*}} [[B_SRC:".*b.hip"]]
// generate image for device side path on gfx803
// CHECK: [[CLANG]] "-cc1" "-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
// CHECK-SAME: "-emit-llvm-bc"
// CHECK-SAME: {{.*}} "-main-file-name" "a.cu"
// CHECK-SAME: "-fcuda-is-device" "-fgpu-rdc"
// CHECK-SAME: "-target-cpu" "gfx803"
// CHECK-SAME: {{.*}} "-o" [[A_BC1:".*bc"]] "-x" "hip"
// CHECK-SAME: {{.*}} [[A_SRC]]
// CHECK: [[CLANG]] "-cc1" "-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
// CHECK-SAME: "-emit-llvm-bc"
// CHECK-SAME: {{.*}} "-main-file-name" "b.hip"
// CHECK-SAME: "-fcuda-is-device" "-fgpu-rdc"
// CHECK-SAME: "-target-cpu" "gfx803"
// CHECK-SAME: {{.*}} "-o" [[B_BC1:".*bc"]] "-x" "hip"
// CHECK-SAME: {{.*}} [[B_SRC]]
// CHECK-NOT: "*.llvm-link"
// CHECK-NOT: ".*opt"
// CHECK-NOT: ".*llc"
// CHECK: [[LLD: ".*lld"]] {{.*}} "-o" "[[IMG_DEV1:.*out]]" [[A_BC1]] [[B_BC1]]
// generate image for device side path on gfx900
// CHECK: [[CLANG]] "-cc1" "-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
// CHECK-SAME: "-emit-llvm-bc"
// CHECK-SAME: {{.*}} "-main-file-name" "a.cu"
// CHECK-SAME: "-fcuda-is-device" "-fgpu-rdc"
// CHECK-SAME: "-target-cpu" "gfx900"
// CHECK-SAME: {{.*}} "-o" [[A_BC2:".*bc"]] "-x" "hip"
// CHECK-SAME: {{.*}} [[A_SRC]]
// CHECK: [[CLANG]] "-cc1" "-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
// CHECK-SAME: "-emit-llvm-bc"
// CHECK-SAME: {{.*}} "-main-file-name" "b.hip"
// CHECK-SAME: "-fcuda-is-device" "-fgpu-rdc"
// CHECK-SAME: "-target-cpu" "gfx900"
// CHECK-SAME: {{.*}} "-o" [[B_BC2:".*bc"]] "-x" "hip"
// CHECK-SAME: {{.*}} [[B_SRC]]
// CHECK-NOT: "*.llvm-link"
// CHECK-NOT: ".*opt"
// CHECK-NOT: ".*llc"
// CHECK: [[LLD]] {{.*}} "-o" "[[IMG_DEV2:.*out]]" [[A_BC2]] [[B_BC2]]
// combine images generated into hip fat binary object
// CHECK: [[BUNDLER:".*clang-offload-bundler"]] "-type=o"
// CHECK-SAME: "-targets={{.*}},hip-amdgcn-amd-amdhsa-gfx803,hip-amdgcn-amd-amdhsa-gfx900"
// CHECK-SAME: "-inputs={{.*}},[[IMG_DEV1]],[[IMG_DEV2]]" "-outputs=[[BUNDLE:.*hipfb]]"
// CHECK: [[MC:".*llvm-mc"]] "-triple" "amdgcn-amd-amdhsa"
// CHECK-SAME: "-o" [[OBJBUNDLE:".*o"]] "{{.*}}.mcin" "--filetype=obj"
// CHECK: [[AR:".*llvm-ar.*"]] "rcsD" "{{.*}}.out" [[A_OBJ_HOST]] [[B_OBJ_HOST]] [[OBJBUNDLE]]