peeling_pass.cpp 34 KB

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