peeling.cpp 39 KB

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