Coverage Report

Created: 2025-06-09 06:35

/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
88.1k
  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
88.1k
    : 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
88.1k
Optimizer::PassToken::~PassToken() {}
68
69
struct Optimizer::Impl {
70
1.83k
  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.83k
Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {
78
1.83k
  assert(env != SPV_ENV_WEBGPU_0);
79
1.83k
}
80
81
1.83k
Optimizer::~Optimizer() {}
82
83
1.83k
void Optimizer::SetMessageConsumer(MessageConsumer c) {
84
  // All passes' message consumer needs to be updated.
85
1.83k
  for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
86
0
    impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
87
0
  }
88
1.83k
  impl_->pass_manager.SetMessageConsumer(std::move(c));
89
1.83k
}
90
91
89.9k
const MessageConsumer& Optimizer::consumer() const {
92
89.9k
  return impl_->pass_manager.consumer();
93
89.9k
}
94
95
88.1k
Optimizer& Optimizer::RegisterPass(PassToken&& p) {
96
  // Change to use the pass manager's consumer.
97
88.1k
  p.impl_->pass->SetMessageConsumer(consumer());
98
88.1k
  impl_->pass_manager.AddPass(std::move(p.impl_->pass));
99
88.1k
  return *this;
100
88.1k
}
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
576
Optimizer& Optimizer::RegisterLegalizationPasses(bool preserve_interface) {
124
576
  return
125
      // Wrap OpKill instructions so all other code can be inlined.
126
576
      RegisterPass(CreateWrapOpKillPass())
127
          // Remove unreachable block so that merge return works.
128
576
          .RegisterPass(CreateDeadBranchElimPass())
129
          // Merge the returns so we can inline.
130
576
          .RegisterPass(CreateMergeReturnPass())
131
          // Make sure uses and definitions are in the same function.
132
576
          .RegisterPass(CreateInlineExhaustivePass())
133
          // Make private variable function scope
134
576
          .RegisterPass(CreateEliminateDeadFunctionsPass())
135
576
          .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
576
          .RegisterPass(CreateFixStorageClassPass())
140
          // Propagate the value stored to the loads in very simple cases.
141
576
          .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
142
576
          .RegisterPass(CreateLocalSingleStoreElimPass())
143
576
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
144
          // Split up aggregates so they are easier to deal with.
145
576
          .RegisterPass(CreateScalarReplacementPass(0))
146
          // Remove loads and stores so everything is in intermediate values.
147
          // Takes care of copy propagation of non-members.
148
576
          .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
149
576
          .RegisterPass(CreateLocalSingleStoreElimPass())
150
576
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
151
576
          .RegisterPass(CreateLocalMultiStoreElimPass())
152
576
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
153
          // Propagate constants to get as many constant conditions on branches
154
          // as possible.
155
576
          .RegisterPass(CreateCCPPass())
156
576
          .RegisterPass(CreateLoopUnrollPass(true))
157
576
          .RegisterPass(CreateDeadBranchElimPass())
158
          // Copy propagate members.  Cleans up code sequences generated by
159
          // scalar replacement.  Also important for removing OpPhi nodes.
160
576
          .RegisterPass(CreateSimplificationPass())
161
576
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
162
576
          .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
576
          .RegisterPass(CreateVectorDCEPass())
168
576
          .RegisterPass(CreateDeadInsertElimPass())
169
576
          .RegisterPass(CreateReduceLoadSizePass())
170
576
          .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
171
576
          .RegisterPass(CreateRemoveUnusedInterfaceVariablesPass())
172
576
          .RegisterPass(CreateInterpolateFixupPass())
173
576
          .RegisterPass(CreateInvocationInterlockPlacementPass())
174
576
          .RegisterPass(CreateOpExtInstWithForwardReferenceFixupPass());
175
576
}
176
177
576
Optimizer& Optimizer::RegisterLegalizationPasses() {
178
576
  return RegisterLegalizationPasses(false);
179
576
}
180
181
1.30k
Optimizer& Optimizer::RegisterPerformancePasses(bool preserve_interface) {
182
1.30k
  return RegisterPass(CreateWrapOpKillPass())
183
1.30k
      .RegisterPass(CreateDeadBranchElimPass())
184
1.30k
      .RegisterPass(CreateMergeReturnPass())
185
1.30k
      .RegisterPass(CreateInlineExhaustivePass())
186
1.30k
      .RegisterPass(CreateEliminateDeadFunctionsPass())
187
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
188
1.30k
      .RegisterPass(CreatePrivateToLocalPass())
189
1.30k
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
190
1.30k
      .RegisterPass(CreateLocalSingleStoreElimPass())
191
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
192
1.30k
      .RegisterPass(CreateScalarReplacementPass(0))
193
1.30k
      .RegisterPass(CreateLocalAccessChainConvertPass())
194
1.30k
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
195
1.30k
      .RegisterPass(CreateLocalSingleStoreElimPass())
196
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
197
1.30k
      .RegisterPass(CreateLocalMultiStoreElimPass())
198
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
199
1.30k
      .RegisterPass(CreateCCPPass())
200
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
201
1.30k
      .RegisterPass(CreateLoopUnrollPass(true))
202
1.30k
      .RegisterPass(CreateDeadBranchElimPass())
203
1.30k
      .RegisterPass(CreateRedundancyEliminationPass())
204
1.30k
      .RegisterPass(CreateCombineAccessChainsPass())
205
1.30k
      .RegisterPass(CreateSimplificationPass())
206
1.30k
      .RegisterPass(CreateScalarReplacementPass(0))
207
1.30k
      .RegisterPass(CreateLocalAccessChainConvertPass())
208
1.30k
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
209
1.30k
      .RegisterPass(CreateLocalSingleStoreElimPass())
210
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
211
1.30k
      .RegisterPass(CreateSSARewritePass())
212
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
213
1.30k
      .RegisterPass(CreateVectorDCEPass())
214
1.30k
      .RegisterPass(CreateDeadInsertElimPass())
215
1.30k
      .RegisterPass(CreateDeadBranchElimPass())
216
1.30k
      .RegisterPass(CreateSimplificationPass())
217
1.30k
      .RegisterPass(CreateIfConversionPass())
218
1.30k
      .RegisterPass(CreateCopyPropagateArraysPass())
219
1.30k
      .RegisterPass(CreateReduceLoadSizePass())
220
1.30k
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
221
1.30k
      .RegisterPass(CreateBlockMergePass())
222
1.30k
      .RegisterPass(CreateRedundancyEliminationPass())
223
1.30k
      .RegisterPass(CreateDeadBranchElimPass())
224
1.30k
      .RegisterPass(CreateBlockMergePass())
225
1.30k
      .RegisterPass(CreateSimplificationPass());
226
1.30k
}
227
228
1.30k
Optimizer& Optimizer::RegisterPerformancePasses() {
229
1.30k
  return RegisterPerformancePasses(false);
230
1.30k
}
231
232
362
Optimizer& Optimizer::RegisterSizePasses(bool preserve_interface) {
233
362
  return RegisterPass(CreateWrapOpKillPass())
234
362
      .RegisterPass(CreateDeadBranchElimPass())
235
362
      .RegisterPass(CreateMergeReturnPass())
236
362
      .RegisterPass(CreateInlineExhaustivePass())
237
362
      .RegisterPass(CreateEliminateDeadFunctionsPass())
238
362
      .RegisterPass(CreatePrivateToLocalPass())
239
362
      .RegisterPass(CreateScalarReplacementPass(0))
240
362
      .RegisterPass(CreateLocalMultiStoreElimPass())
241
362
      .RegisterPass(CreateCCPPass())
242
362
      .RegisterPass(CreateLoopUnrollPass(true))
243
362
      .RegisterPass(CreateDeadBranchElimPass())
244
362
      .RegisterPass(CreateSimplificationPass())
245
362
      .RegisterPass(CreateScalarReplacementPass(0))
246
362
      .RegisterPass(CreateLocalSingleStoreElimPass())
247
362
      .RegisterPass(CreateIfConversionPass())
248
362
      .RegisterPass(CreateSimplificationPass())
249
362
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
250
362
      .RegisterPass(CreateDeadBranchElimPass())
251
362
      .RegisterPass(CreateBlockMergePass())
252
362
      .RegisterPass(CreateLocalAccessChainConvertPass())
253
362
      .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
254
362
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
255
362
      .RegisterPass(CreateCopyPropagateArraysPass())
256
362
      .RegisterPass(CreateVectorDCEPass())
257
362
      .RegisterPass(CreateDeadInsertElimPass())
258
362
      .RegisterPass(CreateEliminateDeadMembersPass())
259
362
      .RegisterPass(CreateLocalSingleStoreElimPass())
260
362
      .RegisterPass(CreateBlockMergePass())
261
362
      .RegisterPass(CreateLocalMultiStoreElimPass())
262
362
      .RegisterPass(CreateRedundancyEliminationPass())
263
362
      .RegisterPass(CreateSimplificationPass())
264
362
      .RegisterPass(CreateAggressiveDCEPass(preserve_interface))
265
362
      .RegisterPass(CreateCFGCleanupPass());
266
362
}
267
268
362
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 {
645
0
    Errorf(consumer(), nullptr, {},
646
0
           "Unknown flag '--%s'. Use --help for a list of valid flags",
647
0
           pass_name.c_str());
648
0
    return false;
649
0
  }
650
651
0
  return true;
652
0
}
653
654
0
void Optimizer::SetTargetEnv(const spv_target_env env) {
655
0
  impl_->target_env = env;
656
0
}
657
658
bool Optimizer::Run(const uint32_t* original_binary,
659
                    const size_t original_binary_size,
660
0
                    std::vector<uint32_t>* optimized_binary) const {
661
0
  return Run(original_binary, original_binary_size, optimized_binary,
662
0
             OptimizerOptions());
663
0
}
664
665
bool Optimizer::Run(const uint32_t* original_binary,
666
                    const size_t original_binary_size,
667
                    std::vector<uint32_t>* optimized_binary,
668
                    const ValidatorOptions& validator_options,
669
0
                    bool skip_validation) const {
670
0
  OptimizerOptions opt_options;
671
0
  opt_options.set_run_validator(!skip_validation);
672
0
  opt_options.set_validator_options(validator_options);
673
0
  return Run(original_binary, original_binary_size, optimized_binary,
674
0
             opt_options);
675
0
}
676
677
bool Optimizer::Run(const uint32_t* original_binary,
678
                    const size_t original_binary_size,
679
                    std::vector<uint32_t>* optimized_binary,
680
1.83k
                    const spv_optimizer_options opt_options) const {
681
1.83k
  spvtools::SpirvTools tools(impl_->target_env);
682
1.83k
  tools.SetMessageConsumer(impl_->pass_manager.consumer());
683
1.83k
  if (opt_options->run_validator_ &&
684
1.83k
      !tools.Validate(original_binary, original_binary_size,
685
1.83k
                      &opt_options->val_options_)) {
686
114
    return false;
687
114
  }
688
689
1.72k
  std::unique_ptr<opt::IRContext> context = BuildModule(
690
1.72k
      impl_->target_env, consumer(), original_binary, original_binary_size);
691
1.72k
  if (context == nullptr) return false;
692
693
1.72k
  context->set_max_id_bound(opt_options->max_id_bound_);
694
1.72k
  context->set_preserve_bindings(opt_options->preserve_bindings_);
695
1.72k
  context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
696
697
1.72k
  impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
698
1.72k
  impl_->pass_manager.SetTargetEnv(impl_->target_env);
699
1.72k
  auto status = impl_->pass_manager.Run(context.get());
700
701
1.72k
  if (status == opt::Pass::Status::Failure) {
702
0
    return false;
703
0
  }
704
705
#ifndef NDEBUG
706
  // We do not keep the result id of DebugScope in struct DebugScope.
707
  // Instead, we assign random ids for them, which results in integrity
708
  // check failures. In addition, propagating the OpLine/OpNoLine to preserve
709
  // the debug information through transformations results in integrity
710
  // check failures. We want to skip the integrity check when the module
711
  // contains DebugScope or OpLine/OpNoLine instructions.
712
  if (status == opt::Pass::Status::SuccessWithoutChange &&
713
      !context->module()->ContainsDebugInfo()) {
714
    std::vector<uint32_t> optimized_binary_with_nop;
715
    context->module()->ToBinary(&optimized_binary_with_nop,
716
                                /* skip_nop = */ false);
717
    assert(optimized_binary_with_nop.size() == original_binary_size &&
718
           "Binary size unexpectedly changed despite the optimizer saying "
719
           "there was no change");
720
721
    // Compare the magic number to make sure the binaries were encoded in the
722
    // endianness.  If not, the contents of the binaries will be different, so
723
    // do not check the contents.
724
    if (optimized_binary_with_nop[0] == original_binary[0]) {
725
      assert(memcmp(optimized_binary_with_nop.data(), original_binary,
726
                    original_binary_size) == 0 &&
727
             "Binary content unexpectedly changed despite the optimizer saying "
728
             "there was no change");
729
    }
730
  }
731
#endif  // !NDEBUG
732
733
  // Note that |original_binary| and |optimized_binary| may share the same
734
  // buffer and the below will invalidate |original_binary|.
735
1.72k
  optimized_binary->clear();
736
1.72k
  context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
737
738
1.72k
  return true;
739
1.72k
}
740
741
0
Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
742
0
  impl_->pass_manager.SetPrintAll(out);
743
0
  return *this;
744
0
}
745
746
0
Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
747
0
  impl_->pass_manager.SetTimeReport(out);
748
0
  return *this;
749
0
}
750
751
0
Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
752
0
  impl_->pass_manager.SetValidateAfterAll(validate);
753
0
  return *this;
754
0
}
755
756
0
Optimizer::PassToken CreateNullPass() {
757
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
758
0
}
759
760
1.66k
Optimizer::PassToken CreateStripDebugInfoPass() {
761
1.66k
  return MakeUnique<Optimizer::PassToken::Impl>(
762
1.66k
      MakeUnique<opt::StripDebugInfoPass>());
763
1.66k
}
764
765
0
Optimizer::PassToken CreateStripReflectInfoPass() {
766
0
  return CreateStripNonSemanticInfoPass();
767
0
}
768
769
0
Optimizer::PassToken CreateStripNonSemanticInfoPass() {
770
0
  return MakeUnique<Optimizer::PassToken::Impl>(
771
0
      MakeUnique<opt::StripNonSemanticInfoPass>());
772
0
}
773
774
2.24k
Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
775
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
776
2.24k
      MakeUnique<opt::EliminateDeadFunctionsPass>());
777
2.24k
}
778
779
362
Optimizer::PassToken CreateEliminateDeadMembersPass() {
780
362
  return MakeUnique<Optimizer::PassToken::Impl>(
781
362
      MakeUnique<opt::EliminateDeadMembersPass>());
782
362
}
783
784
Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
785
0
    const std::unordered_map<uint32_t, std::string>& id_value_map) {
786
0
  return MakeUnique<Optimizer::PassToken::Impl>(
787
0
      MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
788
0
}
789
790
Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
791
0
    const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
792
0
  return MakeUnique<Optimizer::PassToken::Impl>(
793
0
      MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
794
0
}
795
796
0
Optimizer::PassToken CreateFlattenDecorationPass() {
797
0
  return MakeUnique<Optimizer::PassToken::Impl>(
798
0
      MakeUnique<opt::FlattenDecorationPass>());
799
0
}
800
801
0
Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
802
0
  return MakeUnique<Optimizer::PassToken::Impl>(
803
0
      MakeUnique<opt::FreezeSpecConstantValuePass>());
804
0
}
805
806
0
Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
807
0
  return MakeUnique<Optimizer::PassToken::Impl>(
808
0
      MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
809
0
}
810
811
0
Optimizer::PassToken CreateUnifyConstantPass() {
812
0
  return MakeUnique<Optimizer::PassToken::Impl>(
813
0
      MakeUnique<opt::UnifyConstantPass>());
814
0
}
815
816
0
Optimizer::PassToken CreateEliminateDeadConstantPass() {
817
0
  return MakeUnique<Optimizer::PassToken::Impl>(
818
0
      MakeUnique<opt::EliminateDeadConstantPass>());
819
0
}
820
821
0
Optimizer::PassToken CreateDeadVariableEliminationPass() {
822
0
  return MakeUnique<Optimizer::PassToken::Impl>(
823
0
      MakeUnique<opt::DeadVariableElimination>());
824
0
}
825
826
0
Optimizer::PassToken CreateStrengthReductionPass() {
827
0
  return MakeUnique<Optimizer::PassToken::Impl>(
828
0
      MakeUnique<opt::StrengthReductionPass>());
829
0
}
830
831
3.32k
Optimizer::PassToken CreateBlockMergePass() {
832
3.32k
  return MakeUnique<Optimizer::PassToken::Impl>(
833
3.32k
      MakeUnique<opt::BlockMergePass>());
834
3.32k
}
835
836
2.24k
Optimizer::PassToken CreateInlineExhaustivePass() {
837
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
838
2.24k
      MakeUnique<opt::InlineExhaustivePass>());
839
2.24k
}
840
841
0
Optimizer::PassToken CreateInlineOpaquePass() {
842
0
  return MakeUnique<Optimizer::PassToken::Impl>(
843
0
      MakeUnique<opt::InlineOpaquePass>());
844
0
}
845
846
2.96k
Optimizer::PassToken CreateLocalAccessChainConvertPass() {
847
2.96k
  return MakeUnique<Optimizer::PassToken::Impl>(
848
2.96k
      MakeUnique<opt::LocalAccessChainConvertPass>());
849
2.96k
}
850
851
5.42k
Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
852
5.42k
  return MakeUnique<Optimizer::PassToken::Impl>(
853
5.42k
      MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
854
5.42k
}
855
856
5.78k
Optimizer::PassToken CreateLocalSingleStoreElimPass() {
857
5.78k
  return MakeUnique<Optimizer::PassToken::Impl>(
858
5.78k
      MakeUnique<opt::LocalSingleStoreElimPass>());
859
5.78k
}
860
861
0
Optimizer::PassToken CreateInsertExtractElimPass() {
862
0
  return MakeUnique<Optimizer::PassToken::Impl>(
863
0
      MakeUnique<opt::SimplificationPass>());
864
0
}
865
866
2.24k
Optimizer::PassToken CreateDeadInsertElimPass() {
867
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
868
2.24k
      MakeUnique<opt::DeadInsertElimPass>());
869
2.24k
}
870
871
7.44k
Optimizer::PassToken CreateDeadBranchElimPass() {
872
7.44k
  return MakeUnique<Optimizer::PassToken::Impl>(
873
7.44k
      MakeUnique<opt::DeadBranchElimPass>());
874
7.44k
}
875
876
2.60k
Optimizer::PassToken CreateLocalMultiStoreElimPass() {
877
2.60k
  return MakeUnique<Optimizer::PassToken::Impl>(
878
2.60k
      MakeUnique<opt::SSARewritePass>());
879
2.60k
}
880
881
0
Optimizer::PassToken CreateAggressiveDCEPass() {
882
0
  return MakeUnique<Optimizer::PassToken::Impl>(
883
0
      MakeUnique<opt::AggressiveDCEPass>(false, false));
884
0
}
885
886
14.3k
Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface) {
887
14.3k
  return MakeUnique<Optimizer::PassToken::Impl>(
888
14.3k
      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, false));
889
14.3k
}
890
891
Optimizer::PassToken CreateAggressiveDCEPass(bool preserve_interface,
892
0
                                             bool remove_outputs) {
893
0
  return MakeUnique<Optimizer::PassToken::Impl>(
894
0
      MakeUnique<opt::AggressiveDCEPass>(preserve_interface, remove_outputs));
895
0
}
896
897
576
Optimizer::PassToken CreateRemoveUnusedInterfaceVariablesPass() {
898
576
  return MakeUnique<Optimizer::PassToken::Impl>(
899
576
      MakeUnique<opt::RemoveUnusedInterfaceVariablesPass>());
900
576
}
901
902
0
Optimizer::PassToken CreatePropagateLineInfoPass() {
903
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
904
0
}
905
906
0
Optimizer::PassToken CreateRedundantLineInfoElimPass() {
907
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::EmptyPass>());
908
0
}
909
910
0
Optimizer::PassToken CreateCompactIdsPass() {
911
0
  return MakeUnique<Optimizer::PassToken::Impl>(
912
0
      MakeUnique<opt::CompactIdsPass>());
913
0
}
914
915
2.24k
Optimizer::PassToken CreateMergeReturnPass() {
916
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
917
2.24k
      MakeUnique<opt::MergeReturnPass>());
918
2.24k
}
919
920
0
std::vector<const char*> Optimizer::GetPassNames() const {
921
0
  std::vector<const char*> v;
922
0
  for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
923
0
    v.push_back(impl_->pass_manager.GetPass(i)->name());
924
0
  }
925
0
  return v;
926
0
}
927
928
362
Optimizer::PassToken CreateCFGCleanupPass() {
929
362
  return MakeUnique<Optimizer::PassToken::Impl>(
930
362
      MakeUnique<opt::CFGCleanupPass>());
931
362
}
932
933
0
Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
934
0
  return MakeUnique<Optimizer::PassToken::Impl>(
935
0
      MakeUnique<opt::LocalRedundancyEliminationPass>());
936
0
}
937
938
0
Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
939
0
  return MakeUnique<Optimizer::PassToken::Impl>(
940
0
      MakeUnique<opt::LoopFissionPass>(threshold));
941
0
}
942
943
0
Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
944
0
  return MakeUnique<Optimizer::PassToken::Impl>(
945
0
      MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
946
0
}
947
948
0
Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
949
0
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
950
0
}
951
952
0
Optimizer::PassToken CreateLoopPeelingPass() {
953
0
  return MakeUnique<Optimizer::PassToken::Impl>(
954
0
      MakeUnique<opt::LoopPeelingPass>());
955
0
}
956
957
0
Optimizer::PassToken CreateLoopUnswitchPass() {
958
0
  return MakeUnique<Optimizer::PassToken::Impl>(
959
0
      MakeUnique<opt::LoopUnswitchPass>());
960
0
}
961
962
2.96k
Optimizer::PassToken CreateRedundancyEliminationPass() {
963
2.96k
  return MakeUnique<Optimizer::PassToken::Impl>(
964
2.96k
      MakeUnique<opt::RedundancyEliminationPass>());
965
2.96k
}
966
967
0
Optimizer::PassToken CreateRemoveDuplicatesPass() {
968
0
  return MakeUnique<Optimizer::PassToken::Impl>(
969
0
      MakeUnique<opt::RemoveDuplicatesPass>());
970
0
}
971
972
3.90k
Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
973
3.90k
  return MakeUnique<Optimizer::PassToken::Impl>(
974
3.90k
      MakeUnique<opt::ScalarReplacementPass>(size_limit));
975
3.90k
}
976
977
2.24k
Optimizer::PassToken CreatePrivateToLocalPass() {
978
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
979
2.24k
      MakeUnique<opt::PrivateToLocalPass>());
980
2.24k
}
981
982
2.24k
Optimizer::PassToken CreateCCPPass() {
983
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
984
2.24k
}
985
986
0
Optimizer::PassToken CreateWorkaround1209Pass() {
987
0
  return MakeUnique<Optimizer::PassToken::Impl>(
988
0
      MakeUnique<opt::Workaround1209>());
989
0
}
990
991
1.66k
Optimizer::PassToken CreateIfConversionPass() {
992
1.66k
  return MakeUnique<Optimizer::PassToken::Impl>(
993
1.66k
      MakeUnique<opt::IfConversion>());
994
1.66k
}
995
996
0
Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
997
0
  return MakeUnique<Optimizer::PassToken::Impl>(
998
0
      MakeUnique<opt::ReplaceInvalidOpcodePass>());
999
0
}
1000
1001
5.56k
Optimizer::PassToken CreateSimplificationPass() {
1002
5.56k
  return MakeUnique<Optimizer::PassToken::Impl>(
1003
5.56k
      MakeUnique<opt::SimplificationPass>());
1004
5.56k
}
1005
1006
2.24k
Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
1007
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
1008
2.24k
      MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
1009
2.24k
}
1010
1011
1.30k
Optimizer::PassToken CreateSSARewritePass() {
1012
1.30k
  return MakeUnique<Optimizer::PassToken::Impl>(
1013
1.30k
      MakeUnique<opt::SSARewritePass>());
1014
1.30k
}
1015
1016
2.24k
Optimizer::PassToken CreateCopyPropagateArraysPass() {
1017
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(
1018
2.24k
      MakeUnique<opt::CopyPropagateArrays>());
1019
2.24k
}
1020
1021
2.24k
Optimizer::PassToken CreateVectorDCEPass() {
1022
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
1023
2.24k
}
1024
1025
Optimizer::PassToken CreateReduceLoadSizePass(
1026
1.87k
    double load_replacement_threshold) {
1027
1.87k
  return MakeUnique<Optimizer::PassToken::Impl>(
1028
1.87k
      MakeUnique<opt::ReduceLoadSize>(load_replacement_threshold));
1029
1.87k
}
1030
1031
1.30k
Optimizer::PassToken CreateCombineAccessChainsPass() {
1032
1.30k
  return MakeUnique<Optimizer::PassToken::Impl>(
1033
1.30k
      MakeUnique<opt::CombineAccessChains>());
1034
1.30k
}
1035
1036
0
Optimizer::PassToken CreateUpgradeMemoryModelPass() {
1037
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1038
0
      MakeUnique<opt::UpgradeMemoryModel>());
1039
0
}
1040
1041
0
Optimizer::PassToken CreateConvertRelaxedToHalfPass() {
1042
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1043
0
      MakeUnique<opt::ConvertToHalfPass>());
1044
0
}
1045
1046
0
Optimizer::PassToken CreateRelaxFloatOpsPass() {
1047
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1048
0
      MakeUnique<opt::RelaxFloatOpsPass>());
1049
0
}
1050
1051
0
Optimizer::PassToken CreateCodeSinkingPass() {
1052
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1053
0
      MakeUnique<opt::CodeSinkingPass>());
1054
0
}
1055
1056
576
Optimizer::PassToken CreateFixStorageClassPass() {
1057
576
  return MakeUnique<Optimizer::PassToken::Impl>(
1058
576
      MakeUnique<opt::FixStorageClass>());
1059
576
}
1060
1061
0
Optimizer::PassToken CreateGraphicsRobustAccessPass() {
1062
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1063
0
      MakeUnique<opt::GraphicsRobustAccessPass>());
1064
0
}
1065
1066
0
Optimizer::PassToken CreateReplaceDescArrayAccessUsingVarIndexPass() {
1067
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1068
0
      MakeUnique<opt::ReplaceDescArrayAccessUsingVarIndex>());
1069
0
}
1070
1071
0
Optimizer::PassToken CreateSpreadVolatileSemanticsPass() {
1072
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1073
0
      MakeUnique<opt::SpreadVolatileSemantics>());
1074
0
}
1075
1076
0
Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
1077
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1078
0
      MakeUnique<opt::DescriptorScalarReplacement>(
1079
0
          /* flatten_composites= */ true, /* flatten_arrays= */ true));
1080
0
}
1081
1082
0
Optimizer::PassToken CreateDescriptorCompositeScalarReplacementPass() {
1083
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1084
0
      MakeUnique<opt::DescriptorScalarReplacement>(
1085
0
          /* flatten_composites= */ true, /* flatten_arrays= */ false));
1086
0
}
1087
1088
0
Optimizer::PassToken CreateDescriptorArrayScalarReplacementPass() {
1089
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1090
0
      MakeUnique<opt::DescriptorScalarReplacement>(
1091
0
          /* flatten_composites= */ false, /* flatten_arrays= */ true));
1092
0
}
1093
1094
2.24k
Optimizer::PassToken CreateWrapOpKillPass() {
1095
2.24k
  return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
1096
2.24k
}
1097
1098
0
Optimizer::PassToken CreateAmdExtToKhrPass() {
1099
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1100
0
      MakeUnique<opt::AmdExtensionToKhrPass>());
1101
0
}
1102
1103
576
Optimizer::PassToken CreateInterpolateFixupPass() {
1104
576
  return MakeUnique<Optimizer::PassToken::Impl>(
1105
576
      MakeUnique<opt::InterpFixupPass>());
1106
576
}
1107
1108
0
Optimizer::PassToken CreateEliminateDeadInputComponentsPass() {
1109
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1110
0
      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1111
0
                                                     /* safe_mode */ false));
1112
0
}
1113
1114
0
Optimizer::PassToken CreateEliminateDeadOutputComponentsPass() {
1115
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1116
0
      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Output,
1117
0
                                                     /* safe_mode */ false));
1118
0
}
1119
1120
0
Optimizer::PassToken CreateEliminateDeadInputComponentsSafePass() {
1121
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1122
0
      MakeUnique<opt::EliminateDeadIOComponentsPass>(spv::StorageClass::Input,
1123
0
                                                     /* safe_mode */ true));
1124
0
}
1125
1126
Optimizer::PassToken CreateAnalyzeLiveInputPass(
1127
    std::unordered_set<uint32_t>* live_locs,
1128
0
    std::unordered_set<uint32_t>* live_builtins) {
1129
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1130
0
      MakeUnique<opt::AnalyzeLiveInputPass>(live_locs, live_builtins));
1131
0
}
1132
1133
Optimizer::PassToken CreateEliminateDeadOutputStoresPass(
1134
    std::unordered_set<uint32_t>* live_locs,
1135
0
    std::unordered_set<uint32_t>* live_builtins) {
1136
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1137
0
      MakeUnique<opt::EliminateDeadOutputStoresPass>(live_locs, live_builtins));
1138
0
}
1139
1140
Optimizer::PassToken CreateConvertToSampledImagePass(
1141
    const std::vector<opt::DescriptorSetAndBinding>&
1142
0
        descriptor_set_binding_pairs) {
1143
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1144
0
      MakeUnique<opt::ConvertToSampledImagePass>(descriptor_set_binding_pairs));
1145
0
}
1146
1147
0
Optimizer::PassToken CreateInterfaceVariableScalarReplacementPass() {
1148
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1149
0
      MakeUnique<opt::InterfaceVariableScalarReplacement>());
1150
0
}
1151
1152
0
Optimizer::PassToken CreateRemoveDontInlinePass() {
1153
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1154
0
      MakeUnique<opt::RemoveDontInline>());
1155
0
}
1156
1157
0
Optimizer::PassToken CreateFixFuncCallArgumentsPass() {
1158
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1159
0
      MakeUnique<opt::FixFuncCallArgumentsPass>());
1160
0
}
1161
1162
0
Optimizer::PassToken CreateTrimCapabilitiesPass() {
1163
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1164
0
      MakeUnique<opt::TrimCapabilitiesPass>());
1165
0
}
1166
1167
Optimizer::PassToken CreateStructPackingPass(const char* structToPack,
1168
0
                                             const char* packingRule) {
1169
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1170
0
      MakeUnique<opt::StructPackingPass>(
1171
0
          structToPack,
1172
0
          opt::StructPackingPass::ParsePackingRuleFromString(packingRule)));
1173
0
}
1174
1175
0
Optimizer::PassToken CreateSwitchDescriptorSetPass(uint32_t from, uint32_t to) {
1176
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1177
0
      MakeUnique<opt::SwitchDescriptorSetPass>(from, to));
1178
0
}
1179
1180
576
Optimizer::PassToken CreateInvocationInterlockPlacementPass() {
1181
576
  return MakeUnique<Optimizer::PassToken::Impl>(
1182
576
      MakeUnique<opt::InvocationInterlockPlacementPass>());
1183
576
}
1184
1185
0
Optimizer::PassToken CreateModifyMaximalReconvergencePass(bool add) {
1186
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1187
0
      MakeUnique<opt::ModifyMaximalReconvergence>(add));
1188
0
}
1189
1190
576
Optimizer::PassToken CreateOpExtInstWithForwardReferenceFixupPass() {
1191
576
  return MakeUnique<Optimizer::PassToken::Impl>(
1192
576
      MakeUnique<opt::OpExtInstWithForwardReferenceFixupPass>());
1193
576
}
1194
1195
0
Optimizer::PassToken CreateSplitCombinedImageSamplerPass() {
1196
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1197
0
      MakeUnique<opt::SplitCombinedImageSamplerPass>());
1198
0
}
1199
1200
0
Optimizer::PassToken CreateResolveBindingConflictsPass() {
1201
0
  return MakeUnique<Optimizer::PassToken::Impl>(
1202
0
      MakeUnique<opt::ResolveBindingConflictsPass>());
1203
0
}
1204
1205
}  // namespace spvtools
1206
1207
extern "C" {
1208
1209
0
SPIRV_TOOLS_EXPORT spv_optimizer_t* spvOptimizerCreate(spv_target_env env) {
1210
0
  return reinterpret_cast<spv_optimizer_t*>(new spvtools::Optimizer(env));
1211
0
}
1212
1213
0
SPIRV_TOOLS_EXPORT void spvOptimizerDestroy(spv_optimizer_t* optimizer) {
1214
0
  delete reinterpret_cast<spvtools::Optimizer*>(optimizer);
1215
0
}
1216
1217
SPIRV_TOOLS_EXPORT void spvOptimizerSetMessageConsumer(
1218
0
    spv_optimizer_t* optimizer, spv_message_consumer consumer) {
1219
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1220
0
      SetMessageConsumer(
1221
0
          [consumer](spv_message_level_t level, const char* source,
1222
0
                     const spv_position_t& position, const char* message) {
1223
0
            return consumer(level, source, &position, message);
1224
0
          });
1225
0
}
1226
1227
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterLegalizationPasses(
1228
0
    spv_optimizer_t* optimizer) {
1229
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1230
0
      RegisterLegalizationPasses();
1231
0
}
1232
1233
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterPerformancePasses(
1234
0
    spv_optimizer_t* optimizer) {
1235
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1236
0
      RegisterPerformancePasses();
1237
0
}
1238
1239
SPIRV_TOOLS_EXPORT void spvOptimizerRegisterSizePasses(
1240
0
    spv_optimizer_t* optimizer) {
1241
0
  reinterpret_cast<spvtools::Optimizer*>(optimizer)->RegisterSizePasses();
1242
0
}
1243
1244
SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassFromFlag(
1245
    spv_optimizer_t* optimizer, const char* flag)
1246
0
{
1247
0
  return reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1248
0
      RegisterPassFromFlag(flag);
1249
0
}
1250
1251
SPIRV_TOOLS_EXPORT bool spvOptimizerRegisterPassesFromFlags(
1252
0
    spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1253
0
  std::vector<std::string> opt_flags =
1254
0
      spvtools::GetVectorOfStrings(flags, flag_count);
1255
0
  return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1256
0
      ->RegisterPassesFromFlags(opt_flags, false);
1257
0
}
1258
1259
SPIRV_TOOLS_EXPORT bool
1260
spvOptimizerRegisterPassesFromFlagsWhilePreservingTheInterface(
1261
0
    spv_optimizer_t* optimizer, const char** flags, const size_t flag_count) {
1262
0
  std::vector<std::string> opt_flags =
1263
0
      spvtools::GetVectorOfStrings(flags, flag_count);
1264
0
  return reinterpret_cast<spvtools::Optimizer*>(optimizer)
1265
0
      ->RegisterPassesFromFlags(opt_flags, true);
1266
0
}
1267
1268
SPIRV_TOOLS_EXPORT
1269
spv_result_t spvOptimizerRun(spv_optimizer_t* optimizer,
1270
                             const uint32_t* binary,
1271
                             const size_t word_count,
1272
                             spv_binary* optimized_binary,
1273
0
                             const spv_optimizer_options options) {
1274
0
  std::vector<uint32_t> optimized;
1275
1276
0
  if (!reinterpret_cast<spvtools::Optimizer*>(optimizer)->
1277
0
      Run(binary, word_count, &optimized, options)) {
1278
0
    return SPV_ERROR_INTERNAL;
1279
0
  }
1280
1281
0
  auto result_binary = new spv_binary_t();
1282
0
  if (!result_binary) {
1283
0
      *optimized_binary = nullptr;
1284
0
      return SPV_ERROR_OUT_OF_MEMORY;
1285
0
  }
1286
1287
0
  result_binary->code = new uint32_t[optimized.size()];
1288
0
  if (!result_binary->code) {
1289
0
      delete result_binary;
1290
0
      *optimized_binary = nullptr;
1291
0
      return SPV_ERROR_OUT_OF_MEMORY;
1292
0
  }
1293
0
  result_binary->wordCount = optimized.size();
1294
1295
0
  memcpy(result_binary->code, optimized.data(),
1296
0
         optimized.size() * sizeof(uint32_t));
1297
1298
0
  *optimized_binary = result_binary;
1299
1300
0
  return SPV_SUCCESS;
1301
0
}
1302
1303
}  // extern "C"