| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- // Copyright (c) 2020 Google LLC
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- #include "source/fuzz/call_graph.h"
- #include "gtest/gtest.h"
- #include "source/fuzz/fuzzer_util.h"
- #include "test/fuzz/fuzz_test_util.h"
- namespace spvtools {
- namespace fuzz {
- namespace {
- // The SPIR-V came from this GLSL, slightly modified
- // (main is %2, A is %35, B is %48, C is %50, D is %61):
- //
- // #version 310 es
- //
- // int A (int x) {
- // return x + 1;
- // }
- //
- // void D() {
- // }
- //
- // void C() {
- // int x = 0;
- // int y = 0;
- //
- // while (x < 10) {
- // while (y < 10) {
- // y = A(y);
- // }
- // x = A(x);
- // }
- // }
- //
- // void B () {
- // int x = 0;
- // int y = 0;
- //
- // while (x < 10) {
- // D();
- // while (y < 10) {
- // y = A(y);
- // C();
- // }
- // x++;
- // }
- //
- // }
- //
- // void main()
- // {
- // int x = 0;
- // int y = 0;
- // int z = 0;
- //
- // while (x < 10) {
- // while(y < 10) {
- // y = A(x);
- // while (z < 10) {
- // z = A(z);
- // }
- // }
- // x += 2;
- // }
- //
- // B();
- // C();
- // }
- std::string shader = R"(
- OpCapability Shader
- %1 = OpExtInstImport "GLSL.std.450"
- OpMemoryModel Logical GLSL450
- OpEntryPoint Fragment %2 "main"
- OpExecutionMode %2 OriginUpperLeft
- OpSource ESSL 310
- %3 = OpTypeVoid
- %4 = OpTypeFunction %3
- %5 = OpTypeInt 32 1
- %6 = OpTypePointer Function %5
- %7 = OpTypeFunction %5 %6
- %8 = OpConstant %5 1
- %9 = OpConstant %5 0
- %10 = OpConstant %5 10
- %11 = OpTypeBool
- %12 = OpConstant %5 2
- %2 = OpFunction %3 None %4
- %13 = OpLabel
- %14 = OpVariable %6 Function
- %15 = OpVariable %6 Function
- %16 = OpVariable %6 Function
- %17 = OpVariable %6 Function
- %18 = OpVariable %6 Function
- OpStore %14 %9
- OpStore %15 %9
- OpStore %16 %9
- OpBranch %19
- %19 = OpLabel
- OpLoopMerge %20 %21 None
- OpBranch %22
- %22 = OpLabel
- %23 = OpLoad %5 %14
- %24 = OpSLessThan %11 %23 %10
- OpBranchConditional %24 %25 %20
- %25 = OpLabel
- OpBranch %26
- %26 = OpLabel
- OpLoopMerge %27 %28 None
- OpBranch %29
- %29 = OpLabel
- %30 = OpLoad %5 %15
- %31 = OpSLessThan %11 %30 %10
- OpBranchConditional %31 %32 %27
- %32 = OpLabel
- %33 = OpLoad %5 %14
- OpStore %17 %33
- %34 = OpFunctionCall %5 %35 %17
- OpStore %15 %34
- OpBranch %36
- %36 = OpLabel
- OpLoopMerge %37 %38 None
- OpBranch %39
- %39 = OpLabel
- %40 = OpLoad %5 %16
- %41 = OpSLessThan %11 %40 %10
- OpBranchConditional %41 %42 %37
- %42 = OpLabel
- %43 = OpLoad %5 %16
- OpStore %18 %43
- %44 = OpFunctionCall %5 %35 %18
- OpStore %16 %44
- OpBranch %38
- %38 = OpLabel
- OpBranch %36
- %37 = OpLabel
- OpBranch %28
- %28 = OpLabel
- OpBranch %26
- %27 = OpLabel
- %45 = OpLoad %5 %14
- %46 = OpIAdd %5 %45 %12
- OpStore %14 %46
- OpBranch %21
- %21 = OpLabel
- OpBranch %19
- %20 = OpLabel
- %47 = OpFunctionCall %3 %48
- %49 = OpFunctionCall %3 %50
- OpReturn
- OpFunctionEnd
- %35 = OpFunction %5 None %7
- %51 = OpFunctionParameter %6
- %52 = OpLabel
- %53 = OpLoad %5 %51
- %54 = OpIAdd %5 %53 %8
- OpReturnValue %54
- OpFunctionEnd
- %48 = OpFunction %3 None %4
- %55 = OpLabel
- %56 = OpVariable %6 Function
- %57 = OpVariable %6 Function
- %58 = OpVariable %6 Function
- OpStore %56 %9
- OpStore %57 %9
- OpBranch %59
- %59 = OpLabel
- %60 = OpFunctionCall %3 %61
- OpLoopMerge %62 %63 None
- OpBranch %64
- %64 = OpLabel
- OpLoopMerge %65 %66 None
- OpBranch %67
- %67 = OpLabel
- %68 = OpLoad %5 %57
- %69 = OpSLessThan %11 %68 %10
- OpBranchConditional %69 %70 %65
- %70 = OpLabel
- %71 = OpLoad %5 %57
- OpStore %58 %71
- %72 = OpFunctionCall %5 %35 %58
- OpStore %57 %72
- %73 = OpFunctionCall %3 %50
- OpBranch %66
- %66 = OpLabel
- OpBranch %64
- %65 = OpLabel
- %74 = OpLoad %5 %56
- %75 = OpIAdd %5 %74 %8
- OpStore %56 %75
- OpBranch %63
- %63 = OpLabel
- %76 = OpLoad %5 %56
- %77 = OpSLessThan %11 %76 %10
- OpBranchConditional %77 %59 %62
- %62 = OpLabel
- OpReturn
- OpFunctionEnd
- %50 = OpFunction %3 None %4
- %78 = OpLabel
- %79 = OpVariable %6 Function
- %80 = OpVariable %6 Function
- %81 = OpVariable %6 Function
- %82 = OpVariable %6 Function
- OpStore %79 %9
- OpStore %80 %9
- OpBranch %83
- %83 = OpLabel
- OpLoopMerge %84 %85 None
- OpBranch %86
- %86 = OpLabel
- %87 = OpLoad %5 %79
- %88 = OpSLessThan %11 %87 %10
- OpBranchConditional %88 %89 %84
- %89 = OpLabel
- OpBranch %90
- %90 = OpLabel
- OpLoopMerge %91 %92 None
- OpBranch %93
- %93 = OpLabel
- %94 = OpLoad %5 %80
- %95 = OpSLessThan %11 %94 %10
- OpBranchConditional %95 %96 %91
- %96 = OpLabel
- %97 = OpLoad %5 %80
- OpStore %81 %97
- %98 = OpFunctionCall %5 %35 %81
- OpStore %80 %98
- OpBranch %92
- %92 = OpLabel
- OpBranch %90
- %91 = OpLabel
- %99 = OpLoad %5 %79
- OpStore %82 %99
- %100 = OpFunctionCall %5 %35 %82
- OpStore %79 %100
- OpBranch %85
- %85 = OpLabel
- OpBranch %83
- %84 = OpLabel
- OpReturn
- OpFunctionEnd
- %61 = OpFunction %3 None %4
- %101 = OpLabel
- OpReturn
- OpFunctionEnd
- )";
- // We have that:
- // main calls:
- // - A (maximum loop nesting depth of function call: 3)
- // - B (0)
- // - C (0)
- // A calls nothing.
- // B calls:
- // - A (2)
- // - C (2)
- // - D (1)
- // C calls:
- // - A (2)
- // D calls nothing.
- TEST(CallGraphTest, FunctionInDegree) {
- const auto env = SPV_ENV_UNIVERSAL_1_5;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- spvtools::ValidatorOptions validator_options;
- ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
- kConsoleMessageConsumer));
- const auto graph = CallGraph(context.get());
- const auto& function_in_degree = graph.GetFunctionInDegree();
- // Check the in-degrees of, in order: main, A, B, C, D.
- ASSERT_EQ(function_in_degree.at(2), 0);
- ASSERT_EQ(function_in_degree.at(35), 3);
- ASSERT_EQ(function_in_degree.at(48), 1);
- ASSERT_EQ(function_in_degree.at(50), 2);
- ASSERT_EQ(function_in_degree.at(61), 1);
- }
- TEST(CallGraphTest, DirectCallees) {
- const auto env = SPV_ENV_UNIVERSAL_1_5;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- spvtools::ValidatorOptions validator_options;
- ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
- kConsoleMessageConsumer));
- const auto graph = CallGraph(context.get());
- // Check the callee sets of, in order: main, A, B, C, D.
- ASSERT_EQ(graph.GetDirectCallees(2), std::set<uint32_t>({35, 48, 50}));
- ASSERT_EQ(graph.GetDirectCallees(35), std::set<uint32_t>({}));
- ASSERT_EQ(graph.GetDirectCallees(48), std::set<uint32_t>({35, 50, 61}));
- ASSERT_EQ(graph.GetDirectCallees(50), std::set<uint32_t>({35}));
- ASSERT_EQ(graph.GetDirectCallees(61), std::set<uint32_t>({}));
- }
- TEST(CallGraphTest, IndirectCallees) {
- const auto env = SPV_ENV_UNIVERSAL_1_5;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- spvtools::ValidatorOptions validator_options;
- ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
- kConsoleMessageConsumer));
- const auto graph = CallGraph(context.get());
- // Check the callee sets of, in order: main, A, B, C, D.
- ASSERT_EQ(graph.GetIndirectCallees(2), std::set<uint32_t>({35, 48, 50, 61}));
- ASSERT_EQ(graph.GetDirectCallees(35), std::set<uint32_t>({}));
- ASSERT_EQ(graph.GetDirectCallees(48), std::set<uint32_t>({35, 50, 61}));
- ASSERT_EQ(graph.GetDirectCallees(50), std::set<uint32_t>({35}));
- ASSERT_EQ(graph.GetDirectCallees(61), std::set<uint32_t>({}));
- }
- TEST(CallGraphTest, TopologicalOrder) {
- const auto env = SPV_ENV_UNIVERSAL_1_5;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- spvtools::ValidatorOptions validator_options;
- ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
- kConsoleMessageConsumer));
- const auto graph = CallGraph(context.get());
- const auto& topological_ordering = graph.GetFunctionsInTopologicalOrder();
- // The possible topological orderings are:
- // - main, B, D, C, A
- // - main, B, C, D, A
- // - main, B, C, A, D
- ASSERT_TRUE(
- topological_ordering == std::vector<uint32_t>({2, 48, 61, 50, 35}) ||
- topological_ordering == std::vector<uint32_t>({2, 48, 50, 61, 35}) ||
- topological_ordering == std::vector<uint32_t>({2, 48, 50, 35, 61}));
- }
- TEST(CallGraphTest, LoopNestingDepth) {
- const auto env = SPV_ENV_UNIVERSAL_1_5;
- const auto consumer = nullptr;
- const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
- spvtools::ValidatorOptions validator_options;
- ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
- kConsoleMessageConsumer));
- const auto graph = CallGraph(context.get());
- // Check the maximum loop nesting depth for function calls to, in order:
- // main, A, B, C, D
- ASSERT_EQ(graph.GetMaxCallNestingDepth(2), 0);
- ASSERT_EQ(graph.GetMaxCallNestingDepth(35), 4);
- ASSERT_EQ(graph.GetMaxCallNestingDepth(48), 0);
- ASSERT_EQ(graph.GetMaxCallNestingDepth(50), 2);
- ASSERT_EQ(graph.GetMaxCallNestingDepth(61), 1);
- }
- } // namespace
- } // namespace fuzz
- } // namespace spvtools
|