Coverage Report

Created: 2026-03-07 07:04

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/third_party/spirv-tools/source/opt/optimizer.cpp
Line
Count
Source
1
// Copyright (c) 2016 Google Inc.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "spirv-tools/optimizer.hpp"
16
17
#include <cassert>
18
#include <charconv>
19
#include <memory>
20
#include <string>
21
#include <system_error>
22
#include <unordered_map>
23
#include <utility>
24
#include <vector>
25
26
#include "source/opt/build_module.h"
27
#include "source/opt/graphics_robust_access_pass.h"
28
#include "source/opt/log.h"
29
#include "source/opt/pass_manager.h"
30
#include "source/opt/passes.h"
31
#include "source/spirv_optimizer_options.h"
32
#include "source/util/make_unique.h"
33
#include "source/util/string_utils.h"
34
35
namespace spvtools {
36
37
std::vector<std::string> GetVectorOfStrings(const char** strings,
38
0
                                            const size_t string_count) {
39
0
  std::vector<std::string> result;
40
0
  for (uint32_t i = 0; i < string_count; i++) {
41
0
    result.emplace_back(strings[i]);
42
0
  }
43
0
  return result;
44
0
}
45
46
struct Optimizer::PassToken::Impl {
47
7.80k
  Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
48
49
  std::unique_ptr<opt::Pass> pass;  // Internal implementation pass.
50
};
51
52
Optimizer::PassToken::PassToken(
53
    std::unique_ptr<Optimizer::PassToken::Impl> impl)
54
7.80k
    : impl_(std::move(impl)) {}
55
56
Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass)
57
0
    : impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {}
58
59
Optimizer::PassToken::PassToken(PassToken&& that)
60
0
    : impl_(std::move(that.impl_)) {}
61
62
0
Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
63
0
  impl_ = std::move(that.impl_);
64
0
  return *this;
65
0
}
66
67
7.80k
Optimizer::PassToken::~PassToken() {}
68
69
struct Optimizer::Impl {
70
190
  explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
71
72
  spv_target_env target_env;      // Target environment.
73
  opt::PassManager pass_manager;  // Internal implementation pass manager.
74
  std::unordered_set<uint32_t> live_locs;  // Arg to debug dead output passes
75
};
76
77
190
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
78
190
  assert(env != SPV_ENV_WEBGPU_0);
79
190
}
80
81
190
Optimizer::~Optimizer() {}
82
83
190
void Optimizer::SetMessageConsumer(MessageConsumer c) {
84
  // All passes' message consumer needs to be updated.
85
190
  for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
86
0
    impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
87
0
  }
88
190
  impl_->pass_manager.SetMessageConsumer(std::move(c));
89
190
}
90
91
7.93k
const MessageConsumer& Optimizer::consumer() const {
92
7.93k
  return impl_->pass_manager.consumer();
93
7.93k
}
94
95
7.80k
Optimizer& Optimizer::RegisterPass(PassToken&& p) {
96
  // Change to use the pass manager's consumer.
97
7.80k
  p.impl_->pass->SetMessageConsumer(consumer());
98
7.80k
  impl_->pass_manager.AddPass(std::move(p.impl_->pass));
99
7.80k
  return *this;
100
7.80k
}
101
102
// The legalization passes take a spir-v shader generated by an HLSL front-end
103
// and turn it into a valid vulkan spir-v shader.  There are two ways in which
104
// the code will be invalid at the start:
105
//
106
// 1) There will be opaque objects, like images, which will be passed around
107
//    in intermediate objects.  Valid spir-v will have to replace the use of
108
//    the opaque object with an intermediate object that is the result of the
109
//    load of the global opaque object.
110
//
111
// 2) There will be variables that contain pointers to structured or uniform
112
//    buffers.  It be legal, the variables must be eliminated, and the
113
//    references to the structured buffers must use the result of OpVariable
114
//    in the Uniform storage class.
115
//
116
// Optimization in this list must accept shaders with these relaxation of the
117
// rules.  There is not guarantee that this list of optimizations is able to
118
// legalize all inputs, but it is on a best effort basis.
119
//
120
// The legalization problem is essentially a very general copy propagation
121
// problem.  The optimization we use are all used to either do copy propagation
122
// or enable more copy propagation.
123
0
Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) {
124
0
  return
125
      // Wrap OpKill instructions so all other code can be inlined.
126
0
      RegisterPass(CreateWrapOpKillPass())
127
          // Remove unreachable block so that merge return works.
128
0
          .RegisterPass(CreateDeadBranchElimPass())
129
          // Merge the returns so we can inline.
130
0
          .RegisterPass(CreateMergeReturnPass())
131
          // Make sure uses and definitions are in the same function.
132
0
          .RegisterPass(CreateInlineExhaustivePass())
133
          // Make private variable function scope
134
0
          .RegisterPass(CreateEliminateDeadFunctionsPass())
135
0
          .RegisterPass(CreatePrivateToLocalPass())
136
          // Fix up the storage classes that DXC may have purposely generated
137
          // incorrectly.  All functions are inlined, and a lot of dead code has
138
          // been removed.
139
0
          .RegisterPass(CreateFixStorageClassPass())
140
          // Propagate the value stored to the loads in very simple cases.
141
0
          .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
142
0
          .RegisterPass(CreateLocalSingleStoreElimPass())
143
0
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
144
          // Split up aggregates so they are easier to deal with.
145
0
          .RegisterPass(CreateScalarReplacementPass(0))
146
          // Remove loads and stores so everything is in intermediate values.
147
          // Takes care of copy propagation of non-members.
148
0
          .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
149
0
          .RegisterPass(CreateLocalSingleStoreElimPass())
150
0
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
151
0
          .RegisterPass(CreateLocalMultiStoreElimPass())
152
0
          .RegisterPass(CreateCombineAccessChainsPass())
153
0
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
154
0
          .RegisterPass(CreateLegalizeMultidimArrayPass())
155
          // Propagate constants to get as many constant conditions on branches
156
          // as possible.
157
0
          .RegisterPass(CreateCCPPass())
158
0
          .RegisterPass(CreateLoopUnrollPass(true))
159
0
          .RegisterPass(CreateDeadBranchElimPass())
160
          // Copy propagate members.  Cleans up code sequences generated by
161
          // scalar replacement.  Also important for removing OpPhi nodes.
162
0
          .RegisterPass(CreateSimplificationPass())
163
0
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
164
0
          .RegisterPass(CreateCopyPropagateArraysPass())
165
          // May need loop unrolling here see
166
          // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
167
          // Get rid of unused code that contain traces of illegal code
168
          // or unused references to unbound external objects
169
0
          .RegisterPass(CreateVectorDCEPass())
170
0
          .RegisterPass(CreateDeadInsertElimPass())
171
0
          .RegisterPass(CreateReduceLoadSizePass())
172
0
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
173
0
          .RegisterPass(CreateRemoveUnusedInterfaceVariablesPass())
174
0
          .RegisterPass(CreateInterpolateFixupPass())
175
0
          .RegisterPass(CreateInvocationInterlockPlacementPass())
176
0
          .RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass());
177
0
}
178
179
0
Optimizer& Optimizer::RegisterLegalizationPasses() {
180
0
  return RegisterLegalizationPasses(false);
181
0
}
182
183
122
Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) {
184
122
  return RegisterPass(CreateWrapOpKillPass())
185
122
      .RegisterPass(CreateDeadBranchElimPass())
186
122
      .RegisterPass(CreateMergeReturnPass())
187
122
      .RegisterPass(CreateInlineExhaustivePass())
188
122
      .RegisterPass(CreateEliminateDeadFunctionsPass())
189
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
190
122
      .RegisterPass(CreatePrivateToLocalPass())
191
122
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
192
122
      .RegisterPass(CreateLocalSingleStoreElimPass())
193
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
194
122
      .RegisterPass(CreateScalarReplacementPass(0))
195
122
      .RegisterPass(CreateLocalAccessChainConvertPass())
196
122
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
197
122
      .RegisterPass(CreateLocalSingleStoreElimPass())
198
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
199
122
      .RegisterPass(CreateLocalMultiStoreElimPass())
200
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
201
122
      .RegisterPass(CreateCCPPass())
202
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
203
122
      .RegisterPass(CreateLoopUnrollPass(true))
204
122
      .RegisterPass(CreateDeadBranchElimPass())
205
122
      .RegisterPass(CreateRedundancyEliminationPass())
206
122
      .RegisterPass(CreateCombineAccessChainsPass())
207
122
      .RegisterPass(CreateSimplificationPass())
208
122
      .RegisterPass(CreateScalarReplacementPass(0))
209
122
      .RegisterPass(CreateLocalAccessChainConvertPass())
210
122
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
211
122
      .RegisterPass(CreateLocalSingleStoreElimPass())
212
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
213
122
      .RegisterPass(CreateSSARewritePass())
214
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
215
122
      .RegisterPass(CreateVectorDCEPass())
216
122
      .RegisterPass(CreateDeadInsertElimPass())
217
122
      .RegisterPass(CreateDeadBranchElimPass())
218
122
      .RegisterPass(CreateSimplificationPass())
219
122
      .RegisterPass(CreateIfConversionPass())
220
122
      .RegisterPass(CreateCopyPropagateArraysPass())
221
122
      .RegisterPass(CreateReduceLoadSizePass())
222
122
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
223
122
      .RegisterPass(CreateBlockMergePass())
224
122
      .RegisterPass(CreateRedundancyEliminationPass())
225
122
      .RegisterPass(CreateDeadBranchElimPass())
226
122
      .RegisterPass(CreateBlockMergePass())
227
122
      .RegisterPass(CreateSimplificationPass());
228
122
}
229
230
122
Optimizer& Optimizer::RegisterPerformancePasses() {
231
122
  return RegisterPerformancePasses(false);
232
122
}
233
234
68
Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
235
68
  return RegisterPass(CreateWrapOpKillPass())
236
68
      .RegisterPass(CreateDeadBranchElimPass())
237
68
      .RegisterPass(CreateMergeReturnPass())
238
68
      .RegisterPass(CreateInlineExhaustivePass())
239
68
      .RegisterPass(CreateEliminateDeadFunctionsPass())
240
68
      .RegisterPass(CreatePrivateToLocalPass())
241
68
      .RegisterPass(CreateScalarReplacementPass(0))
242
68
      .RegisterPass(CreateLocalMultiStoreElimPass())
243
68
      .RegisterPass(CreateCCPPass())
244
68
      .RegisterPass(CreateLoopUnrollPass(true))
245
68
      .RegisterPass(CreateDeadBranchElimPass())
246
68
      .RegisterPass(CreateSimplificationPass())
247
68
      .RegisterPass(CreateScalarReplacementPass(0))
248
68
      .RegisterPass(CreateLocalSingleStoreElimPass())
249
68
      .RegisterPass(CreateIfConversionPass())
250
68
      .RegisterPass(CreateSimplificationPass())
251
68
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
252
68
      .RegisterPass(CreateDeadBranchElimPass())
253
68
      .RegisterPass(CreateBlockMergePass())
254
68
      .RegisterPass(CreateLocalAccessChainConvertPass())
255
68
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
256
68
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
257
68
      .RegisterPass(CreateCopyPropagateArraysPass())
258
68
      .RegisterPass(CreateVectorDCEPass())
259
68
      .RegisterPass(CreateDeadInsertElimPass())
260
68
      .RegisterPass(CreateEliminateDeadMembersPass())
261
68
      .RegisterPass(CreateLocalSingleStoreElimPass())
262
68
      .RegisterPass(CreateBlockMergePass())
263
68
      .RegisterPass(CreateLocalMultiStoreElimPass())
264
68
      .RegisterPass(CreateRedundancyEliminationPass())
265
68
      .RegisterPass(CreateSimplificationPass())
266
68
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
267
68
      .RegisterPass(CreateCFGCleanupPass());
268
68
}
269
270
68
Optimizer& Optimizer::RegisterSizePasses() { return RegisterSizePasses(false); }
271
272
0
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
273
0
  return RegisterPassesFromFlags(flags, false);
274
0
}
275
276
bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags,
277
0
                                        bool preserve_interface) {
278
0
  for (const auto& flag : flags) {
279
0
    if (!RegisterPassFromFlag(flag, preserve_interface)) {
280
0
      return false;
281
0
    }
282
0
  }
283
284
0
  return true;
285
0
}
286
287
0
bool Optimizer::FlagHasValidForm(const std::string& flag) const {
288
0
  if (flag == "-O" || flag == "-Os") {
289
0
    return true;
290
0
  } else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
291
0
    return true;
292
0
  }
293
294
0
  Errorf(consumer(), nullptr, {},
295
0
         "%s is not a valid flag.  Flag passes should have the form "
296
0
         "'--pass_name[=pass_args]'. Special flag names also accepted: -O "
297
0
         "and -Os.",
298
0
         flag.c_str());
299
0
  return false;
300
0
}
301
302
0
bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
303
0
  return RegisterPassFromFlag(flag, false);
304
0
}
305
306
bool Optimizer::RegisterPassFromFlag(const std::string& flag,
307
0
                                     bool preserve_interface) {
308
0
  if (!FlagHasValidForm(flag)) {
309
0
    return false;
310
0
  }
311
312
  // Split flags of the form --pass_name=pass_args.
313
0
  auto p = utils::SplitFlagArgs(flag);
314
0
  std::string pass_name = p.first;
315
0
  std::string pass_args = p.second;
316
317
  // FIXME(dnovillo): This should be re-factored so that pass names can be
318
  // automatically checked against Pass::name() and PassToken instances created
319
  // via a template function.  Additionally, class Pass should have a desc()
320
  // method that describes the pass (so it can be used in --help).
321
  //
322
  // Both Pass::name() and Pass::desc() should be static class members so they
323
  // can be invoked without creating a pass instance.
324
0
  if (pass_name == "strip-debug") {
325
0
    RegisterPass(CreateStripDebugInfoPass());
326
0
  } else if (pass_name == "strip-reflect") {
327
0
    RegisterPass(CreateStripReflectInfoPass());
328
0
  } else if (pass_name == "strip-nonsemantic") {
329
0
    RegisterPass(CreateStripNonSemanticInfoPass());
330
0
  } else if (pass_name == "fix-opextinst-opcodes") {
331
0
    RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass());
332
0
  } else if (pass_name == "set-spec-const-default-value") {
333
0
    if (pass_args.size() > 0) {
334
0
      auto spec_ids_vals =
335
0
          opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
336
0
              pass_args.c_str());
337
0
      if (!spec_ids_vals) {
338
0
        Errorf(consumer(), nullptr, {},
339
0
               "Invalid argument for --set-spec-const-default-value: %s",
340
0
               pass_args.c_str());
341
0
        return false;
342
0
      }
343
0
      RegisterPass(
344
0
          CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
345
0
    } else {
346
0
      Errorf(consumer(), nullptr, {},
347
0
             "Invalid spec constant value string '%s'. Expected a string of "
348
0
             "<spec id>:<default value> pairs.",
349
0
             pass_args.c_str());
350
0
      return false;
351
0
    }
352
0
  } else if (pass_name == "if-conversion") {
353
0
    RegisterPass(CreateIfConversionPass());
354
0
  } else if (pass_name == "freeze-spec-const") {
355
0
    RegisterPass(CreateFreezeSpecConstantValuePass());
356
0
  } else if (pass_name == "inline-entry-points-exhaustive") {
357
0
    RegisterPass(CreateInlineExhaustivePass());
358
0
  } else if (pass_name == "inline-entry-points-opaque") {
359
0
    RegisterPass(CreateInlineOpaquePass());
360
0
  } else if (pass_name == "combine-access-chains") {
361
0
    RegisterPass(CreateCombineAccessChainsPass());
362
0
  } else if (pass_name == "convert-local-access-chains") {
363
0
    RegisterPass(CreateLocalAccessChainConvertPass());
364
0
  } else if (pass_name == "replace-desc-array-access-using-var-index") {
365
0
    RegisterPass(CreateReplaceDescArrayAccessUsingVarIndexPass());
366
0
  } else if (pass_name == "spread-volatile-semantics") {
367
0
    RegisterPass(CreateSpreadVolatileSemanticsPass());
368
0
  } else if (pass_name == "descriptor-scalar-replacement") {
369
0
    RegisterPass(CreateDescriptorScalarReplacementPass());
370
0
  } else if (pass_name == "descriptor-composite-scalar-replacement") {
371
0
    RegisterPass(CreateDescriptorCompositeScalarReplacementPass());
372
0
  } else if (pass_name == "descriptor-array-scalar-replacement") {
373
0
    RegisterPass(CreateDescriptorArrayScalarReplacementPass());
374
0
  } else if (pass_name == "eliminate-dead-code-aggressive") {
375
0
    RegisterPass(CreateAggressiveDCEPass(preserve_interface));
376
0
  } else if (pass_name == "eliminate-insert-extract") {
377
0
    RegisterPass(CreateInsertExtractElimPass());
378
0
  } else if (pass_name == "eliminate-local-single-block") {
379
0
    RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
380
0
  } else if (pass_name == "eliminate-local-single-store") {
381
0
    RegisterPass(CreateLocalSingleStoreElimPass());
382
0
  } else if (pass_name == "merge-blocks") {
383
0
    RegisterPass(CreateBlockMergePass());
384
0
  } else if (pass_name == "merge-return") {
385
0
    RegisterPass(CreateMergeReturnPass());
386
0
  } else if (pass_name == "eliminate-dead-branches") {
387
0
    RegisterPass(CreateDeadBranchElimPass());
388
0
  } else if (pass_name == "eliminate-dead-functions") {
389
0
    RegisterPass(CreateEliminateDeadFunctionsPass());
390
0
  } else if (pass_name == "eliminate-local-multi-store") {
391
0
    RegisterPass(CreateLocalMultiStoreElimPass());
392
0
  } else if (pass_name == "eliminate-dead-const") {
393
0
    RegisterPass(CreateEliminateDeadConstantPass());
394
0
  } else if (pass_name == "eliminate-dead-inserts") {
395
0
    RegisterPass(CreateDeadInsertElimPass());
396
0
  } else if (pass_name == "eliminate-dead-variables") {
397
0
    RegisterPass(CreateDeadVariableEliminationPass());
398
0
  } else if (pass_name == "eliminate-dead-members") {
399
0
    RegisterPass(CreateEliminateDeadMembersPass());
400
0
  } else if (pass_name == "fold-spec-const-op-composite") {
401
0
    RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
402
0
  } else if (pass_name == "loop-unswitch") {
403
0
    RegisterPass(CreateLoopUnswitchPass());
404
0
  } else if (pass_name == "legalize-multidim-array") {
405
0
    RegisterPass(CreateLegalizeMultidimArrayPass());
406
0
  } else if (pass_name == "scalar-replacement") {
407
0
    if (pass_args.size() == 0) {
408
0
      RegisterPass(CreateScalarReplacementPass(0));
409
0
    } else {
410
0
      int limit = -1;
411
0
      if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
412
0
        limit = atoi(pass_args.c_str());
413
0
      }
414
415
0
      if (limit >= 0) {
416
0
        RegisterPass(CreateScalarReplacementPass(limit));
417
0
      } else {
418
0
        Error(consumer(), nullptr, {},
419
0
              "--scalar-replacement must have no arguments or a non-negative "
420
0
              "integer argument");
421
0
        return false;
422
0
      }
423
0
    }
424
0
  } else if (pass_name == "strength-reduction") {
425
0
    RegisterPass(CreateStrengthReductionPass());
426
0
  } else if (pass_name == "unify-const") {
427
0
    RegisterPass(CreateUnifyConstantPass());
428
0
  } else if (pass_name == "flatten-decorations") {
429
0
    RegisterPass(CreateFlattenDecorationPass());
430
0
  } else if (pass_name == "compact-ids") {
431
0
    RegisterPass(CreateCompactIdsPass());
432
0
  } else if (pass_name == "cfg-cleanup") {
433
0
    RegisterPass(CreateCFGCleanupPass());
434
0
  } else if (pass_name == "local-redundancy-elimination") {
435
0
    RegisterPass(CreateLocalRedundancyEliminationPass());
436
0
  } else if (pass_name == "loop-invariant-code-motion") {
437
0
    RegisterPass(CreateLoopInvariantCodeMotionPass());
438
0
  } else if (pass_name == "reduce-load-size") {
439
0
    if (pass_args.size() == 0) {
440
0
      RegisterPass(CreateReduceLoadSizePass());
441
0
    } else {
442
0
      double load_replacement_threshold = 0.9;
443
0
      if (pass_args.find_first_not_of(".0123456789") == std::string::npos) {
444
0
        load_replacement_threshold = atof(pass_args.c_str());
445
0
      }
446
447
0
      if (load_replacement_threshold >= 0) {
448
0
        RegisterPass(CreateReduceLoadSizePass(load_replacement_threshold));
449
0
      } else {
450
0
        Error(consumer(), nullptr, {},
451
0
              "--reduce-load-size must have no arguments or a non-negative "
452
0
              "double argument");
453
0
        return false;
454
0
      }
455
0
    }
456
0
  } else if (pass_name == "redundancy-elimination") {
457
0
    RegisterPass(CreateRedundancyEliminationPass());
458
0
  } else if (pass_name == "private-to-local") {
459
0
    RegisterPass(CreatePrivateToLocalPass());
460
0
  } else if (pass_name == "remove-duplicates") {
461
0
    RegisterPass(CreateRemoveDuplicatesPass());
462
0
  } else if (pass_name == "workaround-1209") {
463
0
    RegisterPass(CreateWorkaround1209Pass());
464
0
  } else if (pass_name == "replace-invalid-opcode") {
465
0
    RegisterPass(CreateReplaceInvalidOpcodePass());
466
0
  } else if (pass_name == "convert-relaxed-to-half") {
467
0
    RegisterPass(CreateConvertRelaxedToHalfPass());
468
0
  } else if (pass_name == "relax-float-ops") {
469
0
    RegisterPass(CreateRelaxFloatOpsPass());
470
0
  } else if (pass_name == "simplify-instructions") {
471
0
    RegisterPass(CreateSimplificationPass());
472
0
  } else if (pass_name == "ssa-rewrite") {
473
0
    RegisterPass(CreateSSARewritePass());
474
0
  } else if (pass_name == "copy-propagate-arrays") {
475
0
    RegisterPass(CreateCopyPropagateArraysPass());
476
0
  } else if (pass_name == "loop-fission") {
477
0
    int register_threshold_to_split =
478
0
        (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
479
0
    if (register_threshold_to_split > 0) {
480
0
      RegisterPass(CreateLoopFissionPass(
481
0
          static_cast<size_t>(register_threshold_to_split)));
482
0
    } else {
483
0
      Error(consumer(), nullptr, {},
484
0
            "--loop-fission must have a positive integer argument");
485
0
      return false;
486
0
    }
487
0
  } else if (pass_name == "loop-fusion") {
488
0
    int max_registers_per_loop =
489
0
        (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
490
0
    if (max_registers_per_loop > 0) {
491
0
      RegisterPass(
492
0
          CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
493
0
    } else {
494
0
      Error(consumer(), nullptr, {},
495
0
            "--loop-fusion must have a positive integer argument");
496
0
      return false;
497
0
    }
498
0
  } else if (pass_name == "loop-unroll") {
499
0
    RegisterPass(CreateLoopUnrollPass(true));
500
0
  } else if (pass_name == "upgrade-memory-model") {
501
0
    RegisterPass(CreateUpgradeMemoryModelPass());
502
0
  } else if (pass_name == "vector-dce") {
503
0
    RegisterPass(CreateVectorDCEPass());
504
0
  } else if (pass_name == "loop-unroll-partial") {
505
0
    int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
506
0
    if (factor > 0) {
507
0
      RegisterPass(CreateLoopUnrollPass(false, factor));
508
0
    } else {
509
0
      Error(consumer(), nullptr, {},
510
0
            "--loop-unroll-partial must have a positive integer argument");
511
0
      return false;
512
0
    }
513
0
  } else if (pass_name == "loop-peeling") {
514
0
    RegisterPass(CreateLoopPeelingPass());
515
0
  } else if (pass_name == "loop-peeling-threshold") {
516
0
    int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
517
0
    if (factor > 0) {
518
0
      opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
519
0
    } else {
520
0
      Error(consumer(), nullptr, {},
521
0
            "--loop-peeling-threshold must have a positive integer argument");
522
0
      return false;
523
0
    }
524
0
  } else if (pass_name == "ccp") {
525
0
    RegisterPass(CreateCCPPass());
526
0
  } else if (pass_name == "code-sink") {
527
0
    RegisterPass(CreateCodeSinkingPass());
528
0
  } else if (pass_name == "fix-storage-class") {
529
0
    RegisterPass(CreateFixStorageClassPass());
530
0
  } else if (pass_name == "O") {
531
0
    RegisterPerformancePasses(preserve_interface);
532
0
  } else if (pass_name == "Os") {
533
0
    RegisterSizePasses(preserve_interface);
534
0
  } else if (pass_name == "legalize-hlsl") {
535
0
    RegisterLegalizationPasses(preserve_interface);
536
0
  } else if (pass_name == "remove-unused-interface-variables") {
537
0
    RegisterPass(CreateRemoveUnusedInterfaceVariablesPass());
538
0
  } else if (pass_name == "graphics-robust-access") {
539
0
    RegisterPass(CreateGraphicsRobustAccessPass());
540
0
  } else if (pass_name == "wrap-opkill") {
541
0
    RegisterPass(CreateWrapOpKillPass());
542
0
  } else if (pass_name == "amd-ext-to-khr") {
543
0
    RegisterPass(CreateAmdExtToKhrPass());
544
0
  } else if (pass_name == "interpolate-fixup") {
545
0
    RegisterPass(CreateInterpolateFixupPass());
546
0
  } else if (pass_name == "remove-dont-inline") {
547
0
    RegisterPass(CreateRemoveDontInlinePass());
548
0
  } else if (pass_name == "eliminate-dead-input-components") {
549
0
    RegisterPass(CreateEliminateDeadInputComponentsSafePass());
550
0
  } else if (pass_name == "fix-func-call-param") {
551
0
    RegisterPass(CreateFixFuncCallArgumentsPass());
552
0
  } else if (pass_name == "convert-to-sampled-image") {
553
0
    if (pass_args.size() > 0) {
554
0
      auto descriptor_set_binding_pairs =
555
0
          opt::ConvertToSampledImagePass::ParseDescriptorSetBindingPairsString(
556
0
              pass_args.c_str());
557
0
      if (!descriptor_set_binding_pairs) {
558
0
        Errorf(consumer(), nullptr, {},
559
0
               "Invalid argument for --convert-to-sampled-image: %s",
560
0
               pass_args.c_str());
561
0
        return false;
562
0
      }
563
0
      RegisterPass(CreateConvertToSampledImagePass(
564
0
          std::move(*descriptor_set_binding_pairs)));
565
0
    } else {
566
0
      Errorf(consumer(), nullptr, {},
567
0
             "Invalid pairs of descriptor set and binding '%s'. Expected a "
568
0
             "string of <descriptor set>:<binding> pairs.",
569
0
             pass_args.c_str());
570
0
      return false;
571
0
    }
572
0
  } else if (pass_name == "struct-packing") {
573
0
    if (pass_args.size() == 0) {
574
0
      Error(consumer(), nullptr, {},
575
0
            "--struct-packing requires a name:rule argument.");
576
0
      return false;
577
0
    }
578
579
0
    auto separator_pos = pass_args.find(':');
580
0
    if (separator_pos == std::string::npos || separator_pos == 0 ||
581
0
        separator_pos + 1 == pass_args.size()) {
582
0
      Errorf(consumer(), nullptr, {},
583
0
             "Invalid argument for --struct-packing: %s", pass_args.c_str());
584
0
      return false;
585
0
    }
586
587
0
    const std::string struct_name = pass_args.substr(0, separator_pos);
588
0
    const std::string rule_name = pass_args.substr(separator_pos + 1);
589
590
0
    RegisterPass(
591
0
        CreateStructPackingPass(struct_name.c_str(), rule_name.c_str()));
592
0
  } else if (pass_name == "switch-descriptorset") {
593
0
    if (pass_args.size() == 0) {
594
0
      Error(consumer(), nullptr, {},
595
0
            "--switch-descriptorset requires a from:to argument.");
596
0
      return false;
597
0
    }
598
0
    uint32_t from_set = 0, to_set = 0;
599
0
    const char* start = pass_args.data();
600
0
    const char* end = pass_args.data() + pass_args.size();
601
602
0
    auto result = std::from_chars(start, end, from_set);
603
0
    if (result.ec != std::errc()) {
604
0
      Errorf(consumer(), nullptr, {},
605
0
             "Invalid argument for --switch-descriptorset: %s",
606
0
             pass_args.c_str());
607
0
      return false;
608
0
    }
609
0
    start = result.ptr;
610
0
    if (start[0] != ':') {
611
0
      Errorf(consumer(), nullptr, {},
612
0
             "Invalid argument for --switch-descriptorset: %s",
613
0
             pass_args.c_str());
614
0
      return false;
615
0
    }
616
0
    start++;
617
0
    result = std::from_chars(start, end, to_set);
618
0
    if (result.ec != std::errc() || result.ptr != end) {
619
0
      Errorf(consumer(), nullptr, {},
620
0
             "Invalid argument for --switch-descriptorset: %s",
621
0
             pass_args.c_str());
622
0
      return false;
623
0
    }
624
0
    RegisterPass(CreateSwitchDescriptorSetPass(from_set, to_set));
625
0
  } else if (pass_name == "modify-maximal-reconvergence") {
626
0
    if (pass_args.size() == 0) {
627
0
      Error(consumer(), nullptr, {},
628
0
            "--modify-maximal-reconvergence requires an argument");
629
0
      return false;
630
0
    }
631
0
    if (pass_args == "add") {
632
0
      RegisterPass(CreateModifyMaximalReconvergencePass(true));
633
0
    } else if (pass_args == "remove") {
634
0
      RegisterPass(CreateModifyMaximalReconvergencePass(false));
635
0
    } else {
636
0
      Errorf(consumer(), nullptr, {},
637
0
             "Invalid argument for --modify-maximal-reconvergence: %s (must be "
638
0
             "'add' or 'remove')",
639
0
             pass_args.c_str());
640
0
      return false;
641
0
    }
642
0
  } else if (pass_name == "trim-capabilities") {
643
0
    RegisterPass(CreateTrimCapabilitiesPass());
644
0
  } else if (pass_name == "split-combined-image-sampler") {
645
0
    RegisterPass(CreateSplitCombinedImageSamplerPass());
646
0
  } else if (pass_name == "resolve-binding-conflicts") {
647
0
    RegisterPass(CreateResolveBindingConflictsPass());
648
0
  } else if (pass_name == "canonicalize-ids") {
649
0
    RegisterPass(CreateCanonicalizeIdsPass());
650
0
  } else {
651
0
    Errorf(consumer(), nullptr, {},
652
0
           "Unknown flag '--%s'. Use --help for a list of valid flags",
653
0
           pass_name.c_str());
654
0
    return false;
655
0
  }
656
657
0
  return true;
658
0
}
659
660
0
void Optimizer::SetTargetEnv(const spv_target_env env) {
661
0
  impl_->target_env = env;
662
0
}
663
664
bool Optimizer::Run(const uint32_t* original_binary,
665
                    const size_t original_binary_size,
666
0
                    std::vector<uint32_t>* optimized_binary) const {
667
0
  return Run(original_binary, original_binary_size, optimized_binary,
668
0
             OptimizerOptions());
669
0
}
670
671
bool Optimizer::Run(const uint32_t* original_binary,
672
                    const size_t original_binary_size,
673
                    std::vector<uint32_t>* optimized_binary,
674
                    const ValidatorOptions& validator_options,
675
0
                    bool skip_validation) const {
676
0
  OptimizerOptions opt_options;
677
0
  opt_options.set_run_validator(!skip_validation);
678
0
  opt_options.set_validator_options(validator_options);
679
0
  return Run(original_binary, original_binary_size, optimized_binary,
680
0
             opt_options);
681
0
}
682
683
bool Optimizer::Run(const uint32_t* original_binary,
684
                    const size_t original_binary_size,
685
                    std::vector<uint32_t>* optimized_binary,
686
190
                    const spv_optimizer_options opt_options) const {
687
190
  spvtools::SpirvTools tools(impl_->target_env);
688
190
  tools.SetMessageConsumer(impl_->pass_manager.consumer());
689
190
  if (opt_options->run_validator_ &&
690
190
      !tools.Validate(original_binary, original_binary_size,
691
190
                      &opt_options->val_options_)) {
692
62
    return false;
693
62
  }
694
695
128
  std::unique_ptr<opt::IRContext> context = BuildModule(
696
128
      impl_->target_env, consumer(), original_binary, original_binary_size);
697
128
  if (context == nullptr) return false;
698
699
128
  context->set_max_id_bound(opt_options->max_id_bound_);
700
128
  context->set_preserve_bindings(opt_options->preserve_bindings_);
701
128
  context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
702
703
128
  impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
704
128
  impl_->pass_manager.SetTargetEnv(impl_->target_env);
705
128
  auto status = impl_->pass_manager.Run(context.get());
706
707
128
  if (status == opt::Pass::Status::Failure) {
708
0
    return false;
709
0
  }
710
711
#ifndef NDEBUG
712
  // We do not keep the result id of DebugScope in struct DebugScope.
713
  // Instead, we assign random ids for them, which results in integrity
714
  // check failures. In addition, propagating the OpLine/OpNoLine to preserve
715
  // the debug information through transformations results in integrity
716
  // check failures. We want to skip the integrity check when the module
717
  // contains DebugScope or OpLine/OpNoLine instructions.
718
  if (status == opt::Pass::Status::SuccessWithoutChange &&
719
      !context->module()->ContainsDebugInfo()) {
720
    std::vector<uint32_t> optimized_binary_with_nop;
721
    context->module()->ToBinary(&optimized_binary_with_nop,
722
                                /* skip_nop = */ false);
723
    assert(optimized_binary_with_nop.size() == original_binary_size &&
724
           "Binary size unexpectedly changed despite the optimizer saying "
725
           "there was no change");
726
727
    // Compare the magic number to make sure the binaries were encoded in the
728
    // endianness.  If not, the contents of the binaries will be different, so
729
    // do not check the contents.
730
    if (optimized_binary_with_nop[0] == original_binary[0]) {
731
      assert(memcmp(optimized_binary_with_nop.data(), original_binary,
732
                    original_binary_size) == 0 &&
733
             "Binary content unexpectedly changed despite the optimizer saying "
734
             "there was no change");
735
    }
736
  }
737
#endif  // !NDEBUG
738
739
  // Note that |original_binary| and |optimized_binary| may share the same
740
  // buffer and the below will invalidate |original_binary|.
741
128
  optimized_binary->clear();
742
128
  context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
743
744
128
  return true;
745
128
}
746
747
0
Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
748
0
  impl_->pass_manager.SetPrintAll(out);
749
0
  return *this;
750
0
}
751
752
0
Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
753
0
  impl_->pass_manager.SetTimeReport(out);
754
0
  return *this;
755
0
}
756
757
0
Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
758
0
  impl_->pass_manager.SetValidateAfterAll(validate);
759
0
  return *this;
760
0
}
761
762
0
Optimizer::PassToken CreateNullPass() {
763
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
764
0
}
765
766
190
Optimizer::PassToken CreateStripDebugInfoPass() {
767
190
  return MakeUnique<Optimizer::PassToken::Impl>(
768
190
      MakeUnique<opt::StripDebugInfoPass>());
769
190
}
770
771
0
Optimizer::PassToken CreateStripReflectInfoPass() {
772
0
  return CreateStripNonSemanticInfoPass();
773
0
}
774
775
0
Optimizer::PassToken CreateStripNonSemanticInfoPass() {
776
0
  return MakeUnique<Optimizer::PassToken::Impl>(
777
0
      MakeUnique<opt::StripNonSemanticInfoPass>());
778
0
}
779
780
190
Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
781
190
  return MakeUnique<Optimizer::PassToken::Impl>(
782
190
      MakeUnique<opt::EliminateDeadFunctionsPass>());
783
190
}
784
785
68
Optimizer::PassToken CreateEliminateDeadMembersPass() {
786
68
  return MakeUnique<Optimizer::PassToken::Impl>(
787
68
      MakeUnique<opt::EliminateDeadMembersPass>());
788
68
}
789
790
Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
791
0
    const std::unordered_map<uint32_t, std::string>& id_value_map) {
792
0
  return MakeUnique<Optimizer::PassToken::Impl>(
793
0
      MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
794
0
}
795
796
Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
797
0
    const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
798
0
  return MakeUnique<Optimizer::PassToken::Impl>(
799
0
      MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
800
0
}
801
802
0
Optimizer::PassToken CreateFlattenDecorationPass() {
803
0
  return MakeUnique<Optimizer::PassToken::Impl>(
804
0
      MakeUnique<opt::FlattenDecorationPass>());
805
0
}
806
807
0
Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
808
0
  return MakeUnique<Optimizer::PassToken::Impl>(
809
0
      MakeUnique<opt::FreezeSpecConstantValuePass>());
810
0
}
811
812
0
Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
813
0
  return MakeUnique<Optimizer::PassToken::Impl>(
814
0
      MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
815
0
}
816
817
0
Optimizer::PassToken CreateUnifyConstantPass() {
818
0
  return MakeUnique<Optimizer::PassToken::Impl>(
819
0
      MakeUnique<opt::UnifyConstantPass>());
820
0
}
821
822
0
Optimizer::PassToken CreateEliminateDeadConstantPass() {
823
0
  return MakeUnique<Optimizer::PassToken::Impl>(
824
0
      MakeUnique<opt::EliminateDeadConstantPass>());
825
0
}
826
827
0
Optimizer::PassToken CreateDeadVariableEliminationPass() {
828
0
  return MakeUnique<Optimizer::PassToken::Impl>(
829
0
      MakeUnique<opt::DeadVariableElimination>());
830
0
}
831
832
0
Optimizer::PassToken CreateStrengthReductionPass() {
833
0
  return MakeUnique<Optimizer::PassToken::Impl>(
834
0
      MakeUnique<opt::StrengthReductionPass>());
835
0
}
836
837
380
Optimizer::PassToken CreateBlockMergePass() {
838
380
  return MakeUnique<Optimizer::PassToken::Impl>(
839
380
      MakeUnique<opt::BlockMergePass>());
840
380
}
841
842
190
Optimizer::PassToken CreateInlineExhaustivePass() {
843
190
  return MakeUnique<Optimizer::PassToken::Impl>(
844
190
      MakeUnique<opt::InlineExhaustivePass>());
845
190
}
846
847
0
Optimizer::PassToken CreateInlineOpaquePass() {
848
0
  return MakeUnique<Optimizer::PassToken::Impl>(
849
0
      MakeUnique<opt::InlineOpaquePass>());
850
0
}
851
852
312
Optimizer::PassToken CreateLocalAccessChainConvertPass() {
853
312
  return MakeUnique<Optimizer::PassToken::Impl>(
854
312
      MakeUnique<opt::LocalAccessChainConvertPass>());
855
312
}
856
857
434
Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
858
434
  return MakeUnique<Optimizer::PassToken::Impl>(
859
434
      MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
860
434
}
861
862
502
Optimizer::PassToken CreateLocalSingleStoreElimPass() {
863
502
  return MakeUnique<Optimizer::PassToken::Impl>(
864
502
      MakeUnique<opt::LocalSingleStoreElimPass>());
865
502
}
866
867
0
Optimizer::PassToken CreateInsertExtractElimPass() {
868
0
  return MakeUnique<Optimizer::PassToken::Impl>(
869
0
      MakeUnique<opt::SimplificationPass>());
870
0
}
871
872
190
Optimizer::PassToken CreateDeadInsertElimPass() {
873
190
  return MakeUnique<Optimizer::PassToken::Impl>(
874
190
      MakeUnique<opt::DeadInsertElimPass>());
875
190
}
876
877
692
Optimizer::PassToken CreateDeadBranchElimPass() {
878
692
  return MakeUnique<Optimizer::PassToken::Impl>(
879
692
      MakeUnique<opt::DeadBranchElimPass>());
880
692
}
881
882
258
Optimizer::PassToken CreateLocalMultiStoreElimPass() {
883
258
  return MakeUnique<Optimizer::PassToken::Impl>(
884
258
      MakeUnique<opt::SSARewritePass>());
885
258
}
886
887
0
Optimizer::PassToken CreateAggressiveDCEPass() {
888
0
  return MakeUnique<Optimizer::PassToken::Impl>(
889
0
      MakeUnique<opt::AggressiveDCEPass>(false, false));
890
0
}
891
892
1.18k
Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
893
1.18k
  return MakeUnique<Optimizer::PassToken::Impl>(
894
1.18k
      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
895
1.18k
}
896
897
Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
898
0
                                             bool remove_outputs) {
899
0
  return MakeUnique<Optimizer::PassToken::Impl>(
900
0
      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs));
901
0
}
902
903
0
Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
904
0
  return MakeUnique<Optimizer::PassToken::Impl>(
905
0
      MakeUnique<opt::RemoveUnusedInterfaceVariablesPass>());
906
0
}
907
908
0
Optimizer::PassToken CreatePropagateLineInfoPass() {
909
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
910
0
}
911
912
0
Optimizer::PassToken CreateRedundantLineInfoElimPass() {
913
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
914
0
}
915
916
0
Optimizer::PassToken CreateCompactIdsPass() {
917
0
  return MakeUnique<Optimizer::PassToken::Impl>(
918
0
      MakeUnique<opt::CompactIdsPass>());
919
0
}
920
921
190
Optimizer::PassToken CreateMergeReturnPass() {
922
190
  return MakeUnique<Optimizer::PassToken::Impl>(
923
190
      MakeUnique<opt::MergeReturnPass>());
924
190
}
925
926
0
std::vector<const char*> Optimizer::GetPassNames() const {
927
0
  std::vector<const char*> v;
928
0
  for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
929
0
    v.push_back(impl_->pass_manager.GetPass(i)->name());
930
0
  }
931
0
  return v;
932
0
}
933
934
68
Optimizer::PassToken CreateCFGCleanupPass() {
935
68
  return MakeUnique<Optimizer::PassToken::Impl>(
936
68
      MakeUnique<opt::CFGCleanupPass>());
937
68
}
938
939
0
Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
940
0
  return MakeUnique<Optimizer::PassToken::Impl>(
941
0
      MakeUnique<opt::LocalRedundancyEliminationPass>());
942
0
}
943
944
0
Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
945
0
  return MakeUnique<Optimizer::PassToken::Impl>(
946
0
      MakeUnique<opt::LoopFissionPass>(threshold));
947
0
}
948
949
0
Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
950
0
  return MakeUnique<Optimizer::PassToken::Impl>(
951
0
      MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
952
0
}
953
954
0
Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
955
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
956
0
}
957
958
0
Optimizer::PassToken CreateLoopPeelingPass() {
959
0
  return MakeUnique<Optimizer::PassToken::Impl>(
960
0
      MakeUnique<opt::LoopPeelingPass>());
961
0
}
962
963
0
Optimizer::PassToken CreateLoopUnswitchPass() {
964
0
  return MakeUnique<Optimizer::PassToken::Impl>(
965
0
      MakeUnique<opt::LoopUnswitchPass>());
966
0
}
967
968
0
Optimizer::PassToken CreateLegalizeMultidimArrayPass() {
969
0
  return MakeUnique<Optimizer::PassToken::Impl>(
970
0
      MakeUnique<opt::LegalizeMultidimArrayPass>());
971
0
}
972
973
312
Optimizer::PassToken CreateRedundancyEliminationPass() {
974
312
  return MakeUnique<Optimizer::PassToken::Impl>(
975
312
      MakeUnique<opt::RedundancyEliminationPass>());
976
312
}
977
978
0
Optimizer::PassToken CreateRemoveDuplicatesPass() {
979
0
  return MakeUnique<Optimizer::PassToken::Impl>(
980
0
      MakeUnique<opt::RemoveDuplicatesPass>());
981
0
}
982
983
380
Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
984
380
  return MakeUnique<Optimizer::PassToken::Impl>(
985
380
      MakeUnique<opt::ScalarReplacementPass>(size_limit));
986
380
}
987
988
190
Optimizer::PassToken CreatePrivateToLocalPass() {
989
190
  return MakeUnique<Optimizer::PassToken::Impl>(
990
190
      MakeUnique<opt::PrivateToLocalPass>());
991
190
}
992
993
190
Optimizer::PassToken CreateCCPPass() {
994
190
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
995
190
}
996
997
0
Optimizer::PassToken CreateWorkaround1209Pass() {
998
0
  return MakeUnique<Optimizer::PassToken::Impl>(
999
0
      MakeUnique<opt::Workaround1209>());
1000
0
}
1001
1002
190
Optimizer::PassToken CreateIfConversionPass() {
1003
190
  return MakeUnique<Optimizer::PassToken::Impl>(
1004
190
      MakeUnique<opt::IfConversion>());
1005
190
}
1006
1007
0
Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
1008
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1009
0
      MakeUnique<opt::ReplaceInvalidOpcodePass>());
1010
0
}
1011
1012
570
Optimizer::PassToken CreateSimplificationPass() {
1013
570
  return MakeUnique<Optimizer::PassToken::Impl>(
1014
570
      MakeUnique<opt::SimplificationPass>());
1015
570
}
1016
1017
190
Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
1018
190
  return MakeUnique<Optimizer::PassToken::Impl>(
1019
190
      MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
1020
190
}
1021
1022
122
Optimizer::PassToken CreateSSARewritePass() {
1023
122
  return MakeUnique<Optimizer::PassToken::Impl>(
1024
122
      MakeUnique<opt::SSARewritePass>());
1025
122
}
1026
1027
190
Optimizer::PassToken CreateCopyPropagateArraysPass() {
1028
190
  return MakeUnique<Optimizer::PassToken::Impl>(
1029
190
      MakeUnique<opt::CopyPropagateArrays>());
1030
190
}
1031
1032
190
Optimizer::PassToken CreateVectorDCEPass() {
1033
190
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
1034
190
}
1035
1036
Optimizer::PassToken CreateReduceLoadSizePass(
1037
122
    double load_replacement_threshold) {
1038
122
  return MakeUnique<Optimizer::PassToken::Impl>(
1039
122
      MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
1040
122
}
1041
1042
122
Optimizer::PassToken CreateCombineAccessChainsPass() {
1043
122
  return MakeUnique<Optimizer::PassToken::Impl>(
1044
122
      MakeUnique<opt::CombineAccessChains>());
1045
122
}
1046
1047
0
Optimizer::PassToken CreateUpgradeMemoryModelPass() {
1048
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1049
0
      MakeUnique<opt::UpgradeMemoryModel>());
1050
0
}
1051
1052
0
Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
1053
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1054
0
      MakeUnique<opt::ConvertToHalfPass>());
1055
0
}
1056
1057
0
Optimizer::PassToken CreateRelaxFloatOpsPass() {
1058
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1059
0
      MakeUnique<opt::RelaxFloatOpsPass>());
1060
0
}
1061
1062
0
Optimizer::PassToken CreateCodeSinkingPass() {
1063
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1064
0
      MakeUnique<opt::CodeSinkingPass>());
1065
0
}
1066
1067
0
Optimizer::PassToken CreateFixStorageClassPass() {
1068
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1069
0
      MakeUnique<opt::FixStorageClass>());
1070
0
}
1071
1072
0
Optimizer::PassToken CreateGraphicsRobustAccessPass() {
1073
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1074
0
      MakeUnique<opt::GraphicsRobustAccessPass>());
1075
0
}
1076
1077
0
Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
1078
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1079
0
      MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
1080
0
}
1081
1082
0
Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
1083
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1084
0
      MakeUnique<opt::SpreadVolatileSemantics>());
1085
0
}
1086
1087
0
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
1088
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1089
0
      MakeUnique<opt::DescriptorScalarReplacement>(
1090
0
          /* flatten_composites= */ true, /* flatten_arrays= */ true));
1091
0
}
1092
1093
0
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass() {
1094
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1095
0
      MakeUnique<opt::DescriptorScalarReplacement>(
1096
0
          /* flatten_composites= */ true, /* flatten_arrays= */ false));
1097
0
}
1098
1099
0
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass() {
1100
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1101
0
      MakeUnique<opt::DescriptorScalarReplacement>(
1102
0
          /* flatten_composites= */ false, /* flatten_arrays= */ true));
1103
0
}
1104
1105
190
Optimizer::PassToken CreateWrapOpKillPass() {
1106
190
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
1107
190
}
1108
1109
0
Optimizer::PassToken CreateAmdExtToKhrPass() {
1110
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1111
0
      MakeUnique<opt::AmdExtensionToKhrPass>());
1112
0
}
1113
1114
0
Optimizer::PassToken CreateInterpolateFixupPass() {
1115
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1116
0
      MakeUnique<opt::InterpFixupPass>());
1117
0
}
1118
1119
0
Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
1120
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1121
0
      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1122
0
                                                     /* safe_mode */ false));
1123
0
}
1124
1125
0
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
1126
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1127
0
      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Output,
1128
0
                                                     /* safe_mode */ false));
1129
0
}
1130
1131
0
Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
1132
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1133
0
      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1134
0
                                                     /* safe_mode */ true));
1135
0
}
1136
1137
Optimizer::PassToken CreateAnalyzeLiveInputPass(
1138
    std::unordered_set<uint32_t>* live_locs,
1139
0
    std::unordered_set<uint32_t>* live_builtins) {
1140
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1141
0
      MakeUnique<opt::AnalyzeLiveInputPass>(live_locs, live_builtins));
1142
0
}
1143
1144
Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
1145
    std::unordered_set<uint32_t>* live_locs,
1146
0
    std::unordered_set<uint32_t>* live_builtins) {
1147
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1148
0
      MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
1149
0
}
1150
1151
Optimizer::PassToken CreateConvertToSampledImagePass(
1152
    const std::vector<opt::DescriptorSetAndBinding>&
1153
0
        descriptor_set_binding_pairs) {
1154
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1155
0
      MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
1156
0
}
1157
1158
0
Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() {
1159
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1160
0
      MakeUnique<opt::InterfaceVariableScalarReplacement>());
1161
0
}
1162
1163
0
Optimizer::PassToken CreateRemoveDontInlinePass() {
1164
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1165
0
      MakeUnique<opt::RemoveDontInline>());
1166
0
}
1167
1168
0
Optimizer::PassToken CreateFixFuncCallArgumentsPass() {
1169
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1170
0
      MakeUnique<opt::FixFuncCallArgumentsPass>());
1171
0
}
1172
1173
0
Optimizer::PassToken CreateTrimCapabilitiesPass() {
1174
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1175
0
      MakeUnique<opt::TrimCapabilitiesPass>());
1176
0
}
1177
1178
Optimizer::PassToken CreateStructPackingPass(const char* structToPack,
1179
0
                                             const char* packingRule) {
1180
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1181
0
      MakeUnique<opt::StructPackingPass>(
1182
0
          structToPack,
1183
0
          opt::StructPackingPass::ParsePackingRuleFromString(packingRule)));
1184
0
}
1185
1186
0
Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) {
1187
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1188
0
      MakeUnique<opt::SwitchDescriptorSetPass>(from, to));
1189
0
}
1190
1191
0
Optimizer::PassToken CreateInvocationInterlockPlacementPass() {
1192
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1193
0
      MakeUnique<opt::InvocationInterlockPlacementPass>());
1194
0
}
1195
1196
0
Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) {
1197
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1198
0
      MakeUnique<opt::ModifyMaximalReconvergence>(add));
1199
0
}
1200
1201
0
Optimizer::PassToken CreateOpExtInstWithForwardReferenceFixupPass() {
1202
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1203
0
      MakeUnique<opt::OpExtInstWithForwardReferenceFixupPass>());
1204
0
}
1205
1206
0
Optimizer::PassToken CreateSplitCombinedImageSamplerPass() {
1207
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1208
0
      MakeUnique<opt::SplitCombinedImageSamplerPass>());
1209
0
}
1210
1211
0
Optimizer::PassToken CreateResolveBindingConflictsPass() {
1212
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1213
0
      MakeUnique<opt::ResolveBindingConflictsPass>());
1214
0
}
1215
1216
0
Optimizer::PassToken CreateCanonicalizeIdsPass() {
1217
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1218
0
      MakeUnique<opt::CanonicalizeIdsPass>());
1219
0
}
1220
1221
}  // namespace spvtools
1222
1223
extern "C" {
1224
1225
0
SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) {
1226
0
  return reinterpret_cast<spv_optimizer_t*>(new spvtools::Optimizer(env));
1227
0
}
1228
1229
0
SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) {
1230
0
  delete reinterpret_cast<spvtools::Optimizer*>(optimizer);
1231
0
}
1232
1233
SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
1234
0
    spv_optimizer_t* optimizer, spv_message_consumer consumer) {
1235
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1236
0
      SetMessageConsumer(
1237
0
          [consumer](spv_message_level_t level, const char* source,
1238
0
                     const spv_position_t& position, const char* message) {
1239
0
            return consumer(level, source, &position, message);
1240
0
          });
1241
0
}
1242
1243
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
1244
0
    spv_optimizer_t* optimizer) {
1245
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1246
0
      RegisterLegalizationPasses();
1247
0
}
1248
1249
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
1250
0
    spv_optimizer_t* optimizer) {
1251
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1252
0
      RegisterPerformancePasses();
1253
0
}
1254
1255
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
1256
0
    spv_optimizer_t* optimizer) {
1257
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->RegisterSizePasses();
1258
0
}
1259
1260
SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
1261
    spv_optimizer_t* optimizer, const char* flag)
1262
0
{
1263
0
  return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1264
0
      RegisterPassFromFlag(flag);
1265
0
}
1266
1267
SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
1268
0
    spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1269
0
  std::vector<std::string> opt_flags =
1270
0
      spvtools::GetVectorOfStrings(flags, flag_count);
1271
0
  return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1272
0
      ->RegisterPassesFromFlags(opt_flags, false);
1273
0
}
1274
1275
SPIRV_TOOLS_EXPORT bool
1276
spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(
1277
0
    spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1278
0
  std::vector<std::string> opt_flags =
1279
0
      spvtools::GetVectorOfStrings(flags, flag_count);
1280
0
  return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1281
0
      ->RegisterPassesFromFlags(opt_flags, true);
1282
0
}
1283
1284
SPIRV_TOOLS_EXPORT
1285
spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer,
1286
                             const uint32_t* binary,
1287
                             const size_t word_count,
1288
                             spv_binary* optimized_binary,
1289
0
                             const spv_optimizer_options options) {
1290
0
  std::vector<uint32_t> optimized;
1291
1292
0
  if (!reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1293
0
      Run(binary, word_count, &optimized, options)) {
1294
0
    return SPV_ERROR_INTERNAL;
1295
0
  }
1296
1297
0
  auto result_binary = new spv_binary_t();
1298
0
  if (!result_binary) {
1299
0
      *optimized_binary = nullptr;
1300
0
      return SPV_ERROR_OUT_OF_MEMORY;
1301
0
  }
1302
1303
0
  result_binary->code = new uint32_t[optimized.size()];
1304
0
  if (!result_binary->code) {
1305
0
      delete result_binary;
1306
0
      *optimized_binary = nullptr;
1307
0
      return SPV_ERROR_OUT_OF_MEMORY;
1308
0
  }
1309
0
  result_binary->wordCount = optimized.size();
1310
1311
0
  memcpy(result_binary->code, optimized.data(),
1312
0
         optimized.size() * sizeof(uint32_t));
1313
1314
0
  *optimized_binary = result_binary;
1315
1316
0
  return SPV_SUCCESS;
1317
0
}
1318
1319
}  // extern "C"