// 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(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(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(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(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(1000); auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); TransformationContext transformation_context( MakeUnique(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(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