Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 124 additions & 33 deletions clang/lib/Driver/ToolChains/HIPSPV.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "clang/Driver/Driver.h"
#include "clang/Driver/InputInfo.h"
#include "clang/Options/Options.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"

Expand Down Expand Up @@ -48,6 +49,34 @@ static std::string findPassPlugin(const Driver &D,
return std::string();
}

// Is the in-tree SPIR-V backend built into this clang?
static bool isSPIRVBackendAvailable(const llvm::Triple &T) {
std::string IgnoredError;
return llvm::TargetRegistry::lookupTarget(T, IgnoredError);
}

// Runs the HipSpvPasses plugin via `opt` on TempFile when the plugin is found.
// Returns the lowered bitcode path, or TempFile unchanged if no plugin exists.
static const char *runHipSpvPasses(Compilation &C, const JobAction &JA,
const Tool &Creator, const ToolChain &TC,
const InputInfoList &Inputs,
const InputInfo &Output,
const llvm::opt::ArgList &Args,
StringRef Name, const char *TempFile) {
auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
if (PassPluginPath.empty())
return TempFile;
const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
const char *OptOutput = HIP::getTempFile(C, Name.str() + "-lower", "bc");
ArgStringList OptArgs{TempFile, "-load-pass-plugin",
PassPathCStr, "-passes=hip-post-link-passes",
"-o", OptOutput};
const char *Opt = Args.MakeArgString(TC.GetProgramPath("opt"));
C.addCommand(std::make_unique<Command>(
JA, Creator, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output));
return OptOutput;
}

void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
Compilation &C, const JobAction &JA, const InputInfoList &Inputs,
const InputInfo &Output, const llvm::opt::ArgList &Args) const {
Expand All @@ -73,44 +102,101 @@ void HIPSPV::Linker::constructLinkAndEmitSpirvCommand(
tools::constructLLVMLinkCommand(C, *this, JA, Inputs, LinkArgs, Output, Args,
TempFile);

// Post-link HIP lowering.
auto T = getToolChain().getTriple();

// Run LLVM IR passes to lower/expand/emulate HIP code that does not translate
// to SPIR-V (E.g. dynamic shared memory).
auto PassPluginPath = findPassPlugin(C.getDriver(), Args);
if (!PassPluginPath.empty()) {
const char *PassPathCStr = C.getArgs().MakeArgString(PassPluginPath);
const char *OptOutput = HIP::getTempFile(C, Name + "-lower", "bc");
ArgStringList OptArgs{TempFile, "-load-pass-plugin",
PassPathCStr, "-passes=hip-post-link-passes",
"-o", OptOutput};
const char *Opt = Args.MakeArgString(getToolChain().GetProgramPath("opt"));
if (T.getOS() == llvm::Triple::ChipStar) {
// chipStar: run HipSpvPasses via opt, then emit SPIR-V with the in-tree
// SPIR-V backend by default, or with the external llvm-spirv translator
// when -fno-integrated-objemitter is given (or the backend is not built).

// Run HipSpvPasses plugin via opt (must run on LLVM IR before
// the SPIR-V backend lowers to MIR).
TempFile = runHipSpvPasses(C, JA, *this, getToolChain(), Inputs, Output,
Args, Name, TempFile);

// Note that useIntegratedBackend() is consulted first so that an explicit
// -f(no-)integrated-objemitter still gets diagnosed against this toolchain.
if (!getToolChain().useIntegratedBackend() || !isSPIRVBackendAvailable(T)) {
// External translator path: BC -> SPIR-V via llvm-spirv.
llvm::opt::ArgStringList TrArgs;
if (T.getSubArch() == llvm::Triple::NoSubArch)
TrArgs.push_back("--spirv-max-version=1.2");
// Keep this extension list in sync with the in-tree backend fallback
// below.
TrArgs.push_back("--spirv-ext=-all"
",+SPV_INTEL_function_pointers"
",+SPV_INTEL_subgroups"
",+SPV_KHR_bit_instructions"
",+SPV_EXT_shader_atomic_float_add");

// Preserve debug info in the NonSemantic.Shader.DebugInfo form (see the
// comment on the equivalent block in the non-chipStar path below).
// These flags are passed unconditionally instead of gating on -g: in
// RDC-mode links this job runs in a clang invoked by
// clang-linker-wrapper where the original -g is not visible, but the
// debug info itself travels in the bitcode. SPV_KHR_non_semantic_info
// and the debug info version only take effect when the bitcode carries
// debug info. SPV_INTEL_optnone is not tied to debug info: clang emits
// optnone at -O0 even without -g, and the emitter needs the extension
// allowed to encode it.
TrArgs.push_back("--spirv-ext=+SPV_KHR_non_semantic_info"
",+SPV_INTEL_optnone");
TrArgs.push_back("--spirv-debug-info-version=nonsemantic-shader-200");

InputInfo TrInput = InputInfo(types::TY_LLVM_BC, TempFile, "");
SPIRV::constructTranslateCommand(C, *this, JA, Output, TrInput, TrArgs);
return;
}

// Default: compile the lowered bitcode to SPIR-V with the in-tree backend.
// Invoke `clang -cc1` directly rather than the clang driver: the driver
// would re-run config-file loading, toolchain detection and argument
// translation over an input that is already device-compiled and lowered,
// which is both wasteful and fragile. This mirrors how HIPAMD drives its
// SPIR-V backend emission (see HIPAMD::constructLinkAndEmitSpirvCommand).
// Keep the default -O0 backend pipeline (i.e. no -disable-llvm-optzns) so
// the mandatory lowering passes still run, matching the previously
// validated driver `-c` behavior.
ArgStringList Cc1Args;
Cc1Args.push_back("-cc1");
Cc1Args.push_back("-triple");
Cc1Args.push_back(C.getArgs().MakeArgString(T.getTriple()));
Cc1Args.push_back("-emit-obj");

// SPIR-V extensions the chipStar runtime relies on. Keep in sync with the
// llvm-spirv translator path above. SPV_KHR_non_semantic_info and
// SPV_INTEL_optnone let the backend emit NonSemantic.Shader.DebugInfo and
// the OptNoneINTEL function control when the bitcode carries debug info /
// optnone attributes (the backend's debug handler is a no-op otherwise).
Cc1Args.push_back("-mllvm");
Cc1Args.push_back("-spirv-ext=+SPV_INTEL_function_pointers"
",+SPV_INTEL_subgroups"
",+SPV_KHR_bit_instructions"
",+SPV_EXT_shader_atomic_float_add"
",+SPV_KHR_non_semantic_info"
",+SPV_INTEL_optnone");

Cc1Args.push_back(TempFile);
Cc1Args.push_back("-o");
Cc1Args.push_back(Output.getFilename());

const Driver &Drv = C.getDriver();
const char *Clang = Drv.getDriverProgramPath();
C.addCommand(std::make_unique<Command>(
JA, *this, ResponseFileSupport::None(), Opt, OptArgs, Inputs, Output));
TempFile = OptOutput;
JA, *this, ResponseFileSupport::None(), Clang, Cc1Args, Inputs, Output,
Drv.getPrependArg()));
return;
}

// Emit SPIR-V binary.
// Non-chipStar: run HIP passes via opt, then translate with llvm-spirv.
TempFile = runHipSpvPasses(C, JA, *this, getToolChain(), Inputs, Output, Args,
Name, TempFile);

// Emit SPIR-V binary via llvm-spirv translator (non-chipStar targets).
llvm::opt::ArgStringList TrArgs;
auto T = getToolChain().getTriple();
bool HasNoSubArch = T.getSubArch() == llvm::Triple::NoSubArch;
if (T.getOS() == llvm::Triple::ChipStar) {
// chipStar needs 1.2 for supporting warp-level primitivies via sub-group
// extensions. Strictly put we'd need 1.3 for the standard non-extension
// shuffle operations, but it's not supported by any backend driver of the
// chipStar.
if (HasNoSubArch)
TrArgs.push_back("--spirv-max-version=1.2");
TrArgs.push_back("--spirv-ext=-all"
// Needed for experimental indirect call support.
",+SPV_INTEL_function_pointers"
// Needed for shuffles below SPIR-V 1.3
",+SPV_INTEL_subgroups");
} else {
if (HasNoSubArch)
TrArgs.push_back("--spirv-max-version=1.1");
TrArgs.push_back("--spirv-ext=+all");
}
if (T.getSubArch() == llvm::Triple::NoSubArch)
TrArgs.push_back("--spirv-max-version=1.1");
TrArgs.push_back("--spirv-ext=+all");

// Preserve debug info requested via -g into the emitted SPIR-V using the
// NonSemantic.Shader.DebugInfo form. Downstream consumers such as Intel's IGC
Expand Down Expand Up @@ -175,6 +261,11 @@ HIPSPVToolChain::HIPSPVToolChain(const Driver &D, const llvm::Triple &Triple,
getProgramPaths().push_back(getDriver().Dir);
}

bool HIPSPVToolChain::IsIntegratedBackendSupported() const {
// The in-tree SPIR-V backend can only be requested when it is built.
return isSPIRVBackendAvailable(getTriple());
}

void HIPSPVToolChain::addClangTargetOptions(
const llvm::opt::ArgList &DriverArgs, llvm::opt::ArgStringList &CC1Args,
BoundArch BA, Action::OffloadKind DeviceOffloadingKind) const {
Expand Down
12 changes: 10 additions & 2 deletions clang/lib/Driver/ToolChains/HIPSPV.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,18 @@ class LLVM_LIBRARY_VISIBILITY HIPSPVToolChain final : public ToolChain {
const llvm::opt::ArgList &Args);

const llvm::Triple *getAuxTriple() const override {
assert(HostTC);
return &HostTC->getTriple();
return HostTC ? &HostTC->getTriple() : nullptr;
}

// Keep IsIntegratedBackendDefault() at the base class' "true": it also
// decides whether clang's compile and backend jobs are collapsed into a
// single -cc1 invocation, so making it depend on whether the SPIR-V backend
// was built would change the device compilation job layout of every HIPSPV
// compile. The fallback to the external llvm-spirv translator is decided in
// HIPSPV::Linker::constructLinkAndEmitSpirvCommand instead.
bool IsIntegratedBackendSupported() const override;
bool IsNonIntegratedBackendSupported() const override { return true; }

void
addClangTargetOptions(const llvm::opt::ArgList &DriverArgs,
llvm::opt::ArgStringList &CC1Args, BoundArch BA,
Expand Down
3 changes: 2 additions & 1 deletion clang/test/Driver/hipspv-link-static-library.hip
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
// DELETE-SDL-NEW: "{{.*}}llvm-link" "-o" "{{.*}}.bc" "{{.*}}.o" "{{.*}}.o"

// SDL-NEW-WRAPPER: clang{{.*}}" --no-default-config -o {{[^ ]*.img}}
// SDL-NEW-WRAPPER-SAME: {{[^ ]*.o}} {{[^ ]*.o}}
// SDL-NEW-WRAPPER-SAME: --target=spirv64-unknown-chipstar
// SDL-NEW-WRAPPER-SAME: {{[^ ]*.o}}
// SDL-NEW-WRAPPER-SAME: --hip-path=[[HIP_PATH]]

// SDL: "{{.*}}opt"
Expand Down
41 changes: 41 additions & 0 deletions clang/test/Driver/hipspv-no-spirv-backend.hip
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Coverage for HIPSPV behavior that must not depend on whether the in-tree
// SPIR-V backend was built. Deliberately not guarded by the
// spirv-registered-target feature, so builders configured without the SPIR-V
// target (e.g. X86-only bots) exercise it too.

// UNSUPPORTED: system-windows

// The device compilation job layout is a property of the driver, not of the
// registered targets: an RDC device compile stays a single -cc1 invocation
// that emits the object directly, rather than being split into a separate
// -emit-llvm-bc job plus a backend job.

// RUN: %clang -### -x hip --target=x86_64-linux-gnu --offload=spirv64 \
// RUN: --no-offload-new-driver -fgpu-rdc --hip-path=%S/Inputs/hipspv \
// RUN: -nohipwrapperinc %S/Inputs/hip_multiple_inputs/a.cu \
// RUN: 2>&1 | FileCheck --check-prefix=RDC-JOB %s

// RDC-JOB: "-cc1" "-triple" "spirv64"
// RDC-JOB-SAME: "-aux-triple" "x86_64-unknown-linux-gnu"
// RDC-JOB-SAME: "-emit-obj"
// RDC-JOB-SAME: "-fcuda-is-device"

// -fno-integrated-objemitter selects the external llvm-spirv translator, and
// it is accepted in either configuration.

// RUN: touch %t.o
// RUN: %clang -### --no-default-config -o %t.img \
// RUN: --target=spirv64-unknown-chipstar %t.o --hip-path=%S/Inputs/hipspv \
// RUN: -fno-integrated-objemitter \
// RUN: 2>&1 | FileCheck --check-prefix=XTOR -DHIP_PATH=%S/Inputs/hipspv %s

// XTOR: {{".*llvm-link"}}
// XTOR-SAME: "-o" [[LINK_BC:".*bc"]] "{{[^ ]*.o}}"

// XTOR: {{".*opt"}} [[LINK_BC]] "-load-pass-plugin"
// XTOR-SAME: "[[HIP_PATH]]/lib/libLLVMHipSpvPasses.so"
// XTOR-SAME: "-passes=hip-post-link-passes" "-o" [[LOWER_BC:".*bc"]]

// XTOR: {{".*llvm-spirv.*"}} "--spirv-max-version=1.2"
// XTOR-SAME: [[LOWER_BC]] "-o" "{{.*img}}"
// XTOR-NOT: "-cc1"
22 changes: 13 additions & 9 deletions clang/test/Driver/hipspv-pass-plugin.hip
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// REQUIRES: spirv-registered-target
// UNSUPPORTED: system-windows

// RUN: %clang -### -target x86_64-linux-gnu --offload=spirv64 \
Expand All @@ -16,23 +17,24 @@
// RUN: --no-offload-new-driver -nogpuinc -nogpulib %s \
// RUN: 2>&1 | FileCheck --check-prefixes=ALL,NO-PLUGIN %s

// Run commands for the new offload driver:
// Run commands for the new offload driver (chipStar uses in-tree SPIR-V
// backend instead of llvm-spirv):

// RUN: touch %t.dummy.o
// RUN: %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: env "PATH=" %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: %t.dummy.o --hip-path=%S/Inputs/hipspv \
// RUN: 2>&1 | FileCheck %s --check-prefixes=ALL,FROM-HIP-PATH
// RUN: 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,FROM-HIP-PATH

// RUN: %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: env "PATH=" %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: %t.dummy.o --hipspv-pass-plugin=%S/Inputs/pass-plugin.so \
// RUN: 2>&1 | FileCheck %s --check-prefixes=ALL,FROM-OPTION
// RUN: 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,FROM-OPTION

// RUN: not %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: not env "PATH=" %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: %t.dummy.o --hipspv-pass-plugin=foo.so \
// RUN: 2>&1 | FileCheck %s --check-prefixes=ALL,FROM-OPTION-INVALID
// RUN: 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,FROM-OPTION-INVALID

// RUN: %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: %t.dummy.o 2>&1 | FileCheck %s --check-prefixes=ALL,NO-PLUGIN
// RUN: env "PATH=" %clang -### --no-default-config -o /dev/null --target=spirv64-unknown-chipstar \
// RUN: %t.dummy.o 2>&1 | FileCheck %s --check-prefixes=CHIPSTAR,NO-PLUGIN

// FROM-HIP-PATH: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
// FROM-HIP-PATH-SAME: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
Expand All @@ -42,3 +44,5 @@
// NO-PLUGIN-NOT: {{".*opt"}} {{".*.bc"}} "-load-pass-plugin"
// NO-PLUGIN-NOT: {{".*/Inputs/hipspv/lib/libLLVMHipSpvPasses.so"}}
// ALL: {{".*llvm-spirv[^ ]*"}}
// CHIPSTAR: {{".*clang.*"}} "-cc1"
// CHIPSTAR-SAME: "-emit-obj"
Loading
Loading