nested_loops.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795
  1. // Copyright (c) 2017 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 <memory>
  15. #include <string>
  16. #include <unordered_set>
  17. #include <vector>
  18. #include "gmock/gmock.h"
  19. #include "source/opt/iterator.h"
  20. #include "source/opt/loop_descriptor.h"
  21. #include "source/opt/pass.h"
  22. #include "source/opt/tree_iterator.h"
  23. #include "test/opt/assembly_builder.h"
  24. #include "test/opt/function_utils.h"
  25. #include "test/opt/pass_fixture.h"
  26. #include "test/opt/pass_utils.h"
  27. namespace spvtools {
  28. namespace opt {
  29. namespace {
  30. using ::testing::UnorderedElementsAre;
  31. bool Validate(const std::vector<uint32_t>& bin) {
  32. spv_target_env target_env = SPV_ENV_UNIVERSAL_1_2;
  33. spv_context spvContext = spvContextCreate(target_env);
  34. spv_diagnostic diagnostic = nullptr;
  35. spv_const_binary_t binary = {bin.data(), bin.size()};
  36. spv_result_t error = spvValidate(spvContext, &binary, &diagnostic);
  37. if (error != 0) spvDiagnosticPrint(diagnostic);
  38. spvDiagnosticDestroy(diagnostic);
  39. spvContextDestroy(spvContext);
  40. return error == 0;
  41. }
  42. using PassClassTest = PassTest<::testing::Test>;
  43. /*
  44. Generated from the following GLSL
  45. #version 330 core
  46. layout(location = 0) out vec4 c;
  47. void main() {
  48. int i = 0;
  49. for (; i < 10; ++i) {
  50. int j = 0;
  51. int k = 0;
  52. for (; j < 11; ++j) {}
  53. for (; k < 12; ++k) {}
  54. }
  55. }
  56. */
  57. TEST_F(PassClassTest, BasicVisitFromEntryPoint) {
  58. const std::string text = R"(
  59. OpCapability Shader
  60. %1 = OpExtInstImport "GLSL.std.450"
  61. OpMemoryModel Logical GLSL450
  62. OpEntryPoint Fragment %2 "main" %3
  63. OpExecutionMode %2 OriginUpperLeft
  64. OpSource GLSL 330
  65. OpName %2 "main"
  66. OpName %4 "i"
  67. OpName %5 "j"
  68. OpName %6 "k"
  69. OpName %3 "c"
  70. OpDecorate %3 Location 0
  71. %7 = OpTypeVoid
  72. %8 = OpTypeFunction %7
  73. %9 = OpTypeInt 32 1
  74. %10 = OpTypePointer Function %9
  75. %11 = OpConstant %9 0
  76. %12 = OpConstant %9 10
  77. %13 = OpTypeBool
  78. %14 = OpConstant %9 11
  79. %15 = OpConstant %9 1
  80. %16 = OpConstant %9 12
  81. %17 = OpTypeFloat 32
  82. %18 = OpTypeVector %17 4
  83. %19 = OpTypePointer Output %18
  84. %3 = OpVariable %19 Output
  85. %2 = OpFunction %7 None %8
  86. %20 = OpLabel
  87. %4 = OpVariable %10 Function
  88. %5 = OpVariable %10 Function
  89. %6 = OpVariable %10 Function
  90. OpStore %4 %11
  91. OpBranch %21
  92. %21 = OpLabel
  93. OpLoopMerge %22 %23 None
  94. OpBranch %24
  95. %24 = OpLabel
  96. %25 = OpLoad %9 %4
  97. %26 = OpSLessThan %13 %25 %12
  98. OpBranchConditional %26 %27 %22
  99. %27 = OpLabel
  100. OpStore %5 %11
  101. OpStore %6 %11
  102. OpBranch %28
  103. %28 = OpLabel
  104. OpLoopMerge %29 %30 None
  105. OpBranch %31
  106. %31 = OpLabel
  107. %32 = OpLoad %9 %5
  108. %33 = OpSLessThan %13 %32 %14
  109. OpBranchConditional %33 %34 %29
  110. %34 = OpLabel
  111. OpBranch %30
  112. %30 = OpLabel
  113. %35 = OpLoad %9 %5
  114. %36 = OpIAdd %9 %35 %15
  115. OpStore %5 %36
  116. OpBranch %28
  117. %29 = OpLabel
  118. OpBranch %37
  119. %37 = OpLabel
  120. OpLoopMerge %38 %39 None
  121. OpBranch %40
  122. %40 = OpLabel
  123. %41 = OpLoad %9 %6
  124. %42 = OpSLessThan %13 %41 %16
  125. OpBranchConditional %42 %43 %38
  126. %43 = OpLabel
  127. OpBranch %39
  128. %39 = OpLabel
  129. %44 = OpLoad %9 %6
  130. %45 = OpIAdd %9 %44 %15
  131. OpStore %6 %45
  132. OpBranch %37
  133. %38 = OpLabel
  134. OpBranch %23
  135. %23 = OpLabel
  136. %46 = OpLoad %9 %4
  137. %47 = OpIAdd %9 %46 %15
  138. OpStore %4 %47
  139. OpBranch %21
  140. %22 = OpLabel
  141. OpReturn
  142. OpFunctionEnd
  143. )";
  144. // clang-format on
  145. std::unique_ptr<IRContext> context =
  146. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  147. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  148. Module* module = context->module();
  149. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  150. << text << std::endl;
  151. const Function* f = spvtest::GetFunction(module, 2);
  152. LoopDescriptor& ld = *context->GetLoopDescriptor(f);
  153. EXPECT_EQ(ld.NumLoops(), 3u);
  154. // Invalid basic block id.
  155. EXPECT_EQ(ld[0u], nullptr);
  156. // Not a loop header.
  157. EXPECT_EQ(ld[20], nullptr);
  158. Loop& parent_loop = *ld[21];
  159. EXPECT_TRUE(parent_loop.HasNestedLoops());
  160. EXPECT_FALSE(parent_loop.IsNested());
  161. EXPECT_EQ(parent_loop.GetDepth(), 1u);
  162. EXPECT_EQ(std::distance(parent_loop.begin(), parent_loop.end()), 2u);
  163. EXPECT_EQ(parent_loop.GetHeaderBlock(), spvtest::GetBasicBlock(f, 21));
  164. EXPECT_EQ(parent_loop.GetLatchBlock(), spvtest::GetBasicBlock(f, 23));
  165. EXPECT_EQ(parent_loop.GetMergeBlock(), spvtest::GetBasicBlock(f, 22));
  166. Loop& child_loop_1 = *ld[28];
  167. EXPECT_FALSE(child_loop_1.HasNestedLoops());
  168. EXPECT_TRUE(child_loop_1.IsNested());
  169. EXPECT_EQ(child_loop_1.GetDepth(), 2u);
  170. EXPECT_EQ(std::distance(child_loop_1.begin(), child_loop_1.end()), 0u);
  171. EXPECT_EQ(child_loop_1.GetHeaderBlock(), spvtest::GetBasicBlock(f, 28));
  172. EXPECT_EQ(child_loop_1.GetLatchBlock(), spvtest::GetBasicBlock(f, 30));
  173. EXPECT_EQ(child_loop_1.GetMergeBlock(), spvtest::GetBasicBlock(f, 29));
  174. Loop& child_loop_2 = *ld[37];
  175. EXPECT_FALSE(child_loop_2.HasNestedLoops());
  176. EXPECT_TRUE(child_loop_2.IsNested());
  177. EXPECT_EQ(child_loop_2.GetDepth(), 2u);
  178. EXPECT_EQ(std::distance(child_loop_2.begin(), child_loop_2.end()), 0u);
  179. EXPECT_EQ(child_loop_2.GetHeaderBlock(), spvtest::GetBasicBlock(f, 37));
  180. EXPECT_EQ(child_loop_2.GetLatchBlock(), spvtest::GetBasicBlock(f, 39));
  181. EXPECT_EQ(child_loop_2.GetMergeBlock(), spvtest::GetBasicBlock(f, 38));
  182. }
  183. static void CheckLoopBlocks(Loop* loop,
  184. std::unordered_set<uint32_t>* expected_ids) {
  185. SCOPED_TRACE("Check loop " + std::to_string(loop->GetHeaderBlock()->id()));
  186. for (uint32_t bb_id : loop->GetBlocks()) {
  187. EXPECT_EQ(expected_ids->count(bb_id), 1u);
  188. expected_ids->erase(bb_id);
  189. }
  190. EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
  191. EXPECT_EQ(expected_ids->size(), 0u);
  192. }
  193. /*
  194. Generated from the following GLSL
  195. #version 330 core
  196. layout(location = 0) out vec4 c;
  197. void main() {
  198. int i = 0;
  199. for (; i < 10; ++i) {
  200. for (int j = 0; j < 11; ++j) {
  201. if (j < 5) {
  202. for (int k = 0; k < 12; ++k) {}
  203. }
  204. else {}
  205. for (int k = 0; k < 12; ++k) {}
  206. }
  207. }
  208. }*/
  209. TEST_F(PassClassTest, TripleNestedLoop) {
  210. const std::string text = R"(
  211. OpCapability Shader
  212. %1 = OpExtInstImport "GLSL.std.450"
  213. OpMemoryModel Logical GLSL450
  214. OpEntryPoint Fragment %2 "main" %3
  215. OpExecutionMode %2 OriginUpperLeft
  216. OpSource GLSL 330
  217. OpName %2 "main"
  218. OpName %4 "i"
  219. OpName %5 "j"
  220. OpName %6 "k"
  221. OpName %7 "k"
  222. OpName %3 "c"
  223. OpDecorate %3 Location 0
  224. %8 = OpTypeVoid
  225. %9 = OpTypeFunction %8
  226. %10 = OpTypeInt 32 1
  227. %11 = OpTypePointer Function %10
  228. %12 = OpConstant %10 0
  229. %13 = OpConstant %10 10
  230. %14 = OpTypeBool
  231. %15 = OpConstant %10 11
  232. %16 = OpConstant %10 5
  233. %17 = OpConstant %10 12
  234. %18 = OpConstant %10 1
  235. %19 = OpTypeFloat 32
  236. %20 = OpTypeVector %19 4
  237. %21 = OpTypePointer Output %20
  238. %3 = OpVariable %21 Output
  239. %2 = OpFunction %8 None %9
  240. %22 = OpLabel
  241. %4 = OpVariable %11 Function
  242. %5 = OpVariable %11 Function
  243. %6 = OpVariable %11 Function
  244. %7 = OpVariable %11 Function
  245. OpStore %4 %12
  246. OpBranch %23
  247. %23 = OpLabel
  248. OpLoopMerge %24 %25 None
  249. OpBranch %26
  250. %26 = OpLabel
  251. %27 = OpLoad %10 %4
  252. %28 = OpSLessThan %14 %27 %13
  253. OpBranchConditional %28 %29 %24
  254. %29 = OpLabel
  255. OpStore %5 %12
  256. OpBranch %30
  257. %30 = OpLabel
  258. OpLoopMerge %31 %32 None
  259. OpBranch %33
  260. %33 = OpLabel
  261. %34 = OpLoad %10 %5
  262. %35 = OpSLessThan %14 %34 %15
  263. OpBranchConditional %35 %36 %31
  264. %36 = OpLabel
  265. %37 = OpLoad %10 %5
  266. %38 = OpSLessThan %14 %37 %16
  267. OpSelectionMerge %39 None
  268. OpBranchConditional %38 %40 %39
  269. %40 = OpLabel
  270. OpStore %6 %12
  271. OpBranch %41
  272. %41 = OpLabel
  273. OpLoopMerge %42 %43 None
  274. OpBranch %44
  275. %44 = OpLabel
  276. %45 = OpLoad %10 %6
  277. %46 = OpSLessThan %14 %45 %17
  278. OpBranchConditional %46 %47 %42
  279. %47 = OpLabel
  280. OpBranch %43
  281. %43 = OpLabel
  282. %48 = OpLoad %10 %6
  283. %49 = OpIAdd %10 %48 %18
  284. OpStore %6 %49
  285. OpBranch %41
  286. %42 = OpLabel
  287. OpBranch %39
  288. %39 = OpLabel
  289. OpStore %7 %12
  290. OpBranch %50
  291. %50 = OpLabel
  292. OpLoopMerge %51 %52 None
  293. OpBranch %53
  294. %53 = OpLabel
  295. %54 = OpLoad %10 %7
  296. %55 = OpSLessThan %14 %54 %17
  297. OpBranchConditional %55 %56 %51
  298. %56 = OpLabel
  299. OpBranch %52
  300. %52 = OpLabel
  301. %57 = OpLoad %10 %7
  302. %58 = OpIAdd %10 %57 %18
  303. OpStore %7 %58
  304. OpBranch %50
  305. %51 = OpLabel
  306. OpBranch %32
  307. %32 = OpLabel
  308. %59 = OpLoad %10 %5
  309. %60 = OpIAdd %10 %59 %18
  310. OpStore %5 %60
  311. OpBranch %30
  312. %31 = OpLabel
  313. OpBranch %25
  314. %25 = OpLabel
  315. %61 = OpLoad %10 %4
  316. %62 = OpIAdd %10 %61 %18
  317. OpStore %4 %62
  318. OpBranch %23
  319. %24 = OpLabel
  320. OpReturn
  321. OpFunctionEnd
  322. )";
  323. // clang-format on
  324. std::unique_ptr<IRContext> context =
  325. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  326. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  327. Module* module = context->module();
  328. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  329. << text << std::endl;
  330. const Function* f = spvtest::GetFunction(module, 2);
  331. LoopDescriptor& ld = *context->GetLoopDescriptor(f);
  332. EXPECT_EQ(ld.NumLoops(), 4u);
  333. // Invalid basic block id.
  334. EXPECT_EQ(ld[0u], nullptr);
  335. // Not in a loop.
  336. EXPECT_EQ(ld[22], nullptr);
  337. // Check that we can map basic block to the correct loop.
  338. // The following block ids do not belong to a loop.
  339. for (uint32_t bb_id : {22, 24}) EXPECT_EQ(ld[bb_id], nullptr);
  340. {
  341. std::unordered_set<uint32_t> basic_block_in_loop = {
  342. {23, 26, 29, 30, 33, 36, 40, 41, 44, 47, 43,
  343. 42, 39, 50, 53, 56, 52, 51, 32, 31, 25}};
  344. Loop* loop = ld[23];
  345. CheckLoopBlocks(loop, &basic_block_in_loop);
  346. EXPECT_TRUE(loop->HasNestedLoops());
  347. EXPECT_FALSE(loop->IsNested());
  348. EXPECT_EQ(loop->GetDepth(), 1u);
  349. EXPECT_EQ(std::distance(loop->begin(), loop->end()), 1u);
  350. EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 22));
  351. EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 23));
  352. EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 25));
  353. EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 24));
  354. EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
  355. EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
  356. }
  357. {
  358. std::unordered_set<uint32_t> basic_block_in_loop = {
  359. {30, 33, 36, 40, 41, 44, 47, 43, 42, 39, 50, 53, 56, 52, 51, 32}};
  360. Loop* loop = ld[30];
  361. CheckLoopBlocks(loop, &basic_block_in_loop);
  362. EXPECT_TRUE(loop->HasNestedLoops());
  363. EXPECT_TRUE(loop->IsNested());
  364. EXPECT_EQ(loop->GetDepth(), 2u);
  365. EXPECT_EQ(std::distance(loop->begin(), loop->end()), 2u);
  366. EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 29));
  367. EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 30));
  368. EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 32));
  369. EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 31));
  370. EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
  371. EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
  372. }
  373. {
  374. std::unordered_set<uint32_t> basic_block_in_loop = {{41, 44, 47, 43}};
  375. Loop* loop = ld[41];
  376. CheckLoopBlocks(loop, &basic_block_in_loop);
  377. EXPECT_FALSE(loop->HasNestedLoops());
  378. EXPECT_TRUE(loop->IsNested());
  379. EXPECT_EQ(loop->GetDepth(), 3u);
  380. EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u);
  381. EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 40));
  382. EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 41));
  383. EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 43));
  384. EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 42));
  385. EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
  386. EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
  387. }
  388. {
  389. std::unordered_set<uint32_t> basic_block_in_loop = {{50, 53, 56, 52}};
  390. Loop* loop = ld[50];
  391. CheckLoopBlocks(loop, &basic_block_in_loop);
  392. EXPECT_FALSE(loop->HasNestedLoops());
  393. EXPECT_TRUE(loop->IsNested());
  394. EXPECT_EQ(loop->GetDepth(), 3u);
  395. EXPECT_EQ(std::distance(loop->begin(), loop->end()), 0u);
  396. EXPECT_EQ(loop->GetPreHeaderBlock(), spvtest::GetBasicBlock(f, 39));
  397. EXPECT_EQ(loop->GetHeaderBlock(), spvtest::GetBasicBlock(f, 50));
  398. EXPECT_EQ(loop->GetLatchBlock(), spvtest::GetBasicBlock(f, 52));
  399. EXPECT_EQ(loop->GetMergeBlock(), spvtest::GetBasicBlock(f, 51));
  400. EXPECT_FALSE(loop->IsInsideLoop(loop->GetMergeBlock()));
  401. EXPECT_FALSE(loop->IsInsideLoop(loop->GetPreHeaderBlock()));
  402. }
  403. // Make sure LoopDescriptor gives us the inner most loop when we query for
  404. // loops.
  405. for (const BasicBlock& bb : *f) {
  406. if (Loop* loop = ld[&bb]) {
  407. for (Loop& sub_loop :
  408. make_range(++TreeDFIterator<Loop>(loop), TreeDFIterator<Loop>())) {
  409. EXPECT_FALSE(sub_loop.IsInsideLoop(bb.id()));
  410. }
  411. }
  412. }
  413. }
  414. /*
  415. Generated from the following GLSL
  416. #version 330 core
  417. layout(location = 0) out vec4 c;
  418. void main() {
  419. for (int i = 0; i < 10; ++i) {
  420. for (int j = 0; j < 11; ++j) {
  421. for (int k = 0; k < 11; ++k) {}
  422. }
  423. for (int k = 0; k < 12; ++k) {}
  424. }
  425. }
  426. */
  427. TEST_F(PassClassTest, LoopParentTest) {
  428. const std::string text = R"(
  429. OpCapability Shader
  430. %1 = OpExtInstImport "GLSL.std.450"
  431. OpMemoryModel Logical GLSL450
  432. OpEntryPoint Fragment %2 "main" %3
  433. OpExecutionMode %2 OriginUpperLeft
  434. OpSource GLSL 330
  435. OpName %2 "main"
  436. OpName %4 "i"
  437. OpName %5 "j"
  438. OpName %6 "k"
  439. OpName %7 "k"
  440. OpName %3 "c"
  441. OpDecorate %3 Location 0
  442. %8 = OpTypeVoid
  443. %9 = OpTypeFunction %8
  444. %10 = OpTypeInt 32 1
  445. %11 = OpTypePointer Function %10
  446. %12 = OpConstant %10 0
  447. %13 = OpConstant %10 10
  448. %14 = OpTypeBool
  449. %15 = OpConstant %10 11
  450. %16 = OpConstant %10 1
  451. %17 = OpConstant %10 12
  452. %18 = OpTypeFloat 32
  453. %19 = OpTypeVector %18 4
  454. %20 = OpTypePointer Output %19
  455. %3 = OpVariable %20 Output
  456. %2 = OpFunction %8 None %9
  457. %21 = OpLabel
  458. %4 = OpVariable %11 Function
  459. %5 = OpVariable %11 Function
  460. %6 = OpVariable %11 Function
  461. %7 = OpVariable %11 Function
  462. OpStore %4 %12
  463. OpBranch %22
  464. %22 = OpLabel
  465. OpLoopMerge %23 %24 None
  466. OpBranch %25
  467. %25 = OpLabel
  468. %26 = OpLoad %10 %4
  469. %27 = OpSLessThan %14 %26 %13
  470. OpBranchConditional %27 %28 %23
  471. %28 = OpLabel
  472. OpStore %5 %12
  473. OpBranch %29
  474. %29 = OpLabel
  475. OpLoopMerge %30 %31 None
  476. OpBranch %32
  477. %32 = OpLabel
  478. %33 = OpLoad %10 %5
  479. %34 = OpSLessThan %14 %33 %15
  480. OpBranchConditional %34 %35 %30
  481. %35 = OpLabel
  482. OpStore %6 %12
  483. OpBranch %36
  484. %36 = OpLabel
  485. OpLoopMerge %37 %38 None
  486. OpBranch %39
  487. %39 = OpLabel
  488. %40 = OpLoad %10 %6
  489. %41 = OpSLessThan %14 %40 %15
  490. OpBranchConditional %41 %42 %37
  491. %42 = OpLabel
  492. OpBranch %38
  493. %38 = OpLabel
  494. %43 = OpLoad %10 %6
  495. %44 = OpIAdd %10 %43 %16
  496. OpStore %6 %44
  497. OpBranch %36
  498. %37 = OpLabel
  499. OpBranch %31
  500. %31 = OpLabel
  501. %45 = OpLoad %10 %5
  502. %46 = OpIAdd %10 %45 %16
  503. OpStore %5 %46
  504. OpBranch %29
  505. %30 = OpLabel
  506. OpStore %7 %12
  507. OpBranch %47
  508. %47 = OpLabel
  509. OpLoopMerge %48 %49 None
  510. OpBranch %50
  511. %50 = OpLabel
  512. %51 = OpLoad %10 %7
  513. %52 = OpSLessThan %14 %51 %17
  514. OpBranchConditional %52 %53 %48
  515. %53 = OpLabel
  516. OpBranch %49
  517. %49 = OpLabel
  518. %54 = OpLoad %10 %7
  519. %55 = OpIAdd %10 %54 %16
  520. OpStore %7 %55
  521. OpBranch %47
  522. %48 = OpLabel
  523. OpBranch %24
  524. %24 = OpLabel
  525. %56 = OpLoad %10 %4
  526. %57 = OpIAdd %10 %56 %16
  527. OpStore %4 %57
  528. OpBranch %22
  529. %23 = OpLabel
  530. OpReturn
  531. OpFunctionEnd
  532. )";
  533. // clang-format on
  534. std::unique_ptr<IRContext> context =
  535. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  536. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  537. Module* module = context->module();
  538. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  539. << text << std::endl;
  540. const Function* f = spvtest::GetFunction(module, 2);
  541. LoopDescriptor& ld = *context->GetLoopDescriptor(f);
  542. EXPECT_EQ(ld.NumLoops(), 4u);
  543. {
  544. Loop& loop = *ld[22];
  545. EXPECT_TRUE(loop.HasNestedLoops());
  546. EXPECT_FALSE(loop.IsNested());
  547. EXPECT_EQ(loop.GetDepth(), 1u);
  548. EXPECT_EQ(loop.GetParent(), nullptr);
  549. }
  550. {
  551. Loop& loop = *ld[29];
  552. EXPECT_TRUE(loop.HasNestedLoops());
  553. EXPECT_TRUE(loop.IsNested());
  554. EXPECT_EQ(loop.GetDepth(), 2u);
  555. EXPECT_EQ(loop.GetParent(), ld[22]);
  556. }
  557. {
  558. Loop& loop = *ld[36];
  559. EXPECT_FALSE(loop.HasNestedLoops());
  560. EXPECT_TRUE(loop.IsNested());
  561. EXPECT_EQ(loop.GetDepth(), 3u);
  562. EXPECT_EQ(loop.GetParent(), ld[29]);
  563. }
  564. {
  565. Loop& loop = *ld[47];
  566. EXPECT_FALSE(loop.HasNestedLoops());
  567. EXPECT_TRUE(loop.IsNested());
  568. EXPECT_EQ(loop.GetDepth(), 2u);
  569. EXPECT_EQ(loop.GetParent(), ld[22]);
  570. }
  571. }
  572. /*
  573. Generated from the following GLSL + --eliminate-local-multi-store
  574. The preheader of loop %33 and %41 were removed as well.
  575. #version 330 core
  576. void main() {
  577. int a = 0;
  578. for (int i = 0; i < 10; ++i) {
  579. if (i == 0) {
  580. a = 1;
  581. } else {
  582. a = 2;
  583. }
  584. for (int j = 0; j < 11; ++j) {
  585. a++;
  586. }
  587. }
  588. for (int k = 0; k < 12; ++k) {}
  589. }
  590. */
  591. TEST_F(PassClassTest, CreatePreheaderTest) {
  592. const std::string text = R"(
  593. OpCapability Shader
  594. %1 = OpExtInstImport "GLSL.std.450"
  595. OpMemoryModel Logical GLSL450
  596. OpEntryPoint Fragment %2 "main"
  597. OpExecutionMode %2 OriginUpperLeft
  598. OpSource GLSL 330
  599. OpName %2 "main"
  600. %3 = OpTypeVoid
  601. %4 = OpTypeFunction %3
  602. %5 = OpTypeInt 32 1
  603. %6 = OpTypePointer Function %5
  604. %7 = OpConstant %5 0
  605. %8 = OpConstant %5 10
  606. %9 = OpTypeBool
  607. %10 = OpConstant %5 1
  608. %11 = OpConstant %5 2
  609. %12 = OpConstant %5 11
  610. %13 = OpConstant %5 12
  611. %14 = OpUndef %5
  612. %2 = OpFunction %3 None %4
  613. %15 = OpLabel
  614. OpBranch %16
  615. %16 = OpLabel
  616. %17 = OpPhi %5 %7 %15 %18 %19
  617. %20 = OpPhi %5 %7 %15 %21 %19
  618. %22 = OpPhi %5 %14 %15 %23 %19
  619. OpLoopMerge %41 %19 None
  620. OpBranch %25
  621. %25 = OpLabel
  622. %26 = OpSLessThan %9 %20 %8
  623. OpBranchConditional %26 %27 %41
  624. %27 = OpLabel
  625. %28 = OpIEqual %9 %20 %7
  626. OpSelectionMerge %33 None
  627. OpBranchConditional %28 %30 %31
  628. %30 = OpLabel
  629. OpBranch %33
  630. %31 = OpLabel
  631. OpBranch %33
  632. %33 = OpLabel
  633. %18 = OpPhi %5 %10 %30 %11 %31 %34 %35
  634. %23 = OpPhi %5 %7 %30 %7 %31 %36 %35
  635. OpLoopMerge %37 %35 None
  636. OpBranch %38
  637. %38 = OpLabel
  638. %39 = OpSLessThan %9 %23 %12
  639. OpBranchConditional %39 %40 %37
  640. %40 = OpLabel
  641. %34 = OpIAdd %5 %18 %10
  642. OpBranch %35
  643. %35 = OpLabel
  644. %36 = OpIAdd %5 %23 %10
  645. OpBranch %33
  646. %37 = OpLabel
  647. OpBranch %19
  648. %19 = OpLabel
  649. %21 = OpIAdd %5 %20 %10
  650. OpBranch %16
  651. %41 = OpLabel
  652. %42 = OpPhi %5 %7 %25 %43 %44
  653. OpLoopMerge %45 %44 None
  654. OpBranch %46
  655. %46 = OpLabel
  656. %47 = OpSLessThan %9 %42 %13
  657. OpBranchConditional %47 %48 %45
  658. %48 = OpLabel
  659. OpBranch %44
  660. %44 = OpLabel
  661. %43 = OpIAdd %5 %42 %10
  662. OpBranch %41
  663. %45 = OpLabel
  664. OpReturn
  665. OpFunctionEnd
  666. )";
  667. // clang-format on
  668. std::unique_ptr<IRContext> context =
  669. BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
  670. SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  671. Module* module = context->module();
  672. EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
  673. << text << std::endl;
  674. const Function* f = spvtest::GetFunction(module, 2);
  675. LoopDescriptor& ld = *context->GetLoopDescriptor(f);
  676. // No invalidation of the cfg should occur during this test.
  677. CFG* cfg = context->cfg();
  678. EXPECT_EQ(ld.NumLoops(), 3u);
  679. {
  680. Loop& loop = *ld[16];
  681. EXPECT_TRUE(loop.HasNestedLoops());
  682. EXPECT_FALSE(loop.IsNested());
  683. EXPECT_EQ(loop.GetDepth(), 1u);
  684. EXPECT_EQ(loop.GetParent(), nullptr);
  685. }
  686. {
  687. Loop& loop = *ld[33];
  688. EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
  689. EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr);
  690. // Make sure the loop descriptor was properly updated.
  691. EXPECT_EQ(ld[loop.GetPreHeaderBlock()], ld[16]);
  692. {
  693. const std::vector<uint32_t>& preds =
  694. cfg->preds(loop.GetPreHeaderBlock()->id());
  695. std::unordered_set<uint32_t> pred_set(preds.begin(), preds.end());
  696. EXPECT_EQ(pred_set.size(), 2u);
  697. EXPECT_TRUE(pred_set.count(30));
  698. EXPECT_TRUE(pred_set.count(31));
  699. // Check the phi instructions.
  700. loop.GetPreHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) {
  701. for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
  702. EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i)));
  703. }
  704. });
  705. }
  706. {
  707. const std::vector<uint32_t>& preds =
  708. cfg->preds(loop.GetHeaderBlock()->id());
  709. std::unordered_set<uint32_t> pred_set(preds.begin(), preds.end());
  710. EXPECT_EQ(pred_set.size(), 2u);
  711. EXPECT_TRUE(pred_set.count(loop.GetPreHeaderBlock()->id()));
  712. EXPECT_TRUE(pred_set.count(35));
  713. // Check the phi instructions.
  714. loop.GetHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) {
  715. for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
  716. EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i)));
  717. }
  718. });
  719. }
  720. }
  721. {
  722. Loop& loop = *ld[41];
  723. EXPECT_EQ(loop.GetPreHeaderBlock(), nullptr);
  724. EXPECT_NE(loop.GetOrCreatePreHeaderBlock(), nullptr);
  725. EXPECT_EQ(ld[loop.GetPreHeaderBlock()], nullptr);
  726. EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id()).size(), 1u);
  727. EXPECT_EQ(cfg->preds(loop.GetPreHeaderBlock()->id())[0], 25u);
  728. // Check the phi instructions.
  729. loop.GetPreHeaderBlock()->ForEachPhiInst([](Instruction* phi) {
  730. EXPECT_EQ(phi->NumInOperands(), 2u);
  731. EXPECT_EQ(phi->GetSingleWordInOperand(1), 25u);
  732. });
  733. {
  734. const std::vector<uint32_t>& preds =
  735. cfg->preds(loop.GetHeaderBlock()->id());
  736. std::unordered_set<uint32_t> pred_set(preds.begin(), preds.end());
  737. EXPECT_EQ(pred_set.size(), 2u);
  738. EXPECT_TRUE(pred_set.count(loop.GetPreHeaderBlock()->id()));
  739. EXPECT_TRUE(pred_set.count(44));
  740. // Check the phi instructions.
  741. loop.GetHeaderBlock()->ForEachPhiInst([&pred_set](Instruction* phi) {
  742. for (uint32_t i = 1; i < phi->NumInOperands(); i += 2) {
  743. EXPECT_TRUE(pred_set.count(phi->GetSingleWordInOperand(i)));
  744. }
  745. });
  746. }
  747. }
  748. // Make sure pre-header insertion leaves the module valid.
  749. std::vector<uint32_t> bin;
  750. context->module()->ToBinary(&bin, true);
  751. EXPECT_TRUE(Validate(bin));
  752. }
  753. } // namespace
  754. } // namespace opt
  755. } // namespace spvtools