[clang] Add spirv-val compilation step when targeting SPIR-V (PR #188150)
Joshua Batista via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 13 13:51:15 PDT 2026
https://github.com/bob80905 updated https://github.com/llvm/llvm-project/pull/188150
>From 26229c0a9b848bbbd02e42b644b465f4c4ae224b Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 23 Mar 2026 16:31:43 -0700
Subject: [PATCH 1/6] first attempt
---
.../clang/Basic/DiagnosticDriverKinds.td | 3 +
clang/include/clang/Basic/DiagnosticGroups.td | 3 +
clang/include/clang/Options/Options.td | 2 +
clang/lib/Driver/Driver.cpp | 38 +++++++++---
clang/lib/Driver/ToolChains/HLSL.cpp | 58 ++++++++++++++++---
clang/lib/Driver/ToolChains/HLSL.h | 17 +++++-
clang/test/Driver/dxc_spirv-val_path.hlsl | 26 +++++++++
7 files changed, 131 insertions(+), 16 deletions(-)
create mode 100644 clang/test/Driver/dxc_spirv-val_path.hlsl
diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index ed6a9107002af..89486026d9b48 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -873,6 +873,9 @@ def err_drv_hlsl_bad_shader_unsupported : Error<
def warn_drv_dxc_missing_dxv : Warning<
"dxv not found; resulting DXIL will not be validated or signed for use in "
"release environment">, InGroup<DXILValidation>;
+def warn_drv_dxc_missing_spirv_val : Warning<
+ "spirv-val not found; resulting SPIR-V will not be validated">,
+ InGroup<SPIRVValidation>;
def err_drv_invalid_range_dxil_validator_version : Error<
"invalid validator version : %0; validator version must be less than or "
diff --git a/clang/include/clang/Basic/DiagnosticGroups.td b/clang/include/clang/Basic/DiagnosticGroups.td
index e440c9d2fb982..11fe963f8066a 100644
--- a/clang/include/clang/Basic/DiagnosticGroups.td
+++ b/clang/include/clang/Basic/DiagnosticGroups.td
@@ -1875,6 +1875,9 @@ def HLSLExplicitBinding : DiagGroup<"hlsl-explicit-binding">;
// Warnings for DXIL validation
def DXILValidation : DiagGroup<"dxil-validation">;
+// Warnings for SPIRV validation
+def SPIRVValidation : DiagGroup<"spirv-validation">;
+
// Warning for HLSL API availability
def HLSLAvailability : DiagGroup<"hlsl-availability">;
diff --git a/clang/include/clang/Options/Options.td b/clang/include/clang/Options/Options.td
index 8b0c701521728..9b7909b1a5d60 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -9976,6 +9976,8 @@ def dxc_hlsl_version : Option<["/", "-"], "HV", KIND_JOINED_OR_SEPARATE>,
Values<"2016, 2017, 2018, 2021, 202x, 202y">;
def dxc_validator_path_EQ : Joined<["--"], "dxv-path=">, Group<dxc_Group>,
HelpText<"DXIL validator installation path">;
+def spirv_validator_path_EQ : Joined<["--"], "spirv-val-path=">, Group<dxc_Group>,
+ HelpText<"SPIRV validator installation path">;
def dxc_disable_validation : DXCFlag<"Vd">,
HelpText<"Disable validation">;
def dxc_gis : DXCFlag<"Gis">,
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 3e2553686d87f..70dd8285f346a 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4739,6 +4739,18 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
}
}
+ if (C.getDefaultToolChain().getTriple().isSPIRV()) {
+ const auto &TC =
+ static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain());
+
+ // Call spirv-val for SPIR-V when -Vd not in Args.
+ if (TC.requiresValidation(Args)) {
+ Action *LastAction = Actions.back();
+ Actions.push_back(
+ C.MakeAction<BinaryAnalyzeJobAction>(LastAction, types::TY_Object));
+ }
+ }
+
// Claim ignored clang-cl options.
Args.ClaimAllArgs(options::OPT_cl_ignored_Group);
}
@@ -5381,11 +5393,9 @@ void Driver::BuildJobs(Compilation &C) const {
unsigned NumOutputs = 0;
unsigned NumIfsOutputs = 0;
for (const Action *A : C.getActions()) {
- // The actions below do not increase the number of outputs, when operating
- // on DX containers.
- if (A->getType() == types::TY_DX_CONTAINER &&
- (A->getKind() == clang::driver::Action::BinaryAnalyzeJobClass ||
- A->getKind() == clang::driver::Action::BinaryTranslatorJobClass))
+ // The actions below do not increase the number of outputs.
+ if (A->getKind() == clang::driver::Action::BinaryAnalyzeJobClass ||
+ A->getKind() == clang::driver::Action::BinaryTranslatorJobClass)
continue;
if (A->getType() != types::TY_Nothing &&
@@ -6434,10 +6444,22 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA,
const char *Suffix = types::getTypeTempSuffix(JA.getType(), true);
return CreateTempFile(C, Split.first, Suffix, false);
}
- // We don't have SPIRV-val integrated (yet), so for now we can assume this
- // is the final output.
+
assert(C.getDefaultToolChain().getTriple().isSPIRV());
- return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA);
+ const auto &TC =
+ static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain());
+
+ // If this is the last job in the compilation for this input, and Fo is
+ // non-empty, we can return the Fo file (the final output)
+ if (TC.isLastJob(C.getArgs(), JA.getKind()) && !FoValue.empty())
+ return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA);
+
+ // Otherwise, create a temporary file for validation (like DXIL creates
+ // temp files).
+ StringRef Name = llvm::sys::path::filename(BaseInput);
+ std::pair<StringRef, StringRef> Split = Name.split('.');
+ const char *Suffix = types::getTypeTempSuffix(JA.getType(), true);
+ return CreateTempFile(C, Split.first, Suffix, false);
}
// Is this the assembly listing for /FA?
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index 1070f0ef6ee9e..be0e2ccdc911b 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -315,6 +315,23 @@ void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
Exec, CmdArgs, Inputs, Input));
}
+void tools::hlsl::SPIRV_Validator::ConstructJob(
+ Compilation &C, const JobAction &JA, const InputInfo &Output,
+ const InputInfoList &Inputs, const ArgList &Args,
+ const char *LinkingOutput) const {
+ std::string SPIRVValPath = getToolChain().GetProgramPath("spirv-val");
+ assert(SPIRVValPath != "spirv-val" && "cannot find spirv-val");
+
+ ArgStringList CmdArgs;
+ assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
+ const InputInfo &Input = Inputs[0];
+ CmdArgs.push_back(Input.getFilename());
+
+ const char *Exec = Args.MakeArgString(SPIRVValPath);
+ C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+ Exec, CmdArgs, Inputs, Input));
+}
+
void tools::hlsl::MetalConverter::ConstructJob(
Compilation &C, const JobAction &JA, const InputInfo &Output,
const InputInfoList &Inputs, const ArgList &Args,
@@ -384,12 +401,20 @@ HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
if (Args.hasArg(options::OPT_dxc_validator_path_EQ))
getProgramPaths().push_back(
Args.getLastArgValue(options::OPT_dxc_validator_path_EQ).str());
+ if (Args.hasArg(options::OPT_spirv_validator_path_EQ))
+ getProgramPaths().push_back(
+ Args.getLastArgValue(options::OPT_spirv_validator_path_EQ).str());
}
Tool *clang::driver::toolchains::HLSLToolChain::getTool(
Action::ActionClass AC) const {
switch (AC) {
case Action::BinaryAnalyzeJobClass:
+ if (getTriple().isSPIRV()) {
+ if (!SPIRVValidator)
+ SPIRVValidator.reset(new tools::hlsl::SPIRV_Validator(*this));
+ return SPIRVValidator.get();
+ }
if (!Validator)
Validator.reset(new tools::hlsl::Validator(*this));
return Validator.get();
@@ -574,18 +599,31 @@ HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
return DAL;
}
-bool HLSLToolChain::requiresValidation(DerivedArgList &Args) const {
- if (!Args.hasArg(options::OPT_dxc_Fo))
+bool HLSLToolChain::requiresValidation(DerivedArgList &Args,
+ bool Diagnose) const {
+ bool HasFo = Args.hasArg(options::OPT_dxc_Fo);
+ bool DisableValidation =
+ Args.getLastArg(options::OPT_dxc_disable_validation) != nullptr;
+
+ if (DisableValidation || !HasFo)
return false;
- if (Args.getLastArg(options::OPT_dxc_disable_validation))
+ if (getArch() != llvm::Triple::spirv) {
+ std::string DxvPath = GetProgramPath("dxv");
+ if (DxvPath != "dxv")
+ return true;
+
+ if (Diagnose)
+ getDriver().Diag(diag::warn_drv_dxc_missing_dxv);
return false;
+ }
- std::string DxvPath = GetProgramPath("dxv");
- if (DxvPath != "dxv")
+ std::string SpirvValPath = GetProgramPath("spirv-val");
+ if (SpirvValPath != "spirv-val")
return true;
- getDriver().Diag(diag::warn_drv_dxc_missing_dxv);
+ if (Diagnose)
+ getDriver().Diag(diag::warn_drv_dxc_missing_spirv_val);
return false;
}
@@ -604,8 +642,14 @@ bool HLSLToolChain::isLastJob(DerivedArgList &Args,
// Note: we check in the reverse order of execution
if (requiresBinaryTranslation(Args))
return AC == Action::Action::BinaryTranslatorJobClass;
- if (requiresValidation(Args))
+ // For SPIR-V, spirv-val is a pure validator that doesn't produce output
+ // files, so the compile step is the output-producing step. For DXIL, dxv
+ // validates and signs, producing the final output.
+ if (requiresValidation(Args, /*Diagnose=*/false)) {
+ if (getTriple().isSPIRV())
+ return AC != Action::Action::BinaryAnalyzeJobClass;
return AC == Action::Action::BinaryAnalyzeJobClass;
+ }
if (requiresObjcopy(Args))
return AC == Action::Action::ObjcopyJobClass;
diff --git a/clang/lib/Driver/ToolChains/HLSL.h b/clang/lib/Driver/ToolChains/HLSL.h
index 5bf385e13e962..2bc592e44f84d 100644
--- a/clang/lib/Driver/ToolChains/HLSL.h
+++ b/clang/lib/Driver/ToolChains/HLSL.h
@@ -30,6 +30,19 @@ class LLVM_LIBRARY_VISIBILITY Validator : public Tool {
const char *LinkingOutput) const override;
};
+class LLVM_LIBRARY_VISIBILITY SPIRV_Validator : public Tool {
+public:
+ SPIRV_Validator(const ToolChain &TC)
+ : Tool("hlsl::SPIRV_Validator", "spirv-val", TC) {}
+
+ bool hasIntegratedCPP() const override { return false; }
+
+ void ConstructJob(Compilation &C, const JobAction &JA,
+ const InputInfo &Output, const InputInfoList &Inputs,
+ const llvm::opt::ArgList &TCArgs,
+ const char *LinkingOutput) const override;
+};
+
class LLVM_LIBRARY_VISIBILITY MetalConverter : public Tool {
public:
MetalConverter(const ToolChain &TC)
@@ -77,7 +90,8 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch,
Action::OffloadKind DeviceOffloadKind) const override;
static std::optional<std::string> parseTargetProfile(StringRef TargetProfile);
- bool requiresValidation(llvm::opt::DerivedArgList &Args) const;
+ bool requiresValidation(llvm::opt::DerivedArgList &Args,
+ bool Diagnose = true) const;
bool requiresBinaryTranslation(llvm::opt::DerivedArgList &Args) const;
bool requiresObjcopy(llvm::opt::DerivedArgList &Args) const;
@@ -95,6 +109,7 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
private:
mutable std::unique_ptr<tools::hlsl::Validator> Validator;
+ mutable std::unique_ptr<tools::hlsl::SPIRV_Validator> SPIRVValidator;
mutable std::unique_ptr<tools::hlsl::MetalConverter> MetalConverter;
mutable std::unique_ptr<tools::hlsl::LLVMObjcopy> LLVMObjcopy;
};
diff --git a/clang/test/Driver/dxc_spirv-val_path.hlsl b/clang/test/Driver/dxc_spirv-val_path.hlsl
new file mode 100644
index 0000000000000..1ae21ca356510
--- /dev/null
+++ b/clang/test/Driver/dxc_spirv-val_path.hlsl
@@ -0,0 +1,26 @@
+// RUN: mkdir -p %t.dir
+// RUN: env PATH="" %clang_dxc -spirv -I test -Tlib_6_3 -Fo %t.dir/a.spv -### %s 2>&1 | FileCheck %s
+
+// Make sure report warning, and only once.
+// CHECK:spirv-val not found
+// CHECK-NOT:spirv-val not found
+
+// RUN: echo "spirv-val" > %t.dir/spirv-val && chmod 754 %t.dir/spirv-val && %clang_dxc -spirv --spirv-val-path=%t.dir %s -Tlib_6_3 -Fo %t.dir/a.spv -### 2>&1 | FileCheck %s --check-prefix=SPIRV_VAL_PATH
+// SPIRV_VAL_PATH:spirv-val{{(.exe)?}}" "{{.*}}.spv"
+
+// RUN: %clang_dxc -spirv -I test -Vd -Tlib_6_3 -### %s 2>&1 | FileCheck %s --check-prefix=VD
+// VD:"-cc1"{{.*}}"-triple" "spirv1.6-unknown-vulkan1.3-library"
+// VD-NOT:spirv-val not found
+
+// RUN: %clang_dxc -spirv -Tlib_6_3 -ccc-print-bindings --spirv-val-path=%t.dir -Fo %t.spv %s 2>&1 | FileCheck %s --check-prefix=BINDINGS
+// BINDINGS: "spirv1.6-unknown-vulkan1.3-library" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[spv:.+]].spv"
+// BINDINGS-NEXT: "spirv1.6-unknown-vulkan1.3-library" - "hlsl::SPIRV_Validator", inputs: ["[[spv]].spv"], output: "{{.+}}.obj"
+
+// RUN: %clang_dxc -spirv -Tlib_6_3 -ccc-print-phases --spirv-val-path=%t.dir -Fo %t.spv %s 2>&1 | FileCheck %s --check-prefix=PHASES
+
+// PHASES: 0: input, "[[INPUT:.+]]", hlsl
+// PHASES-NEXT: 1: preprocessor, {0}, c++-cpp-output
+// PHASES-NEXT: 2: compiler, {1}, ir
+// PHASES-NEXT: 3: backend, {2}, assembler
+// PHASES-NEXT: 4: assembler, {3}, object
+// PHASES-NEXT: 5: binary-analyzer, {4}, object
\ No newline at end of file
>From b084fa7a5172e2502324f9df2723e22dad071e3a Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 27 Mar 2026 10:39:06 -0700
Subject: [PATCH 2/6] address Finn and Steven
---
clang/lib/Driver/ToolChains/HLSL.cpp | 9 ++++++++-
clang/test/Driver/dxc_spirv-val_path.hlsl | 13 ++++++++-----
2 files changed, 16 insertions(+), 6 deletions(-)
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index be0e2ccdc911b..634e22bcf3a22 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -325,6 +325,13 @@ void tools::hlsl::SPIRV_Validator::ConstructJob(
ArgStringList CmdArgs;
assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
const InputInfo &Input = Inputs[0];
+
+ const llvm::Triple &T = getToolChain().getTriple();
+ CmdArgs.push_back("--target-env");
+ CmdArgs.push_back(Args.MakeArgString(T.getOSName()));
+
+ CmdArgs.push_back("--scalar-block-layout");
+
CmdArgs.push_back(Input.getFilename());
const char *Exec = Args.MakeArgString(SPIRVValPath);
@@ -608,7 +615,7 @@ bool HLSLToolChain::requiresValidation(DerivedArgList &Args,
if (DisableValidation || !HasFo)
return false;
- if (getArch() != llvm::Triple::spirv) {
+ if (getArch() == llvm::Triple::dxil) {
std::string DxvPath = GetProgramPath("dxv");
if (DxvPath != "dxv")
return true;
diff --git a/clang/test/Driver/dxc_spirv-val_path.hlsl b/clang/test/Driver/dxc_spirv-val_path.hlsl
index 1ae21ca356510..4f39fa37c69e5 100644
--- a/clang/test/Driver/dxc_spirv-val_path.hlsl
+++ b/clang/test/Driver/dxc_spirv-val_path.hlsl
@@ -2,15 +2,18 @@
// RUN: env PATH="" %clang_dxc -spirv -I test -Tlib_6_3 -Fo %t.dir/a.spv -### %s 2>&1 | FileCheck %s
// Make sure report warning, and only once.
-// CHECK:spirv-val not found
-// CHECK-NOT:spirv-val not found
+// CHECK:spirv-val not found; resulting SPIR-V will not be validated
+// CHECK-NOT:spirv-val not found; resulting SPIR-V will not be validated
// RUN: echo "spirv-val" > %t.dir/spirv-val && chmod 754 %t.dir/spirv-val && %clang_dxc -spirv --spirv-val-path=%t.dir %s -Tlib_6_3 -Fo %t.dir/a.spv -### 2>&1 | FileCheck %s --check-prefix=SPIRV_VAL_PATH
-// SPIRV_VAL_PATH:spirv-val{{(.exe)?}}" "{{.*}}.spv"
+// SPIRV_VAL_PATH:spirv-val{{(.exe)?}}" "--target-env" "vulkan1.3" "--scalar-block-layout" "{{.*}}.spv"
+
+// RUN: echo "spirv-val" > %t.dir/spirv-val && chmod 754 %t.dir/spirv-val && %clang_dxc -spirv --spirv-val-path=%t.dir %s -Tlib_6_3 -fspv-target-env=vulkan1.2 -Fo %t.dir/a.spv -### 2>&1 | FileCheck %s --check-prefix=SPIRV_VAL_VK12
+// SPIRV_VAL_VK12:spirv-val{{(.exe)?}}" "--target-env" "vulkan1.2" "--scalar-block-layout" "{{.*}}.spv"
// RUN: %clang_dxc -spirv -I test -Vd -Tlib_6_3 -### %s 2>&1 | FileCheck %s --check-prefix=VD
// VD:"-cc1"{{.*}}"-triple" "spirv1.6-unknown-vulkan1.3-library"
-// VD-NOT:spirv-val not found
+// VD-NOT:spirv-val not found; resulting SPIR-V will not be validated
// RUN: %clang_dxc -spirv -Tlib_6_3 -ccc-print-bindings --spirv-val-path=%t.dir -Fo %t.spv %s 2>&1 | FileCheck %s --check-prefix=BINDINGS
// BINDINGS: "spirv1.6-unknown-vulkan1.3-library" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[spv:.+]].spv"
@@ -23,4 +26,4 @@
// PHASES-NEXT: 2: compiler, {1}, ir
// PHASES-NEXT: 3: backend, {2}, assembler
// PHASES-NEXT: 4: assembler, {3}, object
-// PHASES-NEXT: 5: binary-analyzer, {4}, object
\ No newline at end of file
+// PHASES-NEXT: 5: binary-analyzer, {4}, object
>From 2dcfc33aed1d4fbc5a542fa952bbb28b2dca0c65 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 3 Apr 2026 16:11:33 -0700
Subject: [PATCH 3/6] address Justin
---
clang/lib/Driver/Driver.cpp | 5 +-
clang/lib/Driver/ToolChains/HLSL.cpp | 80 ++++++++++-------------
clang/lib/Driver/ToolChains/HLSL.h | 29 +++-----
clang/test/Driver/dxc_spirv-val_path.hlsl | 2 +-
4 files changed, 49 insertions(+), 67 deletions(-)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 70dd8285f346a..225af09c60552 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4746,8 +4746,9 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
// Call spirv-val for SPIR-V when -Vd not in Args.
if (TC.requiresValidation(Args)) {
Action *LastAction = Actions.back();
- Actions.push_back(
- C.MakeAction<BinaryAnalyzeJobAction>(LastAction, types::TY_Object));
+ if (LastAction->getType() == types::TY_Object)
+ Actions.push_back(
+ C.MakeAction<BinaryAnalyzeJobAction>(LastAction, types::TY_Object));
}
}
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index 634e22bcf3a22..d1f41c8f127cb 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -300,43 +300,35 @@ void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
- std::string DxvPath = getToolChain().GetProgramPath("dxv");
- assert(DxvPath != "dxv" && "cannot find dxv");
-
- ArgStringList CmdArgs;
- assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
- const InputInfo &Input = Inputs[0];
- CmdArgs.push_back(Input.getFilename());
- CmdArgs.push_back("-o");
- CmdArgs.push_back(Output.getFilename());
-
- const char *Exec = Args.MakeArgString(DxvPath);
- C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
- Exec, CmdArgs, Inputs, Input));
-}
-
-void tools::hlsl::SPIRV_Validator::ConstructJob(
- Compilation &C, const JobAction &JA, const InputInfo &Output,
- const InputInfoList &Inputs, const ArgList &Args,
- const char *LinkingOutput) const {
- std::string SPIRVValPath = getToolChain().GetProgramPath("spirv-val");
- assert(SPIRVValPath != "spirv-val" && "cannot find spirv-val");
-
ArgStringList CmdArgs;
assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
const InputInfo &Input = Inputs[0];
const llvm::Triple &T = getToolChain().getTriple();
- CmdArgs.push_back("--target-env");
- CmdArgs.push_back(Args.MakeArgString(T.getOSName()));
-
- CmdArgs.push_back("--scalar-block-layout");
-
- CmdArgs.push_back(Input.getFilename());
-
- const char *Exec = Args.MakeArgString(SPIRVValPath);
- C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
- Exec, CmdArgs, Inputs, Input));
+ if (T.isSPIRV()) {
+ std::string SPIRVValPath = getToolChain().GetProgramPath("spirv-val");
+ assert(SPIRVValPath != "spirv-val" && "cannot find spirv-val");
+
+ CmdArgs.push_back("--target-env");
+ CmdArgs.push_back(Args.MakeArgString(T.getOSName()));
+ CmdArgs.push_back("--scalar-block-layout");
+ CmdArgs.push_back(Input.getFilename());
+
+ const char *Exec = Args.MakeArgString(SPIRVValPath);
+ C.addCommand(std::make_unique<Command>(
+ JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Input));
+ } else {
+ std::string DxvPath = getToolChain().GetProgramPath("dxv");
+ assert(DxvPath != "dxv" && "cannot find dxv");
+
+ CmdArgs.push_back(Input.getFilename());
+ CmdArgs.push_back("-o");
+ CmdArgs.push_back(Output.getFilename());
+
+ const char *Exec = Args.MakeArgString(DxvPath);
+ C.addCommand(std::make_unique<Command>(
+ JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Input));
+ }
}
void tools::hlsl::MetalConverter::ConstructJob(
@@ -417,11 +409,6 @@ Tool *clang::driver::toolchains::HLSLToolChain::getTool(
Action::ActionClass AC) const {
switch (AC) {
case Action::BinaryAnalyzeJobClass:
- if (getTriple().isSPIRV()) {
- if (!SPIRVValidator)
- SPIRVValidator.reset(new tools::hlsl::SPIRV_Validator(*this));
- return SPIRVValidator.get();
- }
if (!Validator)
Validator.reset(new tools::hlsl::Validator(*this));
return Validator.get();
@@ -625,12 +612,15 @@ bool HLSLToolChain::requiresValidation(DerivedArgList &Args,
return false;
}
- std::string SpirvValPath = GetProgramPath("spirv-val");
- if (SpirvValPath != "spirv-val")
- return true;
+ if (getTriple().isSPIRV()) {
+ std::string SpirvValPath = GetProgramPath("spirv-val");
+ if (SpirvValPath != "spirv-val")
+ return true;
+
+ if (Diagnose)
+ getDriver().Diag(diag::warn_drv_dxc_missing_spirv_val);
+ }
- if (Diagnose)
- getDriver().Diag(diag::warn_drv_dxc_missing_spirv_val);
return false;
}
@@ -650,11 +640,11 @@ bool HLSLToolChain::isLastJob(DerivedArgList &Args,
if (requiresBinaryTranslation(Args))
return AC == Action::Action::BinaryTranslatorJobClass;
// For SPIR-V, spirv-val is a pure validator that doesn't produce output
- // files, so the compile step is the output-producing step. For DXIL, dxv
- // validates and signs, producing the final output.
+ // files, so the compile step is the last output-producing job. For DXIL,
+ // dxv validates and signs, producing the final output.
if (requiresValidation(Args, /*Diagnose=*/false)) {
if (getTriple().isSPIRV())
- return AC != Action::Action::BinaryAnalyzeJobClass;
+ return AC == Action::Action::AssembleJobClass;
return AC == Action::Action::BinaryAnalyzeJobClass;
}
if (requiresObjcopy(Args))
diff --git a/clang/lib/Driver/ToolChains/HLSL.h b/clang/lib/Driver/ToolChains/HLSL.h
index 2bc592e44f84d..ced551a4aa24c 100644
--- a/clang/lib/Driver/ToolChains/HLSL.h
+++ b/clang/lib/Driver/ToolChains/HLSL.h
@@ -20,20 +20,9 @@ namespace tools {
namespace hlsl {
class LLVM_LIBRARY_VISIBILITY Validator : public Tool {
public:
- Validator(const ToolChain &TC) : Tool("hlsl::Validator", "dxv", TC) {}
-
- bool hasIntegratedCPP() const override { return false; }
-
- void ConstructJob(Compilation &C, const JobAction &JA,
- const InputInfo &Output, const InputInfoList &Inputs,
- const llvm::opt::ArgList &TCArgs,
- const char *LinkingOutput) const override;
-};
-
-class LLVM_LIBRARY_VISIBILITY SPIRV_Validator : public Tool {
-public:
- SPIRV_Validator(const ToolChain &TC)
- : Tool("hlsl::SPIRV_Validator", "spirv-val", TC) {}
+ Validator(const ToolChain &TC)
+ : Tool("hlsl::Validator", TC.getTriple().isSPIRV() ? "spirv-val" : "dxv",
+ TC) {}
bool hasIntegratedCPP() const override { return false; }
@@ -95,11 +84,14 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
bool requiresBinaryTranslation(llvm::opt::DerivedArgList &Args) const;
bool requiresObjcopy(llvm::opt::DerivedArgList &Args) const;
- /// If we are targeting DXIL then the last job should output the DXContainer
- /// to the specified output file with /Fo. Otherwise, we will emit to a
- /// temporary file for the next job to use.
+ /// Determines whether the given action class is the last job that produces
+ /// an output file. This is used to decide whether to write to the -Fo
+ /// output path or to a temporary file.
///
- /// Returns true if we should output to the final result file.
+ /// For example, spirv-val is a pure validator that runs after the compile
+ /// step but doesn't produce output, so the compile step is the last
+ /// output-producing job. For DXIL, dxv validates and signs, producing the
+ /// final output.
bool isLastJob(llvm::opt::DerivedArgList &Args, Action::ActionClass AC) const;
// Set default DWARF version to 4 for DXIL uses version 4.
@@ -109,7 +101,6 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
private:
mutable std::unique_ptr<tools::hlsl::Validator> Validator;
- mutable std::unique_ptr<tools::hlsl::SPIRV_Validator> SPIRVValidator;
mutable std::unique_ptr<tools::hlsl::MetalConverter> MetalConverter;
mutable std::unique_ptr<tools::hlsl::LLVMObjcopy> LLVMObjcopy;
};
diff --git a/clang/test/Driver/dxc_spirv-val_path.hlsl b/clang/test/Driver/dxc_spirv-val_path.hlsl
index 4f39fa37c69e5..4a5090fc56863 100644
--- a/clang/test/Driver/dxc_spirv-val_path.hlsl
+++ b/clang/test/Driver/dxc_spirv-val_path.hlsl
@@ -17,7 +17,7 @@
// RUN: %clang_dxc -spirv -Tlib_6_3 -ccc-print-bindings --spirv-val-path=%t.dir -Fo %t.spv %s 2>&1 | FileCheck %s --check-prefix=BINDINGS
// BINDINGS: "spirv1.6-unknown-vulkan1.3-library" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[spv:.+]].spv"
-// BINDINGS-NEXT: "spirv1.6-unknown-vulkan1.3-library" - "hlsl::SPIRV_Validator", inputs: ["[[spv]].spv"], output: "{{.+}}.obj"
+// BINDINGS-NEXT: "spirv1.6-unknown-vulkan1.3-library" - "hlsl::Validator", inputs: ["[[spv]].spv"], output: "{{.+}}.obj"
// RUN: %clang_dxc -spirv -Tlib_6_3 -ccc-print-phases --spirv-val-path=%t.dir -Fo %t.spv %s 2>&1 | FileCheck %s --check-prefix=PHASES
>From 940d8539849fffcf51539dad31a87026cc010f76 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Fri, 3 Apr 2026 16:53:28 -0700
Subject: [PATCH 4/6] account for spirv include tests edge case not found
failure
---
clang/test/Driver/dxc_spirv-val_missing.hlsl | 15 +++++++++++++++
clang/test/Driver/dxc_spirv-val_path.hlsl | 9 ---------
clang/test/lit.cfg.py | 4 ++++
3 files changed, 19 insertions(+), 9 deletions(-)
create mode 100644 clang/test/Driver/dxc_spirv-val_missing.hlsl
diff --git a/clang/test/Driver/dxc_spirv-val_missing.hlsl b/clang/test/Driver/dxc_spirv-val_missing.hlsl
new file mode 100644
index 0000000000000..c284d71203d80
--- /dev/null
+++ b/clang/test/Driver/dxc_spirv-val_missing.hlsl
@@ -0,0 +1,15 @@
+// UNSUPPORTED: spirv-val
+//
+// Verify that a warning is emitted exactly once when spirv-val is not found.
+// This test is unsupported when spirv-val is in the build directory because
+// GetProgramPath finds it via the application directory search, bypassing PATH.
+
+// RUN: env PATH="" %clang_dxc -spirv -I test -Tlib_6_3 -Fo %t.spv -### %s 2>&1 | FileCheck %s
+
+// CHECK: spirv-val not found; resulting SPIR-V will not be validated
+// CHECK-NOT: spirv-val not found; resulting SPIR-V will not be validated
+
+// Also verify -Vd suppresses the warning.
+// RUN: %clang_dxc -spirv -I test -Vd -Tlib_6_3 -### %s 2>&1 | FileCheck %s --check-prefix=VD
+// VD: "-cc1"{{.*}}"-triple" "spirv1.6-unknown-vulkan1.3-library"
+// VD-NOT: spirv-val not found; resulting SPIR-V will not be validated
diff --git a/clang/test/Driver/dxc_spirv-val_path.hlsl b/clang/test/Driver/dxc_spirv-val_path.hlsl
index 4a5090fc56863..1fed1d01d4b18 100644
--- a/clang/test/Driver/dxc_spirv-val_path.hlsl
+++ b/clang/test/Driver/dxc_spirv-val_path.hlsl
@@ -1,9 +1,4 @@
// RUN: mkdir -p %t.dir
-// RUN: env PATH="" %clang_dxc -spirv -I test -Tlib_6_3 -Fo %t.dir/a.spv -### %s 2>&1 | FileCheck %s
-
-// Make sure report warning, and only once.
-// CHECK:spirv-val not found; resulting SPIR-V will not be validated
-// CHECK-NOT:spirv-val not found; resulting SPIR-V will not be validated
// RUN: echo "spirv-val" > %t.dir/spirv-val && chmod 754 %t.dir/spirv-val && %clang_dxc -spirv --spirv-val-path=%t.dir %s -Tlib_6_3 -Fo %t.dir/a.spv -### 2>&1 | FileCheck %s --check-prefix=SPIRV_VAL_PATH
// SPIRV_VAL_PATH:spirv-val{{(.exe)?}}" "--target-env" "vulkan1.3" "--scalar-block-layout" "{{.*}}.spv"
@@ -11,10 +6,6 @@
// RUN: echo "spirv-val" > %t.dir/spirv-val && chmod 754 %t.dir/spirv-val && %clang_dxc -spirv --spirv-val-path=%t.dir %s -Tlib_6_3 -fspv-target-env=vulkan1.2 -Fo %t.dir/a.spv -### 2>&1 | FileCheck %s --check-prefix=SPIRV_VAL_VK12
// SPIRV_VAL_VK12:spirv-val{{(.exe)?}}" "--target-env" "vulkan1.2" "--scalar-block-layout" "{{.*}}.spv"
-// RUN: %clang_dxc -spirv -I test -Vd -Tlib_6_3 -### %s 2>&1 | FileCheck %s --check-prefix=VD
-// VD:"-cc1"{{.*}}"-triple" "spirv1.6-unknown-vulkan1.3-library"
-// VD-NOT:spirv-val not found; resulting SPIR-V will not be validated
-
// RUN: %clang_dxc -spirv -Tlib_6_3 -ccc-print-bindings --spirv-val-path=%t.dir -Fo %t.spv %s 2>&1 | FileCheck %s --check-prefix=BINDINGS
// BINDINGS: "spirv1.6-unknown-vulkan1.3-library" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[spv:.+]].spv"
// BINDINGS-NEXT: "spirv1.6-unknown-vulkan1.3-library" - "hlsl::Validator", inputs: ["[[spv]].spv"], output: "{{.+}}.obj"
diff --git a/clang/test/lit.cfg.py b/clang/test/lit.cfg.py
index 3b0b3092d424e..4de86f555bcb8 100644
--- a/clang/test/lit.cfg.py
+++ b/clang/test/lit.cfg.py
@@ -268,6 +268,10 @@ def have_host_clang_repl_cuda():
if config.clang_enable_cir:
config.available_features.add("cir-support")
+# SPIRV-Tools validator availability (e.g. built with -DLLVM_INCLUDE_SPIRV_TOOLS_TESTS)
+if lit.util.which("spirv-val", config.llvm_tools_dir):
+ config.available_features.add("spirv-val")
+
llvm_config.add_tool_substitutions(tools, tool_dirs)
config.substitutions.append(
>From c1d5c32adf62057e0723763e669bb3cd2d7a8eb4 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Tue, 7 Apr 2026 18:29:35 -0700
Subject: [PATCH 5/6] address Justin
---
clang/lib/Driver/Driver.cpp | 60 ++++++++----------------
clang/lib/Driver/ToolChains/HLSL.cpp | 69 +++++++++++++++-------------
clang/lib/Driver/ToolChains/HLSL.h | 16 +++++--
3 files changed, 68 insertions(+), 77 deletions(-)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 225af09c60552..577b06a31a04d 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4705,7 +4705,8 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
}
}
- if (C.getDefaultToolChain().getTriple().isDXIL()) {
+ if (C.getDefaultToolChain().getTriple().isDXIL() ||
+ C.getDefaultToolChain().getTriple().isSPIRV()) {
const auto &TC =
static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain());
@@ -4719,11 +4720,16 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
C.MakeAction<ObjcopyJobAction>(LastAction, types::TY_Object));
}
- // Call validator for dxil when -Vd not in Args.
- if (TC.requiresValidation(Args)) {
+ // Call validator when -Vd not in Args.
+ auto ValInfo = TC.getValidationInfo(Args);
+ if (ValInfo.NeedsValidation) {
Action *LastAction = Actions.back();
- Actions.push_back(C.MakeAction<BinaryAnalyzeJobAction>(
- LastAction, types::TY_DX_CONTAINER));
+ if (LastAction->getType() == types::TY_Object) {
+ types::ID OutType =
+ ValInfo.ProducesOutput ? types::TY_DX_CONTAINER : types::TY_Object;
+ Actions.push_back(
+ C.MakeAction<BinaryAnalyzeJobAction>(LastAction, OutType));
+ }
}
// Call metal-shaderconverter when targeting metal.
@@ -4739,19 +4745,6 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
}
}
- if (C.getDefaultToolChain().getTriple().isSPIRV()) {
- const auto &TC =
- static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain());
-
- // Call spirv-val for SPIR-V when -Vd not in Args.
- if (TC.requiresValidation(Args)) {
- Action *LastAction = Actions.back();
- if (LastAction->getType() == types::TY_Object)
- Actions.push_back(
- C.MakeAction<BinaryAnalyzeJobAction>(LastAction, types::TY_Object));
- }
- }
-
// Claim ignored clang-cl options.
Args.ClaimAllArgs(options::OPT_cl_ignored_Group);
}
@@ -6430,33 +6423,16 @@ const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA,
C.getArgs().hasArg(options::OPT_dxc_Fo)) ||
JA.getType() == types::TY_DX_CONTAINER) {
StringRef FoValue = C.getArgs().getLastArgValue(options::OPT_dxc_Fo);
- // If we are targeting DXIL and not validating/translating/objcopying, we
- // should set the final result file. Otherwise we should emit to a
- // temporary.
- if (C.getDefaultToolChain().getTriple().isDXIL()) {
- const auto &TC = static_cast<const toolchains::HLSLToolChain &>(
- C.getDefaultToolChain());
- // Fo can be empty here if the validator is running for a compiler flow
- // that is using Fc or just printing disassembly.
- if (TC.isLastJob(C.getArgs(), JA.getKind()) && !FoValue.empty())
- return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA);
- StringRef Name = llvm::sys::path::filename(BaseInput);
- std::pair<StringRef, StringRef> Split = Name.split('.');
- const char *Suffix = types::getTypeTempSuffix(JA.getType(), true);
- return CreateTempFile(C, Split.first, Suffix, false);
- }
-
- assert(C.getDefaultToolChain().getTriple().isSPIRV());
+ assert((C.getDefaultToolChain().getTriple().isDXIL() ||
+ C.getDefaultToolChain().getTriple().isSPIRV()) &&
+ "expected DXIL or SPIR-V triple for HLSL output path");
const auto &TC =
static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain());
-
- // If this is the last job in the compilation for this input, and Fo is
- // non-empty, we can return the Fo file (the final output)
- if (TC.isLastJob(C.getArgs(), JA.getKind()) && !FoValue.empty())
+ // Fo can be empty here if the validator is running for a compiler flow
+ // that is using Fc or just printing disassembly.
+ if (TC.isLastOutputProducingJob(C.getArgs(), JA.getKind()) &&
+ !FoValue.empty())
return C.addResultFile(C.getArgs().MakeArgString(FoValue.str()), &JA);
-
- // Otherwise, create a temporary file for validation (like DXIL creates
- // temp files).
StringRef Name = llvm::sys::path::filename(BaseInput);
std::pair<StringRef, StringRef> Split = Name.split('.');
const char *Suffix = types::getTypeTempSuffix(JA.getType(), true);
diff --git a/clang/lib/Driver/ToolChains/HLSL.cpp b/clang/lib/Driver/ToolChains/HLSL.cpp
index d1f41c8f127cb..834b8acc78734 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -305,30 +305,29 @@ void tools::hlsl::Validator::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Input = Inputs[0];
const llvm::Triple &T = getToolChain().getTriple();
+ std::string ExecPath;
if (T.isSPIRV()) {
- std::string SPIRVValPath = getToolChain().GetProgramPath("spirv-val");
- assert(SPIRVValPath != "spirv-val" && "cannot find spirv-val");
+ ExecPath = getToolChain().GetProgramPath("spirv-val");
+ assert(ExecPath != "spirv-val" && "cannot find spirv-val");
CmdArgs.push_back("--target-env");
CmdArgs.push_back(Args.MakeArgString(T.getOSName()));
CmdArgs.push_back("--scalar-block-layout");
CmdArgs.push_back(Input.getFilename());
-
- const char *Exec = Args.MakeArgString(SPIRVValPath);
- C.addCommand(std::make_unique<Command>(
- JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Input));
- } else {
- std::string DxvPath = getToolChain().GetProgramPath("dxv");
- assert(DxvPath != "dxv" && "cannot find dxv");
+ } else if (T.isDXIL()) {
+ ExecPath = getToolChain().GetProgramPath("dxv");
+ assert(ExecPath != "dxv" && "cannot find dxv");
CmdArgs.push_back(Input.getFilename());
CmdArgs.push_back("-o");
CmdArgs.push_back(Output.getFilename());
-
- const char *Exec = Args.MakeArgString(DxvPath);
- C.addCommand(std::make_unique<Command>(
- JA, *this, ResponseFileSupport::None(), Exec, CmdArgs, Inputs, Input));
+ } else {
+ llvm_unreachable("unexpected triple for HLSL validation");
}
+
+ const char *Exec = Args.MakeArgString(ExecPath);
+ C.addCommand(std::make_unique<Command>(JA, *this, ResponseFileSupport::None(),
+ Exec, CmdArgs, Inputs, Input));
}
void tools::hlsl::MetalConverter::ConstructJob(
@@ -593,35 +592,43 @@ HLSLToolChain::TranslateArgs(const DerivedArgList &Args, StringRef BoundArch,
return DAL;
}
-bool HLSLToolChain::requiresValidation(DerivedArgList &Args,
- bool Diagnose) const {
+HLSLToolChain::ValidationInfo
+HLSLToolChain::getValidationInfo(DerivedArgList &Args, bool Diagnose) const {
+ ValidationInfo Info;
+
bool HasFo = Args.hasArg(options::OPT_dxc_Fo);
bool DisableValidation =
Args.getLastArg(options::OPT_dxc_disable_validation) != nullptr;
if (DisableValidation || !HasFo)
- return false;
+ return Info;
- if (getArch() == llvm::Triple::dxil) {
+ if (getTriple().isDXIL()) {
std::string DxvPath = GetProgramPath("dxv");
- if (DxvPath != "dxv")
- return true;
+ if (DxvPath != "dxv") {
+ Info.NeedsValidation = true;
+ Info.ProducesOutput = true;
+ return Info;
+ }
if (Diagnose)
getDriver().Diag(diag::warn_drv_dxc_missing_dxv);
- return false;
+ return Info;
}
if (getTriple().isSPIRV()) {
std::string SpirvValPath = GetProgramPath("spirv-val");
- if (SpirvValPath != "spirv-val")
- return true;
+ if (SpirvValPath != "spirv-val") {
+ Info.NeedsValidation = true;
+ Info.ProducesOutput = false;
+ return Info;
+ }
if (Diagnose)
getDriver().Diag(diag::warn_drv_dxc_missing_spirv_val);
}
- return false;
+ return Info;
}
bool HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {
@@ -634,18 +641,16 @@ bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const {
Args.hasArg(options::OPT_dxc_Frs) || isRootSignatureTarget(Args));
}
-bool HLSLToolChain::isLastJob(DerivedArgList &Args,
- Action::ActionClass AC) const {
+bool HLSLToolChain::isLastOutputProducingJob(DerivedArgList &Args,
+ Action::ActionClass AC) const {
// Note: we check in the reverse order of execution
if (requiresBinaryTranslation(Args))
return AC == Action::Action::BinaryTranslatorJobClass;
- // For SPIR-V, spirv-val is a pure validator that doesn't produce output
- // files, so the compile step is the last output-producing job. For DXIL,
- // dxv validates and signs, producing the final output.
- if (requiresValidation(Args, /*Diagnose=*/false)) {
- if (getTriple().isSPIRV())
- return AC == Action::Action::AssembleJobClass;
- return AC == Action::Action::BinaryAnalyzeJobClass;
+ auto ValInfo = getValidationInfo(Args, /*Diagnose=*/false);
+ if (ValInfo.NeedsValidation) {
+ if (ValInfo.ProducesOutput)
+ return AC == Action::Action::BinaryAnalyzeJobClass;
+ return AC == Action::Action::AssembleJobClass;
}
if (requiresObjcopy(Args))
return AC == Action::Action::ObjcopyJobClass;
diff --git a/clang/lib/Driver/ToolChains/HLSL.h b/clang/lib/Driver/ToolChains/HLSL.h
index ced551a4aa24c..0806c252c1073 100644
--- a/clang/lib/Driver/ToolChains/HLSL.h
+++ b/clang/lib/Driver/ToolChains/HLSL.h
@@ -79,8 +79,17 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
TranslateArgs(const llvm::opt::DerivedArgList &Args, StringRef BoundArch,
Action::OffloadKind DeviceOffloadKind) const override;
static std::optional<std::string> parseTargetProfile(StringRef TargetProfile);
- bool requiresValidation(llvm::opt::DerivedArgList &Args,
- bool Diagnose = true) const;
+
+ struct ValidationInfo {
+ bool NeedsValidation = false;
+ bool ProducesOutput = false;
+ };
+
+ /// Returns information about whether validation is required and whether the
+ /// validator produces output. When Diagnose is true, emits a warning if the
+ /// required validator executable cannot be found.
+ ValidationInfo getValidationInfo(llvm::opt::DerivedArgList &Args,
+ bool Diagnose = true) const;
bool requiresBinaryTranslation(llvm::opt::DerivedArgList &Args) const;
bool requiresObjcopy(llvm::opt::DerivedArgList &Args) const;
@@ -92,7 +101,8 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public ToolChain {
/// step but doesn't produce output, so the compile step is the last
/// output-producing job. For DXIL, dxv validates and signs, producing the
/// final output.
- bool isLastJob(llvm::opt::DerivedArgList &Args, Action::ActionClass AC) const;
+ bool isLastOutputProducingJob(llvm::opt::DerivedArgList &Args,
+ Action::ActionClass AC) const;
// Set default DWARF version to 4 for DXIL uses version 4.
unsigned GetDefaultDwarfVersion() const override { return 4; }
>From f178e5ac2cf4c44e233b61c859ad400ff6762747 Mon Sep 17 00:00:00 2001
From: Joshua Batista <jbatista at microsoft.com>
Date: Mon, 13 Apr 2026 13:50:47 -0700
Subject: [PATCH 6/6] address Justin's last comment
---
clang/lib/Driver/Driver.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 577b06a31a04d..eb3dd3c09be31 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4704,9 +4704,10 @@ void Driver::BuildActions(Compilation &C, DerivedArgList &Args,
I.second->claim();
}
}
-
- if (C.getDefaultToolChain().getTriple().isDXIL() ||
- C.getDefaultToolChain().getTriple().isSPIRV()) {
+
+ llvm::Triple TargetTriple(C.getDriver().getTargetTriple());
+ if (TargetTriple.getOS() == llvm::Triple::Vulkan ||
+ TargetTriple.getOS() == llvm::Triple::ShaderModel) {
const auto &TC =
static_cast<const toolchains::HLSLToolChain &>(C.getDefaultToolChain());
More information about the cfe-commits
mailing list