// Copyright (c) 2019 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_replace_constant_with_uniform.h" #include "gtest/gtest.h" #include "source/fuzz/fuzzer_util.h" #include "source/fuzz/instruction_descriptor.h" #include "source/fuzz/uniform_buffer_element_descriptor.h" #include "test/fuzz/fuzz_test_util.h" namespace spvtools { namespace fuzz { namespace { bool AddFactHelper( TransformationContext* transformation_context, uint32_t word, const protobufs::UniformBufferElementDescriptor& descriptor) { protobufs::FactConstantUniform constant_uniform_fact; constant_uniform_fact.add_constant_word(word); *constant_uniform_fact.mutable_uniform_buffer_element_descriptor() = descriptor; protobufs::Fact fact; *fact.mutable_constant_uniform_fact() = constant_uniform_fact; return transformation_context->GetFactManager()->MaybeAddFact(fact); } TEST(TransformationReplaceConstantWithUniformTest, BasicReplacements) { // This test came from the following GLSL: // // #version 450 // // uniform blockname { // int a; // int b; // int c; // }; // // void main() // { // int x; // x = 1; // x = x + 2; // x = 3 + x; // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "x" OpName %16 "blockname" OpMemberName %16 0 "a" OpMemberName %16 1 "b" OpMemberName %16 2 "c" OpName %18 "" OpMemberDecorate %16 0 Offset 0 OpMemberDecorate %16 1 Offset 4 OpMemberDecorate %16 2 Offset 8 OpDecorate %16 Block OpDecorate %18 DescriptorSet 0 OpDecorate %18 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %50 = OpConstant %6 0 %9 = OpConstant %6 1 %11 = OpConstant %6 2 %14 = OpConstant %6 3 %16 = OpTypeStruct %6 %6 %6 %17 = OpTypePointer Uniform %16 %51 = OpTypePointer Uniform %6 %18 = OpVariable %17 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function OpStore %8 %9 %10 = OpLoad %6 %8 %12 = OpIAdd %6 %10 %11 OpStore %8 %12 %13 = OpLoad %6 %8 %15 = OpIAdd %6 %14 %13 OpStore %8 %15 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_a = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_b = MakeUniformBufferElementDescriptor(0, 0, {1}); protobufs::UniformBufferElementDescriptor blockname_c = MakeUniformBufferElementDescriptor(0, 0, {2}); ASSERT_TRUE(AddFactHelper(&transformation_context, 1, blockname_a)); ASSERT_TRUE(AddFactHelper(&transformation_context, 2, blockname_b)); ASSERT_TRUE(AddFactHelper(&transformation_context, 3, blockname_c)); // The constant ids are 9, 11 and 14, for 1, 2 and 3 respectively. protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor( 9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1); protobufs::IdUseDescriptor use_of_11_in_add = MakeIdUseDescriptor( 11, MakeInstructionDescriptor(12, spv::Op::OpIAdd, 0), 1); protobufs::IdUseDescriptor use_of_14_in_add = MakeIdUseDescriptor( 14, MakeInstructionDescriptor(15, spv::Op::OpIAdd, 0), 0); // These transformations work: they match the facts. auto transformation_use_of_9_in_store = TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_a, 100, 101); ASSERT_TRUE(transformation_use_of_9_in_store.IsApplicable( context.get(), transformation_context)); auto transformation_use_of_11_in_add = TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, 102, 103); ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable( context.get(), transformation_context)); auto transformation_use_of_14_in_add = TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_c, 104, 105); ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable( context.get(), transformation_context)); // The transformations are not applicable if we change which uniforms are // applied to which constants. ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_b, 101, 102) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_c, 101, 102) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_14_in_add, blockname_a, 101, 102) .IsApplicable(context.get(), transformation_context)); // The following transformations do not apply because the uniform descriptors // are not sensible. protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor1 = MakeUniformBufferElementDescriptor(1, 2, {0}); protobufs::UniformBufferElementDescriptor nonsense_uniform_descriptor2 = MakeUniformBufferElementDescriptor(0, 0, {5}); ASSERT_FALSE(TransformationReplaceConstantWithUniform( use_of_9_in_store, nonsense_uniform_descriptor1, 101, 102) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform( use_of_9_in_store, nonsense_uniform_descriptor2, 101, 102) .IsApplicable(context.get(), transformation_context)); // The following transformation does not apply because the id descriptor is // not sensible. protobufs::IdUseDescriptor nonsense_id_use_descriptor = MakeIdUseDescriptor( 9, MakeInstructionDescriptor(15, spv::Op::OpIAdd, 0), 0); ASSERT_FALSE(TransformationReplaceConstantWithUniform( nonsense_id_use_descriptor, blockname_a, 101, 102) .IsApplicable(context.get(), transformation_context)); // The following transformations do not apply because the ids are not fresh. ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, 15, 103) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_add, blockname_b, 102, 15) .IsApplicable(context.get(), transformation_context)); // Apply the use of 9 in a store. ApplyAndCheckFreshIds(transformation_use_of_9_in_store, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); std::string after_replacing_use_of_9_in_store = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "x" OpName %16 "blockname" OpMemberName %16 0 "a" OpMemberName %16 1 "b" OpMemberName %16 2 "c" OpName %18 "" OpMemberDecorate %16 0 Offset 0 OpMemberDecorate %16 1 Offset 4 OpMemberDecorate %16 2 Offset 8 OpDecorate %16 Block OpDecorate %18 DescriptorSet 0 OpDecorate %18 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %50 = OpConstant %6 0 %9 = OpConstant %6 1 %11 = OpConstant %6 2 %14 = OpConstant %6 3 %16 = OpTypeStruct %6 %6 %6 %17 = OpTypePointer Uniform %16 %51 = OpTypePointer Uniform %6 %18 = OpVariable %17 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function %100 = OpAccessChain %51 %18 %50 %101 = OpLoad %6 %100 OpStore %8 %101 %10 = OpLoad %6 %8 %12 = OpIAdd %6 %10 %11 OpStore %8 %12 %13 = OpLoad %6 %8 %15 = OpIAdd %6 %14 %13 OpStore %8 %15 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_replacing_use_of_9_in_store, context.get())); ASSERT_TRUE(transformation_use_of_11_in_add.IsApplicable( context.get(), transformation_context)); // Apply the use of 11 in an add. ApplyAndCheckFreshIds(transformation_use_of_11_in_add, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); std::string after_replacing_use_of_11_in_add = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "x" OpName %16 "blockname" OpMemberName %16 0 "a" OpMemberName %16 1 "b" OpMemberName %16 2 "c" OpName %18 "" OpMemberDecorate %16 0 Offset 0 OpMemberDecorate %16 1 Offset 4 OpMemberDecorate %16 2 Offset 8 OpDecorate %16 Block OpDecorate %18 DescriptorSet 0 OpDecorate %18 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %50 = OpConstant %6 0 %9 = OpConstant %6 1 %11 = OpConstant %6 2 %14 = OpConstant %6 3 %16 = OpTypeStruct %6 %6 %6 %17 = OpTypePointer Uniform %16 %51 = OpTypePointer Uniform %6 %18 = OpVariable %17 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function %100 = OpAccessChain %51 %18 %50 %101 = OpLoad %6 %100 OpStore %8 %101 %10 = OpLoad %6 %8 %102 = OpAccessChain %51 %18 %9 %103 = OpLoad %6 %102 %12 = OpIAdd %6 %10 %103 OpStore %8 %12 %13 = OpLoad %6 %8 %15 = OpIAdd %6 %14 %13 OpStore %8 %15 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_replacing_use_of_11_in_add, context.get())); ASSERT_TRUE(transformation_use_of_14_in_add.IsApplicable( context.get(), transformation_context)); // Apply the use of 15 in an add. ApplyAndCheckFreshIds(transformation_use_of_14_in_add, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); std::string after_replacing_use_of_14_in_add = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "x" OpName %16 "blockname" OpMemberName %16 0 "a" OpMemberName %16 1 "b" OpMemberName %16 2 "c" OpName %18 "" OpMemberDecorate %16 0 Offset 0 OpMemberDecorate %16 1 Offset 4 OpMemberDecorate %16 2 Offset 8 OpDecorate %16 Block OpDecorate %18 DescriptorSet 0 OpDecorate %18 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %50 = OpConstant %6 0 %9 = OpConstant %6 1 %11 = OpConstant %6 2 %14 = OpConstant %6 3 %16 = OpTypeStruct %6 %6 %6 %17 = OpTypePointer Uniform %16 %51 = OpTypePointer Uniform %6 %18 = OpVariable %17 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function %100 = OpAccessChain %51 %18 %50 %101 = OpLoad %6 %100 OpStore %8 %101 %10 = OpLoad %6 %8 %102 = OpAccessChain %51 %18 %9 %103 = OpLoad %6 %102 %12 = OpIAdd %6 %10 %103 OpStore %8 %12 %13 = OpLoad %6 %8 %104 = OpAccessChain %51 %18 %11 %105 = OpLoad %6 %104 %15 = OpIAdd %6 %105 %13 OpStore %8 %15 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_replacing_use_of_14_in_add, context.get())); } TEST(TransformationReplaceConstantWithUniformTest, NestedStruct) { // This test came from the following GLSL: // // #version 450 // // struct U { // int x; // == 4 // }; // // struct T { // int x; // == 3 // U y; // }; // // struct S { // T x; // int y; // == 2 // }; // // uniform blockname { // int x; // == 1 // S y; // }; // // void foo(int a) { } // // void main() // { // int x; // x = 1; // x = x + 2; // x = 3 + x; // foo(4); // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %10 "foo(i1;" OpName %9 "a" OpName %12 "x" OpName %21 "param" OpName %23 "U" OpMemberName %23 0 "x" OpName %24 "T" OpMemberName %24 0 "x" OpMemberName %24 1 "y" OpName %25 "S" OpMemberName %25 0 "x" OpMemberName %25 1 "y" OpName %26 "blockname" OpMemberName %26 0 "x" OpMemberName %26 1 "y" OpName %28 "" OpMemberDecorate %23 0 Offset 0 OpMemberDecorate %24 0 Offset 0 OpMemberDecorate %24 1 Offset 16 OpMemberDecorate %25 0 Offset 0 OpMemberDecorate %25 1 Offset 32 OpMemberDecorate %26 0 Offset 0 OpMemberDecorate %26 1 Offset 16 OpDecorate %26 Block OpDecorate %28 DescriptorSet 0 OpDecorate %28 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %8 = OpTypeFunction %2 %7 %50 = OpConstant %6 0 %13 = OpConstant %6 1 %15 = OpConstant %6 2 %17 = OpConstant %6 3 %20 = OpConstant %6 4 %23 = OpTypeStruct %6 %24 = OpTypeStruct %6 %23 %25 = OpTypeStruct %24 %6 %26 = OpTypeStruct %6 %25 %27 = OpTypePointer Uniform %26 %51 = OpTypePointer Uniform %6 %28 = OpVariable %27 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %12 = OpVariable %7 Function %21 = OpVariable %7 Function OpStore %12 %13 %14 = OpLoad %6 %12 %16 = OpIAdd %6 %14 %15 OpStore %12 %16 %18 = OpLoad %6 %12 %19 = OpIAdd %6 %17 %18 OpStore %12 %19 OpStore %21 %20 %22 = OpFunctionCall %2 %10 %21 OpReturn OpFunctionEnd %10 = OpFunction %2 None %8 %9 = OpFunctionParameter %7 %11 = 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(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_1 = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_2 = MakeUniformBufferElementDescriptor(0, 0, {1, 1}); protobufs::UniformBufferElementDescriptor blockname_3 = MakeUniformBufferElementDescriptor(0, 0, {1, 0, 0}); protobufs::UniformBufferElementDescriptor blockname_4 = MakeUniformBufferElementDescriptor(0, 0, {1, 0, 1, 0}); ASSERT_TRUE(AddFactHelper(&transformation_context, 1, blockname_1)); ASSERT_TRUE(AddFactHelper(&transformation_context, 2, blockname_2)); ASSERT_TRUE(AddFactHelper(&transformation_context, 3, blockname_3)); ASSERT_TRUE(AddFactHelper(&transformation_context, 4, blockname_4)); // The constant ids are 13, 15, 17 and 20, for 1, 2, 3 and 4 respectively. protobufs::IdUseDescriptor use_of_13_in_store = MakeIdUseDescriptor( 13, MakeInstructionDescriptor(21, spv::Op::OpStore, 0), 1); protobufs::IdUseDescriptor use_of_15_in_add = MakeIdUseDescriptor( 15, MakeInstructionDescriptor(16, spv::Op::OpIAdd, 0), 1); protobufs::IdUseDescriptor use_of_17_in_add = MakeIdUseDescriptor( 17, MakeInstructionDescriptor(19, spv::Op::OpIAdd, 0), 0); protobufs::IdUseDescriptor use_of_20_in_store = MakeIdUseDescriptor( 20, MakeInstructionDescriptor(19, spv::Op::OpStore, 1), 1); // These transformations work: they match the facts. auto transformation_use_of_13_in_store = TransformationReplaceConstantWithUniform(use_of_13_in_store, blockname_1, 100, 101); ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable( context.get(), transformation_context)); auto transformation_use_of_15_in_add = TransformationReplaceConstantWithUniform(use_of_15_in_add, blockname_2, 102, 103); ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( context.get(), transformation_context)); auto transformation_use_of_17_in_add = TransformationReplaceConstantWithUniform(use_of_17_in_add, blockname_3, 104, 105); ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( context.get(), transformation_context)); auto transformation_use_of_20_in_store = TransformationReplaceConstantWithUniform(use_of_20_in_store, blockname_4, 106, 107); ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_13_in_store.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation_use_of_13_in_store, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_15_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation_use_of_15_in_add, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( context.get(), transformation_context)); ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_17_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation_use_of_17_in_add, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( context.get(), transformation_context)); ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_TRUE(transformation_use_of_20_in_store.IsApplicable( context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation_use_of_20_in_store, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); ASSERT_FALSE(transformation_use_of_13_in_store.IsApplicable( context.get(), transformation_context)); ASSERT_FALSE(transformation_use_of_15_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_FALSE(transformation_use_of_17_in_add.IsApplicable( context.get(), transformation_context)); ASSERT_FALSE(transformation_use_of_20_in_store.IsApplicable( context.get(), transformation_context)); std::string after = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %10 "foo(i1;" OpName %9 "a" OpName %12 "x" OpName %21 "param" OpName %23 "U" OpMemberName %23 0 "x" OpName %24 "T" OpMemberName %24 0 "x" OpMemberName %24 1 "y" OpName %25 "S" OpMemberName %25 0 "x" OpMemberName %25 1 "y" OpName %26 "blockname" OpMemberName %26 0 "x" OpMemberName %26 1 "y" OpName %28 "" OpMemberDecorate %23 0 Offset 0 OpMemberDecorate %24 0 Offset 0 OpMemberDecorate %24 1 Offset 16 OpMemberDecorate %25 0 Offset 0 OpMemberDecorate %25 1 Offset 32 OpMemberDecorate %26 0 Offset 0 OpMemberDecorate %26 1 Offset 16 OpDecorate %26 Block OpDecorate %28 DescriptorSet 0 OpDecorate %28 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %8 = OpTypeFunction %2 %7 %50 = OpConstant %6 0 %13 = OpConstant %6 1 %15 = OpConstant %6 2 %17 = OpConstant %6 3 %20 = OpConstant %6 4 %23 = OpTypeStruct %6 %24 = OpTypeStruct %6 %23 %25 = OpTypeStruct %24 %6 %26 = OpTypeStruct %6 %25 %27 = OpTypePointer Uniform %26 %51 = OpTypePointer Uniform %6 %28 = OpVariable %27 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %12 = OpVariable %7 Function %21 = OpVariable %7 Function %100 = OpAccessChain %51 %28 %50 %101 = OpLoad %6 %100 OpStore %12 %101 %14 = OpLoad %6 %12 %102 = OpAccessChain %51 %28 %13 %13 %103 = OpLoad %6 %102 %16 = OpIAdd %6 %14 %103 OpStore %12 %16 %18 = OpLoad %6 %12 %104 = OpAccessChain %51 %28 %13 %50 %50 %105 = OpLoad %6 %104 %19 = OpIAdd %6 %105 %18 OpStore %12 %19 %106 = OpAccessChain %51 %28 %13 %50 %13 %50 %107 = OpLoad %6 %106 OpStore %21 %107 %22 = OpFunctionCall %2 %10 %21 OpReturn OpFunctionEnd %10 = OpFunction %2 None %8 %9 = OpFunctionParameter %7 %11 = OpLabel OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after, context.get())); } TEST(TransformationReplaceConstantWithUniformTest, NoUniformIntPointerPresent) { // This test came from the following GLSL: // // #version 450 // // uniform blockname { // int x; // == 0 // }; // // void main() // { // int a; // a = 0; // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "a" OpName %10 "blockname" OpMemberName %10 0 "x" OpName %12 "" OpMemberDecorate %10 0 Offset 0 OpDecorate %10 Block OpDecorate %12 DescriptorSet 0 OpDecorate %12 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %9 = OpConstant %6 0 %10 = OpTypeStruct %6 %11 = OpTypePointer Uniform %10 %12 = OpVariable %11 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function OpStore %8 %9 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_0 = MakeUniformBufferElementDescriptor(0, 0, {0}); ASSERT_TRUE(AddFactHelper(&transformation_context, 0, blockname_0)); // The constant id is 9 for 0. protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor( 9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1); // This transformation is not available because no uniform pointer to integer // type is present: ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_0, 100, 101) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, NoConstantPresentForIndex) { // This test came from the following GLSL: // // #version 450 // // uniform blockname { // int x; // == 0 // int y; // == 9 // }; // // void main() // { // int a; // a = 9; // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "a" OpName %10 "blockname" OpMemberName %10 0 "x" OpMemberName %10 1 "y" OpName %12 "" OpMemberDecorate %10 0 Offset 0 OpMemberDecorate %10 1 Offset 4 OpDecorate %10 Block OpDecorate %12 DescriptorSet 0 OpDecorate %12 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %9 = OpConstant %6 9 %10 = OpTypeStruct %6 %6 %11 = OpTypePointer Uniform %10 %50 = OpTypePointer Uniform %6 %12 = OpVariable %11 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function OpStore %8 %9 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_0 = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_9 = MakeUniformBufferElementDescriptor(0, 0, {1}); ASSERT_TRUE(AddFactHelper(&transformation_context, 9, blockname_9)); // The constant id is 9 for 9. protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor( 9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1); // This transformation is not available because no constant is present for the // index 1 required to index into the uniform buffer: ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_9, 100, 101) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, NoIntTypePresentToEnableIndexing) { // This test came from the following GLSL: // // #version 450 // // uniform blockname { // float f; // == 9 // }; // // void main() // { // float a; // a = 3.0; // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "a" OpName %10 "blockname" OpMemberName %10 0 "f" OpName %12 "" OpMemberDecorate %10 0 Offset 0 OpDecorate %10 Block OpDecorate %12 DescriptorSet 0 OpDecorate %12 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypePointer Function %6 %9 = OpConstant %6 3 %10 = OpTypeStruct %6 %11 = OpTypePointer Uniform %10 %12 = OpVariable %11 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function OpStore %8 %9 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_3 = MakeUniformBufferElementDescriptor(0, 0, {0}); uint32_t float_data[1]; float temp = 3.0; memcpy(&float_data[0], &temp, sizeof(float)); ASSERT_TRUE( AddFactHelper(&transformation_context, float_data[0], blockname_3)); // The constant id is 9 for 3.0. protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor( 9, MakeInstructionDescriptor(8, spv::Op::OpStore, 0), 1); // This transformation is not available because no integer type is present to // allow a constant index to be expressed: ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_3, 100, 101) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, UniformFactsDoNotMatchConstants) { // This test came from the following GLSL: // // #version 450 // // uniform blockname { // int x; // == 9 // int y; // == 10 // }; // // void main() // { // int a; // int b; // a = 9; // b = 10; // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %8 "a" OpName %10 "b" OpName %12 "blockname" OpMemberName %12 0 "x" OpMemberName %12 1 "y" OpName %14 "" OpMemberDecorate %12 0 Offset 0 OpMemberDecorate %12 1 Offset 4 OpDecorate %12 Block OpDecorate %14 DescriptorSet 0 OpDecorate %14 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %9 = OpConstant %6 9 %11 = OpConstant %6 10 %50 = OpConstant %6 0 %51 = OpConstant %6 1 %12 = OpTypeStruct %6 %6 %13 = OpTypePointer Uniform %12 %52 = OpTypePointer Uniform %6 %14 = OpVariable %13 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function %10 = OpVariable %7 Function OpStore %8 %9 OpStore %10 %11 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_9 = MakeUniformBufferElementDescriptor(0, 0, {0}); protobufs::UniformBufferElementDescriptor blockname_10 = MakeUniformBufferElementDescriptor(0, 0, {1}); ASSERT_TRUE(AddFactHelper(&transformation_context, 9, blockname_9)); ASSERT_TRUE(AddFactHelper(&transformation_context, 10, blockname_10)); // The constant ids for 9 and 10 are 9 and 11 respectively protobufs::IdUseDescriptor use_of_9_in_store = MakeIdUseDescriptor( 9, MakeInstructionDescriptor(10, spv::Op::OpStore, 0), 1); protobufs::IdUseDescriptor use_of_11_in_store = MakeIdUseDescriptor( 11, MakeInstructionDescriptor(10, spv::Op::OpStore, 1), 1); // These are right: ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_9, 100, 101) .IsApplicable(context.get(), transformation_context)); ASSERT_TRUE(TransformationReplaceConstantWithUniform(use_of_11_in_store, blockname_10, 102, 103) .IsApplicable(context.get(), transformation_context)); // These are wrong because the constants do not match the facts about // uniforms. ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_11_in_store, blockname_9, 100, 101) .IsApplicable(context.get(), transformation_context)); ASSERT_FALSE(TransformationReplaceConstantWithUniform(use_of_9_in_store, blockname_10, 102, 103) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, ComplexReplacements) { // The following GLSL was the basis for this test: // #version 450 // // struct T { // float a[5]; // [1.0, 1.5, 1.75, 1.875, 1.9375] // ivec4 b; // (1, 2, 3, 4) // vec3 c; // (2.0, 2.5, 2.75) // uint d; // 42u // bool e; // Not used in test // }; // // uniform block { // T f; // int g; // 22 // uvec2 h; // (100u, 200u) // }; // // void main() // { // T myT; // // myT.a[0] = 1.9375; // myT.a[1] = 1.875; // myT.a[2] = 1.75; // myT.a[3] = 1.5; // myT.a[4] = 1.0; // // myT.b.x = 4; // myT.b.y = 3; // myT.b.z = 2; // myT.b.w = 1; // // myT.b.r = 22; // // myT.c[0] = 2.75; // myT.c[0] = 2.5; // myT.c[0] = 2.0; // // myT.d = 42u; // myT.d = 100u; // myT.d = 200u; // // myT.e = true; // No attempt to replace 'true' by a uniform value // // } std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %14 "T" OpMemberName %14 0 "a" OpMemberName %14 1 "b" OpMemberName %14 2 "c" OpMemberName %14 3 "d" OpMemberName %14 4 "e" OpName %16 "myT" OpName %61 "T" OpMemberName %61 0 "a" OpMemberName %61 1 "b" OpMemberName %61 2 "c" OpMemberName %61 3 "d" OpMemberName %61 4 "e" OpName %63 "block" OpMemberName %63 0 "f" OpMemberName %63 1 "g" OpMemberName %63 2 "h" OpName %65 "" OpDecorate %60 ArrayStride 16 OpMemberDecorate %61 0 Offset 0 OpMemberDecorate %61 1 Offset 80 OpMemberDecorate %61 2 Offset 96 OpMemberDecorate %61 3 Offset 108 OpMemberDecorate %61 4 Offset 112 OpMemberDecorate %63 0 Offset 0 OpMemberDecorate %63 1 Offset 128 OpMemberDecorate %63 2 Offset 136 OpDecorate %63 Block OpDecorate %65 DescriptorSet 0 OpDecorate %65 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeInt 32 0 %8 = OpConstant %7 5 %9 = OpTypeArray %6 %8 %10 = OpTypeInt 32 1 %11 = OpTypeVector %10 4 %12 = OpTypeVector %6 3 %13 = OpTypeBool %14 = OpTypeStruct %9 %11 %12 %7 %13 %15 = OpTypePointer Function %14 %17 = OpConstant %10 0 %18 = OpConstant %6 1.9375 %19 = OpTypePointer Function %6 %21 = OpConstant %10 1 %22 = OpConstant %6 1.875 %24 = OpConstant %10 2 %25 = OpConstant %6 1.75 %27 = OpConstant %10 3 %28 = OpConstant %6 1.5 %30 = OpConstant %10 4 %31 = OpConstant %6 1 %33 = OpConstant %7 0 %34 = OpTypePointer Function %10 %36 = OpConstant %7 1 %38 = OpConstant %7 2 %40 = OpConstant %7 3 %42 = OpConstant %10 22 %44 = OpConstant %6 2.75 %46 = OpConstant %6 2.5 %48 = OpConstant %6 2 %50 = OpConstant %7 42 %51 = OpTypePointer Function %7 %53 = OpConstant %7 100 %55 = OpConstant %7 200 %57 = OpConstantTrue %13 %58 = OpTypePointer Function %13 %60 = OpTypeArray %6 %8 %61 = OpTypeStruct %60 %11 %12 %7 %7 %62 = OpTypeVector %7 2 %63 = OpTypeStruct %61 %10 %62 %64 = OpTypePointer Uniform %63 %100 = OpTypePointer Uniform %10 %101 = OpTypePointer Uniform %7 %102 = OpTypePointer Uniform %6 %103 = OpTypePointer Uniform %13 %65 = OpVariable %64 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %16 = OpVariable %15 Function %20 = OpAccessChain %19 %16 %17 %17 OpStore %20 %18 %23 = OpAccessChain %19 %16 %17 %21 OpStore %23 %22 %26 = OpAccessChain %19 %16 %17 %24 OpStore %26 %25 %29 = OpAccessChain %19 %16 %17 %27 OpStore %29 %28 %32 = OpAccessChain %19 %16 %17 %30 OpStore %32 %31 %35 = OpAccessChain %34 %16 %21 %33 OpStore %35 %30 %37 = OpAccessChain %34 %16 %21 %36 OpStore %37 %27 %39 = OpAccessChain %34 %16 %21 %38 OpStore %39 %24 %41 = OpAccessChain %34 %16 %21 %40 OpStore %41 %21 %43 = OpAccessChain %34 %16 %21 %33 OpStore %43 %42 %45 = OpAccessChain %19 %16 %24 %33 OpStore %45 %44 %47 = OpAccessChain %19 %16 %24 %33 OpStore %47 %46 %49 = OpAccessChain %19 %16 %24 %33 OpStore %49 %48 %52 = OpAccessChain %51 %16 %27 OpStore %52 %50 %54 = OpAccessChain %51 %16 %27 OpStore %54 %53 %56 = OpAccessChain %51 %16 %27 OpStore %56 %55 %59 = OpAccessChain %58 %16 %30 OpStore %59 %57 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); const float float_array_values[5] = {1.0, 1.5, 1.75, 1.875, 1.9375}; uint32_t float_array_data[5]; memcpy(&float_array_data, &float_array_values, sizeof(float_array_values)); const float float_vector_values[3] = {2.0, 2.5, 2.75}; uint32_t float_vector_data[3]; memcpy(&float_vector_data, &float_vector_values, sizeof(float_vector_values)); protobufs::UniformBufferElementDescriptor uniform_f_a_0 = MakeUniformBufferElementDescriptor(0, 0, {0, 0, 0}); protobufs::UniformBufferElementDescriptor uniform_f_a_1 = MakeUniformBufferElementDescriptor(0, 0, {0, 0, 1}); protobufs::UniformBufferElementDescriptor uniform_f_a_2 = MakeUniformBufferElementDescriptor(0, 0, {0, 0, 2}); protobufs::UniformBufferElementDescriptor uniform_f_a_3 = MakeUniformBufferElementDescriptor(0, 0, {0, 0, 3}); protobufs::UniformBufferElementDescriptor uniform_f_a_4 = MakeUniformBufferElementDescriptor(0, 0, {0, 0, 4}); protobufs::UniformBufferElementDescriptor uniform_f_b_x = MakeUniformBufferElementDescriptor(0, 0, {0, 1, 0}); protobufs::UniformBufferElementDescriptor uniform_f_b_y = MakeUniformBufferElementDescriptor(0, 0, {0, 1, 1}); protobufs::UniformBufferElementDescriptor uniform_f_b_z = MakeUniformBufferElementDescriptor(0, 0, {0, 1, 2}); protobufs::UniformBufferElementDescriptor uniform_f_b_w = MakeUniformBufferElementDescriptor(0, 0, {0, 1, 3}); protobufs::UniformBufferElementDescriptor uniform_f_c_x = MakeUniformBufferElementDescriptor(0, 0, {0, 2, 0}); protobufs::UniformBufferElementDescriptor uniform_f_c_y = MakeUniformBufferElementDescriptor(0, 0, {0, 2, 1}); protobufs::UniformBufferElementDescriptor uniform_f_c_z = MakeUniformBufferElementDescriptor(0, 0, {0, 2, 2}); protobufs::UniformBufferElementDescriptor uniform_f_d = MakeUniformBufferElementDescriptor(0, 0, {0, 3}); protobufs::UniformBufferElementDescriptor uniform_g = MakeUniformBufferElementDescriptor(0, 0, {1}); protobufs::UniformBufferElementDescriptor uniform_h_x = MakeUniformBufferElementDescriptor(0, 0, {2, 0}); protobufs::UniformBufferElementDescriptor uniform_h_y = MakeUniformBufferElementDescriptor(0, 0, {2, 1}); ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[0], uniform_f_a_0)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[1], uniform_f_a_1)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[2], uniform_f_a_2)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[3], uniform_f_a_3)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_array_data[4], uniform_f_a_4)); ASSERT_TRUE(AddFactHelper(&transformation_context, 1, uniform_f_b_x)); ASSERT_TRUE(AddFactHelper(&transformation_context, 2, uniform_f_b_y)); ASSERT_TRUE(AddFactHelper(&transformation_context, 3, uniform_f_b_z)); ASSERT_TRUE(AddFactHelper(&transformation_context, 4, uniform_f_b_w)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_vector_data[0], uniform_f_c_x)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_vector_data[1], uniform_f_c_y)); ASSERT_TRUE(AddFactHelper(&transformation_context, float_vector_data[2], uniform_f_c_z)); ASSERT_TRUE(AddFactHelper(&transformation_context, 42, uniform_f_d)); ASSERT_TRUE(AddFactHelper(&transformation_context, 22, uniform_g)); ASSERT_TRUE(AddFactHelper(&transformation_context, 100, uniform_h_x)); ASSERT_TRUE(AddFactHelper(&transformation_context, 200, uniform_h_y)); std::vector transformations; transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 18, MakeInstructionDescriptor(20, spv::Op::OpStore, 0), 1), uniform_f_a_4, 200, 201)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 22, MakeInstructionDescriptor(23, spv::Op::OpStore, 0), 1), uniform_f_a_3, 202, 203)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 25, MakeInstructionDescriptor(26, spv::Op::OpStore, 0), 1), uniform_f_a_2, 204, 205)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 28, MakeInstructionDescriptor(29, spv::Op::OpStore, 0), 1), uniform_f_a_1, 206, 207)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 31, MakeInstructionDescriptor(32, spv::Op::OpStore, 0), 1), uniform_f_a_0, 208, 209)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 30, MakeInstructionDescriptor(35, spv::Op::OpStore, 0), 1), uniform_f_b_w, 210, 211)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 27, MakeInstructionDescriptor(37, spv::Op::OpStore, 0), 1), uniform_f_b_z, 212, 213)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 24, MakeInstructionDescriptor(39, spv::Op::OpStore, 0), 1), uniform_f_b_y, 214, 215)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 21, MakeInstructionDescriptor(41, spv::Op::OpStore, 0), 1), uniform_f_b_x, 216, 217)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 44, MakeInstructionDescriptor(45, spv::Op::OpStore, 0), 1), uniform_f_c_z, 220, 221)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 46, MakeInstructionDescriptor(47, spv::Op::OpStore, 0), 1), uniform_f_c_y, 222, 223)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 48, MakeInstructionDescriptor(49, spv::Op::OpStore, 0), 1), uniform_f_c_x, 224, 225)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 50, MakeInstructionDescriptor(52, spv::Op::OpStore, 0), 1), uniform_f_d, 226, 227)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 53, MakeInstructionDescriptor(54, spv::Op::OpStore, 0), 1), uniform_h_x, 228, 229)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 55, MakeInstructionDescriptor(56, spv::Op::OpStore, 0), 1), uniform_h_y, 230, 231)); transformations.emplace_back(TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 42, MakeInstructionDescriptor(43, spv::Op::OpStore, 0), 1), uniform_g, 218, 219)); for (auto& transformation : transformations) { 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 = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpName %4 "main" OpName %14 "T" OpMemberName %14 0 "a" OpMemberName %14 1 "b" OpMemberName %14 2 "c" OpMemberName %14 3 "d" OpMemberName %14 4 "e" OpName %16 "myT" OpName %61 "T" OpMemberName %61 0 "a" OpMemberName %61 1 "b" OpMemberName %61 2 "c" OpMemberName %61 3 "d" OpMemberName %61 4 "e" OpName %63 "block" OpMemberName %63 0 "f" OpMemberName %63 1 "g" OpMemberName %63 2 "h" OpName %65 "" OpDecorate %60 ArrayStride 16 OpMemberDecorate %61 0 Offset 0 OpMemberDecorate %61 1 Offset 80 OpMemberDecorate %61 2 Offset 96 OpMemberDecorate %61 3 Offset 108 OpMemberDecorate %61 4 Offset 112 OpMemberDecorate %63 0 Offset 0 OpMemberDecorate %63 1 Offset 128 OpMemberDecorate %63 2 Offset 136 OpDecorate %63 Block OpDecorate %65 DescriptorSet 0 OpDecorate %65 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeFloat 32 %7 = OpTypeInt 32 0 %8 = OpConstant %7 5 %9 = OpTypeArray %6 %8 %10 = OpTypeInt 32 1 %11 = OpTypeVector %10 4 %12 = OpTypeVector %6 3 %13 = OpTypeBool %14 = OpTypeStruct %9 %11 %12 %7 %13 %15 = OpTypePointer Function %14 %17 = OpConstant %10 0 %18 = OpConstant %6 1.9375 %19 = OpTypePointer Function %6 %21 = OpConstant %10 1 %22 = OpConstant %6 1.875 %24 = OpConstant %10 2 %25 = OpConstant %6 1.75 %27 = OpConstant %10 3 %28 = OpConstant %6 1.5 %30 = OpConstant %10 4 %31 = OpConstant %6 1 %33 = OpConstant %7 0 %34 = OpTypePointer Function %10 %36 = OpConstant %7 1 %38 = OpConstant %7 2 %40 = OpConstant %7 3 %42 = OpConstant %10 22 %44 = OpConstant %6 2.75 %46 = OpConstant %6 2.5 %48 = OpConstant %6 2 %50 = OpConstant %7 42 %51 = OpTypePointer Function %7 %53 = OpConstant %7 100 %55 = OpConstant %7 200 %57 = OpConstantTrue %13 %58 = OpTypePointer Function %13 %60 = OpTypeArray %6 %8 %61 = OpTypeStruct %60 %11 %12 %7 %7 %62 = OpTypeVector %7 2 %63 = OpTypeStruct %61 %10 %62 %64 = OpTypePointer Uniform %63 %100 = OpTypePointer Uniform %10 %101 = OpTypePointer Uniform %7 %102 = OpTypePointer Uniform %6 %103 = OpTypePointer Uniform %13 %65 = OpVariable %64 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %16 = OpVariable %15 Function %20 = OpAccessChain %19 %16 %17 %17 %200 = OpAccessChain %102 %65 %17 %17 %30 %201 = OpLoad %6 %200 OpStore %20 %201 %23 = OpAccessChain %19 %16 %17 %21 %202 = OpAccessChain %102 %65 %17 %17 %27 %203 = OpLoad %6 %202 OpStore %23 %203 %26 = OpAccessChain %19 %16 %17 %24 %204 = OpAccessChain %102 %65 %17 %17 %24 %205 = OpLoad %6 %204 OpStore %26 %205 %29 = OpAccessChain %19 %16 %17 %27 %206 = OpAccessChain %102 %65 %17 %17 %21 %207 = OpLoad %6 %206 OpStore %29 %207 %32 = OpAccessChain %19 %16 %17 %30 %208 = OpAccessChain %102 %65 %17 %17 %17 %209 = OpLoad %6 %208 OpStore %32 %209 %35 = OpAccessChain %34 %16 %21 %33 %210 = OpAccessChain %100 %65 %17 %21 %27 %211 = OpLoad %10 %210 OpStore %35 %211 %37 = OpAccessChain %34 %16 %21 %36 %212 = OpAccessChain %100 %65 %17 %21 %24 %213 = OpLoad %10 %212 OpStore %37 %213 %39 = OpAccessChain %34 %16 %21 %38 %214 = OpAccessChain %100 %65 %17 %21 %21 %215 = OpLoad %10 %214 OpStore %39 %215 %41 = OpAccessChain %34 %16 %21 %40 %216 = OpAccessChain %100 %65 %17 %21 %17 %217 = OpLoad %10 %216 OpStore %41 %217 %43 = OpAccessChain %34 %16 %21 %33 %218 = OpAccessChain %100 %65 %21 %219 = OpLoad %10 %218 OpStore %43 %219 %45 = OpAccessChain %19 %16 %24 %33 %220 = OpAccessChain %102 %65 %17 %24 %24 %221 = OpLoad %6 %220 OpStore %45 %221 %47 = OpAccessChain %19 %16 %24 %33 %222 = OpAccessChain %102 %65 %17 %24 %21 %223 = OpLoad %6 %222 OpStore %47 %223 %49 = OpAccessChain %19 %16 %24 %33 %224 = OpAccessChain %102 %65 %17 %24 %17 %225 = OpLoad %6 %224 OpStore %49 %225 %52 = OpAccessChain %51 %16 %27 %226 = OpAccessChain %101 %65 %17 %27 %227 = OpLoad %7 %226 OpStore %52 %227 %54 = OpAccessChain %51 %16 %27 %228 = OpAccessChain %101 %65 %24 %17 %229 = OpLoad %7 %228 OpStore %54 %229 %56 = OpAccessChain %51 %16 %27 %230 = OpAccessChain %101 %65 %24 %21 %231 = OpLoad %7 %230 OpStore %56 %231 %59 = OpAccessChain %58 %16 %30 OpStore %59 %57 OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after, context.get())); } TEST(TransformationReplaceConstantWithUniformTest, DoNotReplaceVariableInitializer) { // If a local variable has a constant initializer, this cannot be replaced // by a uniform. std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource GLSL 450 OpMemberDecorate %16 0 Offset 0 OpDecorate %16 Block OpDecorate %18 DescriptorSet 0 OpDecorate %18 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpTypePointer Function %6 %50 = OpConstant %6 0 %16 = OpTypeStruct %6 %17 = OpTypePointer Uniform %16 %51 = OpTypePointer Uniform %6 %18 = OpVariable %17 Uniform %4 = OpFunction %2 None %3 %5 = OpLabel %8 = OpVariable %7 Function %50 OpReturn OpFunctionEnd )"; const auto env = SPV_ENV_UNIVERSAL_1_3; const auto consumer = nullptr; const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption); spvtools::ValidatorOptions validator_options; ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options, kConsoleMessageConsumer)); TransformationContext transformation_context( MakeUnique(context.get()), validator_options); protobufs::UniformBufferElementDescriptor blockname_a = MakeUniformBufferElementDescriptor(0, 0, {0}); ASSERT_TRUE(AddFactHelper(&transformation_context, 0, blockname_a)); ASSERT_FALSE( TransformationReplaceConstantWithUniform( MakeIdUseDescriptor( 50, MakeInstructionDescriptor(8, spv::Op::OpVariable, 0), 1), blockname_a, 100, 101) .IsApplicable(context.get(), transformation_context)); } TEST(TransformationReplaceConstantWithUniformTest, ReplaceOpPhiOperand) { std::string shader = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 320 OpDecorate %32 DescriptorSet 0 OpDecorate %32 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 2 %13 = OpConstant %6 4 %21 = OpConstant %6 1 %34 = OpConstant %6 0 %10 = OpTypeBool %30 = OpTypeStruct %6 %31 = OpTypePointer Uniform %30 %32 = OpVariable %31 Uniform %33 = OpTypePointer Uniform %6 %4 = OpFunction %2 None %3 %11 = OpLabel OpBranch %5 %5 = OpLabel %23 = OpPhi %6 %7 %11 %20 %15 %9 = OpSLessThan %10 %23 %13 OpLoopMerge %8 %15 None OpBranchConditional %9 %15 %8 %15 = OpLabel %20 = OpIAdd %6 %23 %21 OpBranch %5 %8 = 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(context.get()), validator_options); auto int_descriptor = MakeUniformBufferElementDescriptor(0, 0, {0}); ASSERT_TRUE(AddFactHelper(&transformation_context, 2, int_descriptor)); { TransformationReplaceConstantWithUniform transformation( MakeIdUseDescriptor(7, MakeInstructionDescriptor(23, spv::Op::OpPhi, 0), 0), int_descriptor, 50, 51); ASSERT_TRUE( transformation.IsApplicable(context.get(), transformation_context)); ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context); ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed( context.get(), validator_options, kConsoleMessageConsumer)); } std::string after_transformation = R"( OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint Fragment %4 "main" OpExecutionMode %4 OriginUpperLeft OpSource ESSL 320 OpDecorate %32 DescriptorSet 0 OpDecorate %32 Binding 0 %2 = OpTypeVoid %3 = OpTypeFunction %2 %6 = OpTypeInt 32 1 %7 = OpConstant %6 2 %13 = OpConstant %6 4 %21 = OpConstant %6 1 %34 = OpConstant %6 0 %10 = OpTypeBool %30 = OpTypeStruct %6 %31 = OpTypePointer Uniform %30 %32 = OpVariable %31 Uniform %33 = OpTypePointer Uniform %6 %4 = OpFunction %2 None %3 %11 = OpLabel %50 = OpAccessChain %33 %32 %34 %51 = OpLoad %6 %50 OpBranch %5 %5 = OpLabel %23 = OpPhi %6 %51 %11 %20 %15 %9 = OpSLessThan %10 %23 %13 OpLoopMerge %8 %15 None OpBranchConditional %9 %15 %8 %15 = OpLabel %20 = OpIAdd %6 %23 %21 OpBranch %5 %8 = OpLabel OpReturn OpFunctionEnd )"; ASSERT_TRUE(IsEqual(env, after_transformation, context.get())); } } // namespace } // namespace fuzz } // namespace spvtools