optimizer.cpp 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929
  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. #include "spirv-tools/optimizer.hpp"
  15. #include <cassert>
  16. #include <memory>
  17. #include <string>
  18. #include <unordered_map>
  19. #include <utility>
  20. #include <vector>
  21. #include <source/spirv_optimizer_options.h>
  22. #include "source/opt/build_module.h"
  23. #include "source/opt/graphics_robust_access_pass.h"
  24. #include "source/opt/log.h"
  25. #include "source/opt/pass_manager.h"
  26. #include "source/opt/passes.h"
  27. #include "source/util/make_unique.h"
  28. #include "source/util/string_utils.h"
  29. namespace spvtools {
  30. struct Optimizer::PassToken::Impl {
  31. Impl(std::unique_ptr<opt::Pass> p) : pass(std::move(p)) {}
  32. std::unique_ptr<opt::Pass> pass; // Internal implementation pass.
  33. };
  34. Optimizer::PassToken::PassToken(
  35. std::unique_ptr<Optimizer::PassToken::Impl> impl)
  36. : impl_(std::move(impl)) {}
  37. Optimizer::PassToken::PassToken(std::unique_ptr<opt::Pass>&& pass)
  38. : impl_(MakeUnique<Optimizer::PassToken::Impl>(std::move(pass))) {}
  39. Optimizer::PassToken::PassToken(PassToken&& that)
  40. : impl_(std::move(that.impl_)) {}
  41. Optimizer::PassToken& Optimizer::PassToken::operator=(PassToken&& that) {
  42. impl_ = std::move(that.impl_);
  43. return *this;
  44. }
  45. Optimizer::PassToken::~PassToken() {}
  46. struct Optimizer::Impl {
  47. explicit Impl(spv_target_env env) : target_env(env), pass_manager() {}
  48. spv_target_env target_env; // Target environment.
  49. opt::PassManager pass_manager; // Internal implementation pass manager.
  50. };
  51. Optimizer::Optimizer(spv_target_env env) : impl_(new Impl(env)) {}
  52. Optimizer::~Optimizer() {}
  53. void Optimizer::SetMessageConsumer(MessageConsumer c) {
  54. // All passes' message consumer needs to be updated.
  55. for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); ++i) {
  56. impl_->pass_manager.GetPass(i)->SetMessageConsumer(c);
  57. }
  58. impl_->pass_manager.SetMessageConsumer(std::move(c));
  59. }
  60. const MessageConsumer& Optimizer::consumer() const {
  61. return impl_->pass_manager.consumer();
  62. }
  63. Optimizer& Optimizer::RegisterPass(PassToken&& p) {
  64. // Change to use the pass manager's consumer.
  65. p.impl_->pass->SetMessageConsumer(consumer());
  66. impl_->pass_manager.AddPass(std::move(p.impl_->pass));
  67. return *this;
  68. }
  69. // The legalization passes take a spir-v shader generated by an HLSL front-end
  70. // and turn it into a valid vulkan spir-v shader. There are two ways in which
  71. // the code will be invalid at the start:
  72. //
  73. // 1) There will be opaque objects, like images, which will be passed around
  74. // in intermediate objects. Valid spir-v will have to replace the use of
  75. // the opaque object with an intermediate object that is the result of the
  76. // load of the global opaque object.
  77. //
  78. // 2) There will be variables that contain pointers to structured or uniform
  79. // buffers. It be legal, the variables must be eliminated, and the
  80. // references to the structured buffers must use the result of OpVariable
  81. // in the Uniform storage class.
  82. //
  83. // Optimization in this list must accept shaders with these relaxation of the
  84. // rules. There is not guarantee that this list of optimizations is able to
  85. // legalize all inputs, but it is on a best effort basis.
  86. //
  87. // The legalization problem is essentially a very general copy propagation
  88. // problem. The optimization we use are all used to either do copy propagation
  89. // or enable more copy propagation.
  90. Optimizer& Optimizer::RegisterLegalizationPasses() {
  91. return
  92. // Wrap OpKill instructions so all other code can be inlined.
  93. RegisterPass(CreateWrapOpKillPass())
  94. // Remove unreachable block so that merge return works.
  95. .RegisterPass(CreateDeadBranchElimPass())
  96. // Merge the returns so we can inline.
  97. .RegisterPass(CreateMergeReturnPass())
  98. // Make sure uses and definitions are in the same function.
  99. .RegisterPass(CreateInlineExhaustivePass())
  100. // Make private variable function scope
  101. .RegisterPass(CreateEliminateDeadFunctionsPass())
  102. .RegisterPass(CreatePrivateToLocalPass())
  103. // Fix up the storage classes that DXC may have purposely generated
  104. // incorrectly. All functions are inlined, and a lot of dead code has
  105. // been removed.
  106. .RegisterPass(CreateFixStorageClassPass())
  107. // Propagate the value stored to the loads in very simple cases.
  108. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
  109. .RegisterPass(CreateLocalSingleStoreElimPass())
  110. .RegisterPass(CreateAggressiveDCEPass())
  111. // Split up aggregates so they are easier to deal with.
  112. .RegisterPass(CreateScalarReplacementPass(0))
  113. // Remove loads and stores so everything is in intermediate values.
  114. // Takes care of copy propagation of non-members.
  115. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
  116. .RegisterPass(CreateLocalSingleStoreElimPass())
  117. .RegisterPass(CreateAggressiveDCEPass())
  118. .RegisterPass(CreateLocalMultiStoreElimPass())
  119. .RegisterPass(CreateAggressiveDCEPass())
  120. // Propagate constants to get as many constant conditions on branches
  121. // as possible.
  122. .RegisterPass(CreateCCPPass())
  123. .RegisterPass(CreateLoopUnrollPass(true))
  124. .RegisterPass(CreateDeadBranchElimPass())
  125. // Copy propagate members. Cleans up code sequences generated by
  126. // scalar replacement. Also important for removing OpPhi nodes.
  127. .RegisterPass(CreateSimplificationPass())
  128. .RegisterPass(CreateAggressiveDCEPass())
  129. .RegisterPass(CreateCopyPropagateArraysPass())
  130. // May need loop unrolling here see
  131. // https://github.com/Microsoft/DirectXShaderCompiler/pull/930
  132. // Get rid of unused code that contain traces of illegal code
  133. // or unused references to unbound external objects
  134. .RegisterPass(CreateVectorDCEPass())
  135. .RegisterPass(CreateDeadInsertElimPass())
  136. .RegisterPass(CreateReduceLoadSizePass())
  137. .RegisterPass(CreateAggressiveDCEPass());
  138. }
  139. Optimizer& Optimizer::RegisterPerformancePasses() {
  140. return RegisterPass(CreateWrapOpKillPass())
  141. .RegisterPass(CreateDeadBranchElimPass())
  142. .RegisterPass(CreateMergeReturnPass())
  143. .RegisterPass(CreateInlineExhaustivePass())
  144. .RegisterPass(CreateAggressiveDCEPass())
  145. .RegisterPass(CreatePrivateToLocalPass())
  146. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
  147. .RegisterPass(CreateLocalSingleStoreElimPass())
  148. .RegisterPass(CreateAggressiveDCEPass())
  149. .RegisterPass(CreateScalarReplacementPass())
  150. .RegisterPass(CreateLocalAccessChainConvertPass())
  151. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
  152. .RegisterPass(CreateLocalSingleStoreElimPass())
  153. .RegisterPass(CreateAggressiveDCEPass())
  154. .RegisterPass(CreateLocalMultiStoreElimPass())
  155. .RegisterPass(CreateAggressiveDCEPass())
  156. .RegisterPass(CreateCCPPass())
  157. .RegisterPass(CreateAggressiveDCEPass())
  158. .RegisterPass(CreateRedundancyEliminationPass())
  159. .RegisterPass(CreateCombineAccessChainsPass())
  160. .RegisterPass(CreateSimplificationPass())
  161. .RegisterPass(CreateVectorDCEPass())
  162. .RegisterPass(CreateDeadInsertElimPass())
  163. .RegisterPass(CreateDeadBranchElimPass())
  164. .RegisterPass(CreateSimplificationPass())
  165. .RegisterPass(CreateIfConversionPass())
  166. .RegisterPass(CreateCopyPropagateArraysPass())
  167. .RegisterPass(CreateReduceLoadSizePass())
  168. .RegisterPass(CreateAggressiveDCEPass())
  169. .RegisterPass(CreateBlockMergePass())
  170. .RegisterPass(CreateRedundancyEliminationPass())
  171. .RegisterPass(CreateDeadBranchElimPass())
  172. .RegisterPass(CreateBlockMergePass())
  173. .RegisterPass(CreateSimplificationPass());
  174. }
  175. Optimizer& Optimizer::RegisterSizePasses() {
  176. return RegisterPass(CreateWrapOpKillPass())
  177. .RegisterPass(CreateDeadBranchElimPass())
  178. .RegisterPass(CreateMergeReturnPass())
  179. .RegisterPass(CreateInlineExhaustivePass())
  180. .RegisterPass(CreateAggressiveDCEPass())
  181. .RegisterPass(CreatePrivateToLocalPass())
  182. .RegisterPass(CreateScalarReplacementPass())
  183. .RegisterPass(CreateLocalAccessChainConvertPass())
  184. .RegisterPass(CreateLocalSingleBlockLoadStoreElimPass())
  185. .RegisterPass(CreateLocalSingleStoreElimPass())
  186. .RegisterPass(CreateAggressiveDCEPass())
  187. .RegisterPass(CreateSimplificationPass())
  188. .RegisterPass(CreateDeadInsertElimPass())
  189. .RegisterPass(CreateLocalMultiStoreElimPass())
  190. .RegisterPass(CreateAggressiveDCEPass())
  191. .RegisterPass(CreateCCPPass())
  192. .RegisterPass(CreateAggressiveDCEPass())
  193. .RegisterPass(CreateDeadBranchElimPass())
  194. .RegisterPass(CreateIfConversionPass())
  195. .RegisterPass(CreateAggressiveDCEPass())
  196. .RegisterPass(CreateBlockMergePass())
  197. .RegisterPass(CreateSimplificationPass())
  198. .RegisterPass(CreateDeadInsertElimPass())
  199. .RegisterPass(CreateRedundancyEliminationPass())
  200. .RegisterPass(CreateCFGCleanupPass())
  201. .RegisterPass(CreateAggressiveDCEPass());
  202. }
  203. Optimizer& Optimizer::RegisterVulkanToWebGPUPasses() {
  204. return RegisterPass(CreateStripDebugInfoPass())
  205. .RegisterPass(CreateStripAtomicCounterMemoryPass())
  206. .RegisterPass(CreateGenerateWebGPUInitializersPass())
  207. .RegisterPass(CreateLegalizeVectorShufflePass())
  208. .RegisterPass(CreateSplitInvalidUnreachablePass())
  209. .RegisterPass(CreateEliminateDeadConstantPass())
  210. .RegisterPass(CreateFlattenDecorationPass())
  211. .RegisterPass(CreateAggressiveDCEPass())
  212. .RegisterPass(CreateDeadBranchElimPass())
  213. .RegisterPass(CreateCompactIdsPass());
  214. }
  215. Optimizer& Optimizer::RegisterWebGPUToVulkanPasses() {
  216. return RegisterPass(CreateDecomposeInitializedVariablesPass())
  217. .RegisterPass(CreateCompactIdsPass());
  218. }
  219. bool Optimizer::RegisterPassesFromFlags(const std::vector<std::string>& flags) {
  220. for (const auto& flag : flags) {
  221. if (!RegisterPassFromFlag(flag)) {
  222. return false;
  223. }
  224. }
  225. return true;
  226. }
  227. bool Optimizer::FlagHasValidForm(const std::string& flag) const {
  228. if (flag == "-O" || flag == "-Os") {
  229. return true;
  230. } else if (flag.size() > 2 && flag.substr(0, 2) == "--") {
  231. return true;
  232. }
  233. Errorf(consumer(), nullptr, {},
  234. "%s is not a valid flag. Flag passes should have the form "
  235. "'--pass_name[=pass_args]'. Special flag names also accepted: -O "
  236. "and -Os.",
  237. flag.c_str());
  238. return false;
  239. }
  240. bool Optimizer::RegisterPassFromFlag(const std::string& flag) {
  241. if (!FlagHasValidForm(flag)) {
  242. return false;
  243. }
  244. // Split flags of the form --pass_name=pass_args.
  245. auto p = utils::SplitFlagArgs(flag);
  246. std::string pass_name = p.first;
  247. std::string pass_args = p.second;
  248. // FIXME(dnovillo): This should be re-factored so that pass names can be
  249. // automatically checked against Pass::name() and PassToken instances created
  250. // via a template function. Additionally, class Pass should have a desc()
  251. // method that describes the pass (so it can be used in --help).
  252. //
  253. // Both Pass::name() and Pass::desc() should be static class members so they
  254. // can be invoked without creating a pass instance.
  255. if (pass_name == "strip-atomic-counter-memory") {
  256. RegisterPass(CreateStripAtomicCounterMemoryPass());
  257. } else if (pass_name == "strip-debug") {
  258. RegisterPass(CreateStripDebugInfoPass());
  259. } else if (pass_name == "strip-reflect") {
  260. RegisterPass(CreateStripReflectInfoPass());
  261. } else if (pass_name == "set-spec-const-default-value") {
  262. if (pass_args.size() > 0) {
  263. auto spec_ids_vals =
  264. opt::SetSpecConstantDefaultValuePass::ParseDefaultValuesString(
  265. pass_args.c_str());
  266. if (!spec_ids_vals) {
  267. Errorf(consumer(), nullptr, {},
  268. "Invalid argument for --set-spec-const-default-value: %s",
  269. pass_args.c_str());
  270. return false;
  271. }
  272. RegisterPass(
  273. CreateSetSpecConstantDefaultValuePass(std::move(*spec_ids_vals)));
  274. } else {
  275. Errorf(consumer(), nullptr, {},
  276. "Invalid spec constant value string '%s'. Expected a string of "
  277. "<spec id>:<default value> pairs.",
  278. pass_args.c_str());
  279. return false;
  280. }
  281. } else if (pass_name == "if-conversion") {
  282. RegisterPass(CreateIfConversionPass());
  283. } else if (pass_name == "freeze-spec-const") {
  284. RegisterPass(CreateFreezeSpecConstantValuePass());
  285. } else if (pass_name == "inline-entry-points-exhaustive") {
  286. RegisterPass(CreateInlineExhaustivePass());
  287. } else if (pass_name == "inline-entry-points-opaque") {
  288. RegisterPass(CreateInlineOpaquePass());
  289. } else if (pass_name == "combine-access-chains") {
  290. RegisterPass(CreateCombineAccessChainsPass());
  291. } else if (pass_name == "convert-local-access-chains") {
  292. RegisterPass(CreateLocalAccessChainConvertPass());
  293. } else if (pass_name == "descriptor-scalar-replacement") {
  294. RegisterPass(CreateDescriptorScalarReplacementPass());
  295. } else if (pass_name == "eliminate-dead-code-aggressive") {
  296. RegisterPass(CreateAggressiveDCEPass());
  297. } else if (pass_name == "propagate-line-info") {
  298. RegisterPass(CreatePropagateLineInfoPass());
  299. } else if (pass_name == "eliminate-redundant-line-info") {
  300. RegisterPass(CreateRedundantLineInfoElimPass());
  301. } else if (pass_name == "eliminate-insert-extract") {
  302. RegisterPass(CreateInsertExtractElimPass());
  303. } else if (pass_name == "eliminate-local-single-block") {
  304. RegisterPass(CreateLocalSingleBlockLoadStoreElimPass());
  305. } else if (pass_name == "eliminate-local-single-store") {
  306. RegisterPass(CreateLocalSingleStoreElimPass());
  307. } else if (pass_name == "merge-blocks") {
  308. RegisterPass(CreateBlockMergePass());
  309. } else if (pass_name == "merge-return") {
  310. RegisterPass(CreateMergeReturnPass());
  311. } else if (pass_name == "eliminate-dead-branches") {
  312. RegisterPass(CreateDeadBranchElimPass());
  313. } else if (pass_name == "eliminate-dead-functions") {
  314. RegisterPass(CreateEliminateDeadFunctionsPass());
  315. } else if (pass_name == "eliminate-local-multi-store") {
  316. RegisterPass(CreateLocalMultiStoreElimPass());
  317. } else if (pass_name == "eliminate-dead-const") {
  318. RegisterPass(CreateEliminateDeadConstantPass());
  319. } else if (pass_name == "eliminate-dead-inserts") {
  320. RegisterPass(CreateDeadInsertElimPass());
  321. } else if (pass_name == "eliminate-dead-variables") {
  322. RegisterPass(CreateDeadVariableEliminationPass());
  323. } else if (pass_name == "eliminate-dead-members") {
  324. RegisterPass(CreateEliminateDeadMembersPass());
  325. } else if (pass_name == "fold-spec-const-op-composite") {
  326. RegisterPass(CreateFoldSpecConstantOpAndCompositePass());
  327. } else if (pass_name == "loop-unswitch") {
  328. RegisterPass(CreateLoopUnswitchPass());
  329. } else if (pass_name == "scalar-replacement") {
  330. if (pass_args.size() == 0) {
  331. RegisterPass(CreateScalarReplacementPass());
  332. } else {
  333. int limit = -1;
  334. if (pass_args.find_first_not_of("0123456789") == std::string::npos) {
  335. limit = atoi(pass_args.c_str());
  336. }
  337. if (limit >= 0) {
  338. RegisterPass(CreateScalarReplacementPass(limit));
  339. } else {
  340. Error(consumer(), nullptr, {},
  341. "--scalar-replacement must have no arguments or a non-negative "
  342. "integer argument");
  343. return false;
  344. }
  345. }
  346. } else if (pass_name == "strength-reduction") {
  347. RegisterPass(CreateStrengthReductionPass());
  348. } else if (pass_name == "unify-const") {
  349. RegisterPass(CreateUnifyConstantPass());
  350. } else if (pass_name == "flatten-decorations") {
  351. RegisterPass(CreateFlattenDecorationPass());
  352. } else if (pass_name == "compact-ids") {
  353. RegisterPass(CreateCompactIdsPass());
  354. } else if (pass_name == "cfg-cleanup") {
  355. RegisterPass(CreateCFGCleanupPass());
  356. } else if (pass_name == "local-redundancy-elimination") {
  357. RegisterPass(CreateLocalRedundancyEliminationPass());
  358. } else if (pass_name == "loop-invariant-code-motion") {
  359. RegisterPass(CreateLoopInvariantCodeMotionPass());
  360. } else if (pass_name == "reduce-load-size") {
  361. RegisterPass(CreateReduceLoadSizePass());
  362. } else if (pass_name == "redundancy-elimination") {
  363. RegisterPass(CreateRedundancyEliminationPass());
  364. } else if (pass_name == "private-to-local") {
  365. RegisterPass(CreatePrivateToLocalPass());
  366. } else if (pass_name == "remove-duplicates") {
  367. RegisterPass(CreateRemoveDuplicatesPass());
  368. } else if (pass_name == "workaround-1209") {
  369. RegisterPass(CreateWorkaround1209Pass());
  370. } else if (pass_name == "replace-invalid-opcode") {
  371. RegisterPass(CreateReplaceInvalidOpcodePass());
  372. } else if (pass_name == "inst-bindless-check") {
  373. RegisterPass(CreateInstBindlessCheckPass(7, 23, false, false, 2));
  374. RegisterPass(CreateSimplificationPass());
  375. RegisterPass(CreateDeadBranchElimPass());
  376. RegisterPass(CreateBlockMergePass());
  377. RegisterPass(CreateAggressiveDCEPass());
  378. } else if (pass_name == "inst-desc-idx-check") {
  379. RegisterPass(CreateInstBindlessCheckPass(7, 23, true, true, 2));
  380. RegisterPass(CreateSimplificationPass());
  381. RegisterPass(CreateDeadBranchElimPass());
  382. RegisterPass(CreateBlockMergePass());
  383. RegisterPass(CreateAggressiveDCEPass());
  384. } else if (pass_name == "inst-buff-addr-check") {
  385. RegisterPass(CreateInstBuffAddrCheckPass(7, 23, 2));
  386. RegisterPass(CreateAggressiveDCEPass());
  387. } else if (pass_name == "simplify-instructions") {
  388. RegisterPass(CreateSimplificationPass());
  389. } else if (pass_name == "ssa-rewrite") {
  390. RegisterPass(CreateSSARewritePass());
  391. } else if (pass_name == "copy-propagate-arrays") {
  392. RegisterPass(CreateCopyPropagateArraysPass());
  393. } else if (pass_name == "loop-fission") {
  394. int register_threshold_to_split =
  395. (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
  396. if (register_threshold_to_split > 0) {
  397. RegisterPass(CreateLoopFissionPass(
  398. static_cast<size_t>(register_threshold_to_split)));
  399. } else {
  400. Error(consumer(), nullptr, {},
  401. "--loop-fission must have a positive integer argument");
  402. return false;
  403. }
  404. } else if (pass_name == "loop-fusion") {
  405. int max_registers_per_loop =
  406. (pass_args.size() > 0) ? atoi(pass_args.c_str()) : -1;
  407. if (max_registers_per_loop > 0) {
  408. RegisterPass(
  409. CreateLoopFusionPass(static_cast<size_t>(max_registers_per_loop)));
  410. } else {
  411. Error(consumer(), nullptr, {},
  412. "--loop-fusion must have a positive integer argument");
  413. return false;
  414. }
  415. } else if (pass_name == "loop-unroll") {
  416. RegisterPass(CreateLoopUnrollPass(true));
  417. } else if (pass_name == "upgrade-memory-model") {
  418. RegisterPass(CreateUpgradeMemoryModelPass());
  419. } else if (pass_name == "vector-dce") {
  420. RegisterPass(CreateVectorDCEPass());
  421. } else if (pass_name == "loop-unroll-partial") {
  422. int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
  423. if (factor > 0) {
  424. RegisterPass(CreateLoopUnrollPass(false, factor));
  425. } else {
  426. Error(consumer(), nullptr, {},
  427. "--loop-unroll-partial must have a positive integer argument");
  428. return false;
  429. }
  430. } else if (pass_name == "loop-peeling") {
  431. RegisterPass(CreateLoopPeelingPass());
  432. } else if (pass_name == "loop-peeling-threshold") {
  433. int factor = (pass_args.size() > 0) ? atoi(pass_args.c_str()) : 0;
  434. if (factor > 0) {
  435. opt::LoopPeelingPass::SetLoopPeelingThreshold(factor);
  436. } else {
  437. Error(consumer(), nullptr, {},
  438. "--loop-peeling-threshold must have a positive integer argument");
  439. return false;
  440. }
  441. } else if (pass_name == "ccp") {
  442. RegisterPass(CreateCCPPass());
  443. } else if (pass_name == "code-sink") {
  444. RegisterPass(CreateCodeSinkingPass());
  445. } else if (pass_name == "fix-storage-class") {
  446. RegisterPass(CreateFixStorageClassPass());
  447. } else if (pass_name == "O") {
  448. RegisterPerformancePasses();
  449. } else if (pass_name == "Os") {
  450. RegisterSizePasses();
  451. } else if (pass_name == "legalize-hlsl") {
  452. RegisterLegalizationPasses();
  453. } else if (pass_name == "generate-webgpu-initializers") {
  454. RegisterPass(CreateGenerateWebGPUInitializersPass());
  455. } else if (pass_name == "legalize-vector-shuffle") {
  456. RegisterPass(CreateLegalizeVectorShufflePass());
  457. } else if (pass_name == "split-invalid-unreachable") {
  458. RegisterPass(CreateLegalizeVectorShufflePass());
  459. } else if (pass_name == "decompose-initialized-variables") {
  460. RegisterPass(CreateDecomposeInitializedVariablesPass());
  461. } else if (pass_name == "graphics-robust-access") {
  462. RegisterPass(CreateGraphicsRobustAccessPass());
  463. } else if (pass_name == "wrap-opkill") {
  464. RegisterPass(CreateWrapOpKillPass());
  465. } else if (pass_name == "amd-ext-to-khr") {
  466. RegisterPass(CreateAmdExtToKhrPass());
  467. } else {
  468. Errorf(consumer(), nullptr, {},
  469. "Unknown flag '--%s'. Use --help for a list of valid flags",
  470. pass_name.c_str());
  471. return false;
  472. }
  473. return true;
  474. }
  475. void Optimizer::SetTargetEnv(const spv_target_env env) {
  476. impl_->target_env = env;
  477. }
  478. bool Optimizer::Run(const uint32_t* original_binary,
  479. const size_t original_binary_size,
  480. std::vector<uint32_t>* optimized_binary) const {
  481. return Run(original_binary, original_binary_size, optimized_binary,
  482. OptimizerOptions());
  483. }
  484. bool Optimizer::Run(const uint32_t* original_binary,
  485. const size_t original_binary_size,
  486. std::vector<uint32_t>* optimized_binary,
  487. const ValidatorOptions& validator_options,
  488. bool skip_validation) const {
  489. OptimizerOptions opt_options;
  490. opt_options.set_run_validator(!skip_validation);
  491. opt_options.set_validator_options(validator_options);
  492. return Run(original_binary, original_binary_size, optimized_binary,
  493. opt_options);
  494. }
  495. bool Optimizer::Run(const uint32_t* original_binary,
  496. const size_t original_binary_size,
  497. std::vector<uint32_t>* optimized_binary,
  498. const spv_optimizer_options opt_options) const {
  499. spvtools::SpirvTools tools(impl_->target_env);
  500. tools.SetMessageConsumer(impl_->pass_manager.consumer());
  501. if (opt_options->run_validator_ &&
  502. !tools.Validate(original_binary, original_binary_size,
  503. &opt_options->val_options_)) {
  504. return false;
  505. }
  506. std::unique_ptr<opt::IRContext> context = BuildModule(
  507. impl_->target_env, consumer(), original_binary, original_binary_size);
  508. if (context == nullptr) return false;
  509. context->set_max_id_bound(opt_options->max_id_bound_);
  510. context->set_preserve_bindings(opt_options->preserve_bindings_);
  511. context->set_preserve_spec_constants(opt_options->preserve_spec_constants_);
  512. impl_->pass_manager.SetValidatorOptions(&opt_options->val_options_);
  513. impl_->pass_manager.SetTargetEnv(impl_->target_env);
  514. auto status = impl_->pass_manager.Run(context.get());
  515. if (status == opt::Pass::Status::Failure) {
  516. return false;
  517. }
  518. optimized_binary->clear();
  519. context->module()->ToBinary(optimized_binary, /* skip_nop = */ true);
  520. #ifndef NDEBUG
  521. if (status == opt::Pass::Status::SuccessWithoutChange) {
  522. auto changed = optimized_binary->size() != original_binary_size ||
  523. memcmp(optimized_binary->data(), original_binary,
  524. original_binary_size) != 0;
  525. assert(!changed &&
  526. "Binary unexpectedly changed despite optimizer saying there was no "
  527. "change");
  528. }
  529. #endif // !NDEBUG
  530. return true;
  531. }
  532. Optimizer& Optimizer::SetPrintAll(std::ostream* out) {
  533. impl_->pass_manager.SetPrintAll(out);
  534. return *this;
  535. }
  536. Optimizer& Optimizer::SetTimeReport(std::ostream* out) {
  537. impl_->pass_manager.SetTimeReport(out);
  538. return *this;
  539. }
  540. Optimizer& Optimizer::SetValidateAfterAll(bool validate) {
  541. impl_->pass_manager.SetValidateAfterAll(validate);
  542. return *this;
  543. }
  544. Optimizer::PassToken CreateNullPass() {
  545. return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::NullPass>());
  546. }
  547. Optimizer::PassToken CreateStripAtomicCounterMemoryPass() {
  548. return MakeUnique<Optimizer::PassToken::Impl>(
  549. MakeUnique<opt::StripAtomicCounterMemoryPass>());
  550. }
  551. Optimizer::PassToken CreateStripDebugInfoPass() {
  552. return MakeUnique<Optimizer::PassToken::Impl>(
  553. MakeUnique<opt::StripDebugInfoPass>());
  554. }
  555. Optimizer::PassToken CreateStripReflectInfoPass() {
  556. return MakeUnique<Optimizer::PassToken::Impl>(
  557. MakeUnique<opt::StripReflectInfoPass>());
  558. }
  559. Optimizer::PassToken CreateEliminateDeadFunctionsPass() {
  560. return MakeUnique<Optimizer::PassToken::Impl>(
  561. MakeUnique<opt::EliminateDeadFunctionsPass>());
  562. }
  563. Optimizer::PassToken CreateEliminateDeadMembersPass() {
  564. return MakeUnique<Optimizer::PassToken::Impl>(
  565. MakeUnique<opt::EliminateDeadMembersPass>());
  566. }
  567. Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
  568. const std::unordered_map<uint32_t, std::string>& id_value_map) {
  569. return MakeUnique<Optimizer::PassToken::Impl>(
  570. MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
  571. }
  572. Optimizer::PassToken CreateSetSpecConstantDefaultValuePass(
  573. const std::unordered_map<uint32_t, std::vector<uint32_t>>& id_value_map) {
  574. return MakeUnique<Optimizer::PassToken::Impl>(
  575. MakeUnique<opt::SetSpecConstantDefaultValuePass>(id_value_map));
  576. }
  577. Optimizer::PassToken CreateFlattenDecorationPass() {
  578. return MakeUnique<Optimizer::PassToken::Impl>(
  579. MakeUnique<opt::FlattenDecorationPass>());
  580. }
  581. Optimizer::PassToken CreateFreezeSpecConstantValuePass() {
  582. return MakeUnique<Optimizer::PassToken::Impl>(
  583. MakeUnique<opt::FreezeSpecConstantValuePass>());
  584. }
  585. Optimizer::PassToken CreateFoldSpecConstantOpAndCompositePass() {
  586. return MakeUnique<Optimizer::PassToken::Impl>(
  587. MakeUnique<opt::FoldSpecConstantOpAndCompositePass>());
  588. }
  589. Optimizer::PassToken CreateUnifyConstantPass() {
  590. return MakeUnique<Optimizer::PassToken::Impl>(
  591. MakeUnique<opt::UnifyConstantPass>());
  592. }
  593. Optimizer::PassToken CreateEliminateDeadConstantPass() {
  594. return MakeUnique<Optimizer::PassToken::Impl>(
  595. MakeUnique<opt::EliminateDeadConstantPass>());
  596. }
  597. Optimizer::PassToken CreateDeadVariableEliminationPass() {
  598. return MakeUnique<Optimizer::PassToken::Impl>(
  599. MakeUnique<opt::DeadVariableElimination>());
  600. }
  601. Optimizer::PassToken CreateStrengthReductionPass() {
  602. return MakeUnique<Optimizer::PassToken::Impl>(
  603. MakeUnique<opt::StrengthReductionPass>());
  604. }
  605. Optimizer::PassToken CreateBlockMergePass() {
  606. return MakeUnique<Optimizer::PassToken::Impl>(
  607. MakeUnique<opt::BlockMergePass>());
  608. }
  609. Optimizer::PassToken CreateInlineExhaustivePass() {
  610. return MakeUnique<Optimizer::PassToken::Impl>(
  611. MakeUnique<opt::InlineExhaustivePass>());
  612. }
  613. Optimizer::PassToken CreateInlineOpaquePass() {
  614. return MakeUnique<Optimizer::PassToken::Impl>(
  615. MakeUnique<opt::InlineOpaquePass>());
  616. }
  617. Optimizer::PassToken CreateLocalAccessChainConvertPass() {
  618. return MakeUnique<Optimizer::PassToken::Impl>(
  619. MakeUnique<opt::LocalAccessChainConvertPass>());
  620. }
  621. Optimizer::PassToken CreateLocalSingleBlockLoadStoreElimPass() {
  622. return MakeUnique<Optimizer::PassToken::Impl>(
  623. MakeUnique<opt::LocalSingleBlockLoadStoreElimPass>());
  624. }
  625. Optimizer::PassToken CreateLocalSingleStoreElimPass() {
  626. return MakeUnique<Optimizer::PassToken::Impl>(
  627. MakeUnique<opt::LocalSingleStoreElimPass>());
  628. }
  629. Optimizer::PassToken CreateInsertExtractElimPass() {
  630. return MakeUnique<Optimizer::PassToken::Impl>(
  631. MakeUnique<opt::SimplificationPass>());
  632. }
  633. Optimizer::PassToken CreateDeadInsertElimPass() {
  634. return MakeUnique<Optimizer::PassToken::Impl>(
  635. MakeUnique<opt::DeadInsertElimPass>());
  636. }
  637. Optimizer::PassToken CreateDeadBranchElimPass() {
  638. return MakeUnique<Optimizer::PassToken::Impl>(
  639. MakeUnique<opt::DeadBranchElimPass>());
  640. }
  641. Optimizer::PassToken CreateLocalMultiStoreElimPass() {
  642. return MakeUnique<Optimizer::PassToken::Impl>(
  643. MakeUnique<opt::LocalMultiStoreElimPass>());
  644. }
  645. Optimizer::PassToken CreateAggressiveDCEPass() {
  646. return MakeUnique<Optimizer::PassToken::Impl>(
  647. MakeUnique<opt::AggressiveDCEPass>());
  648. }
  649. Optimizer::PassToken CreatePropagateLineInfoPass() {
  650. return MakeUnique<Optimizer::PassToken::Impl>(
  651. MakeUnique<opt::ProcessLinesPass>(opt::kLinesPropagateLines));
  652. }
  653. Optimizer::PassToken CreateRedundantLineInfoElimPass() {
  654. return MakeUnique<Optimizer::PassToken::Impl>(
  655. MakeUnique<opt::ProcessLinesPass>(opt::kLinesEliminateDeadLines));
  656. }
  657. Optimizer::PassToken CreateCompactIdsPass() {
  658. return MakeUnique<Optimizer::PassToken::Impl>(
  659. MakeUnique<opt::CompactIdsPass>());
  660. }
  661. Optimizer::PassToken CreateMergeReturnPass() {
  662. return MakeUnique<Optimizer::PassToken::Impl>(
  663. MakeUnique<opt::MergeReturnPass>());
  664. }
  665. std::vector<const char*> Optimizer::GetPassNames() const {
  666. std::vector<const char*> v;
  667. for (uint32_t i = 0; i < impl_->pass_manager.NumPasses(); i++) {
  668. v.push_back(impl_->pass_manager.GetPass(i)->name());
  669. }
  670. return v;
  671. }
  672. Optimizer::PassToken CreateCFGCleanupPass() {
  673. return MakeUnique<Optimizer::PassToken::Impl>(
  674. MakeUnique<opt::CFGCleanupPass>());
  675. }
  676. Optimizer::PassToken CreateLocalRedundancyEliminationPass() {
  677. return MakeUnique<Optimizer::PassToken::Impl>(
  678. MakeUnique<opt::LocalRedundancyEliminationPass>());
  679. }
  680. Optimizer::PassToken CreateLoopFissionPass(size_t threshold) {
  681. return MakeUnique<Optimizer::PassToken::Impl>(
  682. MakeUnique<opt::LoopFissionPass>(threshold));
  683. }
  684. Optimizer::PassToken CreateLoopFusionPass(size_t max_registers_per_loop) {
  685. return MakeUnique<Optimizer::PassToken::Impl>(
  686. MakeUnique<opt::LoopFusionPass>(max_registers_per_loop));
  687. }
  688. Optimizer::PassToken CreateLoopInvariantCodeMotionPass() {
  689. return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::LICMPass>());
  690. }
  691. Optimizer::PassToken CreateLoopPeelingPass() {
  692. return MakeUnique<Optimizer::PassToken::Impl>(
  693. MakeUnique<opt::LoopPeelingPass>());
  694. }
  695. Optimizer::PassToken CreateLoopUnswitchPass() {
  696. return MakeUnique<Optimizer::PassToken::Impl>(
  697. MakeUnique<opt::LoopUnswitchPass>());
  698. }
  699. Optimizer::PassToken CreateRedundancyEliminationPass() {
  700. return MakeUnique<Optimizer::PassToken::Impl>(
  701. MakeUnique<opt::RedundancyEliminationPass>());
  702. }
  703. Optimizer::PassToken CreateRemoveDuplicatesPass() {
  704. return MakeUnique<Optimizer::PassToken::Impl>(
  705. MakeUnique<opt::RemoveDuplicatesPass>());
  706. }
  707. Optimizer::PassToken CreateScalarReplacementPass(uint32_t size_limit) {
  708. return MakeUnique<Optimizer::PassToken::Impl>(
  709. MakeUnique<opt::ScalarReplacementPass>(size_limit));
  710. }
  711. Optimizer::PassToken CreatePrivateToLocalPass() {
  712. return MakeUnique<Optimizer::PassToken::Impl>(
  713. MakeUnique<opt::PrivateToLocalPass>());
  714. }
  715. Optimizer::PassToken CreateCCPPass() {
  716. return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::CCPPass>());
  717. }
  718. Optimizer::PassToken CreateWorkaround1209Pass() {
  719. return MakeUnique<Optimizer::PassToken::Impl>(
  720. MakeUnique<opt::Workaround1209>());
  721. }
  722. Optimizer::PassToken CreateIfConversionPass() {
  723. return MakeUnique<Optimizer::PassToken::Impl>(
  724. MakeUnique<opt::IfConversion>());
  725. }
  726. Optimizer::PassToken CreateReplaceInvalidOpcodePass() {
  727. return MakeUnique<Optimizer::PassToken::Impl>(
  728. MakeUnique<opt::ReplaceInvalidOpcodePass>());
  729. }
  730. Optimizer::PassToken CreateSimplificationPass() {
  731. return MakeUnique<Optimizer::PassToken::Impl>(
  732. MakeUnique<opt::SimplificationPass>());
  733. }
  734. Optimizer::PassToken CreateLoopUnrollPass(bool fully_unroll, int factor) {
  735. return MakeUnique<Optimizer::PassToken::Impl>(
  736. MakeUnique<opt::LoopUnroller>(fully_unroll, factor));
  737. }
  738. Optimizer::PassToken CreateSSARewritePass() {
  739. return MakeUnique<Optimizer::PassToken::Impl>(
  740. MakeUnique<opt::SSARewritePass>());
  741. }
  742. Optimizer::PassToken CreateCopyPropagateArraysPass() {
  743. return MakeUnique<Optimizer::PassToken::Impl>(
  744. MakeUnique<opt::CopyPropagateArrays>());
  745. }
  746. Optimizer::PassToken CreateVectorDCEPass() {
  747. return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::VectorDCE>());
  748. }
  749. Optimizer::PassToken CreateReduceLoadSizePass() {
  750. return MakeUnique<Optimizer::PassToken::Impl>(
  751. MakeUnique<opt::ReduceLoadSize>());
  752. }
  753. Optimizer::PassToken CreateCombineAccessChainsPass() {
  754. return MakeUnique<Optimizer::PassToken::Impl>(
  755. MakeUnique<opt::CombineAccessChains>());
  756. }
  757. Optimizer::PassToken CreateUpgradeMemoryModelPass() {
  758. return MakeUnique<Optimizer::PassToken::Impl>(
  759. MakeUnique<opt::UpgradeMemoryModel>());
  760. }
  761. Optimizer::PassToken CreateInstBindlessCheckPass(uint32_t desc_set,
  762. uint32_t shader_id,
  763. bool input_length_enable,
  764. bool input_init_enable,
  765. uint32_t version) {
  766. return MakeUnique<Optimizer::PassToken::Impl>(
  767. MakeUnique<opt::InstBindlessCheckPass>(desc_set, shader_id,
  768. input_length_enable,
  769. input_init_enable, version));
  770. }
  771. Optimizer::PassToken CreateInstBuffAddrCheckPass(uint32_t desc_set,
  772. uint32_t shader_id,
  773. uint32_t version) {
  774. return MakeUnique<Optimizer::PassToken::Impl>(
  775. MakeUnique<opt::InstBuffAddrCheckPass>(desc_set, shader_id, version));
  776. }
  777. Optimizer::PassToken CreateCodeSinkingPass() {
  778. return MakeUnique<Optimizer::PassToken::Impl>(
  779. MakeUnique<opt::CodeSinkingPass>());
  780. }
  781. Optimizer::PassToken CreateGenerateWebGPUInitializersPass() {
  782. return MakeUnique<Optimizer::PassToken::Impl>(
  783. MakeUnique<opt::GenerateWebGPUInitializersPass>());
  784. }
  785. Optimizer::PassToken CreateFixStorageClassPass() {
  786. return MakeUnique<Optimizer::PassToken::Impl>(
  787. MakeUnique<opt::FixStorageClass>());
  788. }
  789. Optimizer::PassToken CreateLegalizeVectorShufflePass() {
  790. return MakeUnique<Optimizer::PassToken::Impl>(
  791. MakeUnique<opt::LegalizeVectorShufflePass>());
  792. }
  793. Optimizer::PassToken CreateDecomposeInitializedVariablesPass() {
  794. return MakeUnique<Optimizer::PassToken::Impl>(
  795. MakeUnique<opt::DecomposeInitializedVariablesPass>());
  796. }
  797. Optimizer::PassToken CreateSplitInvalidUnreachablePass() {
  798. return MakeUnique<Optimizer::PassToken::Impl>(
  799. MakeUnique<opt::SplitInvalidUnreachablePass>());
  800. }
  801. Optimizer::PassToken CreateGraphicsRobustAccessPass() {
  802. return MakeUnique<Optimizer::PassToken::Impl>(
  803. MakeUnique<opt::GraphicsRobustAccessPass>());
  804. }
  805. Optimizer::PassToken CreateDescriptorScalarReplacementPass() {
  806. return MakeUnique<Optimizer::PassToken::Impl>(
  807. MakeUnique<opt::DescriptorScalarReplacement>());
  808. }
  809. Optimizer::PassToken CreateWrapOpKillPass() {
  810. return MakeUnique<Optimizer::PassToken::Impl>(MakeUnique<opt::WrapOpKill>());
  811. }
  812. Optimizer::PassToken CreateAmdExtToKhrPass() {
  813. return MakeUnique<Optimizer::PassToken::Impl>(
  814. MakeUnique<opt::AmdExtensionToKhrPass>());
  815. }
  816. } // namespace spvtools