| // Copyright (c) 2020 André Perez Maselco |
| // |
| // 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/transformation_inline_function.h" |
| |
| #include "gtest/gtest.h" |
| #include "source/fuzz/counter_overflow_id_source.h" |
| #include "source/fuzz/fuzzer_util.h" |
| #include "source/fuzz/instruction_descriptor.h" |
| #include "test/fuzz/fuzz_test_util.h" |
| |
| namespace spvtools { |
| namespace fuzz { |
| namespace { |
| |
| TEST(TransformationInlineFunctionTest, IsApplicable) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %52 "main" |
| OpExecutionMode %52 OriginUpperLeft |
| OpName %56 "function_with_void_return" |
| |
| ; Types |
| %2 = OpTypeBool |
| %3 = OpTypeFloat 32 |
| %4 = OpTypeVector %3 4 |
| %5 = OpTypePointer Function %4 |
| %6 = OpTypeVoid |
| %7 = OpTypeFunction %6 |
| %8 = OpTypeFunction %3 %5 %5 |
| |
| ; Constant scalars |
| %9 = OpConstant %3 1 |
| %10 = OpConstant %3 2 |
| %11 = OpConstant %3 3 |
| %12 = OpConstant %3 4 |
| %13 = OpConstant %3 5 |
| %14 = OpConstant %3 6 |
| %15 = OpConstant %3 7 |
| %16 = OpConstant %3 8 |
| %17 = OpConstantTrue %2 |
| |
| ; Constant vectors |
| %18 = OpConstantComposite %4 %9 %10 %11 %12 |
| %19 = OpConstantComposite %4 %13 %14 %15 %16 |
| |
| ; function with void return |
| %20 = OpFunction %6 None %7 |
| %21 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; function with early return |
| %22 = OpFunction %6 None %7 |
| %23 = OpLabel |
| OpSelectionMerge %26 None |
| OpBranchConditional %17 %24 %25 |
| %24 = OpLabel |
| OpReturn |
| %25 = OpLabel |
| OpBranch %26 |
| %26 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| |
| ; function containing an OpKill instruction |
| %27 = OpFunction %6 None %7 |
| %28 = OpLabel |
| OpKill |
| OpFunctionEnd |
| |
| ; function containing an OpUnreachable instruction |
| %29 = OpFunction %6 None %7 |
| %30 = OpLabel |
| OpUnreachable |
| OpFunctionEnd |
| |
| ; dot product function |
| %31 = OpFunction %3 None %8 |
| %32 = OpFunctionParameter %5 |
| %33 = OpFunctionParameter %5 |
| %34 = OpLabel |
| %35 = OpLoad %4 %32 |
| %36 = OpLoad %4 %33 |
| %37 = OpCompositeExtract %3 %35 0 |
| %38 = OpCompositeExtract %3 %36 0 |
| %39 = OpFMul %3 %37 %38 |
| %40 = OpCompositeExtract %3 %35 1 |
| %41 = OpCompositeExtract %3 %36 1 |
| %42 = OpFMul %3 %40 %41 |
| %43 = OpCompositeExtract %3 %35 2 |
| %44 = OpCompositeExtract %3 %36 2 |
| %45 = OpFMul %3 %43 %44 |
| %46 = OpCompositeExtract %3 %35 3 |
| %47 = OpCompositeExtract %3 %36 3 |
| %48 = OpFMul %3 %46 %47 |
| %49 = OpFAdd %3 %39 %42 |
| %50 = OpFAdd %3 %45 %49 |
| %51 = OpFAdd %3 %48 %50 |
| OpReturnValue %51 |
| OpFunctionEnd |
| |
| ; main function |
| %52 = OpFunction %6 None %7 |
| %53 = OpLabel |
| %54 = OpVariable %5 Function |
| %55 = OpVariable %5 Function |
| %56 = OpFunctionCall %6 %20 ; function with void return |
| OpBranch %57 |
| %57 = OpLabel |
| %59 = OpFunctionCall %6 %22 ; function with early return |
| OpBranch %60 |
| %60 = OpLabel |
| %61 = OpFunctionCall %6 %27 ; function containing OpKill |
| OpBranch %62 |
| %62 = OpLabel |
| %63 = OpFunctionCall %6 %29 ; function containing OpUnreachable |
| OpBranch %64 |
| %64 = OpLabel |
| OpStore %54 %18 |
| OpStore %55 %19 |
| %65 = OpFunctionCall %3 %31 %54 %55 ; dot product function |
| OpBranch %66 |
| %66 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| 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)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| // Tests undefined OpFunctionCall instruction. |
| auto transformation = TransformationInlineFunction(67, {}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| // Tests false OpFunctionCall instruction. |
| transformation = TransformationInlineFunction(42, {}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| // Tests use of called function with void return. |
| transformation = TransformationInlineFunction(56, {}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| // Tests called function having an early return. |
| transformation = |
| TransformationInlineFunction(59, {{24, 67}, {25, 68}, {26, 69}}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| // Tests called function containing an OpKill instruction. |
| transformation = TransformationInlineFunction(61, {}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| // Tests called function containing an OpUnreachable instruction. |
| transformation = TransformationInlineFunction(63, {}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| // Tests applicable transformation. |
| transformation = TransformationInlineFunction(65, {{35, 67}, |
| {36, 68}, |
| {37, 69}, |
| {38, 70}, |
| {39, 71}, |
| {40, 72}, |
| {41, 73}, |
| {42, 74}, |
| {43, 75}, |
| {44, 76}, |
| {45, 77}, |
| {46, 78}, |
| {47, 79}, |
| {48, 80}, |
| {49, 81}, |
| {50, 82}, |
| {51, 83}}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| } |
| |
| TEST(TransformationInlineFunctionTest, Apply) { |
| std::string reference_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %39 "main" |
| |
| ; Types |
| %2 = OpTypeFloat 32 |
| %3 = OpTypeVector %2 4 |
| %4 = OpTypePointer Function %3 |
| %5 = OpTypeVoid |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFunction %2 %4 %4 |
| |
| ; Constant scalars |
| %8 = OpConstant %2 1 |
| %9 = OpConstant %2 2 |
| %10 = OpConstant %2 3 |
| %11 = OpConstant %2 4 |
| %12 = OpConstant %2 5 |
| %13 = OpConstant %2 6 |
| %14 = OpConstant %2 7 |
| %15 = OpConstant %2 8 |
| |
| ; Constant vectors |
| %16 = OpConstantComposite %3 %8 %9 %10 %11 |
| %17 = OpConstantComposite %3 %12 %13 %14 %15 |
| |
| ; dot product function |
| %18 = OpFunction %2 None %7 |
| %19 = OpFunctionParameter %4 |
| %20 = OpFunctionParameter %4 |
| %21 = OpLabel |
| %22 = OpLoad %3 %19 |
| %23 = OpLoad %3 %20 |
| %24 = OpCompositeExtract %2 %22 0 |
| %25 = OpCompositeExtract %2 %23 0 |
| %26 = OpFMul %2 %24 %25 |
| %27 = OpCompositeExtract %2 %22 1 |
| %28 = OpCompositeExtract %2 %23 1 |
| %29 = OpFMul %2 %27 %28 |
| %30 = OpCompositeExtract %2 %22 2 |
| %31 = OpCompositeExtract %2 %23 2 |
| %32 = OpFMul %2 %30 %31 |
| %33 = OpCompositeExtract %2 %22 3 |
| %34 = OpCompositeExtract %2 %23 3 |
| %35 = OpFMul %2 %33 %34 |
| %36 = OpFAdd %2 %26 %29 |
| %37 = OpFAdd %2 %32 %36 |
| %38 = OpFAdd %2 %35 %37 |
| OpReturnValue %38 |
| OpFunctionEnd |
| |
| ; main function |
| %39 = OpFunction %5 None %6 |
| %40 = OpLabel |
| %41 = OpVariable %4 Function |
| %42 = OpVariable %4 Function |
| OpStore %41 %16 |
| OpStore %42 %17 |
| %43 = OpFunctionCall %2 %18 %41 %42 ; dot product function call |
| OpBranch %44 |
| %44 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = |
| BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| auto transformation = TransformationInlineFunction(43, {{22, 45}, |
| {23, 46}, |
| {24, 47}, |
| {25, 48}, |
| {26, 49}, |
| {27, 50}, |
| {28, 51}, |
| {29, 52}, |
| {30, 53}, |
| {31, 54}, |
| {32, 55}, |
| {33, 56}, |
| {34, 57}, |
| {35, 58}, |
| {36, 59}, |
| {37, 60}, |
| {38, 61}}); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| |
| std::string variant_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %39 "main" |
| |
| ; Types |
| %2 = OpTypeFloat 32 |
| %3 = OpTypeVector %2 4 |
| %4 = OpTypePointer Function %3 |
| %5 = OpTypeVoid |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFunction %2 %4 %4 |
| |
| ; Constant scalars |
| %8 = OpConstant %2 1 |
| %9 = OpConstant %2 2 |
| %10 = OpConstant %2 3 |
| %11 = OpConstant %2 4 |
| %12 = OpConstant %2 5 |
| %13 = OpConstant %2 6 |
| %14 = OpConstant %2 7 |
| %15 = OpConstant %2 8 |
| |
| ; Constant vectors |
| %16 = OpConstantComposite %3 %8 %9 %10 %11 |
| %17 = OpConstantComposite %3 %12 %13 %14 %15 |
| |
| ; dot product function |
| %18 = OpFunction %2 None %7 |
| %19 = OpFunctionParameter %4 |
| %20 = OpFunctionParameter %4 |
| %21 = OpLabel |
| %22 = OpLoad %3 %19 |
| %23 = OpLoad %3 %20 |
| %24 = OpCompositeExtract %2 %22 0 |
| %25 = OpCompositeExtract %2 %23 0 |
| %26 = OpFMul %2 %24 %25 |
| %27 = OpCompositeExtract %2 %22 1 |
| %28 = OpCompositeExtract %2 %23 1 |
| %29 = OpFMul %2 %27 %28 |
| %30 = OpCompositeExtract %2 %22 2 |
| %31 = OpCompositeExtract %2 %23 2 |
| %32 = OpFMul %2 %30 %31 |
| %33 = OpCompositeExtract %2 %22 3 |
| %34 = OpCompositeExtract %2 %23 3 |
| %35 = OpFMul %2 %33 %34 |
| %36 = OpFAdd %2 %26 %29 |
| %37 = OpFAdd %2 %32 %36 |
| %38 = OpFAdd %2 %35 %37 |
| OpReturnValue %38 |
| OpFunctionEnd |
| |
| ; main function |
| %39 = OpFunction %5 None %6 |
| %40 = OpLabel |
| %41 = OpVariable %4 Function |
| %42 = OpVariable %4 Function |
| OpStore %41 %16 |
| OpStore %42 %17 |
| %45 = OpLoad %3 %41 |
| %46 = OpLoad %3 %42 |
| %47 = OpCompositeExtract %2 %45 0 |
| %48 = OpCompositeExtract %2 %46 0 |
| %49 = OpFMul %2 %47 %48 |
| %50 = OpCompositeExtract %2 %45 1 |
| %51 = OpCompositeExtract %2 %46 1 |
| %52 = OpFMul %2 %50 %51 |
| %53 = OpCompositeExtract %2 %45 2 |
| %54 = OpCompositeExtract %2 %46 2 |
| %55 = OpFMul %2 %53 %54 |
| %56 = OpCompositeExtract %2 %45 3 |
| %57 = OpCompositeExtract %2 %46 3 |
| %58 = OpFMul %2 %56 %57 |
| %59 = OpFAdd %2 %49 %52 |
| %60 = OpFAdd %2 %55 %59 |
| %61 = OpFAdd %2 %58 %60 |
| %43 = OpCopyObject %2 %61 |
| OpBranch %44 |
| %44 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); |
| } |
| |
| TEST(TransformationInlineFunctionTest, ApplyToMultipleFunctions) { |
| std::string reference_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %15 "main" |
| |
| ; Types |
| %2 = OpTypeInt 32 1 |
| %3 = OpTypeBool |
| %4 = OpTypePointer Private %2 |
| %5 = OpTypePointer Function %2 |
| %6 = OpTypeVoid |
| %7 = OpTypeFunction %6 |
| %8 = OpTypeFunction %2 %5 |
| %9 = OpTypeFunction %2 %2 |
| |
| ; Constants |
| %10 = OpConstant %2 0 |
| %11 = OpConstant %2 1 |
| %12 = OpConstant %2 2 |
| %13 = OpConstant %2 3 |
| |
| ; Global variable |
| %14 = OpVariable %4 Private |
| |
| ; main function |
| %15 = OpFunction %6 None %7 |
| %16 = OpLabel |
| %17 = OpVariable %5 Function |
| %18 = OpVariable %5 Function |
| %19 = OpVariable %5 Function |
| OpStore %17 %13 |
| %20 = OpLoad %2 %17 |
| OpStore %18 %20 |
| %21 = OpFunctionCall %2 %36 %18 |
| OpBranch %22 |
| %22 = OpLabel |
| %23 = OpFunctionCall %2 %36 %18 |
| OpStore %17 %21 |
| %24 = OpLoad %2 %17 |
| %25 = OpFunctionCall %2 %54 %24 |
| OpBranch %26 |
| %26 = OpLabel |
| %27 = OpFunctionCall %2 %54 %24 |
| %28 = OpLoad %2 %17 |
| %29 = OpIAdd %2 %28 %25 |
| OpStore %17 %29 |
| %30 = OpFunctionCall %6 %67 |
| OpBranch %31 |
| %31 = OpLabel |
| %32 = OpFunctionCall %6 %67 |
| %33 = OpLoad %2 %14 |
| %34 = OpLoad %2 %17 |
| %35 = OpIAdd %2 %34 %33 |
| OpStore %17 %35 |
| OpReturn |
| OpFunctionEnd |
| |
| ; Function %36 |
| %36 = OpFunction %2 None %8 |
| %37 = OpFunctionParameter %5 |
| %38 = OpLabel |
| %39 = OpVariable %5 Function |
| %40 = OpVariable %5 Function |
| OpStore %39 %10 |
| OpBranch %41 |
| %41 = OpLabel |
| OpLoopMerge %52 %49 None |
| OpBranch %42 |
| %42 = OpLabel |
| %43 = OpLoad %2 %39 |
| %44 = OpLoad %2 %37 |
| %45 = OpSLessThan %3 %43 %44 |
| OpBranchConditional %45 %46 %52 |
| %46 = OpLabel |
| %47 = OpLoad %2 %40 |
| %48 = OpIAdd %2 %47 %11 |
| OpStore %40 %48 |
| OpBranch %49 |
| %49 = OpLabel |
| %50 = OpLoad %2 %39 |
| %51 = OpIAdd %2 %50 %12 |
| OpStore %39 %51 |
| OpBranch %41 |
| %52 = OpLabel |
| %53 = OpLoad %2 %40 |
| OpReturnValue %53 |
| OpFunctionEnd |
| |
| ; Function %54 |
| %54 = OpFunction %2 None %9 |
| %55 = OpFunctionParameter %2 |
| %56 = OpLabel |
| %57 = OpVariable %5 Function |
| OpStore %57 %10 |
| %58 = OpSGreaterThan %3 %55 %10 |
| OpSelectionMerge %62 None |
| OpBranchConditional %58 %64 %59 |
| %59 = OpLabel |
| %60 = OpLoad %2 %57 |
| %61 = OpISub %2 %60 %12 |
| OpStore %57 %61 |
| OpBranch %62 |
| %62 = OpLabel |
| %63 = OpLoad %2 %57 |
| OpReturnValue %63 |
| %64 = OpLabel |
| %65 = OpLoad %2 %57 |
| %66 = OpIAdd %2 %65 %11 |
| OpStore %57 %66 |
| OpBranch %62 |
| OpFunctionEnd |
| |
| ; Function %67 |
| %67 = OpFunction %6 None %7 |
| %68 = OpLabel |
| OpStore %14 %12 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_3; |
| const auto consumer = nullptr; |
| const auto context = |
| BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| auto transformation = TransformationInlineFunction(30, {}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| |
| // Tests a parameter included in the id map. |
| transformation = TransformationInlineFunction(25, {{55, 69}, |
| {56, 70}, |
| {57, 71}, |
| {58, 72}, |
| {59, 73}, |
| {60, 74}, |
| {61, 75}, |
| {62, 76}, |
| {63, 77}, |
| {64, 78}, |
| {65, 79}, |
| {66, 80}}); |
| ASSERT_FALSE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| #ifndef NDEBUG |
| // Tests the id of the returned value not included in the id map. |
| transformation = TransformationInlineFunction(25, {{56, 69}, |
| {57, 70}, |
| {58, 71}, |
| {59, 72}, |
| {60, 73}, |
| {61, 74}, |
| {62, 75}, |
| {64, 76}, |
| {65, 77}, |
| {66, 78}}); |
| ASSERT_DEATH( |
| transformation.IsApplicable(context.get(), transformation_context), |
| "Bad attempt to query whether overflow ids are available."); |
| #endif |
| |
| transformation = TransformationInlineFunction(25, {{57, 69}, |
| {58, 70}, |
| {59, 71}, |
| {60, 72}, |
| {61, 73}, |
| {62, 74}, |
| {63, 75}, |
| {64, 76}, |
| {65, 77}, |
| {66, 78}}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| |
| transformation = TransformationInlineFunction(21, {{39, 79}, |
| {40, 80}, |
| {41, 81}, |
| {42, 82}, |
| {43, 83}, |
| {44, 84}, |
| {45, 85}, |
| {46, 86}, |
| {47, 87}, |
| {48, 88}, |
| {49, 89}, |
| {50, 90}, |
| {51, 91}, |
| {52, 92}, |
| {53, 93}}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| |
| std::string variant_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %15 "main" |
| |
| ; Types |
| %2 = OpTypeInt 32 1 |
| %3 = OpTypeBool |
| %4 = OpTypePointer Private %2 |
| %5 = OpTypePointer Function %2 |
| %6 = OpTypeVoid |
| %7 = OpTypeFunction %6 |
| %8 = OpTypeFunction %2 %5 |
| %9 = OpTypeFunction %2 %2 |
| |
| ; Constants |
| %10 = OpConstant %2 0 |
| %11 = OpConstant %2 1 |
| %12 = OpConstant %2 2 |
| %13 = OpConstant %2 3 |
| |
| ; Global variable |
| %14 = OpVariable %4 Private |
| |
| ; main function |
| %15 = OpFunction %6 None %7 |
| %16 = OpLabel |
| %80 = OpVariable %5 Function |
| %79 = OpVariable %5 Function |
| %69 = OpVariable %5 Function |
| %17 = OpVariable %5 Function |
| %18 = OpVariable %5 Function |
| %19 = OpVariable %5 Function |
| OpStore %17 %13 |
| %20 = OpLoad %2 %17 |
| OpStore %18 %20 |
| OpStore %79 %10 |
| OpBranch %81 |
| %81 = OpLabel |
| OpLoopMerge %92 %89 None |
| OpBranch %82 |
| %82 = OpLabel |
| %83 = OpLoad %2 %79 |
| %84 = OpLoad %2 %18 |
| %85 = OpSLessThan %3 %83 %84 |
| OpBranchConditional %85 %86 %92 |
| %86 = OpLabel |
| %87 = OpLoad %2 %80 |
| %88 = OpIAdd %2 %87 %11 |
| OpStore %80 %88 |
| OpBranch %89 |
| %89 = OpLabel |
| %90 = OpLoad %2 %79 |
| %91 = OpIAdd %2 %90 %12 |
| OpStore %79 %91 |
| OpBranch %81 |
| %92 = OpLabel |
| %93 = OpLoad %2 %80 |
| %21 = OpCopyObject %2 %93 |
| OpBranch %22 |
| %22 = OpLabel |
| %23 = OpFunctionCall %2 %36 %18 |
| OpStore %17 %21 |
| %24 = OpLoad %2 %17 |
| OpStore %69 %10 |
| %70 = OpSGreaterThan %3 %24 %10 |
| OpSelectionMerge %74 None |
| OpBranchConditional %70 %76 %71 |
| %71 = OpLabel |
| %72 = OpLoad %2 %69 |
| %73 = OpISub %2 %72 %12 |
| OpStore %69 %73 |
| OpBranch %74 |
| %74 = OpLabel |
| %75 = OpLoad %2 %69 |
| %25 = OpCopyObject %2 %75 |
| OpBranch %26 |
| %76 = OpLabel |
| %77 = OpLoad %2 %69 |
| %78 = OpIAdd %2 %77 %11 |
| OpStore %69 %78 |
| OpBranch %74 |
| %26 = OpLabel |
| %27 = OpFunctionCall %2 %54 %24 |
| %28 = OpLoad %2 %17 |
| %29 = OpIAdd %2 %28 %25 |
| OpStore %17 %29 |
| OpStore %14 %12 |
| OpBranch %31 |
| %31 = OpLabel |
| %32 = OpFunctionCall %6 %67 |
| %33 = OpLoad %2 %14 |
| %34 = OpLoad %2 %17 |
| %35 = OpIAdd %2 %34 %33 |
| OpStore %17 %35 |
| OpReturn |
| OpFunctionEnd |
| |
| ; Function %36 |
| %36 = OpFunction %2 None %8 |
| %37 = OpFunctionParameter %5 |
| %38 = OpLabel |
| %39 = OpVariable %5 Function |
| %40 = OpVariable %5 Function |
| OpStore %39 %10 |
| OpBranch %41 |
| %41 = OpLabel |
| OpLoopMerge %52 %49 None |
| OpBranch %42 |
| %42 = OpLabel |
| %43 = OpLoad %2 %39 |
| %44 = OpLoad %2 %37 |
| %45 = OpSLessThan %3 %43 %44 |
| OpBranchConditional %45 %46 %52 |
| %46 = OpLabel |
| %47 = OpLoad %2 %40 |
| %48 = OpIAdd %2 %47 %11 |
| OpStore %40 %48 |
| OpBranch %49 |
| %49 = OpLabel |
| %50 = OpLoad %2 %39 |
| %51 = OpIAdd %2 %50 %12 |
| OpStore %39 %51 |
| OpBranch %41 |
| %52 = OpLabel |
| %53 = OpLoad %2 %40 |
| OpReturnValue %53 |
| OpFunctionEnd |
| |
| ; Function %54 |
| %54 = OpFunction %2 None %9 |
| %55 = OpFunctionParameter %2 |
| %56 = OpLabel |
| %57 = OpVariable %5 Function |
| OpStore %57 %10 |
| %58 = OpSGreaterThan %3 %55 %10 |
| OpSelectionMerge %62 None |
| OpBranchConditional %58 %64 %59 |
| %59 = OpLabel |
| %60 = OpLoad %2 %57 |
| %61 = OpISub %2 %60 %12 |
| OpStore %57 %61 |
| OpBranch %62 |
| %62 = OpLabel |
| %63 = OpLoad %2 %57 |
| OpReturnValue %63 |
| %64 = OpLabel |
| %65 = OpLoad %2 %57 |
| %66 = OpIAdd %2 %65 %11 |
| OpStore %57 %66 |
| OpBranch %62 |
| OpFunctionEnd |
| |
| ; Function %67 |
| %67 = OpFunction %6 None %7 |
| %68 = OpLabel |
| OpStore %14 %12 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); |
| } |
| |
| TEST(TransformationInlineFunctionTest, HandlesOpPhisInTheSecondBlock) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %10 = OpTypeInt 32 0 |
| %11 = OpUndef %10 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %6 = OpFunctionCall %2 %7 |
| OpBranch %14 |
| %14 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %7 = OpFunction %2 None %3 |
| %8 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| %12 = OpPhi %10 %11 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_3; |
| 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)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| TransformationInlineFunction transformation(6, |
| {{{8, 20}, {13, 21}, {12, 22}}}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| std::string after_transformation = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %10 = OpTypeInt 32 0 |
| %11 = OpUndef %10 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| OpBranch %21 |
| %21 = OpLabel |
| %22 = OpPhi %10 %11 %5 |
| OpBranch %14 |
| %14 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %7 = OpFunction %2 None %3 |
| %8 = OpLabel |
| OpBranch %13 |
| %13 = OpLabel |
| %12 = OpPhi %10 %11 %8 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| TEST(TransformationInlineFunctionTest, OverflowIds) { |
| std::string reference_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %39 "main" |
| |
| ; Types |
| %2 = OpTypeFloat 32 |
| %3 = OpTypeVector %2 4 |
| %4 = OpTypePointer Function %3 |
| %5 = OpTypeVoid |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFunction %2 %4 %4 |
| |
| ; Constant scalars |
| %8 = OpConstant %2 1 |
| %9 = OpConstant %2 2 |
| %10 = OpConstant %2 3 |
| %11 = OpConstant %2 4 |
| %12 = OpConstant %2 5 |
| %13 = OpConstant %2 6 |
| %14 = OpConstant %2 7 |
| %15 = OpConstant %2 8 |
| |
| ; Constant vectors |
| %16 = OpConstantComposite %3 %8 %9 %10 %11 |
| %17 = OpConstantComposite %3 %12 %13 %14 %15 |
| |
| ; dot product function |
| %18 = OpFunction %2 None %7 |
| %19 = OpFunctionParameter %4 |
| %20 = OpFunctionParameter %4 |
| %21 = OpLabel |
| %22 = OpLoad %3 %19 |
| %23 = OpLoad %3 %20 |
| %24 = OpCompositeExtract %2 %22 0 |
| %25 = OpCompositeExtract %2 %23 0 |
| %26 = OpFMul %2 %24 %25 |
| %27 = OpCompositeExtract %2 %22 1 |
| %28 = OpCompositeExtract %2 %23 1 |
| %29 = OpFMul %2 %27 %28 |
| OpBranch %100 |
| %100 = OpLabel |
| %30 = OpCompositeExtract %2 %22 2 |
| %31 = OpCompositeExtract %2 %23 2 |
| %32 = OpFMul %2 %30 %31 |
| %33 = OpCompositeExtract %2 %22 3 |
| %34 = OpCompositeExtract %2 %23 3 |
| %35 = OpFMul %2 %33 %34 |
| %36 = OpFAdd %2 %26 %29 |
| %37 = OpFAdd %2 %32 %36 |
| %38 = OpFAdd %2 %35 %37 |
| OpReturnValue %38 |
| OpFunctionEnd |
| |
| ; main function |
| %39 = OpFunction %5 None %6 |
| %40 = OpLabel |
| %41 = OpVariable %4 Function |
| %42 = OpVariable %4 Function |
| OpStore %41 %16 |
| OpStore %42 %17 |
| %43 = OpFunctionCall %2 %18 %41 %42 ; dot product function call |
| OpBranch %44 |
| %44 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = |
| BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000); |
| auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options, |
| std::move(overflow_ids_unique_ptr)); |
| auto transformation = TransformationInlineFunction(43, {{22, 45}, |
| {23, 46}, |
| {24, 47}, |
| {25, 48}, |
| {26, 49}, |
| {27, 50}, |
| {28, 51}, |
| {29, 52}}); |
| |
| // The following ids are left un-mapped; overflow ids will be required for |
| // them: 30, 31, 32, 33, 34, 35, 36, 37, 38, 100 |
| |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context, |
| overflow_ids_ptr->GetIssuedOverflowIds()); |
| |
| std::string variant_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Vertex %39 "main" |
| |
| ; Types |
| %2 = OpTypeFloat 32 |
| %3 = OpTypeVector %2 4 |
| %4 = OpTypePointer Function %3 |
| %5 = OpTypeVoid |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFunction %2 %4 %4 |
| |
| ; Constant scalars |
| %8 = OpConstant %2 1 |
| %9 = OpConstant %2 2 |
| %10 = OpConstant %2 3 |
| %11 = OpConstant %2 4 |
| %12 = OpConstant %2 5 |
| %13 = OpConstant %2 6 |
| %14 = OpConstant %2 7 |
| %15 = OpConstant %2 8 |
| |
| ; Constant vectors |
| %16 = OpConstantComposite %3 %8 %9 %10 %11 |
| %17 = OpConstantComposite %3 %12 %13 %14 %15 |
| |
| ; dot product function |
| %18 = OpFunction %2 None %7 |
| %19 = OpFunctionParameter %4 |
| %20 = OpFunctionParameter %4 |
| %21 = OpLabel |
| %22 = OpLoad %3 %19 |
| %23 = OpLoad %3 %20 |
| %24 = OpCompositeExtract %2 %22 0 |
| %25 = OpCompositeExtract %2 %23 0 |
| %26 = OpFMul %2 %24 %25 |
| %27 = OpCompositeExtract %2 %22 1 |
| %28 = OpCompositeExtract %2 %23 1 |
| %29 = OpFMul %2 %27 %28 |
| OpBranch %100 |
| %100 = OpLabel |
| %30 = OpCompositeExtract %2 %22 2 |
| %31 = OpCompositeExtract %2 %23 2 |
| %32 = OpFMul %2 %30 %31 |
| %33 = OpCompositeExtract %2 %22 3 |
| %34 = OpCompositeExtract %2 %23 3 |
| %35 = OpFMul %2 %33 %34 |
| %36 = OpFAdd %2 %26 %29 |
| %37 = OpFAdd %2 %32 %36 |
| %38 = OpFAdd %2 %35 %37 |
| OpReturnValue %38 |
| OpFunctionEnd |
| |
| ; main function |
| %39 = OpFunction %5 None %6 |
| %40 = OpLabel |
| %41 = OpVariable %4 Function |
| %42 = OpVariable %4 Function |
| OpStore %41 %16 |
| OpStore %42 %17 |
| %45 = OpLoad %3 %41 |
| %46 = OpLoad %3 %42 |
| %47 = OpCompositeExtract %2 %45 0 |
| %48 = OpCompositeExtract %2 %46 0 |
| %49 = OpFMul %2 %47 %48 |
| %50 = OpCompositeExtract %2 %45 1 |
| %51 = OpCompositeExtract %2 %46 1 |
| %52 = OpFMul %2 %50 %51 |
| OpBranch %1000 |
| %1000 = OpLabel |
| %1001 = OpCompositeExtract %2 %45 2 |
| %1002 = OpCompositeExtract %2 %46 2 |
| %1003 = OpFMul %2 %1001 %1002 |
| %1004 = OpCompositeExtract %2 %45 3 |
| %1005 = OpCompositeExtract %2 %46 3 |
| %1006 = OpFMul %2 %1004 %1005 |
| %1007 = OpFAdd %2 %49 %52 |
| %1008 = OpFAdd %2 %1003 %1007 |
| %1009 = OpFAdd %2 %1006 %1008 |
| %43 = OpCopyObject %2 %1009 |
| OpBranch %44 |
| %44 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); |
| } |
| |
| TEST(TransformationInlineFunctionTest, OpPhiInBlockFollowingCall) { |
| // This test checks that if the block after the inlined function call has an |
| // OpPhi instruction and the called function contains multiple blocks then the |
| // OpPhi instruction gets updated to refer to the return block of the inlined |
| // function, since the block containing the call will no longer be a |
| // predecessor. |
| |
| std::string reference_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource ESSL 320 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %13 = OpTypeBool |
| %14 = OpConstantTrue %13 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| OpBranch %10 |
| %10 = OpLabel |
| %8 = OpFunctionCall %2 %6 |
| OpBranch %11 |
| %11 = OpLabel |
| %12 = OpPhi %13 %14 %10 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| OpBranch %20 |
| %20 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = |
| BuildModule(env, consumer, reference_shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| auto transformation = TransformationInlineFunction(8, {{7, 100}, {20, 101}}); |
| |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| std::string variant_shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %4 "main" |
| OpExecutionMode %4 OriginUpperLeft |
| OpSource ESSL 320 |
| %2 = OpTypeVoid |
| %3 = OpTypeFunction %2 |
| %13 = OpTypeBool |
| %14 = OpConstantTrue %13 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| OpBranch %10 |
| %10 = OpLabel |
| OpBranch %101 |
| %101 = OpLabel |
| OpBranch %11 |
| %11 = OpLabel |
| %12 = OpPhi %13 %14 %101 |
| OpReturn |
| OpFunctionEnd |
| %6 = OpFunction %2 None %3 |
| %7 = OpLabel |
| OpBranch %20 |
| %20 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| ASSERT_TRUE(IsEqual(env, variant_shader, context.get())); |
| } |
| |
| } // namespace |
| } // namespace fuzz |
| } // namespace spvtools |