Author: Finn Plummer
Date: 2025-08-26T09:56:21-07:00
New Revision: 74a4d815dc53d583053c30edd0ac2a91e9c90b1b

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

LOG: [HLSL][DirectX] Add the Qdx-rootsignature-strip driver option (#154454)

This pr adds the `Qstrip-rootsignature` as a `DXC` driver option.

To do so, this pr introduces the `BinaryModifyJobClass` as an `Action`
to modify a produced object file before its final output.

Further, it registers `llvm-objcopy` as the tool to modify a produced
`DXContainer` on the `HLSL` toolchain.

This allows us to specify the `Qstrip-rootsignature` option to
`clang-dxc` which will invoke `llvm-objcopy` with a
`--remove-section=RTS0` argument to implement its functionality.

Resolves: https://github.com/llvm/llvm-project/issues/150275.

Added: 
    clang/test/Driver/dxc_strip_rootsignature.hlsl

Modified: 
    clang/include/clang/Driver/Action.h
    clang/include/clang/Driver/Options.td
    clang/lib/Driver/Action.cpp
    clang/lib/Driver/Driver.cpp
    clang/lib/Driver/ToolChain.cpp
    clang/lib/Driver/ToolChains/HLSL.cpp
    clang/lib/Driver/ToolChains/HLSL.h

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Driver/Action.h 
b/clang/include/clang/Driver/Action.h
index 7aecfd886adb8..dbf1187da4db9 100644
--- a/clang/include/clang/Driver/Action.h
+++ b/clang/include/clang/Driver/Action.h
@@ -76,9 +76,10 @@ class Action {
     StaticLibJobClass,
     BinaryAnalyzeJobClass,
     BinaryTranslatorJobClass,
+    ObjcopyJobClass,
 
     JobClassFirst = PreprocessJobClass,
-    JobClassLast = BinaryTranslatorJobClass
+    JobClassLast = ObjcopyJobClass
   };
 
   // The offloading kind determines if this action is binded to a particular
@@ -687,6 +688,17 @@ class BinaryTranslatorJobAction : public JobAction {
   }
 };
 
+class ObjcopyJobAction : public JobAction {
+  void anchor() override;
+
+public:
+  ObjcopyJobAction(Action *Input, types::ID Type);
+
+  static bool classof(const Action *A) {
+    return A->getKind() == ObjcopyJobClass;
+  }
+};
+
 } // namespace driver
 } // namespace clang
 

diff  --git a/clang/include/clang/Driver/Options.td 
b/clang/include/clang/Driver/Options.td
index 7313b360f521a..82e8212bee12d 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -9404,6 +9404,11 @@ def res_may_alias : Option<["/", "-"], "res-may-alias", 
KIND_FLAG>,
   Visibility<[DXCOption, ClangOption, CC1Option]>,
   HelpText<"Assume that UAVs/SRVs may alias">,
   MarshallingInfoFlag<CodeGenOpts<"ResMayAlias">>;
+def dxc_strip_rootsignature :
+  Option<["/", "-"], "Qstrip-rootsignature", KIND_FLAG>,
+  Group<dxc_Group>,
+  Visibility<[DXCOption]>,
+  HelpText<"Omit the root signature from produced DXContainer">;
 def target_profile : DXCJoinedOrSeparate<"T">, MetaVarName<"<profile>">,
   HelpText<"Set target profile">,
   Values<"ps_6_0, ps_6_1, ps_6_2, ps_6_3, ps_6_4, ps_6_5, ps_6_6, ps_6_7,"

diff  --git a/clang/lib/Driver/Action.cpp b/clang/lib/Driver/Action.cpp
index ec09726044812..e19daa9cb7abf 100644
--- a/clang/lib/Driver/Action.cpp
+++ b/clang/lib/Driver/Action.cpp
@@ -52,6 +52,8 @@ const char *Action::getClassName(ActionClass AC) {
     return "binary-analyzer";
   case BinaryTranslatorJobClass:
     return "binary-translator";
+  case ObjcopyJobClass:
+    return "objcopy";
   }
 
   llvm_unreachable("invalid class");
@@ -467,3 +469,8 @@ void BinaryTranslatorJobAction::anchor() {}
 BinaryTranslatorJobAction::BinaryTranslatorJobAction(Action *Input,
                                                      types::ID Type)
     : JobAction(BinaryTranslatorJobClass, Input, Type) {}
+
+void ObjcopyJobAction::anchor() {}
+
+ObjcopyJobAction::ObjcopyJobAction(Action *Input, types::ID Type)
+    : JobAction(ObjcopyJobClass, Input, Type) {}

diff  --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index 34eaad43d6f79..f110dbab3e5a5 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4642,16 +4642,28 @@ void Driver::BuildDefaultActions(Compilation &C, 
DerivedArgList &Args,
     }
   }
 
-  // Call validator for dxil when -Vd not in Args.
   if (C.getDefaultToolChain().getTriple().isDXIL()) {
-    // Only add action when needValidation.
     const auto &TC =
         static_cast<const toolchains::HLSLToolChain 
&>(C.getDefaultToolChain());
+
+    // Call objcopy for manipulation of the unvalidated DXContainer when an
+    // option in Args requires it.
+    if (TC.requiresObjcopy(Args)) {
+      Action *LastAction = Actions.back();
+      // llvm-objcopy expects an unvalidated DXIL container (TY_OBJECT).
+      if (LastAction->getType() == types::TY_Object)
+        Actions.push_back(
+            C.MakeAction<ObjcopyJobAction>(LastAction, types::TY_Object));
+    }
+
+    // Call validator for dxil when -Vd not in Args.
     if (TC.requiresValidation(Args)) {
       Action *LastAction = Actions.back();
       Actions.push_back(C.MakeAction<BinaryAnalyzeJobAction>(
           LastAction, types::TY_DX_CONTAINER));
     }
+
+    // Call metal-shaderconverter when targeting metal.
     if (TC.requiresBinaryTranslation(Args)) {
       Action *LastAction = Actions.back();
       // Metal shader converter runs on DXIL containers, which can either be
@@ -6253,8 +6265,9 @@ 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 or translating, we should 
set
-    // the final result file. Otherwise we should emit to a temporary.
+    // 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());

diff  --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp
index 65b36217a940f..49c89ab0c037f 100644
--- a/clang/lib/Driver/ToolChain.cpp
+++ b/clang/lib/Driver/ToolChain.cpp
@@ -652,6 +652,7 @@ Tool *ToolChain::getTool(Action::ActionClass AC) const {
   case Action::VerifyDebugInfoJobClass:
   case Action::BinaryAnalyzeJobClass:
   case Action::BinaryTranslatorJobClass:
+  case Action::ObjcopyJobClass:
     llvm_unreachable("Invalid tool kind.");
 
   case Action::CompileJobClass:

diff  --git a/clang/lib/Driver/ToolChains/HLSL.cpp 
b/clang/lib/Driver/ToolChains/HLSL.cpp
index 5a0ed779262e9..660661945d62a 100644
--- a/clang/lib/Driver/ToolChains/HLSL.cpp
+++ b/clang/lib/Driver/ToolChains/HLSL.cpp
@@ -294,6 +294,32 @@ void tools::hlsl::MetalConverter::ConstructJob(
                                          Exec, CmdArgs, Inputs, Input));
 }
 
+void tools::hlsl::LLVMObjcopy::ConstructJob(Compilation &C, const JobAction 
&JA,
+                                            const InputInfo &Output,
+                                            const InputInfoList &Inputs,
+                                            const ArgList &Args,
+                                            const char *LinkingOutput) const {
+
+  std::string ObjcopyPath = getToolChain().GetProgramPath("llvm-objcopy");
+  const char *Exec = Args.MakeArgString(ObjcopyPath);
+
+  ArgStringList CmdArgs;
+  assert(Inputs.size() == 1 && "Unable to handle multiple inputs.");
+  const InputInfo &Input = Inputs[0];
+  CmdArgs.push_back(Input.getFilename());
+  CmdArgs.push_back(Output.getFilename());
+
+  if (Args.hasArg(options::OPT_dxc_strip_rootsignature)) {
+    const char *Frs = Args.MakeArgString("--remove-section=RTS0");
+    CmdArgs.push_back(Frs);
+  }
+
+  assert(CmdArgs.size() > 2 && "Unnecessary invocation of objcopy.");
+
+  C.addCommand(std::make_unique<Command>(JA, *this, 
ResponseFileSupport::None(),
+                                         Exec, CmdArgs, Inputs, Input));
+}
+
 /// DirectX Toolchain
 HLSLToolChain::HLSLToolChain(const Driver &D, const llvm::Triple &Triple,
                              const ArgList &Args)
@@ -314,6 +340,10 @@ Tool *clang::driver::toolchains::HLSLToolChain::getTool(
     if (!MetalConverter)
       MetalConverter.reset(new tools::hlsl::MetalConverter(*this));
     return MetalConverter.get();
+  case Action::ObjcopyJobClass:
+    if (!LLVMObjcopy)
+      LLVMObjcopy.reset(new tools::hlsl::LLVMObjcopy(*this));
+    return LLVMObjcopy.get();
   default:
     return ToolChain::getTool(AC);
   }
@@ -458,16 +488,22 @@ bool 
HLSLToolChain::requiresBinaryTranslation(DerivedArgList &Args) const {
   return Args.hasArg(options::OPT_metal) && Args.hasArg(options::OPT_dxc_Fo);
 }
 
+bool HLSLToolChain::requiresObjcopy(DerivedArgList &Args) const {
+  return Args.hasArg(options::OPT_dxc_Fo) &&
+         Args.hasArg(options::OPT_dxc_strip_rootsignature);
+}
+
 bool HLSLToolChain::isLastJob(DerivedArgList &Args,
                               Action::ActionClass AC) const {
-  bool HasTranslation = requiresBinaryTranslation(Args);
-  bool HasValidation = requiresValidation(Args);
-  // If translation and validation are not required, we should only have one
-  // action.
-  if (!HasTranslation && !HasValidation)
-    return true;
-  if ((HasTranslation && AC == Action::BinaryTranslatorJobClass) ||
-      (!HasTranslation && HasValidation && AC == 
Action::BinaryAnalyzeJobClass))
-    return true;
-  return false;
+  // Note: we check in the reverse order of execution
+  if (requiresBinaryTranslation(Args))
+    return AC == Action::Action::BinaryTranslatorJobClass;
+  if (requiresValidation(Args))
+    return AC == Action::Action::BinaryAnalyzeJobClass;
+  if (requiresObjcopy(Args))
+    return AC == Action::Action::ObjcopyJobClass;
+
+  // No translation, validation, or objcopy are required, so this action must
+  // output to the result file.
+  return true;
 }

diff  --git a/clang/lib/Driver/ToolChains/HLSL.h 
b/clang/lib/Driver/ToolChains/HLSL.h
index 3824b4252324b..3aed904648429 100644
--- a/clang/lib/Driver/ToolChains/HLSL.h
+++ b/clang/lib/Driver/ToolChains/HLSL.h
@@ -42,6 +42,20 @@ class LLVM_LIBRARY_VISIBILITY MetalConverter : public Tool {
                     const llvm::opt::ArgList &TCArgs,
                     const char *LinkingOutput) const override;
 };
+
+class LLVM_LIBRARY_VISIBILITY LLVMObjcopy : public Tool {
+public:
+  LLVMObjcopy(const ToolChain &TC)
+      : Tool("hlsl::LLVMObjcopy", "llvm-objcopy", 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;
+};
+
 } // namespace hlsl
 } // namespace tools
 
@@ -65,6 +79,13 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public 
ToolChain {
   static std::optional<std::string> parseTargetProfile(StringRef 
TargetProfile);
   bool requiresValidation(llvm::opt::DerivedArgList &Args) const;
   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.
+  ///
+  /// Returns true if we should output to the final result file.
   bool isLastJob(llvm::opt::DerivedArgList &Args, Action::ActionClass AC) 
const;
 
   // Set default DWARF version to 4 for DXIL uses version 4.
@@ -73,6 +94,7 @@ class LLVM_LIBRARY_VISIBILITY HLSLToolChain : public 
ToolChain {
 private:
   mutable std::unique_ptr<tools::hlsl::Validator> Validator;
   mutable std::unique_ptr<tools::hlsl::MetalConverter> MetalConverter;
+  mutable std::unique_ptr<tools::hlsl::LLVMObjcopy> LLVMObjcopy;
 };
 
 } // end namespace toolchains

diff  --git a/clang/test/Driver/dxc_strip_rootsignature.hlsl 
b/clang/test/Driver/dxc_strip_rootsignature.hlsl
new file mode 100644
index 0000000000000..79d5ef246c987
--- /dev/null
+++ b/clang/test/Driver/dxc_strip_rootsignature.hlsl
@@ -0,0 +1,15 @@
+// Create a dummy dxv to run
+// RUN: mkdir -p %t.dir
+// RUN: echo "dxv" > %t.dir/dxv && chmod 754 %t.dir/dxv
+
+// RUN: %clang_dxc -Qstrip-rootsignature --dxv-path=%t.dir -T cs_6_0 /Fo 
%t.dxo -### %s 2>&1 | FileCheck %s
+
+// Test to demonstrate that we specify to the root signature with the
+// -Qstrip-rootsignature option and that it occurs before DXV
+
+// CHECK: "{{.*}}llvm-objcopy{{(.exe)?}}" "{{.*}}.obj" "{{.*}}.obj" 
"--remove-section=RTS0"
+// CHECK: "{{.*}}dxv{{(.exe)?}}" "{{.*}}.obj" "-o" "{{.*}}.dxo"
+
+[shader("compute"), RootSignature("")]
+[numthreads(1,1,1)]
+void EmptyEntry() {}


        
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to