| // Copyright (c) 2020 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "source/fuzz/transformation_merge_function_returns.h" |
| |
| #include "gtest/gtest.h" |
| #include "source/fuzz/counter_overflow_id_source.h" |
| #include "source/fuzz/fuzzer_util.h" |
| #include "test/fuzz/fuzz_test_util.h" |
| |
| namespace spvtools { |
| namespace fuzz { |
| namespace { |
| |
| protobufs::ReturnMergingInfo MakeReturnMergingInfo( |
| uint32_t merge_block_id, uint32_t is_returning_id, |
| uint32_t maybe_return_val_id, |
| const std::map<uint32_t, uint32_t>& opphi_to_suitable_id) { |
| protobufs::ReturnMergingInfo result; |
| result.set_merge_block_id(merge_block_id); |
| result.set_is_returning_id(is_returning_id); |
| result.set_maybe_return_val_id(maybe_return_val_id); |
| *result.mutable_opphi_to_suitable_id() = |
| fuzzerutil::MapToRepeatedUInt32Pair(opphi_to_suitable_id); |
| return result; |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, SimpleInapplicable) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFloat 32 |
| %8 = OpTypeFunction %7 |
| %9 = OpTypeBool |
| %10 = OpConstantTrue %9 |
| %11 = OpConstantFalse %9 |
| %12 = OpConstant %5 0 |
| %13 = OpConstant %5 1 |
| %2 = OpFunction %3 None %4 |
| %14 = OpLabel |
| %15 = OpFunctionCall %3 %16 |
| %17 = OpFunctionCall %3 %18 |
| %19 = OpFunctionCall %3 %20 |
| %21 = OpFunctionCall %7 %22 |
| OpReturn |
| OpFunctionEnd |
| %16 = OpFunction %3 None %4 |
| %23 = OpLabel |
| OpSelectionMerge %24 None |
| OpBranchConditional %10 %25 %26 |
| %25 = OpLabel |
| OpReturn |
| %26 = OpLabel |
| OpReturn |
| %24 = OpLabel |
| OpUnreachable |
| OpFunctionEnd |
| %18 = OpFunction %3 None %4 |
| %27 = OpLabel |
| OpBranch %28 |
| %28 = OpLabel |
| OpLoopMerge %29 %30 None |
| OpBranch %31 |
| %31 = OpLabel |
| OpBranchConditional %10 %32 %29 |
| %32 = OpLabel |
| OpReturn |
| %30 = OpLabel |
| OpBranch %28 |
| %29 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %20 = OpFunction %3 None %4 |
| %33 = OpLabel |
| OpBranch %34 |
| %34 = OpLabel |
| OpLoopMerge %35 %36 None |
| OpBranch %37 |
| %37 = OpLabel |
| OpBranchConditional %10 %38 %35 |
| %38 = OpLabel |
| OpReturn |
| %36 = OpLabel |
| OpBranch %34 |
| %35 = OpLabel |
| %39 = OpFunctionCall %3 %18 |
| OpReturn |
| OpFunctionEnd |
| %22 = OpFunction %7 None %8 |
| %40 = OpLabel |
| OpBranch %51 |
| %51 = OpLabel |
| OpLoopMerge %41 %53 None |
| OpBranchConditional %10 %42 %41 |
| %42 = OpLabel |
| %43 = OpConvertSToF %7 %12 |
| OpReturnValue %43 |
| %41 = OpLabel |
| %44 = OpConvertSToF %7 %13 |
| OpReturnValue %44 |
| %53 = OpLabel |
| OpBranch %51 |
| OpFunctionEnd |
| %45 = OpFunction %5 None %6 |
| %46 = OpLabel |
| OpBranch %52 |
| %52 = OpLabel |
| %47 = OpConvertSToF %7 %13 |
| OpLoopMerge %48 %54 None |
| OpBranchConditional %10 %49 %48 |
| %49 = OpLabel |
| OpReturnValue %12 |
| %48 = OpLabel |
| %50 = OpCopyObject %5 %12 |
| OpReturnValue %13 |
| %54 = OpLabel |
| OpBranch %52 |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // Function %1 does not exist. |
| ASSERT_FALSE(TransformationMergeFunctionReturns(1, 100, 200, 101, 0, 0, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // The entry block (%22) of function %15 does not branch unconditionally to |
| // the following block. |
| ASSERT_FALSE(TransformationMergeFunctionReturns(16, 100, 200, 101, 0, 0, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Block %28 is the merge block of a loop containing a return instruction, but |
| // it contains an OpReturn instruction (so, it contains instructions that are |
| // not OpLabel, OpPhi or OpBranch). |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(29, 102, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Block %34 is the merge block of a loop containing a return instruction, but |
| // it contains an OpFunctionCall instruction (so, it contains instructions |
| // that are not OpLabel, OpPhi or OpBranch). |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 20, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(35, 102, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %1000 cannot be found in the module and there is no id of the correct |
| // type (float) available at the end of the entry block of function %21. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns(22, 100, 200, 101, 102, 1000, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %47 is of type float, while function %45 has return type int. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 47, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %50 is not available at the end of the entry block of function %45. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns(45, 100, 200, 101, 102, 50, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, MissingBooleans) { |
| { |
| // OpConstantTrue is missing. |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| OpName %2 "main" |
| OpName %3 "A(" |
| OpDecorate %3 RelaxedPrecision |
| OpDecorate %4 RelaxedPrecision |
| %5 = OpTypeVoid |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeInt 32 1 |
| %8 = OpTypeFunction %7 |
| %9 = OpTypeBool |
| %10 = OpConstantFalse %9 |
| %11 = OpConstant %7 1 |
| %12 = OpConstant %7 2 |
| %2 = OpFunction %5 None %6 |
| %13 = OpLabel |
| %4 = OpFunctionCall %7 %3 |
| OpReturn |
| OpFunctionEnd |
| %3 = OpFunction %7 None %8 |
| %14 = OpLabel |
| OpBranch %15 |
| %15 = OpLabel |
| OpSelectionMerge %16 None |
| OpBranchConditional %10 %17 %16 |
| %17 = OpLabel |
| OpReturnValue %11 |
| %16 = OpLabel |
| OpReturnValue %12 |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = |
| BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| } |
| { |
| // OpConstantFalse is missing. |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| OpName %2 "main" |
| OpName %3 "A(" |
| OpDecorate %3 RelaxedPrecision |
| OpDecorate %4 RelaxedPrecision |
| %5 = OpTypeVoid |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeInt 32 1 |
| %8 = OpTypeFunction %7 |
| %9 = OpTypeBool |
| %10 = OpConstantTrue %9 |
| %11 = OpConstant %7 1 |
| %12 = OpConstant %7 2 |
| %2 = OpFunction %5 None %6 |
| %13 = OpLabel |
| %4 = OpFunctionCall %7 %3 |
| OpReturn |
| OpFunctionEnd |
| %3 = OpFunction %7 None %8 |
| %14 = OpLabel |
| OpBranch %15 |
| %15 = OpLabel |
| OpSelectionMerge %16 None |
| OpBranchConditional %10 %17 %16 |
| %17 = OpLabel |
| OpReturnValue %11 |
| %16 = OpLabel |
| OpReturnValue %12 |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = |
| BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( |
| context.get(), validator_options, kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns(3, 100, 200, 101, 0, 0, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| } |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, InvalidIds) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %42 = OpTypeFloat 32 |
| %7 = OpTypeBool |
| %8 = OpConstantTrue %7 |
| %9 = OpConstantFalse %7 |
| %10 = OpConstant %5 0 |
| %11 = OpConstant %5 1 |
| %2 = OpFunction %3 None %4 |
| %12 = OpLabel |
| %13 = OpFunctionCall %5 %14 |
| %15 = OpFunctionCall %3 %16 |
| OpReturn |
| OpFunctionEnd |
| %17 = OpFunction %3 None %4 |
| %18 = OpLabel |
| OpBranch %19 |
| %19 = OpLabel |
| OpLoopMerge %20 %21 None |
| OpBranch %22 |
| %22 = OpLabel |
| OpBranchConditional %8 %23 %20 |
| %23 = OpLabel |
| OpReturn |
| %21 = OpLabel |
| OpBranch %19 |
| %20 = OpLabel |
| OpBranch %24 |
| %24 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %14 = OpFunction %5 None %6 |
| %25 = OpLabel |
| OpBranch %26 |
| %26 = OpLabel |
| OpLoopMerge %27 %28 None |
| OpBranch %29 |
| %29 = OpLabel |
| OpBranchConditional %8 %30 %27 |
| %30 = OpLabel |
| OpReturnValue %10 |
| %28 = OpLabel |
| OpBranch %26 |
| %27 = OpLabel |
| OpBranch %33 |
| %33 = OpLabel |
| OpReturnValue %11 |
| OpFunctionEnd |
| %16 = OpFunction %3 None %4 |
| %34 = OpLabel |
| OpBranch %35 |
| %35 = OpLabel |
| OpLoopMerge %36 %37 None |
| OpBranch %38 |
| %38 = OpLabel |
| %43 = OpConvertSToF %42 %10 |
| OpBranchConditional %8 %39 %36 |
| %39 = OpLabel |
| OpReturn |
| %37 = OpLabel |
| %44 = OpConvertSToF %42 %10 |
| OpBranch %35 |
| %36 = OpLabel |
| %31 = OpPhi %42 %43 %38 |
| %32 = OpPhi %5 %11 %38 |
| OpBranch %40 |
| %40 = OpLabel |
| %41 = OpFunctionCall %3 %17 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // Fresh id %100 is used twice. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 17, 100, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Fresh id %100 is used twice. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 100, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %0 cannot be a fresh id for the new merge block. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 17, 100, 200, 0, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %0 cannot be a fresh id for the new continue block. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 17, 100, 0, 200, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %0 cannot be a fresh id for the new header block. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 17, 0, 200, 100, 0, 0, {{MakeReturnMergingInfo(20, 101, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %0 cannot be a fresh id for the new |is_returning| instruction in an |
| // existing merge block. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 17, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(20, 0, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %0 cannot be a fresh id for the new |return_val| instruction in the new |
| // return block. |
| ASSERT_FALSE(TransformationMergeFunctionReturns( |
| 14, 100, 200, 101, 0, 10, |
| {{MakeReturnMergingInfo(27, 102, 103, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %0 cannot be a fresh id for the new |maybe_return_val| instruction in an |
| // existing merge block, inside a non-void function. |
| ASSERT_FALSE(TransformationMergeFunctionReturns( |
| 14, 100, 200, 101, 102, 10, |
| {{MakeReturnMergingInfo(27, 103, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Fresh id %102 is repeated. |
| ASSERT_FALSE(TransformationMergeFunctionReturns( |
| 14, 100, 200, 101, 102, 10, |
| {{MakeReturnMergingInfo(27, 102, 104, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %11 (type int) does not have the correct type (float) for OpPhi |
| // instruction %31. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 16, 100, 200, 101, 0, 0, |
| {{MakeReturnMergingInfo(36, 103, 104, {{{31, 11}, {32, 11}}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %11 (type int) does not have the correct type (float) for OpPhi |
| // instruction %31. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 16, 100, 200, 101, 0, 0, |
| {{MakeReturnMergingInfo(36, 102, 0, {{{31, 11}, {32, 11}}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %43 is not available at the end of the entry block. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 16, 100, 200, 101, 0, 0, |
| {{MakeReturnMergingInfo(36, 102, 0, {{{31, 44}, {32, 11}}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // There is not a mapping for id %31 (float OpPhi instruction in a loop merge |
| // block) and no suitable id is available at the end of the entry block. |
| ASSERT_FALSE(TransformationMergeFunctionReturns( |
| 16, 100, 200, 101, 0, 0, |
| {{MakeReturnMergingInfo(36, 102, 0, {{{32, 11}}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // Id %1000 cannot be found in the module and no suitable id for OpPhi %31 is |
| // available at the end of the entry block. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 16, 100, 200, 101, 0, 0, |
| {{MakeReturnMergingInfo(36, 102, 0, {{{31, 1000}, {32, 11}}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, Simple) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFloat 32 |
| %8 = OpTypeFunction %7 %7 |
| %9 = OpTypeFunction %7 |
| %10 = OpTypeBool |
| %11 = OpConstantTrue %10 |
| %40 = OpConstantFalse %10 |
| %12 = OpConstant %5 1 |
| %2 = OpFunction %3 None %4 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %14 = OpFunction %3 None %4 |
| %15 = OpLabel |
| OpBranch %16 |
| %16 = OpLabel |
| OpSelectionMerge %17 None |
| OpBranchConditional %11 %18 %17 |
| %18 = OpLabel |
| OpReturn |
| %17 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %19 = OpFunction %5 None %6 |
| %20 = OpLabel |
| OpBranch %21 |
| %21 = OpLabel |
| OpSelectionMerge %22 None |
| OpBranchConditional %11 %23 %24 |
| %23 = OpLabel |
| OpReturnValue %12 |
| %24 = OpLabel |
| %25 = OpIAdd %5 %12 %12 |
| OpReturnValue %25 |
| %22 = OpLabel |
| OpUnreachable |
| OpFunctionEnd |
| %26 = OpFunction %7 None %8 |
| %27 = OpFunctionParameter %7 |
| %28 = OpLabel |
| OpBranch %29 |
| %29 = OpLabel |
| OpSelectionMerge %30 None |
| OpBranchConditional %11 %31 %30 |
| %31 = OpLabel |
| %32 = OpFAdd %7 %27 %27 |
| OpReturnValue %32 |
| %30 = OpLabel |
| OpReturnValue %27 |
| OpFunctionEnd |
| %33 = OpFunction %7 None %9 |
| %34 = OpLabel |
| %35 = OpConvertSToF %7 %12 |
| OpBranch %36 |
| %36 = OpLabel |
| OpSelectionMerge %37 None |
| OpBranchConditional %11 %38 %37 |
| %38 = OpLabel |
| %39 = OpFAdd %7 %35 %35 |
| OpReturnValue %39 |
| %37 = OpLabel |
| OpReturnValue %35 |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // The 0s are allowed because the function's return type is void. |
| auto transformation1 = |
| TransformationMergeFunctionReturns(14, 100, 200, 101, 0, 0, {{}}); |
| ASSERT_TRUE( |
| transformation1.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation1, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // %12 is available at the end of the entry block of %19 (it is a global |
| // variable). |
| ASSERT_TRUE( |
| TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 12, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %1000 cannot be found in the module, but there is a suitable id available |
| // at the end of the entry block (%12). |
| auto transformation2 = |
| TransformationMergeFunctionReturns(19, 110, 210, 111, 112, 1000, {{}}); |
| ASSERT_TRUE( |
| transformation2.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation2, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // %27 is available at the end of the entry block of %26 (it is a function |
| // parameter). |
| ASSERT_TRUE( |
| TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 27, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %1000 cannot be found in the module, but there is a suitable id available |
| // at the end of the entry block (%27). |
| auto transformation3 = |
| TransformationMergeFunctionReturns(26, 120, 220, 121, 122, 1000, {{}}); |
| ASSERT_TRUE( |
| transformation3.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation3, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // %35 is available at the end of the entry block of %33 (it is in the entry |
| // block). |
| ASSERT_TRUE( |
| TransformationMergeFunctionReturns(26, 130, 230, 131, 132, 27, {{}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // %1000 cannot be found in the module, but there is a suitable id available |
| // at the end of the entry block (%35). |
| auto transformation4 = |
| TransformationMergeFunctionReturns(33, 130, 230, 131, 132, 1000, {{}}); |
| ASSERT_TRUE( |
| transformation4.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation4, context.get(), |
| &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| std::string after_transformations = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeFloat 32 |
| %8 = OpTypeFunction %7 %7 |
| %9 = OpTypeFunction %7 |
| %10 = OpTypeBool |
| %11 = OpConstantTrue %10 |
| %40 = OpConstantFalse %10 |
| %12 = OpConstant %5 1 |
| %2 = OpFunction %3 None %4 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %14 = OpFunction %3 None %4 |
| %15 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %16 |
| %16 = OpLabel |
| OpSelectionMerge %17 None |
| OpBranchConditional %11 %18 %17 |
| %18 = OpLabel |
| OpBranch %101 |
| %17 = OpLabel |
| OpBranch %101 |
| %101 = OpLabel |
| OpReturn |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| %19 = OpFunction %5 None %6 |
| %20 = OpLabel |
| OpBranch %110 |
| %110 = OpLabel |
| OpLoopMerge %111 %210 None |
| OpBranch %21 |
| %21 = OpLabel |
| OpSelectionMerge %22 None |
| OpBranchConditional %11 %23 %24 |
| %23 = OpLabel |
| OpBranch %111 |
| %24 = OpLabel |
| %25 = OpIAdd %5 %12 %12 |
| OpBranch %111 |
| %22 = OpLabel |
| OpUnreachable |
| %111 = OpLabel |
| %112 = OpPhi %5 %12 %23 %25 %24 |
| OpReturnValue %112 |
| %210 = OpLabel |
| OpBranch %110 |
| OpFunctionEnd |
| %26 = OpFunction %7 None %8 |
| %27 = OpFunctionParameter %7 |
| %28 = OpLabel |
| OpBranch %120 |
| %120 = OpLabel |
| OpLoopMerge %121 %220 None |
| OpBranch %29 |
| %29 = OpLabel |
| OpSelectionMerge %30 None |
| OpBranchConditional %11 %31 %30 |
| %31 = OpLabel |
| %32 = OpFAdd %7 %27 %27 |
| OpBranch %121 |
| %30 = OpLabel |
| OpBranch %121 |
| %121 = OpLabel |
| %122 = OpPhi %7 %27 %30 %32 %31 |
| OpReturnValue %122 |
| %220 = OpLabel |
| OpBranch %120 |
| OpFunctionEnd |
| %33 = OpFunction %7 None %9 |
| %34 = OpLabel |
| %35 = OpConvertSToF %7 %12 |
| OpBranch %130 |
| %130 = OpLabel |
| OpLoopMerge %131 %230 None |
| OpBranch %36 |
| %36 = OpLabel |
| OpSelectionMerge %37 None |
| OpBranchConditional %11 %38 %37 |
| %38 = OpLabel |
| %39 = OpFAdd %7 %35 %35 |
| OpBranch %131 |
| %37 = OpLabel |
| OpBranch %131 |
| %131 = OpLabel |
| %132 = OpPhi %7 %35 %37 %39 %38 |
| OpReturnValue %132 |
| %230 = OpLabel |
| OpBranch %130 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, NestedLoops) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeBool |
| %8 = OpConstantTrue %7 |
| %9 = OpConstantFalse %7 |
| %10 = OpConstant %5 2 |
| %11 = OpConstant %5 1 |
| %12 = OpConstant %5 3 |
| %2 = OpFunction %3 None %4 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %14 = OpFunction %5 None %6 |
| %15 = OpLabel |
| OpBranch %16 |
| %16 = OpLabel |
| OpLoopMerge %17 %916 None |
| OpBranch %18 |
| %18 = OpLabel |
| OpLoopMerge %19 %20 None |
| OpBranchConditional %8 %19 %21 |
| %19 = OpLabel |
| OpBranch %17 |
| %21 = OpLabel |
| OpReturnValue %12 |
| %17 = OpLabel |
| OpBranch %22 |
| %20 = OpLabel |
| OpBranch %18 |
| %22 = OpLabel |
| OpLoopMerge %23 %24 None |
| OpBranch %25 |
| %25 = OpLabel |
| OpBranchConditional %8 %26 %23 |
| %26 = OpLabel |
| OpSelectionMerge %27 None |
| OpBranchConditional %9 %28 %27 |
| %28 = OpLabel |
| OpBranch %29 |
| %29 = OpLabel |
| OpLoopMerge %30 %929 None |
| OpBranch %30 |
| %30 = OpLabel |
| OpLoopMerge %31 %32 None |
| OpBranch %33 |
| %33 = OpLabel |
| OpBranchConditional %9 %34 %31 |
| %34 = OpLabel |
| OpReturnValue %10 |
| %32 = OpLabel |
| OpBranch %30 |
| %31 = OpLabel |
| %35 = OpPhi %5 %11 %33 |
| %36 = OpPhi %5 %10 %33 |
| OpBranch %37 |
| %37 = OpLabel |
| OpReturnValue %35 |
| %27 = OpLabel |
| OpBranch %24 |
| %24 = OpLabel |
| OpBranch %22 |
| %23 = OpLabel |
| OpBranch %38 |
| %38 = OpLabel |
| OpReturnValue %12 |
| %916 = OpLabel |
| OpBranch %16 |
| %929 = OpLabel |
| OpBranch %29 |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| auto transformation = TransformationMergeFunctionReturns( |
| 14, 100, 200, 101, 102, 11, |
| {{MakeReturnMergingInfo(19, 103, 104, {{}}), |
| MakeReturnMergingInfo(17, 105, 106, {{}}), |
| MakeReturnMergingInfo(31, 107, 108, {{{35, 10}, {36, 12}}}), |
| MakeReturnMergingInfo(23, 109, 110, {})}}); |
| 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 %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeBool |
| %8 = OpConstantTrue %7 |
| %9 = OpConstantFalse %7 |
| %10 = OpConstant %5 2 |
| %11 = OpConstant %5 1 |
| %12 = OpConstant %5 3 |
| %2 = OpFunction %3 None %4 |
| %13 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %14 = OpFunction %5 None %6 |
| %15 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %16 |
| %16 = OpLabel |
| OpLoopMerge %17 %916 None |
| OpBranch %18 |
| %18 = OpLabel |
| OpLoopMerge %19 %20 None |
| OpBranchConditional %8 %19 %21 |
| %19 = OpLabel |
| %103 = OpPhi %7 %8 %21 %9 %18 |
| %104 = OpPhi %5 %12 %21 %11 %18 |
| OpBranch %17 |
| %21 = OpLabel |
| OpBranch %19 |
| %17 = OpLabel |
| %105 = OpPhi %7 %103 %19 |
| %106 = OpPhi %5 %104 %19 |
| OpBranchConditional %105 %101 %22 |
| %20 = OpLabel |
| OpBranch %18 |
| %22 = OpLabel |
| OpLoopMerge %23 %24 None |
| OpBranch %25 |
| %25 = OpLabel |
| OpBranchConditional %8 %26 %23 |
| %26 = OpLabel |
| OpSelectionMerge %27 None |
| OpBranchConditional %9 %28 %27 |
| %28 = OpLabel |
| OpBranch %29 |
| %29 = OpLabel |
| OpLoopMerge %30 %929 None |
| OpBranch %30 |
| %30 = OpLabel |
| OpLoopMerge %31 %32 None |
| OpBranch %33 |
| %33 = OpLabel |
| OpBranchConditional %9 %34 %31 |
| %34 = OpLabel |
| OpBranch %31 |
| %32 = OpLabel |
| OpBranch %30 |
| %31 = OpLabel |
| %107 = OpPhi %7 %8 %34 %9 %33 |
| %108 = OpPhi %5 %10 %34 %11 %33 |
| %35 = OpPhi %5 %11 %33 %10 %34 |
| %36 = OpPhi %5 %10 %33 %12 %34 |
| OpBranchConditional %107 %23 %37 |
| %37 = OpLabel |
| OpBranch %23 |
| %27 = OpLabel |
| OpBranch %24 |
| %24 = OpLabel |
| OpBranch %22 |
| %23 = OpLabel |
| %109 = OpPhi %7 %107 %31 %8 %37 %9 %25 |
| %110 = OpPhi %5 %108 %31 %35 %37 %11 %25 |
| OpBranchConditional %109 %101 %38 |
| %38 = OpLabel |
| OpBranch %101 |
| %916 = OpLabel |
| OpBranch %16 |
| %929 = OpLabel |
| OpBranch %29 |
| %101 = OpLabel |
| %102 = OpPhi %5 %106 %17 %110 %23 %12 %38 |
| OpReturnValue %102 |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, OverflowIds) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeBool |
| %8 = OpConstantTrue %7 |
| %9 = OpConstantFalse %7 |
| %10 = OpConstant %5 1 |
| %2 = OpFunction %3 None %4 |
| %11 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %5 None %6 |
| %13 = OpLabel |
| OpBranch %14 |
| %14 = OpLabel |
| %15 = OpIAdd %5 %10 %10 |
| OpLoopMerge %16 %17 None |
| OpBranch %18 |
| %18 = OpLabel |
| OpBranchConditional %8 %19 %16 |
| %19 = OpLabel |
| OpSelectionMerge %20 None |
| OpBranchConditional %9 %21 %20 |
| %21 = OpLabel |
| OpReturnValue %10 |
| %20 = OpLabel |
| OpBranch %17 |
| %17 = OpLabel |
| OpBranchConditional %8 %14 %16 |
| %16 = OpLabel |
| %22 = OpPhi %5 %15 %17 %10 %18 |
| OpBranch %23 |
| %23 = OpLabel |
| OpReturnValue %22 |
| OpFunctionEnd |
| %24 = OpFunction %3 None %4 |
| %25 = OpLabel |
| OpBranch %26 |
| %26 = OpLabel |
| OpLoopMerge %27 %28 None |
| OpBranch %29 |
| %29 = OpLabel |
| OpBranchConditional %8 %30 %27 |
| %30 = OpLabel |
| OpSelectionMerge %31 None |
| OpBranchConditional %9 %32 %31 |
| %32 = OpLabel |
| OpReturn |
| %31 = OpLabel |
| OpBranch %28 |
| %28 = OpLabel |
| OpBranch %26 |
| %27 = OpLabel |
| %33 = OpPhi %5 %10 %29 |
| OpBranch %34 |
| %34 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| auto overflow_ids_unique_ptr = MakeUnique<CounterOverflowIdSource>(1000); |
| auto overflow_ids_ptr = overflow_ids_unique_ptr.get(); |
| TransformationContext transformation_context_with_overflow_ids( |
| MakeUnique<FactManager>(context.get()), validator_options, |
| std::move(overflow_ids_unique_ptr)); |
| |
| // No mapping from merge block %16 to fresh ids is given, so overflow ids are |
| // needed. |
| auto transformation1 = |
| TransformationMergeFunctionReturns(12, 100, 200, 101, 102, 10, {{}}); |
| |
| #ifndef NDEBUG |
| ASSERT_DEATH( |
| transformation1.IsApplicable(context.get(), transformation_context), |
| "Bad attempt to query whether overflow ids are available."); |
| #endif |
| |
| ASSERT_TRUE(transformation1.IsApplicable( |
| context.get(), transformation_context_with_overflow_ids)); |
| ApplyAndCheckFreshIds(transformation1, context.get(), |
| &transformation_context_with_overflow_ids, |
| overflow_ids_ptr->GetIssuedOverflowIds()); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // No mapping from merge block %27 to fresh ids is given, so overflow ids are |
| // needed. |
| auto transformation2 = |
| TransformationMergeFunctionReturns(24, 110, 210, 111, 0, 0, {{}}); |
| |
| #ifndef NDEBUG |
| ASSERT_DEATH( |
| transformation2.IsApplicable(context.get(), transformation_context), |
| "Bad attempt to query whether overflow ids are available."); |
| #endif |
| |
| ASSERT_TRUE(transformation2.IsApplicable( |
| context.get(), transformation_context_with_overflow_ids)); |
| ApplyAndCheckFreshIds(transformation2, context.get(), |
| &transformation_context_with_overflow_ids, {1002}); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| std::string after_transformations = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeInt 32 1 |
| %6 = OpTypeFunction %5 |
| %7 = OpTypeBool |
| %8 = OpConstantTrue %7 |
| %9 = OpConstantFalse %7 |
| %10 = OpConstant %5 1 |
| %2 = OpFunction %3 None %4 |
| %11 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %5 None %6 |
| %13 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %14 |
| %14 = OpLabel |
| %15 = OpIAdd %5 %10 %10 |
| OpLoopMerge %16 %17 None |
| OpBranch %18 |
| %18 = OpLabel |
| OpBranchConditional %8 %19 %16 |
| %19 = OpLabel |
| OpSelectionMerge %20 None |
| OpBranchConditional %9 %21 %20 |
| %21 = OpLabel |
| OpBranch %16 |
| %20 = OpLabel |
| OpBranch %17 |
| %17 = OpLabel |
| OpBranchConditional %8 %14 %16 |
| %16 = OpLabel |
| %1000 = OpPhi %7 %8 %21 %9 %17 %9 %18 |
| %1001 = OpPhi %5 %10 %21 %10 %17 %10 %18 |
| %22 = OpPhi %5 %15 %17 %10 %18 %10 %21 |
| OpBranchConditional %1000 %101 %23 |
| %23 = OpLabel |
| OpBranch %101 |
| %101 = OpLabel |
| %102 = OpPhi %5 %1001 %16 %22 %23 |
| OpReturnValue %102 |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| %24 = OpFunction %3 None %4 |
| %25 = OpLabel |
| OpBranch %110 |
| %110 = OpLabel |
| OpLoopMerge %111 %210 None |
| OpBranch %26 |
| %26 = OpLabel |
| OpLoopMerge %27 %28 None |
| OpBranch %29 |
| %29 = OpLabel |
| OpBranchConditional %8 %30 %27 |
| %30 = OpLabel |
| OpSelectionMerge %31 None |
| OpBranchConditional %9 %32 %31 |
| %32 = OpLabel |
| OpBranch %27 |
| %31 = OpLabel |
| OpBranch %28 |
| %28 = OpLabel |
| OpBranch %26 |
| %27 = OpLabel |
| %1002 = OpPhi %7 %8 %32 %9 %29 |
| %33 = OpPhi %5 %10 %29 %10 %32 |
| OpBranchConditional %1002 %111 %34 |
| %34 = OpLabel |
| OpBranch %111 |
| %111 = OpLabel |
| OpReturn |
| %210 = OpLabel |
| OpBranch %110 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformations, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, MissingIdsForOpPhi) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %8 = OpTypeInt 32 1 |
| %9 = OpTypeFunction %3 %8 |
| %10 = OpTypeFloat 32 |
| %2 = OpFunction %3 None %4 |
| %11 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %3 None %9 |
| %13 = OpFunctionParameter %8 |
| %14 = OpLabel |
| %15 = OpConvertSToF %10 %13 |
| OpBranch %16 |
| %16 = OpLabel |
| OpLoopMerge %17 %18 None |
| OpBranch %19 |
| %19 = OpLabel |
| OpBranchConditional %6 %20 %17 |
| %20 = OpLabel |
| OpSelectionMerge %21 None |
| OpBranchConditional %7 %22 %21 |
| %22 = OpLabel |
| OpReturn |
| %21 = OpLabel |
| OpBranch %18 |
| %18 = OpLabel |
| OpBranch %16 |
| %17 = OpLabel |
| %23 = OpPhi %8 %13 %19 |
| %24 = OpPhi %10 %15 %19 |
| %25 = OpPhi %5 %6 %19 |
| OpBranch %26 |
| %26 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // This test checks whether the transformation is able to find suitable ids |
| // to use in existing OpPhi instructions if they are not provided in the |
| // corresponding mapping. |
| |
| auto transformation = TransformationMergeFunctionReturns( |
| 12, 101, 200, 102, 0, 0, |
| {{MakeReturnMergingInfo(17, 103, 0, {{{25, 7}, {35, 8}}})}}); |
| 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 %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %8 = OpTypeInt 32 1 |
| %9 = OpTypeFunction %3 %8 |
| %10 = OpTypeFloat 32 |
| %2 = OpFunction %3 None %4 |
| %11 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %12 = OpFunction %3 None %9 |
| %13 = OpFunctionParameter %8 |
| %14 = OpLabel |
| %15 = OpConvertSToF %10 %13 |
| OpBranch %101 |
| %101 = OpLabel |
| OpLoopMerge %102 %200 None |
| OpBranch %16 |
| %16 = OpLabel |
| OpLoopMerge %17 %18 None |
| OpBranch %19 |
| %19 = OpLabel |
| OpBranchConditional %6 %20 %17 |
| %20 = OpLabel |
| OpSelectionMerge %21 None |
| OpBranchConditional %7 %22 %21 |
| %22 = OpLabel |
| OpBranch %17 |
| %21 = OpLabel |
| OpBranch %18 |
| %18 = OpLabel |
| OpBranch %16 |
| %17 = OpLabel |
| %103 = OpPhi %5 %6 %22 %7 %19 |
| %23 = OpPhi %8 %13 %19 %13 %22 |
| %24 = OpPhi %10 %15 %19 %15 %22 |
| %25 = OpPhi %5 %6 %19 %7 %22 |
| OpBranchConditional %103 %102 %26 |
| %26 = OpLabel |
| OpBranch %102 |
| %102 = OpLabel |
| OpReturn |
| %200 = OpLabel |
| OpBranch %101 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules1) { |
| // An id defined in a loop is used in the corresponding merge block. After the |
| // transformation, the id will not dominate the merge block anymore. This is |
| // only OK if the use is inside an OpPhi instruction. (Note that there is also |
| // another condition for this transformation that forbids non-OpPhi |
| // instructions in relevant merge blocks, but that case is also considered |
| // here for completeness). |
| |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| OpSelectionMerge %13 None |
| OpBranchConditional %7 %13 %14 |
| %14 = OpLabel |
| OpReturn |
| %13 = OpLabel |
| %15 = OpCopyObject %5 %7 |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = OpLabel |
| %16 = OpCopyObject %5 %15 |
| OpBranch %17 |
| %17 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %18 = OpFunction %3 None %4 |
| %19 = OpLabel |
| OpBranch %20 |
| %20 = OpLabel |
| OpLoopMerge %21 %22 None |
| OpBranch %23 |
| %23 = OpLabel |
| OpSelectionMerge %24 None |
| OpBranchConditional %7 %24 %25 |
| %25 = OpLabel |
| OpReturn |
| %24 = OpLabel |
| %26 = OpCopyObject %5 %7 |
| OpBranch %22 |
| %22 = OpLabel |
| OpBranchConditional %7 %20 %21 |
| %21 = OpLabel |
| %27 = OpPhi %5 %26 %22 |
| OpBranch %28 |
| %28 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // In function %2, the definition of id %15 will not dominate its use in |
| // instruction %16 (inside merge block %10) after a new branch from return |
| // block %14 is added. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // In function %18, The definition of id %26 will still dominate its use in |
| // instruction %27 (inside merge block %21), because %27 is an OpPhi |
| // instruction. |
| auto transformation = TransformationMergeFunctionReturns( |
| 18, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(21, 102, 103, {{}})}}); |
| 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 %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| OpSelectionMerge %13 None |
| OpBranchConditional %7 %13 %14 |
| %14 = OpLabel |
| OpReturn |
| %13 = OpLabel |
| %15 = OpCopyObject %5 %7 |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = OpLabel |
| %16 = OpCopyObject %5 %15 |
| OpBranch %17 |
| %17 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| %18 = OpFunction %3 None %4 |
| %19 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %20 |
| %20 = OpLabel |
| OpLoopMerge %21 %22 None |
| OpBranch %23 |
| %23 = OpLabel |
| OpSelectionMerge %24 None |
| OpBranchConditional %7 %24 %25 |
| %25 = OpLabel |
| OpBranch %21 |
| %24 = OpLabel |
| %26 = OpCopyObject %5 %7 |
| OpBranch %22 |
| %22 = OpLabel |
| OpBranchConditional %7 %20 %21 |
| %21 = OpLabel |
| %102 = OpPhi %5 %6 %25 %7 %22 |
| %27 = OpPhi %5 %26 %22 %6 %25 |
| OpBranchConditional %102 %101 %28 |
| %28 = OpLabel |
| OpBranch %101 |
| %101 = OpLabel |
| OpReturn |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules2) { |
| // An id defined in a loop is used after the corresponding merge block. After |
| // the transformation, the id will not dominate its use anymore, regardless of |
| // the kind of instruction in which it is used. |
| |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| OpSelectionMerge %13 None |
| OpBranchConditional %7 %13 %14 |
| %14 = OpLabel |
| OpReturn |
| %13 = OpLabel |
| %15 = OpCopyObject %5 %7 |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = OpLabel |
| OpBranch %16 |
| %16 = OpLabel |
| %17 = OpCopyObject %5 %15 |
| OpReturn |
| OpFunctionEnd |
| %18 = OpFunction %3 None %4 |
| %19 = OpLabel |
| OpBranch %20 |
| %20 = OpLabel |
| OpLoopMerge %21 %22 None |
| OpBranch %23 |
| %23 = OpLabel |
| OpSelectionMerge %24 None |
| OpBranchConditional %7 %24 %25 |
| %25 = OpLabel |
| OpReturn |
| %24 = OpLabel |
| %26 = OpCopyObject %5 %7 |
| OpBranch %22 |
| %22 = OpLabel |
| OpBranchConditional %7 %20 %21 |
| %21 = OpLabel |
| OpBranch %27 |
| %27 = OpLabel |
| %28 = OpPhi %5 %26 %21 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // In function %2, the definition of id %15 will not dominate its use in |
| // instruction %17 (inside block %16) after a new branch from return |
| // block %14 to merge block %10 is added. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| // In function %18, the definition of id %26 will not dominate its use in |
| // instruction %28 (inside block %27) after a new branch from return |
| // block %25 to merge block %21 is added. |
| ASSERT_FALSE( |
| TransformationMergeFunctionReturns( |
| 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 0, {{}}), |
| MakeReturnMergingInfo(21, 103, 0, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules3) { |
| // An id defined in a loop is used inside the loop. |
| // Changes to the predecessors of the merge block do not affect the validity |
| // of the uses of such id. |
| |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| OpSelectionMerge %13 None |
| OpBranchConditional %7 %13 %14 |
| %14 = OpLabel |
| OpReturn |
| %13 = OpLabel |
| %15 = OpCopyObject %5 %7 |
| OpSelectionMerge %16 None |
| OpBranchConditional %7 %16 %17 |
| %17 = OpLabel |
| %18 = OpPhi %5 %15 %13 |
| %19 = OpCopyObject %5 %15 |
| OpBranch %16 |
| %16 = OpLabel |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = 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, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // In function %2, the definition of id %15 will still dominate its use in |
| // instructions %18 and %19 after the transformation is applied, because the |
| // fact that the id definition dominates the uses does not depend on it |
| // dominating the merge block. |
| auto transformation = TransformationMergeFunctionReturns( |
| 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); |
| 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 %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| OpSelectionMerge %13 None |
| OpBranchConditional %7 %13 %14 |
| %14 = OpLabel |
| OpBranch %10 |
| %13 = OpLabel |
| %15 = OpCopyObject %5 %7 |
| OpSelectionMerge %16 None |
| OpBranchConditional %7 %16 %17 |
| %17 = OpLabel |
| %18 = OpPhi %5 %15 %13 |
| %19 = OpCopyObject %5 %15 |
| OpBranch %16 |
| %16 = OpLabel |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = OpLabel |
| %102 = OpPhi %5 %6 %14 %7 %11 |
| OpBranchConditional %102 %101 %20 |
| %20 = OpLabel |
| OpBranch %101 |
| %101 = OpLabel |
| OpReturn |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, RespectDominanceRules4) { |
| // An id defined in a loop, which contain 2 return statements, is used after |
| // the loop. We can only apply the transformation if the id dominates all of |
| // the return blocks. |
| |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| %13 = OpCopyObject %5 %7 |
| OpSelectionMerge %14 None |
| OpBranchConditional %7 %14 %15 |
| %15 = OpLabel |
| OpReturn |
| %14 = OpLabel |
| OpSelectionMerge %16 None |
| OpBranchConditional %7 %16 %17 |
| %17 = OpLabel |
| OpReturn |
| %16 = OpLabel |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = OpLabel |
| OpBranch %18 |
| %18 = OpLabel |
| %19 = OpCopyObject %5 %13 |
| OpReturn |
| OpFunctionEnd |
| %20 = OpFunction %3 None %4 |
| %21 = OpLabel |
| OpBranch %22 |
| %22 = OpLabel |
| OpLoopMerge %23 %24 None |
| OpBranch %25 |
| %25 = OpLabel |
| OpSelectionMerge %26 None |
| OpBranchConditional %7 %26 %27 |
| %27 = OpLabel |
| OpReturn |
| %26 = OpLabel |
| %28 = OpCopyObject %5 %7 |
| OpSelectionMerge %29 None |
| OpBranchConditional %7 %29 %30 |
| %30 = OpLabel |
| OpReturn |
| %29 = OpLabel |
| OpBranch %24 |
| %24 = OpLabel |
| OpBranchConditional %7 %22 %23 |
| %23 = OpLabel |
| OpBranch %31 |
| %31 = OpLabel |
| %32 = OpCopyObject %5 %28 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| // In function %2, the definition of id %13 will still dominate its use in |
| // instruction %19 after the transformation is applied, because %13 dominates |
| // all of the return blocks. |
| auto transformation = TransformationMergeFunctionReturns( |
| 2, 100, 200, 101, 0, 0, {{MakeReturnMergingInfo(10, 102, 103, {{}})}}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // In function %20, the definition of id %28 will not dominate its use in |
| // instruction %32 after the transformation is applied, because %28 dominates |
| // only one of the return blocks. |
| ASSERT_FALSE(TransformationMergeFunctionReturns( |
| 20, 100, 200, 101, 0, 0, |
| {{MakeReturnMergingInfo(23, 102, 103, {{}})}}) |
| .IsApplicable(context.get(), transformation_context)); |
| |
| std::string after_transformation = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %9 |
| %9 = OpLabel |
| OpLoopMerge %10 %11 None |
| OpBranch %12 |
| %12 = OpLabel |
| %13 = OpCopyObject %5 %7 |
| OpSelectionMerge %14 None |
| OpBranchConditional %7 %14 %15 |
| %15 = OpLabel |
| OpBranch %10 |
| %14 = OpLabel |
| OpSelectionMerge %16 None |
| OpBranchConditional %7 %16 %17 |
| %17 = OpLabel |
| OpBranch %10 |
| %16 = OpLabel |
| OpBranch %11 |
| %11 = OpLabel |
| OpBranchConditional %7 %9 %10 |
| %10 = OpLabel |
| %102 = OpPhi %5 %6 %15 %6 %17 %7 %11 |
| OpBranchConditional %102 %101 %18 |
| %18 = OpLabel |
| %19 = OpCopyObject %5 %13 |
| OpBranch %101 |
| %101 = OpLabel |
| OpReturn |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| %20 = OpFunction %3 None %4 |
| %21 = OpLabel |
| OpBranch %22 |
| %22 = OpLabel |
| OpLoopMerge %23 %24 None |
| OpBranch %25 |
| %25 = OpLabel |
| OpSelectionMerge %26 None |
| OpBranchConditional %7 %26 %27 |
| %27 = OpLabel |
| OpReturn |
| %26 = OpLabel |
| %28 = OpCopyObject %5 %7 |
| OpSelectionMerge %29 None |
| OpBranchConditional %7 %29 %30 |
| %30 = OpLabel |
| OpReturn |
| %29 = OpLabel |
| OpBranch %24 |
| %24 = OpLabel |
| OpBranchConditional %7 %22 %23 |
| %23 = OpLabel |
| OpBranch %31 |
| %31 = OpLabel |
| %32 = OpCopyObject %5 %28 |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| TEST(TransformationMergeFunctionReturnsTest, OpPhiAfterFirstBlock) { |
| std::string shader = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %9 |
| %9 = OpLabel |
| %10 = OpPhi %5 %6 %8 |
| OpSelectionMerge %11 None |
| OpBranchConditional %6 %12 %11 |
| %12 = OpLabel |
| OpReturn |
| %11 = OpLabel |
| OpReturn |
| OpFunctionEnd |
| )"; |
| |
| const auto env = SPV_ENV_UNIVERSAL_1_5; |
| const auto consumer = nullptr; |
| const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); |
| spvtools::ValidatorOptions validator_options; |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| TransformationContext transformation_context( |
| MakeUnique<FactManager>(context.get()), validator_options); |
| |
| auto transformation = |
| TransformationMergeFunctionReturns(2, 100, 200, 101, 0, 0, {}); |
| ASSERT_TRUE( |
| transformation.IsApplicable(context.get(), transformation_context)); |
| ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); |
| ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, |
| kConsoleMessageConsumer)); |
| |
| // Ensure that all input operands of OpBranchConditional instructions have |
| // the right operand type. |
| context->module()->ForEachInst([](opt::Instruction* inst) { |
| if (inst->opcode() == spv::Op::OpBranchConditional) { |
| ASSERT_EQ(inst->GetInOperand(0).type, SPV_OPERAND_TYPE_ID); |
| ASSERT_EQ(inst->GetInOperand(1).type, SPV_OPERAND_TYPE_ID); |
| ASSERT_EQ(inst->GetInOperand(2).type, SPV_OPERAND_TYPE_ID); |
| } |
| }); |
| |
| std::string after_transformation = R"( |
| OpCapability Shader |
| %1 = OpExtInstImport "GLSL.std.450" |
| OpMemoryModel Logical GLSL450 |
| OpEntryPoint Fragment %2 "main" |
| OpExecutionMode %2 OriginUpperLeft |
| OpSource ESSL 310 |
| %3 = OpTypeVoid |
| %4 = OpTypeFunction %3 |
| %5 = OpTypeBool |
| %6 = OpConstantTrue %5 |
| %7 = OpConstantFalse %5 |
| %2 = OpFunction %3 None %4 |
| %8 = OpLabel |
| OpBranch %100 |
| %100 = OpLabel |
| OpLoopMerge %101 %200 None |
| OpBranch %9 |
| %9 = OpLabel |
| %10 = OpPhi %5 %6 %100 |
| OpSelectionMerge %11 None |
| OpBranchConditional %6 %12 %11 |
| %12 = OpLabel |
| OpBranch %101 |
| %11 = OpLabel |
| OpBranch %101 |
| %101 = OpLabel |
| OpReturn |
| %200 = OpLabel |
| OpBranch %100 |
| OpFunctionEnd |
| )"; |
| |
| ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); |
| } |
| |
| } // namespace |
| } // namespace fuzz |
| } // namespace spvtools |