| // 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 "gtest/gtest.h" |
| #include "source/fuzz/fuzzer_util.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 |
| %51 = OpConstant %11 2 |
| %52 = OpTypeArray %16 %51 |
| %53 = OpConstant %16 7 |
| %54 = OpConstantComposite %52 %53 %53 |
| %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); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(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)); |
| ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(60)); |
| } |
| { |
| TransformationAddParameter correct(9, 68, 52, {{{13, 54}}}, 69); |
| ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(68)); |
| } |
| { |
| TransformationAddParameter correct(17, 62, 7, {{}}, 63); |
| ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(62)); |
| } |
| { |
| TransformationAddParameter correct(29, 64, 31, {{}}, 65); |
| ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| ASSERT_TRUE(transformation_context.GetFactManager()->IdIsIrrelevant(64)); |
| } |
| { |
| TransformationAddParameter correct(34, 66, 7, {{}}, 67); |
| ASSERT_TRUE(correct.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(correct, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| 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 |
| %51 = OpConstant %11 2 |
| %52 = OpTypeArray %16 %51 |
| %53 = OpConstant %16 7 |
| %54 = OpConstantComposite %52 %53 %53 |
| %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 %52 |
| %65 = OpTypeFunction %2 %31 |
| %4 = OpFunction %2 None %3 |
| %5 = OpLabel |
| %13 = OpFunctionCall %7 %9 %12 %8 %54 |
| OpReturn |
| OpFunctionEnd |
| |
| ; adjust type of the function in-place |
| %9 = OpFunction %7 None %6 |
| %14 = OpFunctionParameter %7 |
| %60 = OpFunctionParameter %11 |
| %68 = OpFunctionParameter %52 |
| %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); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(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 valid 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); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(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)); |
| ApplyAndCheckFreshIds(transformation_good_1, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_2, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_3, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_4, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| 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); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(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)); |
| ApplyAndCheckFreshIds(transformation_good_1, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_2, 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)); |
| ApplyAndCheckFreshIds(transformation_good_3, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_4, 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)); |
| ApplyAndCheckFreshIds(transformation_good_5, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_6, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| 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); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(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)); |
| ApplyAndCheckFreshIds(transformation_good_1, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_2, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| 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); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(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)); |
| ApplyAndCheckFreshIds(transformation_good_1, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_2, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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)); |
| ApplyAndCheckFreshIds(transformation_good_3, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // 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 |