// Copyright (c) 2020 Vasyl Teliman // // 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_add_parameter.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { namespace fuzz { namespace { TEST(TransformationAddParameterTest, NonPointerBasicTest) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" %2 = OpTypeVoid %7 = OpTypeBool %11 = OpTypeInt 32 1 %16 = OpTypeFloat 32 %3 = OpTypeFunction %2 %6 = OpTypeFunction %7 %7 %8 = OpConstant %11 23 %12 = OpConstantTrue %7 %15 = OpTypeFunction %2 %16 %24 = OpTypeFunction %2 %16 %7 %31 = OpTypeStruct %7 %11 %32 = OpConstant %16 23 %33 = OpConstantComposite %31 %12 %8 %41 = OpTypeStruct %11 %16 %42 = OpConstantComposite %41 %8 %32 %43 = OpTypeFunction %2 %41 %44 = OpTypeFunction %2 %41 %7 %4 = OpFunction %2 None %3 %5 = OpLabel %13 = OpFunctionCall %7 %9 %12 OpReturn OpFunctionEnd ; adjust type of the function in-place %9 = OpFunction %7 None %6 %14 = OpFunctionParameter %7 %10 = OpLabel OpReturnValue %12 OpFunctionEnd ; reuse an existing function type %17 = OpFunction %2 None %15 %18 = OpFunctionParameter %16 %19 = OpLabel OpReturn OpFunctionEnd %20 = OpFunction %2 None %15 %21 = OpFunctionParameter %16 %22 = OpLabel OpReturn OpFunctionEnd %25 = OpFunction %2 None %24 %26 = OpFunctionParameter %16 %27 = OpFunctionParameter %7 %28 = OpLabel OpReturn OpFunctionEnd ; create a new function type %29 = OpFunction %2 None %3 %30 = OpLabel OpReturn OpFunctionEnd ; don't adjust the type of the function if it creates a duplicate %34 = OpFunction %2 None %43 %35 = OpFunctionParameter %41 %36 = OpLabel OpReturn OpFunctionEnd %37 = OpFunction %2 None %44 %38 = OpFunctionParameter %41 %39 = OpFunctionParameter %7 %40 = OpLabel OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); // Can't modify entry point function. ASSERT_FALSE(TransformationAddParameter(4, 60, 7, {{}}, 61) .IsApplicable(context.get(), transformation_context)); // There is no function with result id 60. ASSERT_FALSE(TransformationAddParameter(60, 60, 11, {{}}, 61) .IsApplicable(context.get(), transformation_context)); // Parameter id is not fresh. ASSERT_FALSE(TransformationAddParameter(9, 14, 11, {{{13, 8}}}, 61) .IsApplicable(context.get(), transformation_context)); // Function type id is not fresh. ASSERT_FALSE(TransformationAddParameter(9, 60, 11, {{{13, 8}}}, 14) .IsApplicable(context.get(), transformation_context)); // Function type id and parameter type id are equal. ASSERT_FALSE(TransformationAddParameter(9, 60, 11, {{{13, 8}}}, 60) .IsApplicable(context.get(), transformation_context)); // Parameter's initializer doesn't exist. ASSERT_FALSE(TransformationAddParameter(9, 60, 11, {{{13, 60}}}, 61) .IsApplicable(context.get(), transformation_context)); // Correct transformations. { TransformationAddParameter correct(9, 60, 11, {{{13, 8}}}, 61); ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); correct.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(60)); } { TransformationAddParameter correct(17, 62, 7, {{}}, 63); ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); correct.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(62)); } { TransformationAddParameter correct(29, 64, 31, {{}}, 65); ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); correct.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(64)); } { TransformationAddParameter correct(34, 66, 7, {{}}, 67); ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); correct.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(66)); } std::string expected_shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" %2 = OpTypeVoid %7 = OpTypeBool %11 = OpTypeInt 32 1 %16 = OpTypeFloat 32 %3 = OpTypeFunction %2 %8 = OpConstant %11 23 %12 = OpConstantTrue %7 %15 = OpTypeFunction %2 %16 %24 = OpTypeFunction %2 %16 %7 %31 = OpTypeStruct %7 %11 %32 = OpConstant %16 23 %33 = OpConstantComposite %31 %12 %8 %41 = OpTypeStruct %11 %16 %42 = OpConstantComposite %41 %8 %32 %44 = OpTypeFunction %2 %41 %7 %6 = OpTypeFunction %7 %7 %11 %65 = OpTypeFunction %2 %31 %4 = OpFunction %2 None %3 %5 = OpLabel %13 = OpFunctionCall %7 %9 %12 %8 OpReturn OpFunctionEnd ; adjust type of the function in-place %9 = OpFunction %7 None %6 %14 = OpFunctionParameter %7 %60 = OpFunctionParameter %11 %10 = OpLabel OpReturnValue %12 OpFunctionEnd ; reuse an existing function type %17 = OpFunction %2 None %24 %18 = OpFunctionParameter %16 %62 = OpFunctionParameter %7 %19 = OpLabel OpReturn OpFunctionEnd %20 = OpFunction %2 None %15 %21 = OpFunctionParameter %16 %22 = OpLabel OpReturn OpFunctionEnd %25 = OpFunction %2 None %24 %26 = OpFunctionParameter %16 %27 = OpFunctionParameter %7 %28 = OpLabel OpReturn OpFunctionEnd ; create a new function type %29 = OpFunction %2 None %65 %64 = OpFunctionParameter %31 %30 = OpLabel OpReturn OpFunctionEnd ; don't adjust the type of the function if it creates a duplicate %34 = OpFunction %2 None %44 %35 = OpFunctionParameter %41 %66 = OpFunctionParameter %7 %36 = OpLabel OpReturn OpFunctionEnd %37 = OpFunction %2 None %44 %38 = OpFunctionParameter %41 %39 = OpFunctionParameter %7 %40 = OpLabel OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationAddParameterTest, NonPointerNotApplicableTest) { // This types handles case of adding a new parameter of a non-pointer type // where the transformation is not applicable. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %6 "fun1(" OpName %12 "fun2(i1;" OpName %11 "a" OpName %14 "fun3(" OpName %24 "f1" OpName %27 "f2" OpName %30 "i1" OpName %31 "i2" OpName %32 "param" OpName %35 "i3" OpName %36 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %10 = OpTypeFunction %8 %9 %18 = OpConstant %8 2 %22 = OpTypeFloat 32 %23 = OpTypePointer Private %22 %24 = OpVariable %23 Private %25 = OpConstant %22 1 %26 = OpTypePointer Function %22 %28 = OpConstant %22 2 %4 = OpFunction %2 None %3 %5 = OpLabel %27 = OpVariable %26 Function %30 = OpVariable %9 Function %31 = OpVariable %9 Function %32 = OpVariable %9 Function %35 = OpVariable %9 Function %36 = OpVariable %9 Function OpStore %24 %25 OpStore %27 %28 %29 = OpFunctionCall %2 %6 OpStore %30 %18 %33 = OpLoad %8 %30 OpStore %32 %33 %34 = OpFunctionCall %8 %12 %32 OpStore %31 %34 %37 = OpLoad %8 %31 OpStore %36 %37 %38 = OpFunctionCall %8 %12 %36 OpStore %35 %38 ; %39 = OpFunctionCall %2 %14 OpReturn OpFunctionEnd %6 = OpFunction %2 None %3 %7 = OpLabel OpReturn OpFunctionEnd %12 = OpFunction %8 None %10 %11 = OpFunctionParameter %9 %13 = OpLabel %17 = OpLoad %8 %11 %19 = OpIAdd %8 %17 %18 OpReturnValue %19 OpFunctionEnd %14 = OpFunction %2 None %3 %15 = OpLabel OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); // Bad: Id 19 is not available in the caller that has id 34. TransformationAddParameter transformation_bad_1(12, 50, 8, {{{34, 19}, {38, 19}}}, 51); ASSERT_FALSE( transformation_bad_1.IsApplicable(context.get(), transformation_context)); // Bad: Id 8 does not have a type. TransformationAddParameter transformation_bad_2(12, 50, 8, {{{34, 8}, {38, 8}}}, 51); ASSERT_FALSE( transformation_bad_2.IsApplicable(context.get(), transformation_context)); // Bad: Types of id 25 and id 18 are different. TransformationAddParameter transformation_bad_3(12, 50, 22, {{{34, 25}, {38, 18}}}, 51); ASSERT_FALSE( transformation_bad_3.IsApplicable(context.get(), transformation_context)); // Function with id 14 does not have any callers. // Bad: Id 18 is not a vaild type. TransformationAddParameter transformation_bad_4(14, 50, 18, {{}}, 51); ASSERT_FALSE( transformation_bad_4.IsApplicable(context.get(), transformation_context)); // Function with id 14 does not have any callers. // Bad: Id 3 refers to OpTypeVoid, which is not supported. TransformationAddParameter transformation_bad_6(14, 50, 3, {{}}, 51); ASSERT_FALSE( transformation_bad_6.IsApplicable(context.get(), transformation_context)); } TEST(TransformationAddParameterTest, PointerFunctionTest) { // This types handles case of adding a new parameter of a pointer type with // storage class Function. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %6 "fun1(" OpName %12 "fun2(i1;" OpName %11 "a" OpName %14 "fun3(" OpName %17 "s" OpName %24 "s" OpName %28 "f1" OpName %31 "f2" OpName %34 "i1" OpName %35 "i2" OpName %36 "param" OpName %39 "i3" OpName %40 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %10 = OpTypeFunction %8 %9 %20 = OpConstant %8 2 %25 = OpConstant %8 0 %26 = OpTypeFloat 32 %27 = OpTypePointer Private %26 %28 = OpVariable %27 Private %60 = OpTypePointer Output %26 %61 = OpVariable %60 Output %29 = OpConstant %26 1 %30 = OpTypePointer Function %26 %32 = OpConstant %26 2 %4 = OpFunction %2 None %3 %5 = OpLabel %31 = OpVariable %30 Function %34 = OpVariable %9 Function %35 = OpVariable %9 Function %36 = OpVariable %9 Function %39 = OpVariable %9 Function %40 = OpVariable %9 Function OpStore %28 %29 OpStore %31 %32 %33 = OpFunctionCall %2 %6 OpStore %34 %20 %37 = OpLoad %8 %34 OpStore %36 %37 %38 = OpFunctionCall %8 %12 %36 OpStore %35 %38 %41 = OpLoad %8 %35 OpStore %40 %41 %42 = OpFunctionCall %8 %12 %40 OpStore %39 %42 %43 = OpFunctionCall %2 %14 OpReturn OpFunctionEnd %6 = OpFunction %2 None %3 %7 = OpLabel OpReturn OpFunctionEnd %12 = OpFunction %8 None %10 %11 = OpFunctionParameter %9 %13 = OpLabel %17 = OpVariable %9 Function %18 = OpLoad %8 %11 OpStore %17 %18 %19 = OpLoad %8 %17 %21 = OpIAdd %8 %19 %20 OpReturnValue %21 OpFunctionEnd %14 = OpFunction %2 None %3 %15 = OpLabel %24 = OpVariable %9 Function OpStore %24 %25 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); // Bad: Pointer of id 61 has storage class Output, which is not supported. TransformationAddParameter transformation_bad_1(12, 50, 60, {{{38, 61}, {42, 61}}}, 51); ASSERT_FALSE( transformation_bad_1.IsApplicable(context.get(), transformation_context)); // Good: Local variable of id 31 is defined in the caller (main). TransformationAddParameter transformation_good_1(12, 50, 30, {{{38, 31}, {42, 31}}}, 51); ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), transformation_context)); transformation_good_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: Local variable of id 34 is defined in the caller (main). TransformationAddParameter transformation_good_2(14, 52, 9, {{{43, 34}}}, 53); ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), transformation_context)); transformation_good_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: Local variable of id 39 is defined in the caller (main). TransformationAddParameter transformation_good_3(6, 54, 9, {{{33, 39}}}, 55); ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), transformation_context)); transformation_good_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: This adds another pointer parameter to the function of id 6. TransformationAddParameter transformation_good_4(6, 56, 30, {{{33, 31}}}, 57); ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(), transformation_context)); transformation_good_4.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected_shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %6 "fun1(" OpName %12 "fun2(i1;" OpName %11 "a" OpName %14 "fun3(" OpName %17 "s" OpName %24 "s" OpName %28 "f1" OpName %31 "f2" OpName %34 "i1" OpName %35 "i2" OpName %36 "param" OpName %39 "i3" OpName %40 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %20 = OpConstant %8 2 %25 = OpConstant %8 0 %26 = OpTypeFloat 32 %27 = OpTypePointer Private %26 %28 = OpVariable %27 Private %60 = OpTypePointer Output %26 %61 = OpVariable %60 Output %29 = OpConstant %26 1 %30 = OpTypePointer Function %26 %32 = OpConstant %26 2 %10 = OpTypeFunction %8 %9 %30 %53 = OpTypeFunction %2 %9 %57 = OpTypeFunction %2 %9 %30 %4 = OpFunction %2 None %3 %5 = OpLabel %31 = OpVariable %30 Function %34 = OpVariable %9 Function %35 = OpVariable %9 Function %36 = OpVariable %9 Function %39 = OpVariable %9 Function %40 = OpVariable %9 Function OpStore %28 %29 OpStore %31 %32 %33 = OpFunctionCall %2 %6 %39 %31 OpStore %34 %20 %37 = OpLoad %8 %34 OpStore %36 %37 %38 = OpFunctionCall %8 %12 %36 %31 OpStore %35 %38 %41 = OpLoad %8 %35 OpStore %40 %41 %42 = OpFunctionCall %8 %12 %40 %31 OpStore %39 %42 %43 = OpFunctionCall %2 %14 %34 OpReturn OpFunctionEnd %6 = OpFunction %2 None %57 %54 = OpFunctionParameter %9 %56 = OpFunctionParameter %30 %7 = OpLabel OpReturn OpFunctionEnd %12 = OpFunction %8 None %10 %11 = OpFunctionParameter %9 %50 = OpFunctionParameter %30 %13 = OpLabel %17 = OpVariable %9 Function %18 = OpLoad %8 %11 OpStore %17 %18 %19 = OpLoad %8 %17 %21 = OpIAdd %8 %19 %20 OpReturnValue %21 OpFunctionEnd %14 = OpFunction %2 None %53 %52 = OpFunctionParameter %9 %15 = OpLabel %24 = OpVariable %9 Function OpStore %24 %25 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationAddParameterTest, PointerPrivateWorkgroupTest) { // This types handles case of adding a new parameter of a pointer type with // storage class Private or Workgroup. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %6 "fun1(" OpName %12 "fun2(i1;" OpName %11 "a" OpName %14 "fun3(" OpName %17 "s" OpName %24 "s" OpName %28 "f1" OpName %31 "f2" OpName %34 "i1" OpName %35 "i2" OpName %36 "param" OpName %39 "i3" OpName %40 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %10 = OpTypeFunction %8 %9 %20 = OpConstant %8 2 %25 = OpConstant %8 0 %26 = OpTypeFloat 32 %27 = OpTypePointer Private %26 %28 = OpVariable %27 Private %60 = OpTypePointer Workgroup %26 %61 = OpVariable %60 Workgroup %29 = OpConstant %26 1 %30 = OpTypePointer Function %26 %32 = OpConstant %26 2 %4 = OpFunction %2 None %3 %5 = OpLabel %31 = OpVariable %30 Function %34 = OpVariable %9 Function %35 = OpVariable %9 Function %36 = OpVariable %9 Function %39 = OpVariable %9 Function %40 = OpVariable %9 Function OpStore %28 %29 OpStore %31 %32 %33 = OpFunctionCall %2 %6 OpStore %34 %20 %37 = OpLoad %8 %34 OpStore %36 %37 %38 = OpFunctionCall %8 %12 %36 OpStore %35 %38 %41 = OpLoad %8 %35 OpStore %40 %41 %42 = OpFunctionCall %8 %12 %40 OpStore %39 %42 %43 = OpFunctionCall %2 %14 OpReturn OpFunctionEnd %6 = OpFunction %2 None %3 %7 = OpLabel OpReturn OpFunctionEnd %12 = OpFunction %8 None %10 %11 = OpFunctionParameter %9 %13 = OpLabel %17 = OpVariable %9 Function %18 = OpLoad %8 %11 OpStore %17 %18 %19 = OpLoad %8 %17 %21 = OpIAdd %8 %19 %20 OpReturnValue %21 OpFunctionEnd %14 = OpFunction %2 None %3 %15 = OpLabel %24 = OpVariable %9 Function OpStore %24 %25 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); // Good: Global variable of id 28 (storage class Private) is defined in the // caller (main). TransformationAddParameter transformation_good_1(12, 70, 27, {{{38, 28}, {42, 28}}}, 71); ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), transformation_context)); transformation_good_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: Global variable of id 61 is (storage class Workgroup) is defined in // the caller (main). TransformationAddParameter transformation_good_2(12, 72, 27, {{{38, 28}, {42, 28}}}, 73); ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), transformation_context)); transformation_good_2.Apply(context.get(), &transformation_context); // Good: Global variable of id 28 (storage class Private) is defined in the // caller (main). TransformationAddParameter transformation_good_3(6, 74, 27, {{{33, 28}}}, 75); ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), transformation_context)); transformation_good_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: Global variable of id 61 is (storage class Workgroup) is defined in // the caller (main). TransformationAddParameter transformation_good_4(6, 76, 60, {{{33, 61}}}, 77); ASSERT_TRUE(transformation_good_4.IsApplicable(context.get(), transformation_context)); transformation_good_4.Apply(context.get(), &transformation_context); // Good: Global variable of id 28 (storage class Private) is defined in the // caller (main). TransformationAddParameter transformation_good_5(14, 78, 27, {{{43, 28}}}, 79); ASSERT_TRUE(transformation_good_5.IsApplicable(context.get(), transformation_context)); transformation_good_5.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: Global variable of id 61 is (storage class Workgroup) is defined in // the caller (main). TransformationAddParameter transformation_good_6(14, 80, 60, {{{43, 61}}}, 81); ASSERT_TRUE(transformation_good_6.IsApplicable(context.get(), transformation_context)); transformation_good_6.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected_shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %6 "fun1(" OpName %12 "fun2(i1;" OpName %11 "a" OpName %14 "fun3(" OpName %17 "s" OpName %24 "s" OpName %28 "f1" OpName %31 "f2" OpName %34 "i1" OpName %35 "i2" OpName %36 "param" OpName %39 "i3" OpName %40 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %8 = OpTypeInt 32 1 %9 = OpTypePointer Function %8 %20 = OpConstant %8 2 %25 = OpConstant %8 0 %26 = OpTypeFloat 32 %27 = OpTypePointer Private %26 %28 = OpVariable %27 Private %60 = OpTypePointer Workgroup %26 %61 = OpVariable %60 Workgroup %29 = OpConstant %26 1 %30 = OpTypePointer Function %26 %32 = OpConstant %26 2 %10 = OpTypeFunction %8 %9 %27 %27 %75 = OpTypeFunction %2 %27 %60 %4 = OpFunction %2 None %3 %5 = OpLabel %31 = OpVariable %30 Function %34 = OpVariable %9 Function %35 = OpVariable %9 Function %36 = OpVariable %9 Function %39 = OpVariable %9 Function %40 = OpVariable %9 Function OpStore %28 %29 OpStore %31 %32 %33 = OpFunctionCall %2 %6 %28 %61 OpStore %34 %20 %37 = OpLoad %8 %34 OpStore %36 %37 %38 = OpFunctionCall %8 %12 %36 %28 %28 OpStore %35 %38 %41 = OpLoad %8 %35 OpStore %40 %41 %42 = OpFunctionCall %8 %12 %40 %28 %28 OpStore %39 %42 %43 = OpFunctionCall %2 %14 %28 %61 OpReturn OpFunctionEnd %6 = OpFunction %2 None %75 %74 = OpFunctionParameter %27 %76 = OpFunctionParameter %60 %7 = OpLabel OpReturn OpFunctionEnd %12 = OpFunction %8 None %10 %11 = OpFunctionParameter %9 %70 = OpFunctionParameter %27 %72 = OpFunctionParameter %27 %13 = OpLabel %17 = OpVariable %9 Function %18 = OpLoad %8 %11 OpStore %17 %18 %19 = OpLoad %8 %17 %21 = OpIAdd %8 %19 %20 OpReturnValue %21 OpFunctionEnd %14 = OpFunction %2 None %75 %78 = OpFunctionParameter %27 %80 = OpFunctionParameter %60 %15 = OpLabel %24 = OpVariable %9 Function OpStore %24 %25 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationAddParameterTest, PointerMoreEntriesInMapTest) { // This types handles case where call_parameter_id has an entry for at least // every caller (there are more entries than it is necessary). std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %10 "fun(i1;" OpName %9 "a" OpName %12 "s" OpName %19 "i1" OpName %21 "i2" OpName %22 "i3" OpName %24 "i4" OpName %25 "param" OpName %28 "i5" OpName %29 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %8 = OpTypeFunction %6 %7 %15 = OpConstant %6 2 %20 = OpConstant %6 1 %23 = OpConstant %6 3 %4 = OpFunction %2 None %3 %5 = OpLabel %19 = OpVariable %7 Function %21 = OpVariable %7 Function %22 = OpVariable %7 Function %24 = OpVariable %7 Function %25 = OpVariable %7 Function %28 = OpVariable %7 Function %29 = OpVariable %7 Function OpStore %19 %20 OpStore %21 %15 OpStore %22 %23 %26 = OpLoad %6 %19 OpStore %25 %26 %27 = OpFunctionCall %6 %10 %25 OpStore %24 %27 %30 = OpLoad %6 %21 OpStore %29 %30 %31 = OpFunctionCall %6 %10 %29 OpStore %28 %31 OpReturn OpFunctionEnd %10 = OpFunction %6 None %8 %9 = OpFunctionParameter %7 %11 = OpLabel %12 = OpVariable %7 Function %13 = OpLoad %6 %9 OpStore %12 %13 %14 = OpLoad %6 %12 %16 = OpIAdd %6 %14 %15 OpReturnValue %16 OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); // Good: Local variable of id 21 is defined in every caller (id 27 and id 31). TransformationAddParameter transformation_good_1( 10, 70, 7, {{{27, 21}, {31, 21}, {30, 21}}}, 71); ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), transformation_context)); transformation_good_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Good: Local variable of id 28 is defined in every caller (id 27 and id 31). TransformationAddParameter transformation_good_2( 10, 72, 7, {{{27, 28}, {31, 28}, {14, 21}, {16, 14}}}, 73); ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), transformation_context)); transformation_good_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); std::string expected_shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %10 "fun(i1;" OpName %9 "a" OpName %12 "s" OpName %19 "i1" OpName %21 "i2" OpName %22 "i3" OpName %24 "i4" OpName %25 "param" OpName %28 "i5" OpName %29 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %15 = OpConstant %6 2 %20 = OpConstant %6 1 %23 = OpConstant %6 3 %8 = OpTypeFunction %6 %7 %7 %7 %4 = OpFunction %2 None %3 %5 = OpLabel %19 = OpVariable %7 Function %21 = OpVariable %7 Function %22 = OpVariable %7 Function %24 = OpVariable %7 Function %25 = OpVariable %7 Function %28 = OpVariable %7 Function %29 = OpVariable %7 Function OpStore %19 %20 OpStore %21 %15 OpStore %22 %23 %26 = OpLoad %6 %19 OpStore %25 %26 %27 = OpFunctionCall %6 %10 %25 %21 %28 OpStore %24 %27 %30 = OpLoad %6 %21 OpStore %29 %30 %31 = OpFunctionCall %6 %10 %29 %21 %28 OpStore %28 %31 OpReturn OpFunctionEnd %10 = OpFunction %6 None %8 %9 = OpFunctionParameter %7 %70 = OpFunctionParameter %7 %72 = OpFunctionParameter %7 %11 = OpLabel %12 = OpVariable %7 Function %13 = OpLoad %6 %9 OpStore %12 %13 %14 = OpLoad %6 %12 %16 = OpIAdd %6 %14 %15 OpReturnValue %16 OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, expected_shader, context.get())); } TEST(TransformationAddParameterTest, PointeeValueIsIrrelevantTest) { // This test checks if the transformation has correctly applied the // PointeeValueIsIrrelevant fact for new pointer parameters. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 310 OpName %4 "main" OpName %10 "fun(i1;" OpName %9 "a" OpName %12 "s" OpName %20 "b" OpName %22 "i1" OpName %24 "i2" OpName %25 "i3" OpName %26 "param" OpName %29 "i4" OpName %30 "param" %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %50 = OpTypePointer Workgroup %6 %51 = OpVariable %50 Workgroup %8 = OpTypeFunction %6 %7 %15 = OpConstant %6 2 %19 = OpTypePointer Private %6 %20 = OpVariable %19 Private %21 = OpConstant %6 0 %23 = OpConstant %6 1 %4 = OpFunction %2 None %3 %5 = OpLabel %22 = OpVariable %7 Function %24 = OpVariable %7 Function %25 = OpVariable %7 Function %26 = OpVariable %7 Function %29 = OpVariable %7 Function %30 = OpVariable %7 Function OpStore %20 %21 OpStore %22 %23 OpStore %24 %15 %27 = OpLoad %6 %22 OpStore %26 %27 %28 = OpFunctionCall %6 %10 %26 OpStore %25 %28 %31 = OpLoad %6 %24 OpStore %30 %31 %32 = OpFunctionCall %6 %10 %30 OpStore %29 %32 OpReturn OpFunctionEnd %10 = OpFunction %6 None %8 %9 = OpFunctionParameter %7 %11 = OpLabel %12 = OpVariable %7 Function %13 = OpLoad %6 %9 OpStore %12 %13 %14 = OpLoad %6 %12 %16 = OpIAdd %6 %14 %15 OpReturnValue %16 OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); ASSERT_TRUE(IsValid(env, context.get())); spvtools::ValidatorOptions validator_options; TransformationContext transformation_context( MakeUnique(context.get()), validator_options); TransformationAddParameter transformation_good_1(10, 70, 7, {{{28, 22}, {32, 22}}}, 71); ASSERT_TRUE(transformation_good_1.IsApplicable(context.get(), transformation_context)); transformation_good_1.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Check if the fact PointeeValueIsIrrelevant is set for the new parameter // (storage class Function). ASSERT_TRUE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(70)); TransformationAddParameter transformation_good_2(10, 72, 19, {{{28, 20}, {32, 20}}}, 73); ASSERT_TRUE(transformation_good_2.IsApplicable(context.get(), transformation_context)); transformation_good_2.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Check if the fact PointeeValueIsIrrelevant is set for the new parameter // (storage class Private). ASSERT_TRUE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(72)); TransformationAddParameter transformation_good_3(10, 74, 50, {{{28, 51}, {32, 51}}}, 75); ASSERT_TRUE(transformation_good_3.IsApplicable(context.get(), transformation_context)); transformation_good_3.Apply(context.get(), &transformation_context); ASSERT_TRUE(IsValid(env, context.get())); // Check if the fact PointeeValueIsIrrelevant is set for the new parameter // (storage class Workgroup). ASSERT_TRUE( transformation_context.GetFactManager()->PointeeValueIsIrrelevant(74)); } } // namespace } // namespace fuzz } // namespace spvtools