peeling.cpp 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. // Copyright (c) 2018 Google LLC.
  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 <memory>
  15. #include <vector>
  16. #include "effcee/effcee.h"
  17. #include "gmock/gmock.h"
  18. #include "source/opt/ir_builder.h"
  19. #include "source/opt/loop_descriptor.h"
  20. #include "source/opt/loop_peeling.h"
  21. #include "test/opt/pass_fixture.h"
  22. namespace spvtools {
  23. namespace opt {
  24. namespace {
  25. using PeelingTest = PassTest<::testing::Test>;
  26. bool Validate(const std::vector<uint32_t>& bin) {
  27. spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
  28. spv_context spvContext = spvContextCreate(target_env);
  29. spv_diagnostic diagnostic = nullptr;
  30. spv_const_binary_t binary = {bin.data(), bin.size()};
  31. spv_result_t error = spvValidate(spvContext, &binary, &diagnostic);
  32. if (error != 0) spvDiagnosticPrint(diagnostic);
  33. spvDiagnosticDestroy(diagnostic);
  34. spvContextDestroy(spvContext);
  35. return error == 0;
  36. }
  37. void Match(const std::string& checks, IRContext* context) {
  38. // Silence unused warnings with !defined(SPIRV_EFFCE)
  39. (void)checks;
  40. std::vector<uint32_t> bin;
  41. context->module()->ToBinary(&bin, true);
  42. EXPECT_TRUE(Validate(bin));
  43. std::string assembly;
  44. SpirvTools tools(SPV_ENV_UNIVERSAL_1_2);
  45. EXPECT_TRUE(
  46. tools.Disassemble(bin, &assembly, SPV_BINARY_TO_TEXT_OPTION_NO_HEADER))
  47. << "Disassembling failed for shader:\n"
  48. << assembly << std::endl;
  49. auto match_result = effcee::Match(assembly, checks);
  50. EXPECT_EQ(effcee::Result::Status::Ok, match_result.status())
  51. << match_result.message() << "\nChecking result:\n"
  52. << assembly;
  53. }
  54. /*
  55. Generated from the following GLSL + --eliminate-local-multi-store
  56. First test:
  57. #version 330 core
  58. void main() {
  59. for(int i = 0; i < 10; ++i) {
  60. if (i < 4)
  61. break;
  62. }
  63. }
  64. Second test (with a common sub-expression elimination):
  65. #version 330 core
  66. void main() {
  67. for(int i = 0; i + 1 < 10; ++i) {
  68. }
  69. }
  70. Third test:
  71. #version 330 core
  72. void main() {
  73. int a[10];
  74. for (int i = 0; a[i] != 0; i++) {}
  75. }
  76. Forth test:
  77. #version 330 core
  78. void main() {
  79. for (long i = 0; i < 10; i++) {}
  80. }
  81. Fifth test:
  82. #version 330 core
  83. void main() {
  84. for (float i = 0; i < 10; i++) {}
  85. }
  86. Sixth test:
  87. #version 450
  88. layout(location = 0)out float o;
  89. void main() {
  90. o = 0.0;
  91. for( int i = 0; true; i++ ) {
  92. o += 1.0;
  93. if (i > 10) break;
  94. }
  95. }
  96. */
  97. TEST_F(PeelingTest, CannotPeel) {
  98. // Build the given SPIR-V program in |text|, take the first loop in the first
  99. // function and test that it is not peelable. |loop_count_id| is the id
  100. // representing the loop count, if equals to 0, then the function build a 10
  101. // constant as loop count.
  102. auto test_cannot_peel = [](const std::string& text, uint32_t loop_count_id) {
  103. std::unique_ptr<IRContext> context =
  104. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  105. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  106. Module* module = context->module();
  107. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  108. << text << std::endl;
  109. Function& f = *module->begin();
  110. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  111. EXPECT_EQ(ld.NumLoops(), 1u);
  112. Instruction* loop_count = nullptr;
  113. if (loop_count_id) {
  114. loop_count = context->get_def_use_mgr()->GetDef(loop_count_id);
  115. } else {
  116. InstructionBuilder builder(context.get(), &*f.begin());
  117. // Exit condition.
  118. loop_count = builder.GetSintConstant(10);
  119. }
  120. LoopPeeling peel(&*ld.begin(), loop_count);
  121. EXPECT_FALSE(peel.CanPeelLoop());
  122. };
  123. {
  124. SCOPED_TRACE("loop with break");
  125. const std::string text = R"(
  126. OpCapability Shader
  127. %1 = OpExtInstImport "GLSL.std.450"
  128. OpMemoryModel Logical GLSL450
  129. OpEntryPoint Fragment %main "main"
  130. OpExecutionMode %main OriginLowerLeft
  131. OpSource GLSL 330
  132. OpName %main "main"
  133. %void = OpTypeVoid
  134. %3 = OpTypeFunction %void
  135. %int = OpTypeInt 32 1
  136. %_ptr_Function_int = OpTypePointer Function %int
  137. %int_0 = OpConstant %int 0
  138. %int_10 = OpConstant %int 10
  139. %bool = OpTypeBool
  140. %int_4 = OpConstant %int 4
  141. %int_1 = OpConstant %int 1
  142. %main = OpFunction %void None %3
  143. %5 = OpLabel
  144. OpBranch %10
  145. %10 = OpLabel
  146. %28 = OpPhi %int %int_0 %5 %27 %13
  147. OpLoopMerge %12 %13 None
  148. OpBranch %14
  149. %14 = OpLabel
  150. %18 = OpSLessThan %bool %28 %int_10
  151. OpBranchConditional %18 %11 %12
  152. %11 = OpLabel
  153. %21 = OpSLessThan %bool %28 %int_4
  154. OpSelectionMerge %23 None
  155. OpBranchConditional %21 %22 %23
  156. %22 = OpLabel
  157. OpBranch %12
  158. %23 = OpLabel
  159. OpBranch %13
  160. %13 = OpLabel
  161. %27 = OpIAdd %int %28 %int_1
  162. OpBranch %10
  163. %12 = OpLabel
  164. OpReturn
  165. OpFunctionEnd
  166. )";
  167. test_cannot_peel(text, 0);
  168. }
  169. {
  170. SCOPED_TRACE("Ambiguous iterator update");
  171. const std::string text = R"(
  172. OpCapability Shader
  173. %1 = OpExtInstImport "GLSL.std.450"
  174. OpMemoryModel Logical GLSL450
  175. OpEntryPoint Fragment %main "main"
  176. OpExecutionMode %main OriginLowerLeft
  177. OpSource GLSL 330
  178. OpName %main "main"
  179. %void = OpTypeVoid
  180. %3 = OpTypeFunction %void
  181. %int = OpTypeInt 32 1
  182. %_ptr_Function_int = OpTypePointer Function %int
  183. %int_0 = OpConstant %int 0
  184. %int_1 = OpConstant %int 1
  185. %int_10 = OpConstant %int 10
  186. %bool = OpTypeBool
  187. %main = OpFunction %void None %3
  188. %5 = OpLabel
  189. OpBranch %10
  190. %10 = OpLabel
  191. %23 = OpPhi %int %int_0 %5 %17 %13
  192. OpLoopMerge %12 %13 None
  193. OpBranch %14
  194. %14 = OpLabel
  195. %17 = OpIAdd %int %23 %int_1
  196. %20 = OpSLessThan %bool %17 %int_10
  197. OpBranchConditional %20 %11 %12
  198. %11 = OpLabel
  199. OpBranch %13
  200. %13 = OpLabel
  201. OpBranch %10
  202. %12 = OpLabel
  203. OpReturn
  204. OpFunctionEnd
  205. )";
  206. test_cannot_peel(text, 0);
  207. }
  208. {
  209. SCOPED_TRACE("No loop static bounds");
  210. const std::string text = R"(
  211. OpCapability Shader
  212. %1 = OpExtInstImport "GLSL.std.450"
  213. OpMemoryModel Logical GLSL450
  214. OpEntryPoint Fragment %main "main"
  215. OpExecutionMode %main OriginLowerLeft
  216. OpSource GLSL 330
  217. OpName %main "main"
  218. OpName %i "i"
  219. OpName %a "a"
  220. %void = OpTypeVoid
  221. %3 = OpTypeFunction %void
  222. %int = OpTypeInt 32 1
  223. %_ptr_Function_int = OpTypePointer Function %int
  224. %int_0 = OpConstant %int 0
  225. %uint = OpTypeInt 32 0
  226. %uint_10 = OpConstant %uint 10
  227. %_arr_int_uint_10 = OpTypeArray %int %uint_10
  228. %_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10
  229. %bool = OpTypeBool
  230. %int_1 = OpConstant %int 1
  231. %main = OpFunction %void None %3
  232. %5 = OpLabel
  233. %i = OpVariable %_ptr_Function_int Function
  234. %a = OpVariable %_ptr_Function__arr_int_uint_10 Function
  235. OpStore %i %int_0
  236. OpBranch %10
  237. %10 = OpLabel
  238. %28 = OpPhi %int %int_0 %5 %27 %13
  239. OpLoopMerge %12 %13 None
  240. OpBranch %14
  241. %14 = OpLabel
  242. %21 = OpAccessChain %_ptr_Function_int %a %28
  243. %22 = OpLoad %int %21
  244. %24 = OpINotEqual %bool %22 %int_0
  245. OpBranchConditional %24 %11 %12
  246. %11 = OpLabel
  247. OpBranch %13
  248. %13 = OpLabel
  249. %27 = OpIAdd %int %28 %int_1
  250. OpStore %i %27
  251. OpBranch %10
  252. %12 = OpLabel
  253. OpReturn
  254. OpFunctionEnd
  255. )";
  256. test_cannot_peel(text, 22);
  257. }
  258. {
  259. SCOPED_TRACE("Int 64 type for conditions");
  260. const std::string text = R"(
  261. OpCapability Shader
  262. %1 = OpExtInstImport "GLSL.std.450"
  263. OpMemoryModel Logical GLSL450
  264. OpEntryPoint Fragment %2 "main"
  265. OpExecutionMode %2 OriginLowerLeft
  266. OpSource GLSL 330
  267. OpName %2 "main"
  268. OpName %4 "i"
  269. %6 = OpTypeVoid
  270. %3 = OpTypeFunction %6
  271. %7 = OpTypeInt 64 1
  272. %8 = OpTypePointer Function %7
  273. %9 = OpConstant %7 0
  274. %15 = OpConstant %7 10
  275. %16 = OpTypeBool
  276. %17 = OpConstant %7 1
  277. %2 = OpFunction %6 None %3
  278. %5 = OpLabel
  279. %4 = OpVariable %8 Function
  280. OpStore %4 %9
  281. OpBranch %10
  282. %10 = OpLabel
  283. %22 = OpPhi %7 %9 %5 %21 %13
  284. OpLoopMerge %12 %13 None
  285. OpBranch %14
  286. %14 = OpLabel
  287. %18 = OpSLessThan %16 %22 %15
  288. OpBranchConditional %18 %11 %12
  289. %11 = OpLabel
  290. OpBranch %13
  291. %13 = OpLabel
  292. %21 = OpIAdd %7 %22 %17
  293. OpStore %4 %21
  294. OpBranch %10
  295. %12 = OpLabel
  296. OpReturn
  297. OpFunctionEnd
  298. )";
  299. // %15 is a constant for a 64 int. Currently rejected.
  300. test_cannot_peel(text, 15);
  301. }
  302. {
  303. SCOPED_TRACE("Float type for conditions");
  304. const std::string text = R"(
  305. OpCapability Shader
  306. %1 = OpExtInstImport "GLSL.std.450"
  307. OpMemoryModel Logical GLSL450
  308. OpEntryPoint Fragment %2 "main"
  309. OpExecutionMode %2 OriginLowerLeft
  310. OpSource GLSL 330
  311. OpName %2 "main"
  312. OpName %4 "i"
  313. %6 = OpTypeVoid
  314. %3 = OpTypeFunction %6
  315. %7 = OpTypeFloat 32
  316. %8 = OpTypePointer Function %7
  317. %9 = OpConstant %7 0
  318. %15 = OpConstant %7 10
  319. %16 = OpTypeBool
  320. %17 = OpConstant %7 1
  321. %2 = OpFunction %6 None %3
  322. %5 = OpLabel
  323. %4 = OpVariable %8 Function
  324. OpStore %4 %9
  325. OpBranch %10
  326. %10 = OpLabel
  327. %22 = OpPhi %7 %9 %5 %21 %13
  328. OpLoopMerge %12 %13 None
  329. OpBranch %14
  330. %14 = OpLabel
  331. %18 = OpFOrdLessThan %16 %22 %15
  332. OpBranchConditional %18 %11 %12
  333. %11 = OpLabel
  334. OpBranch %13
  335. %13 = OpLabel
  336. %21 = OpFAdd %7 %22 %17
  337. OpStore %4 %21
  338. OpBranch %10
  339. %12 = OpLabel
  340. OpReturn
  341. OpFunctionEnd
  342. )";
  343. // %15 is a constant for a float. Currently rejected.
  344. test_cannot_peel(text, 15);
  345. }
  346. {
  347. SCOPED_TRACE("Side effect before exit");
  348. const std::string text = R"(
  349. OpCapability Shader
  350. %1 = OpExtInstImport "GLSL.std.450"
  351. OpMemoryModel Logical GLSL450
  352. OpEntryPoint Fragment %main "main" %o
  353. OpExecutionMode %main OriginLowerLeft
  354. OpSource GLSL 450
  355. OpName %main "main"
  356. OpName %o "o"
  357. OpName %i "i"
  358. OpDecorate %o Location 0
  359. %void = OpTypeVoid
  360. %3 = OpTypeFunction %void
  361. %float = OpTypeFloat 32
  362. %_ptr_Output_float = OpTypePointer Output %float
  363. %o = OpVariable %_ptr_Output_float Output
  364. %float_0 = OpConstant %float 0
  365. %int = OpTypeInt 32 1
  366. %_ptr_Function_int = OpTypePointer Function %int
  367. %int_0 = OpConstant %int 0
  368. %bool = OpTypeBool
  369. %true = OpConstantTrue %bool
  370. %float_1 = OpConstant %float 1
  371. %int_10 = OpConstant %int 10
  372. %int_1 = OpConstant %int 1
  373. %main = OpFunction %void None %3
  374. %5 = OpLabel
  375. %i = OpVariable %_ptr_Function_int Function
  376. OpStore %o %float_0
  377. OpStore %i %int_0
  378. OpBranch %14
  379. %14 = OpLabel
  380. %33 = OpPhi %int %int_0 %5 %32 %17
  381. OpLoopMerge %16 %17 None
  382. OpBranch %15
  383. %15 = OpLabel
  384. %22 = OpLoad %float %o
  385. %23 = OpFAdd %float %22 %float_1
  386. OpStore %o %23
  387. %26 = OpSGreaterThan %bool %33 %int_10
  388. OpSelectionMerge %28 None
  389. OpBranchConditional %26 %27 %28
  390. %27 = OpLabel
  391. OpBranch %16
  392. %28 = OpLabel
  393. OpBranch %17
  394. %17 = OpLabel
  395. %32 = OpIAdd %int %33 %int_1
  396. OpStore %i %32
  397. OpBranch %14
  398. %16 = OpLabel
  399. OpReturn
  400. OpFunctionEnd
  401. )";
  402. test_cannot_peel(text, 0);
  403. }
  404. }
  405. /*
  406. Generated from the following GLSL + --eliminate-local-multi-store
  407. #version 330 core
  408. void main() {
  409. int i = 0;
  410. for (; i < 10; i++) {}
  411. }
  412. */
  413. TEST_F(PeelingTest, SimplePeeling) {
  414. const std::string text = R"(
  415. OpCapability Shader
  416. %1 = OpExtInstImport "GLSL.std.450"
  417. OpMemoryModel Logical GLSL450
  418. OpEntryPoint Fragment %main "main"
  419. OpExecutionMode %main OriginLowerLeft
  420. OpSource GLSL 330
  421. OpName %main "main"
  422. %void = OpTypeVoid
  423. %3 = OpTypeFunction %void
  424. %int = OpTypeInt 32 1
  425. %_ptr_Function_int = OpTypePointer Function %int
  426. %int_0 = OpConstant %int 0
  427. %int_10 = OpConstant %int 10
  428. %bool = OpTypeBool
  429. %int_1 = OpConstant %int 1
  430. %main = OpFunction %void None %3
  431. %5 = OpLabel
  432. OpBranch %10
  433. %10 = OpLabel
  434. %22 = OpPhi %int %int_0 %5 %21 %13
  435. OpLoopMerge %12 %13 None
  436. OpBranch %14
  437. %14 = OpLabel
  438. %18 = OpSLessThan %bool %22 %int_10
  439. OpBranchConditional %18 %11 %12
  440. %11 = OpLabel
  441. OpBranch %13
  442. %13 = OpLabel
  443. %21 = OpIAdd %int %22 %int_1
  444. OpBranch %10
  445. %12 = OpLabel
  446. OpReturn
  447. OpFunctionEnd
  448. )";
  449. // Peel before.
  450. {
  451. SCOPED_TRACE("Peel before");
  452. std::unique_ptr<IRContext> context =
  453. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  454. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  455. Module* module = context->module();
  456. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  457. << text << std::endl;
  458. Function& f = *module->begin();
  459. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  460. EXPECT_EQ(ld.NumLoops(), 1u);
  461. InstructionBuilder builder(context.get(), &*f.begin());
  462. // Exit condition.
  463. Instruction* ten_cst = builder.GetSintConstant(10);
  464. LoopPeeling peel(&*ld.begin(), ten_cst);
  465. EXPECT_TRUE(peel.CanPeelLoop());
  466. peel.PeelBefore(2);
  467. const std::string check = R"(
  468. CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10
  469. CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2
  470. CHECK: OpFunction
  471. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  472. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]]
  473. CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]]
  474. CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
  475. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  476. CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  477. CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
  478. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  479. CHECK-NEXT: OpSLessThan
  480. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
  481. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
  482. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
  483. CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  484. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  485. CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
  486. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  487. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
  488. CHECK: [[AFTER_LOOP]] = OpLabel
  489. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
  490. CHECK-NEXT: OpLoopMerge
  491. )";
  492. Match(check, context.get());
  493. }
  494. // Peel after.
  495. {
  496. SCOPED_TRACE("Peel after");
  497. std::unique_ptr<IRContext> context =
  498. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  499. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  500. Module* module = context->module();
  501. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  502. << text << std::endl;
  503. Function& f = *module->begin();
  504. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  505. EXPECT_EQ(ld.NumLoops(), 1u);
  506. InstructionBuilder builder(context.get(), &*f.begin());
  507. // Exit condition.
  508. Instruction* ten_cst = builder.GetSintConstant(10);
  509. LoopPeeling peel(&*ld.begin(), ten_cst);
  510. EXPECT_TRUE(peel.CanPeelLoop());
  511. peel.PeelAfter(2);
  512. const std::string check = R"(
  513. CHECK: OpFunction
  514. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  515. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
  516. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  517. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
  518. CHECK: [[BEFORE_LOOP]] = OpLabel
  519. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  520. CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  521. CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
  522. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  523. CHECK-NEXT: OpSLessThan
  524. CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
  525. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
  526. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
  527. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
  528. CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  529. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  530. CHECK: [[IF_MERGE]] = OpLabel
  531. CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
  532. CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
  533. CHECK: [[AFTER_LOOP]] = OpLabel
  534. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
  535. CHECK-NEXT: OpLoopMerge
  536. )";
  537. Match(check, context.get());
  538. }
  539. // Same as above, but reuse the induction variable.
  540. // Peel before.
  541. {
  542. SCOPED_TRACE("Peel before with IV reuse");
  543. std::unique_ptr<IRContext> context =
  544. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  545. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  546. Module* module = context->module();
  547. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  548. << text << std::endl;
  549. Function& f = *module->begin();
  550. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  551. EXPECT_EQ(ld.NumLoops(), 1u);
  552. InstructionBuilder builder(context.get(), &*f.begin());
  553. // Exit condition.
  554. Instruction* ten_cst = builder.GetSintConstant(10);
  555. LoopPeeling peel(&*ld.begin(), ten_cst,
  556. context->get_def_use_mgr()->GetDef(22));
  557. EXPECT_TRUE(peel.CanPeelLoop());
  558. peel.PeelBefore(2);
  559. const std::string check = R"(
  560. CHECK: [[CST_TEN:%\w+]] = OpConstant {{%\w+}} 10
  561. CHECK: [[CST_TWO:%\w+]] = OpConstant {{%\w+}} 2
  562. CHECK: OpFunction
  563. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  564. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} [[CST_TWO]] [[CST_TEN]]
  565. CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] [[CST_TWO]] [[CST_TEN]]
  566. CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
  567. CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE:%\w+]]
  568. CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
  569. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  570. CHECK-NEXT: OpSLessThan
  571. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[i]]
  572. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
  573. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
  574. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  575. CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
  576. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  577. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
  578. CHECK: [[AFTER_LOOP]] = OpLabel
  579. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
  580. CHECK-NEXT: OpLoopMerge
  581. )";
  582. Match(check, context.get());
  583. }
  584. // Peel after.
  585. {
  586. SCOPED_TRACE("Peel after IV reuse");
  587. std::unique_ptr<IRContext> context =
  588. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  589. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  590. Module* module = context->module();
  591. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  592. << text << std::endl;
  593. Function& f = *module->begin();
  594. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  595. EXPECT_EQ(ld.NumLoops(), 1u);
  596. InstructionBuilder builder(context.get(), &*f.begin());
  597. // Exit condition.
  598. Instruction* ten_cst = builder.GetSintConstant(10);
  599. LoopPeeling peel(&*ld.begin(), ten_cst,
  600. context->get_def_use_mgr()->GetDef(22));
  601. EXPECT_TRUE(peel.CanPeelLoop());
  602. peel.PeelAfter(2);
  603. const std::string check = R"(
  604. CHECK: OpFunction
  605. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  606. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
  607. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  608. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
  609. CHECK: [[BEFORE_LOOP]] = OpLabel
  610. CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE:%\w+]]
  611. CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
  612. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  613. CHECK-NEXT: OpSLessThan
  614. CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[I]] {{%\w+}}
  615. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
  616. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
  617. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
  618. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  619. CHECK: [[IF_MERGE]] = OpLabel
  620. CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
  621. CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
  622. CHECK: [[AFTER_LOOP]] = OpLabel
  623. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
  624. CHECK-NEXT: OpLoopMerge
  625. )";
  626. Match(check, context.get());
  627. }
  628. }
  629. /*
  630. Generated from the following GLSL + --eliminate-local-multi-store
  631. #version 330 core
  632. void main() {
  633. int a[10];
  634. int n = a[0];
  635. for(int i = 0; i < n; ++i) {}
  636. }
  637. */
  638. TEST_F(PeelingTest, PeelingUncountable) {
  639. const std::string text = R"(
  640. OpCapability Shader
  641. %1 = OpExtInstImport "GLSL.std.450"
  642. OpMemoryModel Logical GLSL450
  643. OpEntryPoint Fragment %main "main"
  644. OpExecutionMode %main OriginLowerLeft
  645. OpSource GLSL 330
  646. OpName %main "main"
  647. OpName %a "a"
  648. %void = OpTypeVoid
  649. %3 = OpTypeFunction %void
  650. %int = OpTypeInt 32 1
  651. %_ptr_Function_int = OpTypePointer Function %int
  652. %uint = OpTypeInt 32 0
  653. %uint_10 = OpConstant %uint 10
  654. %_arr_int_uint_10 = OpTypeArray %int %uint_10
  655. %_ptr_Function__arr_int_uint_10 = OpTypePointer Function %_arr_int_uint_10
  656. %int_0 = OpConstant %int 0
  657. %bool = OpTypeBool
  658. %int_1 = OpConstant %int 1
  659. %main = OpFunction %void None %3
  660. %5 = OpLabel
  661. %a = OpVariable %_ptr_Function__arr_int_uint_10 Function
  662. %15 = OpAccessChain %_ptr_Function_int %a %int_0
  663. %16 = OpLoad %int %15
  664. OpBranch %18
  665. %18 = OpLabel
  666. %30 = OpPhi %int %int_0 %5 %29 %21
  667. OpLoopMerge %20 %21 None
  668. OpBranch %22
  669. %22 = OpLabel
  670. %26 = OpSLessThan %bool %30 %16
  671. OpBranchConditional %26 %19 %20
  672. %19 = OpLabel
  673. OpBranch %21
  674. %21 = OpLabel
  675. %29 = OpIAdd %int %30 %int_1
  676. OpBranch %18
  677. %20 = OpLabel
  678. OpReturn
  679. OpFunctionEnd
  680. )";
  681. // Peel before.
  682. {
  683. SCOPED_TRACE("Peel before");
  684. std::unique_ptr<IRContext> context =
  685. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  686. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  687. Module* module = context->module();
  688. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  689. << text << std::endl;
  690. Function& f = *module->begin();
  691. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  692. EXPECT_EQ(ld.NumLoops(), 1u);
  693. Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
  694. EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
  695. LoopPeeling peel(&*ld.begin(), loop_count);
  696. EXPECT_TRUE(peel.CanPeelLoop());
  697. peel.PeelBefore(1);
  698. const std::string check = R"(
  699. CHECK: OpFunction
  700. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  701. CHECK: [[LOOP_COUNT:%\w+]] = OpLoad
  702. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]]
  703. CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]]
  704. CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
  705. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  706. CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  707. CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
  708. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  709. CHECK-NEXT: OpSLessThan
  710. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
  711. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
  712. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
  713. CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  714. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  715. CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
  716. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  717. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
  718. CHECK: [[AFTER_LOOP]] = OpLabel
  719. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
  720. CHECK-NEXT: OpLoopMerge
  721. )";
  722. Match(check, context.get());
  723. }
  724. // Peel after.
  725. {
  726. SCOPED_TRACE("Peel after");
  727. std::unique_ptr<IRContext> context =
  728. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  729. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  730. Module* module = context->module();
  731. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  732. << text << std::endl;
  733. Function& f = *module->begin();
  734. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  735. EXPECT_EQ(ld.NumLoops(), 1u);
  736. Instruction* loop_count = context->get_def_use_mgr()->GetDef(16);
  737. EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
  738. LoopPeeling peel(&*ld.begin(), loop_count);
  739. EXPECT_TRUE(peel.CanPeelLoop());
  740. peel.PeelAfter(1);
  741. const std::string check = R"(
  742. CHECK: OpFunction
  743. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  744. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
  745. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  746. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
  747. CHECK: [[BEFORE_LOOP]] = OpLabel
  748. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  749. CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  750. CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
  751. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  752. CHECK-NEXT: OpSLessThan
  753. CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
  754. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
  755. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
  756. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
  757. CHECK-NEXT: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  758. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  759. CHECK: [[IF_MERGE]] = OpLabel
  760. CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
  761. CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
  762. CHECK: [[AFTER_LOOP]] = OpLabel
  763. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
  764. CHECK-NEXT: OpLoopMerge
  765. )";
  766. Match(check, context.get());
  767. }
  768. }
  769. /*
  770. Generated from the following GLSL + --eliminate-local-multi-store
  771. #version 330 core
  772. void main() {
  773. int i = 0;
  774. do {
  775. i++;
  776. } while (i < 10);
  777. }
  778. */
  779. TEST_F(PeelingTest, DoWhilePeeling) {
  780. const std::string text = R"(
  781. OpCapability Shader
  782. %1 = OpExtInstImport "GLSL.std.450"
  783. OpMemoryModel Logical GLSL450
  784. OpEntryPoint Fragment %main "main"
  785. OpExecutionMode %main OriginLowerLeft
  786. OpSource GLSL 330
  787. OpName %main "main"
  788. %void = OpTypeVoid
  789. %3 = OpTypeFunction %void
  790. %int = OpTypeInt 32 1
  791. %_ptr_Function_int = OpTypePointer Function %int
  792. %int_0 = OpConstant %int 0
  793. %int_1 = OpConstant %int 1
  794. %int_10 = OpConstant %int 10
  795. %bool = OpTypeBool
  796. %main = OpFunction %void None %3
  797. %5 = OpLabel
  798. OpBranch %10
  799. %10 = OpLabel
  800. %21 = OpPhi %int %int_0 %5 %16 %13
  801. OpLoopMerge %12 %13 None
  802. OpBranch %11
  803. %11 = OpLabel
  804. %16 = OpIAdd %int %21 %int_1
  805. OpBranch %13
  806. %13 = OpLabel
  807. %20 = OpSLessThan %bool %16 %int_10
  808. OpBranchConditional %20 %10 %12
  809. %12 = OpLabel
  810. OpReturn
  811. OpFunctionEnd
  812. )";
  813. // Peel before.
  814. {
  815. SCOPED_TRACE("Peel before");
  816. std::unique_ptr<IRContext> context =
  817. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  818. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  819. Module* module = context->module();
  820. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  821. << text << std::endl;
  822. Function& f = *module->begin();
  823. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  824. EXPECT_EQ(ld.NumLoops(), 1u);
  825. InstructionBuilder builder(context.get(), &*f.begin());
  826. // Exit condition.
  827. Instruction* ten_cst = builder.GetUintConstant(10);
  828. LoopPeeling peel(&*ld.begin(), ten_cst);
  829. EXPECT_TRUE(peel.CanPeelLoop());
  830. peel.PeelBefore(2);
  831. const std::string check = R"(
  832. CHECK: OpFunction
  833. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  834. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}}
  835. CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]]
  836. CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
  837. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  838. CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  839. CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
  840. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
  841. CHECK: [[BE]] = OpLabel
  842. CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  843. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[DUMMY_IT_1]]
  844. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[AFTER_LOOP_PREHEADER]]
  845. CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
  846. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  847. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
  848. CHECK: [[AFTER_LOOP]] = OpLabel
  849. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[I_1]] [[AFTER_LOOP_PREHEADER]]
  850. CHECK-NEXT: OpLoopMerge
  851. )";
  852. Match(check, context.get());
  853. }
  854. // Peel after.
  855. {
  856. SCOPED_TRACE("Peel after");
  857. std::unique_ptr<IRContext> context =
  858. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  859. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  860. Module* module = context->module();
  861. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  862. << text << std::endl;
  863. Function& f = *module->begin();
  864. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  865. EXPECT_EQ(ld.NumLoops(), 1u);
  866. InstructionBuilder builder(context.get(), &*f.begin());
  867. // Exit condition.
  868. Instruction* ten_cst = builder.GetUintConstant(10);
  869. LoopPeeling peel(&*ld.begin(), ten_cst);
  870. EXPECT_TRUE(peel.CanPeelLoop());
  871. peel.PeelAfter(2);
  872. const std::string check = R"(
  873. CHECK: OpFunction
  874. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  875. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpULessThan {{%\w+}}
  876. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  877. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
  878. CHECK: [[BEFORE_LOOP]] = OpLabel
  879. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  880. CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  881. CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
  882. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
  883. CHECK: [[BE]] = OpLabel
  884. CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  885. CHECK-NEXT: [[EXIT_VAL:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT_1]]
  886. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpULessThan {{%\w+}} [[EXIT_VAL]]
  887. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] [[BEFORE_LOOP]] [[BEFORE_LOOP_MERGE]]
  888. CHECK: [[IF_MERGE]] = OpLabel
  889. CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I_1]] [[BEFORE_LOOP_MERGE]]
  890. CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
  891. CHECK: [[AFTER_LOOP]] = OpLabel
  892. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
  893. CHECK-NEXT: OpLoopMerge
  894. )";
  895. Match(check, context.get());
  896. }
  897. }
  898. /*
  899. Generated from the following GLSL + --eliminate-local-multi-store
  900. #version 330 core
  901. void main() {
  902. int a[10];
  903. int n = a[0];
  904. for(int i = 0; i < n; ++i) {}
  905. }
  906. */
  907. TEST_F(PeelingTest, PeelingLoopWithStore) {
  908. const std::string text = R"(
  909. OpCapability Shader
  910. %1 = OpExtInstImport "GLSL.std.450"
  911. OpMemoryModel Logical GLSL450
  912. OpEntryPoint Fragment %main "main" %o %n
  913. OpExecutionMode %main OriginLowerLeft
  914. OpSource GLSL 450
  915. OpName %main "main"
  916. OpName %o "o"
  917. OpName %end "end"
  918. OpName %n "n"
  919. OpName %i "i"
  920. OpDecorate %o Location 0
  921. OpDecorate %n Flat
  922. OpDecorate %n Location 0
  923. %void = OpTypeVoid
  924. %3 = OpTypeFunction %void
  925. %float = OpTypeFloat 32
  926. %_ptr_Output_float = OpTypePointer Output %float
  927. %o = OpVariable %_ptr_Output_float Output
  928. %float_0 = OpConstant %float 0
  929. %int = OpTypeInt 32 1
  930. %_ptr_Function_int = OpTypePointer Function %int
  931. %_ptr_Input_int = OpTypePointer Input %int
  932. %n = OpVariable %_ptr_Input_int Input
  933. %int_0 = OpConstant %int 0
  934. %bool = OpTypeBool
  935. %float_1 = OpConstant %float 1
  936. %int_1 = OpConstant %int 1
  937. %main = OpFunction %void None %3
  938. %5 = OpLabel
  939. %end = OpVariable %_ptr_Function_int Function
  940. %i = OpVariable %_ptr_Function_int Function
  941. OpStore %o %float_0
  942. %15 = OpLoad %int %n
  943. OpStore %end %15
  944. OpStore %i %int_0
  945. OpBranch %18
  946. %18 = OpLabel
  947. %33 = OpPhi %int %int_0 %5 %32 %21
  948. OpLoopMerge %20 %21 None
  949. OpBranch %22
  950. %22 = OpLabel
  951. %26 = OpSLessThan %bool %33 %15
  952. OpBranchConditional %26 %19 %20
  953. %19 = OpLabel
  954. %28 = OpLoad %float %o
  955. %29 = OpFAdd %float %28 %float_1
  956. OpStore %o %29
  957. OpBranch %21
  958. %21 = OpLabel
  959. %32 = OpIAdd %int %33 %int_1
  960. OpStore %i %32
  961. OpBranch %18
  962. %20 = OpLabel
  963. OpReturn
  964. OpFunctionEnd
  965. )";
  966. // Peel before.
  967. {
  968. SCOPED_TRACE("Peel before");
  969. std::unique_ptr<IRContext> context =
  970. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  971. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  972. Module* module = context->module();
  973. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  974. << text << std::endl;
  975. Function& f = *module->begin();
  976. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  977. EXPECT_EQ(ld.NumLoops(), 1u);
  978. Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
  979. EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
  980. LoopPeeling peel(&*ld.begin(), loop_count);
  981. EXPECT_TRUE(peel.CanPeelLoop());
  982. peel.PeelBefore(1);
  983. const std::string check = R"(
  984. CHECK: OpFunction
  985. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  986. CHECK: [[LOOP_COUNT:%\w+]] = OpLoad
  987. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}} {{%\w+}} [[LOOP_COUNT]]
  988. CHECK-NEXT: [[LOOP_COUNT:%\w+]] = OpSelect {{%\w+}} [[MIN_LOOP_COUNT]] {{%\w+}} [[LOOP_COUNT]]
  989. CHECK: [[BEFORE_LOOP:%\w+]] = OpLabel
  990. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  991. CHECK-NEXT: [[i:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  992. CHECK-NEXT: OpLoopMerge [[AFTER_LOOP_PREHEADER:%\w+]] [[BE]] None
  993. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  994. CHECK-NEXT: OpSLessThan
  995. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[DUMMY_IT]]
  996. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[AFTER_LOOP_PREHEADER]]
  997. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[i]]
  998. CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  999. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  1000. CHECK: [[AFTER_LOOP_PREHEADER]] = OpLabel
  1001. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  1002. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[AFTER_LOOP:%\w+]] [[IF_MERGE]]
  1003. CHECK: [[AFTER_LOOP]] = OpLabel
  1004. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[i]] [[AFTER_LOOP_PREHEADER]]
  1005. CHECK-NEXT: OpLoopMerge
  1006. )";
  1007. Match(check, context.get());
  1008. }
  1009. // Peel after.
  1010. {
  1011. SCOPED_TRACE("Peel after");
  1012. std::unique_ptr<IRContext> context =
  1013. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  1014. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  1015. Module* module = context->module();
  1016. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  1017. << text << std::endl;
  1018. Function& f = *module->begin();
  1019. LoopDescriptor& ld = *context->GetLoopDescriptor(&f);
  1020. EXPECT_EQ(ld.NumLoops(), 1u);
  1021. Instruction* loop_count = context->get_def_use_mgr()->GetDef(15);
  1022. EXPECT_EQ(loop_count->opcode(), spv::Op::OpLoad);
  1023. LoopPeeling peel(&*ld.begin(), loop_count);
  1024. EXPECT_TRUE(peel.CanPeelLoop());
  1025. peel.PeelAfter(1);
  1026. const std::string check = R"(
  1027. CHECK: OpFunction
  1028. CHECK-NEXT: [[ENTRY:%\w+]] = OpLabel
  1029. CHECK: [[MIN_LOOP_COUNT:%\w+]] = OpSLessThan {{%\w+}}
  1030. CHECK-NEXT: OpSelectionMerge [[IF_MERGE:%\w+]]
  1031. CHECK-NEXT: OpBranchConditional [[MIN_LOOP_COUNT]] [[BEFORE_LOOP:%\w+]] [[IF_MERGE]]
  1032. CHECK: [[BEFORE_LOOP]] = OpLabel
  1033. CHECK-NEXT: [[DUMMY_IT:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[DUMMY_IT_1:%\w+]] [[BE:%\w+]]
  1034. CHECK-NEXT: [[I:%\w+]] = OpPhi {{%\w+}} {{%\w+}} [[ENTRY]] [[I_1:%\w+]] [[BE]]
  1035. CHECK-NEXT: OpLoopMerge [[BEFORE_LOOP_MERGE:%\w+]] [[BE]] None
  1036. CHECK: [[COND_BLOCK:%\w+]] = OpLabel
  1037. CHECK-NEXT: OpSLessThan
  1038. CHECK-NEXT: [[TMP:%\w+]] = OpIAdd {{%\w+}} [[DUMMY_IT]] {{%\w+}}
  1039. CHECK-NEXT: [[EXIT_COND:%\w+]] = OpSLessThan {{%\w+}} [[TMP]]
  1040. CHECK-NEXT: OpBranchConditional [[EXIT_COND]] {{%\w+}} [[BEFORE_LOOP_MERGE]]
  1041. CHECK: [[I_1]] = OpIAdd {{%\w+}} [[I]]
  1042. CHECK: [[DUMMY_IT_1]] = OpIAdd {{%\w+}} [[DUMMY_IT]]
  1043. CHECK-NEXT: OpBranch [[BEFORE_LOOP]]
  1044. CHECK: [[IF_MERGE]] = OpLabel
  1045. CHECK-NEXT: [[TMP:%\w+]] = OpPhi {{%\w+}} [[I]] [[BEFORE_LOOP_MERGE]]
  1046. CHECK-NEXT: OpBranch [[AFTER_LOOP:%\w+]]
  1047. CHECK: [[AFTER_LOOP]] = OpLabel
  1048. CHECK-NEXT: OpPhi {{%\w+}} {{%\w+}} {{%\w+}} [[TMP]] [[IF_MERGE]]
  1049. CHECK-NEXT: OpLoopMerge
  1050. )";
  1051. Match(check, context.get());
  1052. }
  1053. }
  1054. } // namespace
  1055. } // namespace opt
  1056. } // namespace spvtools