spirv_tools/opt/
tool.rs

1use crate::error;
2
3#[derive(Default)]
4pub struct ToolOptimizer {
5    target_env: crate::TargetEnv,
6    passes: Vec<super::Passes>,
7    use_perf_passes: bool,
8    use_size_passes: bool,
9    //use_vulkan_to_webgpu: bool,
10    //use_webgpu_to_vulkan: bool,
11    legalize_hlsl: bool,
12}
13
14use super::Optimizer;
15
16impl Optimizer for ToolOptimizer {
17    fn with_env(target_env: crate::TargetEnv) -> Self {
18        Self {
19            target_env,
20            ..Default::default()
21        }
22    }
23
24    fn optimize<MC: error::MessageCallback>(
25        &self,
26        input: impl AsRef<[u32]>,
27        msg_callback: &mut MC,
28        options: Option<super::Options>,
29    ) -> Result<crate::binary::Binary, crate::Error> {
30        let mut cmd = std::process::Command::new("spirv-opt");
31
32        // Note here that we don't do cmd.arg("--target-env").arg(self.target_env.to_string());
33        // like with the other tools, because opt is "special" and will fail to parse
34        // command line options correctly if we don't give join them with the =
35        cmd.arg(format!("--target-env={}", self.target_env));
36
37        cmd.args(
38            self.passes
39                .iter()
40                .filter_map(|p| pass_to_string(*p).map(|s| format!("--{s}"))),
41        );
42
43        if self.use_perf_passes {
44            cmd.arg("-O");
45        }
46
47        if self.use_size_passes {
48            cmd.arg("-Os");
49        }
50
51        if self.legalize_hlsl {
52            cmd.arg("--legalize-hlsl");
53        }
54
55        if let Some(opts) = options {
56            if let Some(max_id_bound) = opts.max_id_bound {
57                cmd.arg(format!("--max-id-bound={max_id_bound}"));
58            }
59
60            if opts.preserve_bindings {
61                cmd.arg("--preserve-bindings");
62            }
63
64            if opts.preserve_spec_constants {
65                cmd.arg("--preserve-spec-constants");
66            }
67
68            if let Some(vopts) = opts.validator_options {
69                crate::val::tool::add_options(&mut cmd, vopts);
70            }
71        }
72
73        let input = crate::binary::from_binary(input.as_ref());
74
75        let cmd_output = crate::cmd::exec(cmd, Some(input), crate::cmd::Output::Retrieve)?;
76
77        for msg in cmd_output.messages {
78            msg_callback.on_message(msg);
79        }
80
81        crate::binary::Binary::try_from(cmd_output.binary)
82    }
83
84    /// Register a single pass with the the optimizer.
85    #[inline]
86    fn register_pass(&mut self, pass: super::Passes) -> &mut Self {
87        self.passes.push(pass);
88        self
89    }
90
91    /// Registers passes that attempt to improve performance of generated code.
92    /// This sequence of passes is subject to constant review and will change
93    /// from time to time.
94    #[inline]
95    fn register_performance_passes(&mut self) -> &mut Self {
96        self.use_perf_passes = true;
97        self
98    }
99
100    /// Registers passes that attempt to improve the size of generated code.
101    /// This sequence of passes is subject to constant review and will change
102    /// from time to time.
103    #[inline]
104    fn register_size_passes(&mut self) -> &mut Self {
105        self.use_size_passes = true;
106        self
107    }
108
109    /// Registers passes that attempt to legalize the generated code.
110    ///
111    /// Note: this recipe is specially designed for legalizing SPIR-V. It should be
112    /// used by compilers after translating HLSL source code literally. It should
113    /// *not* be used by general workloads for performance or size improvement.
114    ///
115    /// This sequence of passes is subject to constant review and will change
116    /// from time to time.
117    #[inline]
118    fn register_hlsl_legalization_passes(&mut self) -> &mut Self {
119        self.legalize_hlsl = true;
120        self
121    }
122}
123
124fn pass_to_string(pass: super::Passes) -> Option<&'static str> {
125    #[allow(clippy::enum_glob_use)]
126    use super::Passes::*;
127
128    Some(match pass {
129        AggressiveDCE => "eliminate-dead-code-aggressive",
130        AmdExtToKhr => "amd-ext-to-khr",
131        BlockMerge => "merge-blocks",
132        CFGCleanup => "cfg-cleanup",
133        CodeSinking => "code-sink",
134        CombineAccessChains => "combine-access-chains",
135        CompactIds => "compact-ids",
136        ConditionalConstantPropagation => "ccp",
137        ConvertRelaxedToHalf => "convert-relaxed-to-half",
138        CopyPropagateArrays => "copy-propagate-arrays",
139        DeadBranchElim => "eliminate-dead-branches",
140        DeadInsertElim => "eliminate-dead-inserts",
141        DeadVariableElimination => "eliminate-dead-variables",
142        DescriptorScalarReplacement => "descriptor-scalar-replacement",
143        EliminateDeadConstant => "eliminate-dead-const",
144        EliminateDeadFunctions => "eliminate-dead-functions",
145        EliminateDeadMembers => "eliminate-dead-members",
146        FixStorageClass => "fix-storage-class",
147        FlattenDecoration => "flatten-decorations",
148        FoldSpecConstantOpAndComposite => "fold-spec-const-op-composite",
149        FreezeSpecConstantValue => "freeze-spec-const",
150        GraphicsRobustAccess => "graphics-robust-access",
151        IfConversion => "if-conversion",
152        InlineExhaustive => "inline-entry-points-exhaustive",
153        InlineOpaque => "inline-entry-points-opaque",
154        InsertExtractElim => "eliminate-insert-extract",
155        // This is only part of the --legalize-hlsl meta pass
156        InterpolateFixup | Null => return None,
157        LocalAccessChainConvert => "convert-local-access-chains",
158        LocalMultiStoreElim => "eliminate-local-multi-store",
159        LocalRedundancyElimination => "local-redundancy-elimination",
160        LocalSingleBlockLoadStoreElim => "eliminate-local-single-block",
161        LocalSingleStoreElim => "eliminate-local-single-store",
162        LoopInvariantCodeMotion => "loop-invariant-code-motion",
163        LoopPeeling => "loop-peeling",
164        LoopUnswitch => "loop-unswitch",
165        MergeReturn => "merge-return",
166        PrivateToLocal => "private-to-local",
167        PropagateLineInfo => "propagate-line-info",
168        ReduceLoadSize => "reduce-load-size",
169        RedundancyElimination => "redundancy-elimination",
170        RedundantLineInfoElim => "eliminate-redundant-line-info",
171        RemoveUnusedInterfaceVariables => "remove-unused-interface-variables",
172        RelaxFloatOps => "relax-float-ops",
173        RemoveDuplicates => "remove-duplicates",
174        ReplaceInvalidOpcode => "replace-invalid-opcode",
175        Simplification => "simplify-instructions",
176        SSARewrite => "ssa-rewrite",
177        StrengthReduction => "strength-reduction",
178        StripDebugInfo => "strip-debug",
179        StripNonSemanticInfo => "strip-nonsemantic",
180        UnifyConstant => "unify-const",
181        UpgradeMemoryModel => "upgrade-memory-model",
182        VectorDCE => "vector-dce",
183        Workaround1209 => "workaround-1209",
184        WrapOpKill => "wrap-opkill",
185    })
186}