Coverage Report

Created: 2025-08-28 07:18

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