call_graph_test.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. // Copyright (c) 2020 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 "source/fuzz/call_graph.h"
  15. #include "gtest/gtest.h"
  16. #include "source/fuzz/fuzzer_util.h"
  17. #include "test/fuzz/fuzz_test_util.h"
  18. namespace spvtools {
  19. namespace fuzz {
  20. namespace {
  21. // The SPIR-V came from this GLSL, slightly modified
  22. // (main is %2, A is %35, B is %48, C is %50, D is %61):
  23. //
  24. // #version 310 es
  25. //
  26. // int A (int x) {
  27. // return x + 1;
  28. // }
  29. //
  30. // void D() {
  31. // }
  32. //
  33. // void C() {
  34. // int x = 0;
  35. // int y = 0;
  36. //
  37. // while (x < 10) {
  38. // while (y < 10) {
  39. // y = A(y);
  40. // }
  41. // x = A(x);
  42. // }
  43. // }
  44. //
  45. // void B () {
  46. // int x = 0;
  47. // int y = 0;
  48. //
  49. // while (x < 10) {
  50. // D();
  51. // while (y < 10) {
  52. // y = A(y);
  53. // C();
  54. // }
  55. // x++;
  56. // }
  57. //
  58. // }
  59. //
  60. // void main()
  61. // {
  62. // int x = 0;
  63. // int y = 0;
  64. // int z = 0;
  65. //
  66. // while (x < 10) {
  67. // while(y < 10) {
  68. // y = A(x);
  69. // while (z < 10) {
  70. // z = A(z);
  71. // }
  72. // }
  73. // x += 2;
  74. // }
  75. //
  76. // B();
  77. // C();
  78. // }
  79. std::string shader = R"(
  80. OpCapability Shader
  81. %1 = OpExtInstImport "GLSL.std.450"
  82. OpMemoryModel Logical GLSL450
  83. OpEntryPoint Fragment %2 "main"
  84. OpExecutionMode %2 OriginUpperLeft
  85. OpSource ESSL 310
  86. %3 = OpTypeVoid
  87. %4 = OpTypeFunction %3
  88. %5 = OpTypeInt 32 1
  89. %6 = OpTypePointer Function %5
  90. %7 = OpTypeFunction %5 %6
  91. %8 = OpConstant %5 1
  92. %9 = OpConstant %5 0
  93. %10 = OpConstant %5 10
  94. %11 = OpTypeBool
  95. %12 = OpConstant %5 2
  96. %2 = OpFunction %3 None %4
  97. %13 = OpLabel
  98. %14 = OpVariable %6 Function
  99. %15 = OpVariable %6 Function
  100. %16 = OpVariable %6 Function
  101. %17 = OpVariable %6 Function
  102. %18 = OpVariable %6 Function
  103. OpStore %14 %9
  104. OpStore %15 %9
  105. OpStore %16 %9
  106. OpBranch %19
  107. %19 = OpLabel
  108. OpLoopMerge %20 %21 None
  109. OpBranch %22
  110. %22 = OpLabel
  111. %23 = OpLoad %5 %14
  112. %24 = OpSLessThan %11 %23 %10
  113. OpBranchConditional %24 %25 %20
  114. %25 = OpLabel
  115. OpBranch %26
  116. %26 = OpLabel
  117. OpLoopMerge %27 %28 None
  118. OpBranch %29
  119. %29 = OpLabel
  120. %30 = OpLoad %5 %15
  121. %31 = OpSLessThan %11 %30 %10
  122. OpBranchConditional %31 %32 %27
  123. %32 = OpLabel
  124. %33 = OpLoad %5 %14
  125. OpStore %17 %33
  126. %34 = OpFunctionCall %5 %35 %17
  127. OpStore %15 %34
  128. OpBranch %36
  129. %36 = OpLabel
  130. OpLoopMerge %37 %38 None
  131. OpBranch %39
  132. %39 = OpLabel
  133. %40 = OpLoad %5 %16
  134. %41 = OpSLessThan %11 %40 %10
  135. OpBranchConditional %41 %42 %37
  136. %42 = OpLabel
  137. %43 = OpLoad %5 %16
  138. OpStore %18 %43
  139. %44 = OpFunctionCall %5 %35 %18
  140. OpStore %16 %44
  141. OpBranch %38
  142. %38 = OpLabel
  143. OpBranch %36
  144. %37 = OpLabel
  145. OpBranch %28
  146. %28 = OpLabel
  147. OpBranch %26
  148. %27 = OpLabel
  149. %45 = OpLoad %5 %14
  150. %46 = OpIAdd %5 %45 %12
  151. OpStore %14 %46
  152. OpBranch %21
  153. %21 = OpLabel
  154. OpBranch %19
  155. %20 = OpLabel
  156. %47 = OpFunctionCall %3 %48
  157. %49 = OpFunctionCall %3 %50
  158. OpReturn
  159. OpFunctionEnd
  160. %35 = OpFunction %5 None %7
  161. %51 = OpFunctionParameter %6
  162. %52 = OpLabel
  163. %53 = OpLoad %5 %51
  164. %54 = OpIAdd %5 %53 %8
  165. OpReturnValue %54
  166. OpFunctionEnd
  167. %48 = OpFunction %3 None %4
  168. %55 = OpLabel
  169. %56 = OpVariable %6 Function
  170. %57 = OpVariable %6 Function
  171. %58 = OpVariable %6 Function
  172. OpStore %56 %9
  173. OpStore %57 %9
  174. OpBranch %59
  175. %59 = OpLabel
  176. %60 = OpFunctionCall %3 %61
  177. OpLoopMerge %62 %63 None
  178. OpBranch %64
  179. %64 = OpLabel
  180. OpLoopMerge %65 %66 None
  181. OpBranch %67
  182. %67 = OpLabel
  183. %68 = OpLoad %5 %57
  184. %69 = OpSLessThan %11 %68 %10
  185. OpBranchConditional %69 %70 %65
  186. %70 = OpLabel
  187. %71 = OpLoad %5 %57
  188. OpStore %58 %71
  189. %72 = OpFunctionCall %5 %35 %58
  190. OpStore %57 %72
  191. %73 = OpFunctionCall %3 %50
  192. OpBranch %66
  193. %66 = OpLabel
  194. OpBranch %64
  195. %65 = OpLabel
  196. %74 = OpLoad %5 %56
  197. %75 = OpIAdd %5 %74 %8
  198. OpStore %56 %75
  199. OpBranch %63
  200. %63 = OpLabel
  201. %76 = OpLoad %5 %56
  202. %77 = OpSLessThan %11 %76 %10
  203. OpBranchConditional %77 %59 %62
  204. %62 = OpLabel
  205. OpReturn
  206. OpFunctionEnd
  207. %50 = OpFunction %3 None %4
  208. %78 = OpLabel
  209. %79 = OpVariable %6 Function
  210. %80 = OpVariable %6 Function
  211. %81 = OpVariable %6 Function
  212. %82 = OpVariable %6 Function
  213. OpStore %79 %9
  214. OpStore %80 %9
  215. OpBranch %83
  216. %83 = OpLabel
  217. OpLoopMerge %84 %85 None
  218. OpBranch %86
  219. %86 = OpLabel
  220. %87 = OpLoad %5 %79
  221. %88 = OpSLessThan %11 %87 %10
  222. OpBranchConditional %88 %89 %84
  223. %89 = OpLabel
  224. OpBranch %90
  225. %90 = OpLabel
  226. OpLoopMerge %91 %92 None
  227. OpBranch %93
  228. %93 = OpLabel
  229. %94 = OpLoad %5 %80
  230. %95 = OpSLessThan %11 %94 %10
  231. OpBranchConditional %95 %96 %91
  232. %96 = OpLabel
  233. %97 = OpLoad %5 %80
  234. OpStore %81 %97
  235. %98 = OpFunctionCall %5 %35 %81
  236. OpStore %80 %98
  237. OpBranch %92
  238. %92 = OpLabel
  239. OpBranch %90
  240. %91 = OpLabel
  241. %99 = OpLoad %5 %79
  242. OpStore %82 %99
  243. %100 = OpFunctionCall %5 %35 %82
  244. OpStore %79 %100
  245. OpBranch %85
  246. %85 = OpLabel
  247. OpBranch %83
  248. %84 = OpLabel
  249. OpReturn
  250. OpFunctionEnd
  251. %61 = OpFunction %3 None %4
  252. %101 = OpLabel
  253. OpReturn
  254. OpFunctionEnd
  255. )";
  256. // We have that:
  257. // main calls:
  258. // - A (maximum loop nesting depth of function call: 3)
  259. // - B (0)
  260. // - C (0)
  261. // A calls nothing.
  262. // B calls:
  263. // - A (2)
  264. // - C (2)
  265. // - D (1)
  266. // C calls:
  267. // - A (2)
  268. // D calls nothing.
  269. TEST(CallGraphTest, FunctionInDegree) {
  270. const auto env = SPV_ENV_UNIVERSAL_1_5;
  271. const auto consumer = nullptr;
  272. const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
  273. spvtools::ValidatorOptions validator_options;
  274. ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
  275. kConsoleMessageConsumer));
  276. const auto graph = CallGraph(context.get());
  277. const auto& function_in_degree = graph.GetFunctionInDegree();
  278. // Check the in-degrees of, in order: main, A, B, C, D.
  279. ASSERT_EQ(function_in_degree.at(2), 0);
  280. ASSERT_EQ(function_in_degree.at(35), 3);
  281. ASSERT_EQ(function_in_degree.at(48), 1);
  282. ASSERT_EQ(function_in_degree.at(50), 2);
  283. ASSERT_EQ(function_in_degree.at(61), 1);
  284. }
  285. TEST(CallGraphTest, DirectCallees) {
  286. const auto env = SPV_ENV_UNIVERSAL_1_5;
  287. const auto consumer = nullptr;
  288. const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
  289. spvtools::ValidatorOptions validator_options;
  290. ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
  291. kConsoleMessageConsumer));
  292. const auto graph = CallGraph(context.get());
  293. // Check the callee sets of, in order: main, A, B, C, D.
  294. ASSERT_EQ(graph.GetDirectCallees(2), std::set<uint32_t>({35, 48, 50}));
  295. ASSERT_EQ(graph.GetDirectCallees(35), std::set<uint32_t>({}));
  296. ASSERT_EQ(graph.GetDirectCallees(48), std::set<uint32_t>({35, 50, 61}));
  297. ASSERT_EQ(graph.GetDirectCallees(50), std::set<uint32_t>({35}));
  298. ASSERT_EQ(graph.GetDirectCallees(61), std::set<uint32_t>({}));
  299. }
  300. TEST(CallGraphTest, IndirectCallees) {
  301. const auto env = SPV_ENV_UNIVERSAL_1_5;
  302. const auto consumer = nullptr;
  303. const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
  304. spvtools::ValidatorOptions validator_options;
  305. ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
  306. kConsoleMessageConsumer));
  307. const auto graph = CallGraph(context.get());
  308. // Check the callee sets of, in order: main, A, B, C, D.
  309. ASSERT_EQ(graph.GetIndirectCallees(2), std::set<uint32_t>({35, 48, 50, 61}));
  310. ASSERT_EQ(graph.GetDirectCallees(35), std::set<uint32_t>({}));
  311. ASSERT_EQ(graph.GetDirectCallees(48), std::set<uint32_t>({35, 50, 61}));
  312. ASSERT_EQ(graph.GetDirectCallees(50), std::set<uint32_t>({35}));
  313. ASSERT_EQ(graph.GetDirectCallees(61), std::set<uint32_t>({}));
  314. }
  315. TEST(CallGraphTest, TopologicalOrder) {
  316. const auto env = SPV_ENV_UNIVERSAL_1_5;
  317. const auto consumer = nullptr;
  318. const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
  319. spvtools::ValidatorOptions validator_options;
  320. ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
  321. kConsoleMessageConsumer));
  322. const auto graph = CallGraph(context.get());
  323. const auto& topological_ordering = graph.GetFunctionsInTopologicalOrder();
  324. // The possible topological orderings are:
  325. // - main, B, D, C, A
  326. // - main, B, C, D, A
  327. // - main, B, C, A, D
  328. ASSERT_TRUE(
  329. topological_ordering == std::vector<uint32_t>({2, 48, 61, 50, 35}) ||
  330. topological_ordering == std::vector<uint32_t>({2, 48, 50, 61, 35}) ||
  331. topological_ordering == std::vector<uint32_t>({2, 48, 50, 35, 61}));
  332. }
  333. TEST(CallGraphTest, LoopNestingDepth) {
  334. const auto env = SPV_ENV_UNIVERSAL_1_5;
  335. const auto consumer = nullptr;
  336. const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
  337. spvtools::ValidatorOptions validator_options;
  338. ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
  339. kConsoleMessageConsumer));
  340. const auto graph = CallGraph(context.get());
  341. // Check the maximum loop nesting depth for function calls to, in order:
  342. // main, A, B, C, D
  343. ASSERT_EQ(graph.GetMaxCallNestingDepth(2), 0);
  344. ASSERT_EQ(graph.GetMaxCallNestingDepth(35), 4);
  345. ASSERT_EQ(graph.GetMaxCallNestingDepth(48), 0);
  346. ASSERT_EQ(graph.GetMaxCallNestingDepth(50), 2);
  347. ASSERT_EQ(graph.GetMaxCallNestingDepth(61), 1);
  348. }
  349. } // namespace
  350. } // namespace fuzz
  351. } // namespace spvtools