Skip to content

spirv-fuzz: TransformationReplaceConstantWithUniform doesn't handle OpPhi instructions #3639

@Vasniktel

Description

@Vasniktel

TransformationReplaceConstantWithUniform replaces a use of some id with a value of a uniform variable. Since uniform variables always point to struct types, the transformation first needs to access struct's components. It uses OpAccessChain and OpLoad for that purpose. However, when the replaced use is an operand of some OpPhi instruction, the transformation will insert OpAccessChain and OpLoad right above the OpPhi, thus invalidating the module.

The following example demonstrates the bug.

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);
  ASSERT_TRUE(IsValid(env, context.get()));

  FactManager fact_manager;
  spvtools::ValidatorOptions validator_options;
  TransformationContext transformation_context(&fact_manager,
                                               validator_options);

  protobufs::UniformBufferElementDescriptor descriptor;
  descriptor.set_descriptor_set(0);
  descriptor.set_binding(0);
  descriptor.add_index(0);

  protobufs::FactConstantUniform fact_constant_uniform;
  fact_constant_uniform.add_constant_word(2);
  *fact_constant_uniform.mutable_uniform_buffer_element_descriptor() =
      descriptor;

  protobufs::Fact fact;
  *fact.mutable_constant_uniform_fact() = fact_constant_uniform;

  ASSERT_TRUE(fact_manager.AddFact(fact, context.get()));

  TransformationReplaceConstantWithUniform transformation(
      MakeIdUseDescriptor(7, MakeInstructionDescriptor(23, SpvOpPhi, 0), 0),
      descriptor, 50, 51);
  ASSERT_TRUE(
      transformation.IsApplicable(context.get(), transformation_context));
  transformation.Apply(context.get(), &transformation_context);

  ASSERT_FALSE(IsValid(env, context.get()));

The error message is

error: line 27: OpPhi must appear within a non-entry block before all non-OpPhi instructions (except for OpLine, which can be mixed with OpPhi).
%23 = OpPhi %int %51 %11 %20 %15

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions