Skip to content
Open
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
10 changes: 10 additions & 0 deletions clang/include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -1421,6 +1421,16 @@ def fhip_emit_relocatable : Flag<["-"], "fhip-emit-relocatable">,
HelpText<"Compile HIP source to relocatable">;
def fno_hip_emit_relocatable : Flag<["-"], "fno-hip-emit-relocatable">,
HelpText<"Do not override toolchain to compile HIP source to relocatable">;
def use_experimental_spirv_backend
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a defm helper for no variants right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ty. If we don't require a member variable, then defm does not seem to be the right option (see how gpu_bundle_output and no_gpu_bundle_output are defined).

: Flag<["-"], "use-experimental-spirv-backend">,
Group<hip_Group>,
Flags<[HelpHidden]>,
HelpText<"Use experimental SPIRV backend for compilation ">;
def no_use_experimental_spirv_backend
: Flag<["-"], "no-use-experimental-spirv-backend">,
Group<hip_Group>,
Flags<[HelpHidden]>,
HelpText<"Do not use experimental SPIRV backend for compilation ">;
}

// Clang specific/exclusive options for OpenACC.
Expand Down
1 change: 1 addition & 0 deletions clang/include/clang/Driver/Types.def
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ TYPE("c++-module-cpp-output", PP_CXXModule, INVALID, "iim", phases
TYPE("ada", Ada, INVALID, nullptr, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
TYPE("assembler", PP_Asm, INVALID, "s", phases::Assemble, phases::Link)
TYPE("assembler-with-cpp", Asm, PP_Asm, "S", phases::Preprocess, phases::Assemble, phases::Link)
TYPE("spv", SPV, INVALID, "spv", phases::Backend)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SPIR-V in this context is just the assembler face as far as I'm aware, I think there's some magic setting that splits it if you need to. That's what CUDA does w/ its PTX target. I'd imagine this would imply clang consuming SPIR-V to give you back an ELF or LLVM-IR.


// Note: The `phases::Preprocess` phase is added to ".i" (i.e. Fortran
// pre-processed) files. The reason is that the pre-processor "phase" has to be
Expand Down
40 changes: 28 additions & 12 deletions clang/lib/Driver/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3672,29 +3672,45 @@ class OffloadingActionBuilder final {
// compiler phases, including backend and assemble phases.
ActionList AL;
Action *BackendAction = nullptr;
bool AssembleAndLink = true;
if (ToolChains.front()->getTriple().isSPIRV() ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SPIR-V here should be similar to what CUDA does, I would just expect to skip this step entirely. I'm fairly certain the normal pipeline works for SPIR-V since the standalone clang target does what I expect. The linker is probably the only bit you'd skip, the handling there is a little weird since CUDA overloads it to mean fatbinary while HIP uses the offload bundler (for now).

Copy link
Contributor Author

@mgcarrasco mgcarrasco Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jhuber6 thanks for the feedback! I'm not sure if I follow what's the problem or suggestion. What step(s) and for what scenario(s) can be skipped? If so, where that should/would be handled?

A backend job was always created independently of SPIRV prior to the PR. Now it has been updated so it does using the SPIRV backend directly if needed. The assembler and linker steps are now skipped for SPIRV when using the backend. When using the translator, we still have to maintain the previous phases.

(ToolChains.front()->getTriple().isAMDGCN() &&
GpuArchList[I] == StringRef("amdgcnspirv"))) {
// Emit LLVM bitcode for SPIR-V targets. SPIR-V device tool chain
// (HIPSPVToolChain or HIPAMDToolChain) runs post-link LLVM IR
// passes.
types::ID Output = Args.hasArg(options::OPT_S)

bool UseSPIRVBackend =
Args.hasFlag(options::OPT_use_experimental_spirv_backend,
options::OPT_no_use_experimental_spirv_backend,
/*Default=*/false);

types::ID Output = UseSPIRVBackend ? types::TY_SPV
: Args.hasArg(options::OPT_S)
? types::TY_LLVM_IR
: types::TY_LLVM_BC;

BackendAction =
C.MakeAction<BackendJobAction>(CudaDeviceActions[I], Output);

if (UseSPIRVBackend) {
AssembleAndLink = false;
}

} else
BackendAction = C.getDriver().ConstructPhaseAction(
C, Args, phases::Backend, CudaDeviceActions[I],
AssociatedOffloadKind);
auto AssembleAction = C.getDriver().ConstructPhaseAction(
C, Args, phases::Assemble, BackendAction,
AssociatedOffloadKind);
AL.push_back(AssembleAction);
// Create a link action to link device IR with device library
// and generate ISA.
CudaDeviceActions[I] =
C.MakeAction<LinkJobAction>(AL, types::TY_Image);

if (AssembleAndLink) {
auto AssembleAction = C.getDriver().ConstructPhaseAction(
C, Args, phases::Assemble, BackendAction,
AssociatedOffloadKind);
AL.push_back(AssembleAction);
// Create a link action to link device IR with device library
// and generate ISA.
CudaDeviceActions[I] =
C.MakeAction<LinkJobAction>(AL, types::TY_Image);
} else {
CudaDeviceActions[I] = BackendAction;
}
}

// OffloadingActionBuilder propagates device arch until an offload
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5197,6 +5197,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
rewriteKind = RK_Fragile;
} else if (JA.getType() == types::TY_CIR) {
CmdArgs.push_back("-emit-cir");
} else if (JA.getType() == types::TY_SPV) {
CmdArgs.push_back("-emit-obj");
} else {
assert(JA.getType() == types::TY_PP_Asm && "Unexpected output type!");
}
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/Driver/ToolChains/HIPAMD.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ void AMDGCN::Linker::constructLinkAndEmitSpirvCommand(

constructLlvmLinkCommand(C, JA, Inputs, LinkedBCFile, Args);

// Emit SPIR-V binary.
// Use the SPIRV translator for code gen.
llvm::opt::ArgStringList TrArgs{
"--spirv-max-version=1.6",
"--spirv-ext=+all",
Expand Down
4 changes: 4 additions & 0 deletions clang/test/Driver/hip-spirv-backend-bindings.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// RUN: %clang -x hip %s --cuda-device-only --offload-arch=amdgcnspirv -use-experimental-spirv-backend -nogpuinc -nogpulib -ccc-print-bindings 2>&1 | FileCheck %s
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs a test for the new driver, because if all goes well I'm going to completely delete the old offload driver next release.


// CHECK: # "spirv64-amd-amdhsa" - "clang", inputs: ["{{.*}}.c"], output: "[[SPV_FILE:.*.spv]]"
// CHECK: # "spirv64-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[SPV_FILE]]"], output: "{{.*.hipfb}}"
12 changes: 12 additions & 0 deletions clang/test/Driver/hip-spirv-backend-opt.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// This test case validates the behavior of -use-experimental-spirv-backend

// Test that -use-experimental-spirv-backend calls clang -cc1 with the SPIRV triple.
// RUN: %clang -x hip %s --cuda-device-only --offload-arch=amdgcnspirv -use-experimental-spirv-backend -nogpuinc -nogpulib -### 2>&1 | FileCheck %s --check-prefix=CHECK-SPIRV-BACKEND
// CHECK-SPIRV-BACKEND: "{{.*}}clang{{.*}}" "-cc1" "{{.*-triple}}" "{{spirv64-amd-amdhsa}}"

// Test that -no-use-experimental-spirv-backend calls the SPIRV translator
// RUN: %clang -x hip %s --cuda-device-only --offload-arch=amdgcnspirv -no-use-experimental-spirv-backend -nogpuinc -nogpulib -### 2>&1 | FileCheck %s --check-prefix=CHECK-SPIRV-TRANSLATOR
// CHECK-SPIRV-TRANSLATOR: "{{.*llvm-spirv.*}}" "{{--spirv-max-version=[0-9]+\.[0-9]}}"

// Test that by default we use the translator
// RUN: %clang -x hip %s --cuda-device-only --offload-arch=amdgcnspirv -nogpuinc -nogpulib -### 2>&1 | FileCheck %s --check-prefix=CHECK-SPIRV-TRANSLATOR
9 changes: 9 additions & 0 deletions clang/test/Driver/hip-spirv-backend-phases.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// RUN: %clang -x hip %s --cuda-device-only --offload-arch=amdgcnspirv -use-experimental-spirv-backend -nogpuinc -nogpulib -ccc-print-phases 2>&1 | FileCheck %s

// CHECK: [[P0:[0-9]+]]: input, "{{.*}}.c", hip, (device-hip, amdgcnspirv)
// CHECK: [[P1:[0-9]+]]: preprocessor, {[[P0]]}, hip-cpp-output, (device-hip, amdgcnspirv)
// CHECK: [[P2:[0-9]+]]: compiler, {[[P1]]}, ir, (device-hip, amdgcnspirv)
// CHECK: [[P3:[0-9]+]]: backend, {[[P2]]}, spv, (device-hip, amdgcnspirv)
// CHECK: [[P4:[0-9]+]]: offload, "device-hip (spirv64-amd-amdhsa:amdgcnspirv)" {[[P3]]}, spv
// CHECK: [[P5:[0-9]+]]: linker, {[[P4]]}, hip-fatbin, (device-hip, )
// CHECK: [[P6:[0-9]+]]: offload, "device-hip (spirv64-amd-amdhsa:)" {[[P5]]}, hip-fatbin