Coverage Report

Created: 2025-02-05 06:34

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