peeling_pass.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  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 <string>
  15. #include <utility>
  16. #include <vector>
  17. #include "gmock/gmock.h"
  18. #include "source/opt/loop_descriptor.h"
  19. #include "source/opt/loop_peeling.h"
  20. #include "test/opt/pass_fixture.h"
  21. namespace spvtools {
  22. namespace opt {
  23. namespace {
  24. class PeelingPassTest : public PassTest<::testing::Test> {
  25. public:
  26. // Generic routine to run the loop peeling pass and check
  27. LoopPeelingPass::LoopPeelingStats AssembleAndRunPeelingTest(
  28. const std::string& text_head, const std::string& text_tail,
  29. spv::Op opcode, const std::string& res_id, const std::string& op1,
  30. const std::string& op2) {
  31. std::string opcode_str;
  32. switch (opcode) {
  33. case spv::Op::OpSLessThan:
  34. opcode_str = "OpSLessThan";
  35. break;
  36. case spv::Op::OpSGreaterThan:
  37. opcode_str = "OpSGreaterThan";
  38. break;
  39. case spv::Op::OpSLessThanEqual:
  40. opcode_str = "OpSLessThanEqual";
  41. break;
  42. case spv::Op::OpSGreaterThanEqual:
  43. opcode_str = "OpSGreaterThanEqual";
  44. break;
  45. case spv::Op::OpIEqual:
  46. opcode_str = "OpIEqual";
  47. break;
  48. case spv::Op::OpINotEqual:
  49. opcode_str = "OpINotEqual";
  50. break;
  51. default:
  52. assert(false && "Unhandled");
  53. break;
  54. }
  55. std::string test_cond =
  56. res_id + " = " + opcode_str + " %bool " + op1 + " " + op2 + "\n";
  57. LoopPeelingPass::LoopPeelingStats stats;
  58. SinglePassRunAndDisassemble<LoopPeelingPass>(
  59. text_head + test_cond + text_tail, true, true, &stats);
  60. return stats;
  61. }
  62. // Generic routine to run the loop peeling pass and check
  63. LoopPeelingPass::LoopPeelingStats RunPeelingTest(
  64. const std::string& text_head, const std::string& text_tail,
  65. spv::Op opcode, const std::string& res_id, const std::string& op1,
  66. const std::string& op2, size_t nb_of_loops) {
  67. LoopPeelingPass::LoopPeelingStats stats = AssembleAndRunPeelingTest(
  68. text_head, text_tail, opcode, res_id, op1, op2);
  69. Function& f = *context()->module()->begin();
  70. LoopDescriptor& ld = *context()->GetLoopDescriptor(&f);
  71. EXPECT_EQ(ld.NumLoops(), nb_of_loops);
  72. return stats;
  73. }
  74. using PeelTraceType =
  75. std::vector<std::pair<LoopPeelingPass::PeelDirection, uint32_t>>;
  76. void BuildAndCheckTrace(const std::string& text_head,
  77. const std::string& text_tail, spv::Op opcode,
  78. const std::string& res_id, const std::string& op1,
  79. const std::string& op2,
  80. const PeelTraceType& expected_peel_trace,
  81. size_t expected_nb_of_loops) {
  82. auto stats = RunPeelingTest(text_head, text_tail, opcode, res_id, op1, op2,
  83. expected_nb_of_loops);
  84. EXPECT_EQ(stats.peeled_loops_.size(), expected_peel_trace.size());
  85. if (stats.peeled_loops_.size() != expected_peel_trace.size()) {
  86. return;
  87. }
  88. PeelTraceType::const_iterator expected_trace_it =
  89. expected_peel_trace.begin();
  90. decltype(stats.peeled_loops_)::const_iterator stats_it =
  91. stats.peeled_loops_.begin();
  92. while (expected_trace_it != expected_peel_trace.end()) {
  93. EXPECT_EQ(expected_trace_it->first, std::get<1>(*stats_it));
  94. EXPECT_EQ(expected_trace_it->second, std::get<2>(*stats_it));
  95. ++expected_trace_it;
  96. ++stats_it;
  97. }
  98. }
  99. };
  100. /*
  101. Test are derivation of the following generated test from the following GLSL +
  102. --eliminate-local-multi-store
  103. #version 330 core
  104. void main() {
  105. int a = 0;
  106. for(int i = 1; i < 10; i += 2) {
  107. if (i < 3) {
  108. a += 2;
  109. }
  110. }
  111. }
  112. The condition is interchanged to test < > <= >= == and peel before/after
  113. opportunities.
  114. */
  115. TEST_F(PeelingPassTest, PeelingPassBasic) {
  116. const std::string text_head = R"(
  117. OpCapability Shader
  118. %1 = OpExtInstImport "GLSL.std.450"
  119. OpMemoryModel Logical GLSL450
  120. OpEntryPoint Fragment %main "main"
  121. OpExecutionMode %main OriginLowerLeft
  122. OpSource GLSL 330
  123. OpName %main "main"
  124. OpName %a "a"
  125. OpName %i "i"
  126. %void = OpTypeVoid
  127. %3 = OpTypeFunction %void
  128. %int = OpTypeInt 32 1
  129. %_ptr_Function_int = OpTypePointer Function %int
  130. %bool = OpTypeBool
  131. %int_20 = OpConstant %int 20
  132. %int_19 = OpConstant %int 19
  133. %int_18 = OpConstant %int 18
  134. %int_17 = OpConstant %int 17
  135. %int_16 = OpConstant %int 16
  136. %int_15 = OpConstant %int 15
  137. %int_14 = OpConstant %int 14
  138. %int_13 = OpConstant %int 13
  139. %int_12 = OpConstant %int 12
  140. %int_11 = OpConstant %int 11
  141. %int_10 = OpConstant %int 10
  142. %int_9 = OpConstant %int 9
  143. %int_8 = OpConstant %int 8
  144. %int_7 = OpConstant %int 7
  145. %int_6 = OpConstant %int 6
  146. %int_5 = OpConstant %int 5
  147. %int_4 = OpConstant %int 4
  148. %int_3 = OpConstant %int 3
  149. %int_2 = OpConstant %int 2
  150. %int_1 = OpConstant %int 1
  151. %int_0 = OpConstant %int 0
  152. %main = OpFunction %void None %3
  153. %5 = OpLabel
  154. %a = OpVariable %_ptr_Function_int Function
  155. %i = OpVariable %_ptr_Function_int Function
  156. OpStore %a %int_0
  157. OpStore %i %int_0
  158. OpBranch %11
  159. %11 = OpLabel
  160. %31 = OpPhi %int %int_0 %5 %33 %14
  161. %32 = OpPhi %int %int_1 %5 %30 %14
  162. OpLoopMerge %13 %14 None
  163. OpBranch %15
  164. %15 = OpLabel
  165. %19 = OpSLessThan %bool %32 %int_20
  166. OpBranchConditional %19 %12 %13
  167. %12 = OpLabel
  168. )";
  169. const std::string text_tail = R"(
  170. OpSelectionMerge %24 None
  171. OpBranchConditional %22 %23 %24
  172. %23 = OpLabel
  173. %27 = OpIAdd %int %31 %int_2
  174. OpStore %a %27
  175. OpBranch %24
  176. %24 = OpLabel
  177. %33 = OpPhi %int %31 %12 %27 %23
  178. OpBranch %14
  179. %14 = OpLabel
  180. %30 = OpIAdd %int %32 %int_2
  181. OpStore %i %30
  182. OpBranch %11
  183. %13 = OpLabel
  184. OpReturn
  185. OpFunctionEnd
  186. )";
  187. auto run_test = [&text_head, &text_tail, this](spv::Op opcode,
  188. const std::string& op1,
  189. const std::string& op2) {
  190. auto stats =
  191. RunPeelingTest(text_head, text_tail, opcode, "%22", op1, op2, 2);
  192. EXPECT_EQ(stats.peeled_loops_.size(), 1u);
  193. if (stats.peeled_loops_.size() != 1u)
  194. return std::pair<LoopPeelingPass::PeelDirection, uint32_t>{
  195. LoopPeelingPass::PeelDirection::kNone, 0};
  196. return std::pair<LoopPeelingPass::PeelDirection, uint32_t>{
  197. std::get<1>(*stats.peeled_loops_.begin()),
  198. std::get<2>(*stats.peeled_loops_.begin())};
  199. };
  200. // Test LT
  201. // Peel before by a factor of 2.
  202. {
  203. SCOPED_TRACE("Peel before iv < 4");
  204. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  205. run_test(spv::Op::OpSLessThan, "%32", "%int_4");
  206. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  207. EXPECT_EQ(peel_info.second, 2u);
  208. }
  209. {
  210. SCOPED_TRACE("Peel before 4 > iv");
  211. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  212. run_test(spv::Op::OpSGreaterThan, "%int_4", "%32");
  213. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  214. EXPECT_EQ(peel_info.second, 2u);
  215. }
  216. {
  217. SCOPED_TRACE("Peel before iv < 5");
  218. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  219. run_test(spv::Op::OpSLessThan, "%32", "%int_5");
  220. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  221. EXPECT_EQ(peel_info.second, 2u);
  222. }
  223. {
  224. SCOPED_TRACE("Peel before 5 > iv");
  225. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  226. run_test(spv::Op::OpSGreaterThan, "%int_5", "%32");
  227. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  228. EXPECT_EQ(peel_info.second, 2u);
  229. }
  230. // Peel after by a factor of 2.
  231. {
  232. SCOPED_TRACE("Peel after iv < 16");
  233. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  234. run_test(spv::Op::OpSLessThan, "%32", "%int_16");
  235. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  236. EXPECT_EQ(peel_info.second, 2u);
  237. }
  238. {
  239. SCOPED_TRACE("Peel after 16 > iv");
  240. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  241. run_test(spv::Op::OpSGreaterThan, "%int_16", "%32");
  242. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  243. EXPECT_EQ(peel_info.second, 2u);
  244. }
  245. {
  246. SCOPED_TRACE("Peel after iv < 17");
  247. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  248. run_test(spv::Op::OpSLessThan, "%32", "%int_17");
  249. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  250. EXPECT_EQ(peel_info.second, 2u);
  251. }
  252. {
  253. SCOPED_TRACE("Peel after 17 > iv");
  254. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  255. run_test(spv::Op::OpSGreaterThan, "%int_17", "%32");
  256. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  257. EXPECT_EQ(peel_info.second, 2u);
  258. }
  259. // Test GT
  260. // Peel before by a factor of 2.
  261. {
  262. SCOPED_TRACE("Peel before iv > 5");
  263. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  264. run_test(spv::Op::OpSGreaterThan, "%32", "%int_5");
  265. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  266. EXPECT_EQ(peel_info.second, 2u);
  267. }
  268. {
  269. SCOPED_TRACE("Peel before 5 < iv");
  270. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  271. run_test(spv::Op::OpSLessThan, "%int_5", "%32");
  272. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  273. EXPECT_EQ(peel_info.second, 2u);
  274. }
  275. {
  276. SCOPED_TRACE("Peel before iv > 4");
  277. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  278. run_test(spv::Op::OpSGreaterThan, "%32", "%int_4");
  279. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  280. EXPECT_EQ(peel_info.second, 2u);
  281. }
  282. {
  283. SCOPED_TRACE("Peel before 4 < iv");
  284. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  285. run_test(spv::Op::OpSLessThan, "%int_4", "%32");
  286. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  287. EXPECT_EQ(peel_info.second, 2u);
  288. }
  289. // Peel after by a factor of 2.
  290. {
  291. SCOPED_TRACE("Peel after iv > 16");
  292. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  293. run_test(spv::Op::OpSGreaterThan, "%32", "%int_16");
  294. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  295. EXPECT_EQ(peel_info.second, 2u);
  296. }
  297. {
  298. SCOPED_TRACE("Peel after 16 < iv");
  299. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  300. run_test(spv::Op::OpSLessThan, "%int_16", "%32");
  301. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  302. EXPECT_EQ(peel_info.second, 2u);
  303. }
  304. {
  305. SCOPED_TRACE("Peel after iv > 17");
  306. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  307. run_test(spv::Op::OpSGreaterThan, "%32", "%int_17");
  308. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  309. EXPECT_EQ(peel_info.second, 2u);
  310. }
  311. {
  312. SCOPED_TRACE("Peel after 17 < iv");
  313. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  314. run_test(spv::Op::OpSLessThan, "%int_17", "%32");
  315. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  316. EXPECT_EQ(peel_info.second, 2u);
  317. }
  318. // Test LE
  319. // Peel before by a factor of 2.
  320. {
  321. SCOPED_TRACE("Peel before iv <= 4");
  322. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  323. run_test(spv::Op::OpSLessThanEqual, "%32", "%int_4");
  324. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  325. EXPECT_EQ(peel_info.second, 2u);
  326. }
  327. {
  328. SCOPED_TRACE("Peel before 4 => iv");
  329. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  330. run_test(spv::Op::OpSGreaterThanEqual, "%int_4", "%32");
  331. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  332. EXPECT_EQ(peel_info.second, 2u);
  333. }
  334. {
  335. SCOPED_TRACE("Peel before iv <= 3");
  336. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  337. run_test(spv::Op::OpSLessThanEqual, "%32", "%int_3");
  338. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  339. EXPECT_EQ(peel_info.second, 2u);
  340. }
  341. {
  342. SCOPED_TRACE("Peel before 3 => iv");
  343. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  344. run_test(spv::Op::OpSGreaterThanEqual, "%int_3", "%32");
  345. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  346. EXPECT_EQ(peel_info.second, 2u);
  347. }
  348. // Peel after by a factor of 2.
  349. {
  350. SCOPED_TRACE("Peel after iv <= 16");
  351. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  352. run_test(spv::Op::OpSLessThanEqual, "%32", "%int_16");
  353. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  354. EXPECT_EQ(peel_info.second, 2u);
  355. }
  356. {
  357. SCOPED_TRACE("Peel after 16 => iv");
  358. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  359. run_test(spv::Op::OpSGreaterThanEqual, "%int_16", "%32");
  360. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  361. EXPECT_EQ(peel_info.second, 2u);
  362. }
  363. {
  364. SCOPED_TRACE("Peel after iv <= 15");
  365. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  366. run_test(spv::Op::OpSLessThanEqual, "%32", "%int_15");
  367. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  368. EXPECT_EQ(peel_info.second, 2u);
  369. }
  370. {
  371. SCOPED_TRACE("Peel after 15 => iv");
  372. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  373. run_test(spv::Op::OpSGreaterThanEqual, "%int_15", "%32");
  374. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  375. EXPECT_EQ(peel_info.second, 2u);
  376. }
  377. // Test GE
  378. // Peel before by a factor of 2.
  379. {
  380. SCOPED_TRACE("Peel before iv >= 5");
  381. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  382. run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_5");
  383. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  384. EXPECT_EQ(peel_info.second, 2u);
  385. }
  386. {
  387. SCOPED_TRACE("Peel before 35 >= iv");
  388. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  389. run_test(spv::Op::OpSLessThanEqual, "%int_5", "%32");
  390. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  391. EXPECT_EQ(peel_info.second, 2u);
  392. }
  393. {
  394. SCOPED_TRACE("Peel before iv >= 4");
  395. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  396. run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_4");
  397. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  398. EXPECT_EQ(peel_info.second, 2u);
  399. }
  400. {
  401. SCOPED_TRACE("Peel before 4 <= iv");
  402. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  403. run_test(spv::Op::OpSLessThanEqual, "%int_4", "%32");
  404. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  405. EXPECT_EQ(peel_info.second, 2u);
  406. }
  407. // Peel after by a factor of 2.
  408. {
  409. SCOPED_TRACE("Peel after iv >= 17");
  410. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  411. run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_17");
  412. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  413. EXPECT_EQ(peel_info.second, 2u);
  414. }
  415. {
  416. SCOPED_TRACE("Peel after 17 <= iv");
  417. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  418. run_test(spv::Op::OpSLessThanEqual, "%int_17", "%32");
  419. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  420. EXPECT_EQ(peel_info.second, 2u);
  421. }
  422. {
  423. SCOPED_TRACE("Peel after iv >= 16");
  424. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  425. run_test(spv::Op::OpSGreaterThanEqual, "%32", "%int_16");
  426. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  427. EXPECT_EQ(peel_info.second, 2u);
  428. }
  429. {
  430. SCOPED_TRACE("Peel after 16 <= iv");
  431. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  432. run_test(spv::Op::OpSLessThanEqual, "%int_16", "%32");
  433. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  434. EXPECT_EQ(peel_info.second, 2u);
  435. }
  436. // Test EQ
  437. // Peel before by a factor of 1.
  438. {
  439. SCOPED_TRACE("Peel before iv == 1");
  440. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  441. run_test(spv::Op::OpIEqual, "%32", "%int_1");
  442. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  443. EXPECT_EQ(peel_info.second, 1u);
  444. }
  445. {
  446. SCOPED_TRACE("Peel before 1 == iv");
  447. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  448. run_test(spv::Op::OpIEqual, "%int_1", "%32");
  449. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  450. EXPECT_EQ(peel_info.second, 1u);
  451. }
  452. // Peel after by a factor of 1.
  453. {
  454. SCOPED_TRACE("Peel after iv == 19");
  455. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  456. run_test(spv::Op::OpIEqual, "%32", "%int_19");
  457. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  458. EXPECT_EQ(peel_info.second, 1u);
  459. }
  460. {
  461. SCOPED_TRACE("Peel after 19 == iv");
  462. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  463. run_test(spv::Op::OpIEqual, "%int_19", "%32");
  464. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  465. EXPECT_EQ(peel_info.second, 1u);
  466. }
  467. // Test NE
  468. // Peel before by a factor of 1.
  469. {
  470. SCOPED_TRACE("Peel before iv != 1");
  471. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  472. run_test(spv::Op::OpINotEqual, "%32", "%int_1");
  473. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  474. EXPECT_EQ(peel_info.second, 1u);
  475. }
  476. {
  477. SCOPED_TRACE("Peel before 1 != iv");
  478. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  479. run_test(spv::Op::OpINotEqual, "%int_1", "%32");
  480. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kBefore);
  481. EXPECT_EQ(peel_info.second, 1u);
  482. }
  483. // Peel after by a factor of 1.
  484. {
  485. SCOPED_TRACE("Peel after iv != 19");
  486. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  487. run_test(spv::Op::OpINotEqual, "%32", "%int_19");
  488. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  489. EXPECT_EQ(peel_info.second, 1u);
  490. }
  491. {
  492. SCOPED_TRACE("Peel after 19 != iv");
  493. std::pair<LoopPeelingPass::PeelDirection, uint32_t> peel_info =
  494. run_test(spv::Op::OpINotEqual, "%int_19", "%32");
  495. EXPECT_EQ(peel_info.first, LoopPeelingPass::PeelDirection::kAfter);
  496. EXPECT_EQ(peel_info.second, 1u);
  497. }
  498. // No peel.
  499. {
  500. SCOPED_TRACE("No Peel: 20 => iv");
  501. auto stats = RunPeelingTest(text_head, text_tail, spv::Op::OpSLessThanEqual,
  502. "%22", "%int_20", "%32", 1);
  503. EXPECT_EQ(stats.peeled_loops_.size(), 0u);
  504. }
  505. }
  506. /*
  507. Test are derivation of the following generated test from the following GLSL +
  508. --eliminate-local-multi-store
  509. #version 330 core
  510. void main() {
  511. int a = 0;
  512. for(int i = 0; i < 10; ++i) {
  513. if (i < 3) {
  514. a += 2;
  515. }
  516. if (i < 1) {
  517. a += 2;
  518. }
  519. }
  520. }
  521. The condition is interchanged to test < > <= >= == and peel before/after
  522. opportunities.
  523. */
  524. TEST_F(PeelingPassTest, MultiplePeelingPass) {
  525. const std::string text_head = R"(
  526. OpCapability Shader
  527. %1 = OpExtInstImport "GLSL.std.450"
  528. OpMemoryModel Logical GLSL450
  529. OpEntryPoint Fragment %main "main"
  530. OpExecutionMode %main OriginLowerLeft
  531. OpSource GLSL 330
  532. OpName %main "main"
  533. OpName %a "a"
  534. OpName %i "i"
  535. %void = OpTypeVoid
  536. %3 = OpTypeFunction %void
  537. %int = OpTypeInt 32 1
  538. %_ptr_Function_int = OpTypePointer Function %int
  539. %bool = OpTypeBool
  540. %int_10 = OpConstant %int 10
  541. %int_9 = OpConstant %int 9
  542. %int_8 = OpConstant %int 8
  543. %int_7 = OpConstant %int 7
  544. %int_6 = OpConstant %int 6
  545. %int_5 = OpConstant %int 5
  546. %int_4 = OpConstant %int 4
  547. %int_3 = OpConstant %int 3
  548. %int_2 = OpConstant %int 2
  549. %int_1 = OpConstant %int 1
  550. %int_0 = OpConstant %int 0
  551. %main = OpFunction %void None %3
  552. %5 = OpLabel
  553. %a = OpVariable %_ptr_Function_int Function
  554. %i = OpVariable %_ptr_Function_int Function
  555. OpStore %a %int_0
  556. OpStore %i %int_0
  557. OpBranch %11
  558. %11 = OpLabel
  559. %37 = OpPhi %int %int_0 %5 %40 %14
  560. %38 = OpPhi %int %int_0 %5 %36 %14
  561. OpLoopMerge %13 %14 None
  562. OpBranch %15
  563. %15 = OpLabel
  564. %19 = OpSLessThan %bool %38 %int_10
  565. OpBranchConditional %19 %12 %13
  566. %12 = OpLabel
  567. )";
  568. const std::string text_tail = R"(
  569. OpSelectionMerge %24 None
  570. OpBranchConditional %22 %23 %24
  571. %23 = OpLabel
  572. %27 = OpIAdd %int %37 %int_2
  573. OpStore %a %27
  574. OpBranch %24
  575. %24 = OpLabel
  576. %39 = OpPhi %int %37 %12 %27 %23
  577. %30 = OpSLessThan %bool %38 %int_1
  578. OpSelectionMerge %32 None
  579. OpBranchConditional %30 %31 %32
  580. %31 = OpLabel
  581. %34 = OpIAdd %int %39 %int_2
  582. OpStore %a %34
  583. OpBranch %32
  584. %32 = OpLabel
  585. %40 = OpPhi %int %39 %24 %34 %31
  586. OpBranch %14
  587. %14 = OpLabel
  588. %36 = OpIAdd %int %38 %int_1
  589. OpStore %i %36
  590. OpBranch %11
  591. %13 = OpLabel
  592. OpReturn
  593. OpFunctionEnd
  594. )";
  595. auto run_test = [&text_head, &text_tail, this](
  596. spv::Op opcode, const std::string& op1,
  597. const std::string& op2,
  598. const PeelTraceType& expected_peel_trace) {
  599. BuildAndCheckTrace(text_head, text_tail, opcode, "%22", op1, op2,
  600. expected_peel_trace, expected_peel_trace.size() + 1);
  601. };
  602. // Test LT
  603. // Peel before by a factor of 3.
  604. {
  605. SCOPED_TRACE("Peel before iv < 3");
  606. run_test(spv::Op::OpSLessThan, "%38", "%int_3",
  607. {{LoopPeelingPass::PeelDirection::kBefore, 3u}});
  608. }
  609. {
  610. SCOPED_TRACE("Peel before 3 > iv");
  611. run_test(spv::Op::OpSGreaterThan, "%int_3", "%38",
  612. {{LoopPeelingPass::PeelDirection::kBefore, 3u}});
  613. }
  614. // Peel after by a factor of 2.
  615. {
  616. SCOPED_TRACE("Peel after iv < 8");
  617. run_test(spv::Op::OpSLessThan, "%38", "%int_8",
  618. {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
  619. }
  620. {
  621. SCOPED_TRACE("Peel after 8 > iv");
  622. run_test(spv::Op::OpSGreaterThan, "%int_8", "%38",
  623. {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
  624. }
  625. // Test GT
  626. // Peel before by a factor of 2.
  627. {
  628. SCOPED_TRACE("Peel before iv > 2");
  629. run_test(spv::Op::OpSGreaterThan, "%38", "%int_2",
  630. {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
  631. }
  632. {
  633. SCOPED_TRACE("Peel before 2 < iv");
  634. run_test(spv::Op::OpSLessThan, "%int_2", "%38",
  635. {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
  636. }
  637. // Peel after by a factor of 3.
  638. {
  639. SCOPED_TRACE("Peel after iv > 7");
  640. run_test(spv::Op::OpSGreaterThan, "%38", "%int_7",
  641. {{LoopPeelingPass::PeelDirection::kAfter, 3u}});
  642. }
  643. {
  644. SCOPED_TRACE("Peel after 7 < iv");
  645. run_test(spv::Op::OpSLessThan, "%int_7", "%38",
  646. {{LoopPeelingPass::PeelDirection::kAfter, 3u}});
  647. }
  648. // Test LE
  649. // Peel before by a factor of 2.
  650. {
  651. SCOPED_TRACE("Peel before iv <= 1");
  652. run_test(spv::Op::OpSLessThanEqual, "%38", "%int_1",
  653. {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
  654. }
  655. {
  656. SCOPED_TRACE("Peel before 1 => iv");
  657. run_test(spv::Op::OpSGreaterThanEqual, "%int_1", "%38",
  658. {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
  659. }
  660. // Peel after by a factor of 2.
  661. {
  662. SCOPED_TRACE("Peel after iv <= 7");
  663. run_test(spv::Op::OpSLessThanEqual, "%38", "%int_7",
  664. {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
  665. }
  666. {
  667. SCOPED_TRACE("Peel after 7 => iv");
  668. run_test(spv::Op::OpSGreaterThanEqual, "%int_7", "%38",
  669. {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
  670. }
  671. // Test GE
  672. // Peel before by a factor of 2.
  673. {
  674. SCOPED_TRACE("Peel before iv >= 2");
  675. run_test(spv::Op::OpSGreaterThanEqual, "%38", "%int_2",
  676. {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
  677. }
  678. {
  679. SCOPED_TRACE("Peel before 2 <= iv");
  680. run_test(spv::Op::OpSLessThanEqual, "%int_2", "%38",
  681. {{LoopPeelingPass::PeelDirection::kBefore, 2u}});
  682. }
  683. // Peel after by a factor of 2.
  684. {
  685. SCOPED_TRACE("Peel after iv >= 8");
  686. run_test(spv::Op::OpSGreaterThanEqual, "%38", "%int_8",
  687. {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
  688. }
  689. {
  690. SCOPED_TRACE("Peel after 8 <= iv");
  691. run_test(spv::Op::OpSLessThanEqual, "%int_8", "%38",
  692. {{LoopPeelingPass::PeelDirection::kAfter, 2u}});
  693. }
  694. // Test EQ
  695. // Peel before by a factor of 1.
  696. {
  697. SCOPED_TRACE("Peel before iv == 0");
  698. run_test(spv::Op::OpIEqual, "%38", "%int_0",
  699. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  700. }
  701. {
  702. SCOPED_TRACE("Peel before 0 == iv");
  703. run_test(spv::Op::OpIEqual, "%int_0", "%38",
  704. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  705. }
  706. // Peel after by a factor of 1.
  707. {
  708. SCOPED_TRACE("Peel after iv == 9");
  709. run_test(spv::Op::OpIEqual, "%38", "%int_9",
  710. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  711. }
  712. {
  713. SCOPED_TRACE("Peel after 9 == iv");
  714. run_test(spv::Op::OpIEqual, "%int_9", "%38",
  715. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  716. }
  717. // Test NE
  718. // Peel before by a factor of 1.
  719. {
  720. SCOPED_TRACE("Peel before iv != 0");
  721. run_test(spv::Op::OpINotEqual, "%38", "%int_0",
  722. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  723. }
  724. {
  725. SCOPED_TRACE("Peel before 0 != iv");
  726. run_test(spv::Op::OpINotEqual, "%int_0", "%38",
  727. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  728. }
  729. // Peel after by a factor of 1.
  730. {
  731. SCOPED_TRACE("Peel after iv != 9");
  732. run_test(spv::Op::OpINotEqual, "%38", "%int_9",
  733. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  734. }
  735. {
  736. SCOPED_TRACE("Peel after 9 != iv");
  737. run_test(spv::Op::OpINotEqual, "%int_9", "%38",
  738. {{LoopPeelingPass::PeelDirection::kBefore, 1u}});
  739. }
  740. }
  741. /*
  742. Test are derivation of the following generated test from the following GLSL +
  743. --eliminate-local-multi-store
  744. #version 330 core
  745. void main() {
  746. int a = 0;
  747. for (int i = 0; i < 10; i++) {
  748. for (int j = 0; j < 10; j++) {
  749. if (i < 3) {
  750. a += 2;
  751. }
  752. }
  753. }
  754. }
  755. */
  756. TEST_F(PeelingPassTest, PeelingNestedPass) {
  757. const std::string text_head = R"(
  758. OpCapability Shader
  759. %1 = OpExtInstImport "GLSL.std.450"
  760. OpMemoryModel Logical GLSL450
  761. OpEntryPoint Fragment %main "main"
  762. OpExecutionMode %main OriginLowerLeft
  763. OpSource GLSL 330
  764. OpName %main "main"
  765. OpName %a "a"
  766. OpName %i "i"
  767. OpName %j "j"
  768. %void = OpTypeVoid
  769. %3 = OpTypeFunction %void
  770. %int = OpTypeInt 32 1
  771. %_ptr_Function_int = OpTypePointer Function %int
  772. %int_0 = OpConstant %int 0
  773. %int_10 = OpConstant %int 10
  774. %bool = OpTypeBool
  775. %int_7 = OpConstant %int 7
  776. %int_3 = OpConstant %int 3
  777. %int_2 = OpConstant %int 2
  778. %int_1 = OpConstant %int 1
  779. %43 = OpUndef %int
  780. %main = OpFunction %void None %3
  781. %5 = OpLabel
  782. %a = OpVariable %_ptr_Function_int Function
  783. %i = OpVariable %_ptr_Function_int Function
  784. %j = OpVariable %_ptr_Function_int Function
  785. OpStore %a %int_0
  786. OpStore %i %int_0
  787. OpBranch %11
  788. %11 = OpLabel
  789. %41 = OpPhi %int %int_0 %5 %45 %14
  790. %42 = OpPhi %int %int_0 %5 %40 %14
  791. %44 = OpPhi %int %43 %5 %46 %14
  792. OpLoopMerge %13 %14 None
  793. OpBranch %15
  794. %15 = OpLabel
  795. %19 = OpSLessThan %bool %42 %int_10
  796. OpBranchConditional %19 %12 %13
  797. %12 = OpLabel
  798. OpStore %j %int_0
  799. OpBranch %21
  800. %21 = OpLabel
  801. %45 = OpPhi %int %41 %12 %47 %24
  802. %46 = OpPhi %int %int_0 %12 %38 %24
  803. OpLoopMerge %23 %24 None
  804. OpBranch %25
  805. %25 = OpLabel
  806. %27 = OpSLessThan %bool %46 %int_10
  807. OpBranchConditional %27 %22 %23
  808. %22 = OpLabel
  809. )";
  810. const std::string text_tail = R"(
  811. OpSelectionMerge %32 None
  812. OpBranchConditional %30 %31 %32
  813. %31 = OpLabel
  814. %35 = OpIAdd %int %45 %int_2
  815. OpStore %a %35
  816. OpBranch %32
  817. %32 = OpLabel
  818. %47 = OpPhi %int %45 %22 %35 %31
  819. OpBranch %24
  820. %24 = OpLabel
  821. %38 = OpIAdd %int %46 %int_1
  822. OpStore %j %38
  823. OpBranch %21
  824. %23 = OpLabel
  825. OpBranch %14
  826. %14 = OpLabel
  827. %40 = OpIAdd %int %42 %int_1
  828. OpStore %i %40
  829. OpBranch %11
  830. %13 = OpLabel
  831. OpReturn
  832. OpFunctionEnd
  833. )";
  834. auto run_test =
  835. [&text_head, &text_tail, this](
  836. spv::Op opcode, const std::string& op1, const std::string& op2,
  837. const PeelTraceType& expected_peel_trace, size_t nb_of_loops) {
  838. BuildAndCheckTrace(text_head, text_tail, opcode, "%30", op1, op2,
  839. expected_peel_trace, nb_of_loops);
  840. };
  841. // Peeling outer before by a factor of 3.
  842. {
  843. SCOPED_TRACE("Peel before iv_i < 3");
  844. // Expect peel before by a factor of 3 and 4 loops at the end.
  845. run_test(spv::Op::OpSLessThan, "%42", "%int_3",
  846. {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 4);
  847. }
  848. // Peeling outer loop after by a factor of 3.
  849. {
  850. SCOPED_TRACE("Peel after iv_i < 7");
  851. // Expect peel after by a factor of 3 and 4 loops at the end.
  852. run_test(spv::Op::OpSLessThan, "%42", "%int_7",
  853. {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 4);
  854. }
  855. // Peeling inner loop before by a factor of 3.
  856. {
  857. SCOPED_TRACE("Peel before iv_j < 3");
  858. // Expect peel before by a factor of 3 and 3 loops at the end.
  859. run_test(spv::Op::OpSLessThan, "%46", "%int_3",
  860. {{LoopPeelingPass::PeelDirection::kBefore, 3u}}, 3);
  861. }
  862. // Peeling inner loop after by a factor of 3.
  863. {
  864. SCOPED_TRACE("Peel after iv_j < 7");
  865. // Expect peel after by a factor of 3 and 3 loops at the end.
  866. run_test(spv::Op::OpSLessThan, "%46", "%int_7",
  867. {{LoopPeelingPass::PeelDirection::kAfter, 3u}}, 3);
  868. }
  869. // Not unworkable condition.
  870. {
  871. SCOPED_TRACE("No peel");
  872. // Expect no peeling and 2 loops at the end.
  873. run_test(spv::Op::OpSLessThan, "%46", "%42", {}, 2);
  874. }
  875. // Could do a peeling of 3, but the goes over the threshold.
  876. {
  877. SCOPED_TRACE("Over threshold");
  878. size_t current_threshold = LoopPeelingPass::GetLoopPeelingThreshold();
  879. LoopPeelingPass::SetLoopPeelingThreshold(1u);
  880. // Expect no peeling and 2 loops at the end.
  881. run_test(spv::Op::OpSLessThan, "%46", "%int_7", {}, 2);
  882. LoopPeelingPass::SetLoopPeelingThreshold(current_threshold);
  883. }
  884. }
  885. /*
  886. Test are derivation of the following generated test from the following GLSL +
  887. --eliminate-local-multi-store
  888. #version 330 core
  889. void main() {
  890. int a = 0;
  891. for (int i = 0, j = 0; i < 10; j++, i++) {
  892. if (i < j) {
  893. a += 2;
  894. }
  895. }
  896. }
  897. */
  898. TEST_F(PeelingPassTest, PeelingNoChanges) {
  899. const std::string text = R"(
  900. OpCapability Shader
  901. %1 = OpExtInstImport "GLSL.std.450"
  902. OpMemoryModel Logical GLSL450
  903. OpEntryPoint Fragment %main "main"
  904. OpExecutionMode %main OriginLowerLeft
  905. OpSource GLSL 330
  906. OpName %main "main"
  907. OpName %a "a"
  908. OpName %i "i"
  909. OpName %j "j"
  910. %void = OpTypeVoid
  911. %3 = OpTypeFunction %void
  912. %int = OpTypeInt 32 1
  913. %_ptr_Function_int = OpTypePointer Function %int
  914. %int_0 = OpConstant %int 0
  915. %int_10 = OpConstant %int 10
  916. %bool = OpTypeBool
  917. %int_2 = OpConstant %int 2
  918. %int_1 = OpConstant %int 1
  919. %main = OpFunction %void None %3
  920. %5 = OpLabel
  921. %a = OpVariable %_ptr_Function_int Function
  922. %i = OpVariable %_ptr_Function_int Function
  923. %j = OpVariable %_ptr_Function_int Function
  924. OpStore %a %int_0
  925. OpStore %i %int_0
  926. OpStore %j %int_0
  927. OpBranch %12
  928. %12 = OpLabel
  929. %34 = OpPhi %int %int_0 %5 %37 %15
  930. %35 = OpPhi %int %int_0 %5 %33 %15
  931. %36 = OpPhi %int %int_0 %5 %31 %15
  932. OpLoopMerge %14 %15 None
  933. OpBranch %16
  934. %16 = OpLabel
  935. %20 = OpSLessThan %bool %35 %int_10
  936. OpBranchConditional %20 %13 %14
  937. %13 = OpLabel
  938. %23 = OpSLessThan %bool %35 %36
  939. OpSelectionMerge %25 None
  940. OpBranchConditional %23 %24 %25
  941. %24 = OpLabel
  942. %28 = OpIAdd %int %34 %int_2
  943. OpStore %a %28
  944. OpBranch %25
  945. %25 = OpLabel
  946. %37 = OpPhi %int %34 %13 %28 %24
  947. OpBranch %15
  948. %15 = OpLabel
  949. %31 = OpIAdd %int %36 %int_1
  950. OpStore %j %31
  951. %33 = OpIAdd %int %35 %int_1
  952. OpStore %i %33
  953. OpBranch %12
  954. %14 = OpLabel
  955. OpReturn
  956. OpFunctionEnd
  957. )";
  958. {
  959. auto result =
  960. SinglePassRunAndDisassemble<LoopPeelingPass>(text, true, false);
  961. EXPECT_EQ(Pass::Status::SuccessWithoutChange, std::get<1>(result));
  962. }
  963. }
  964. } // namespace
  965. } // namespace opt
  966. } // namespace spvtools