text: // 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_composite_construct.h"
  
  #include "gtest/gtest.h"
  #include "source/fuzz/data_descriptor.h"
  #include "source/fuzz/fuzzer_util.h"
  #include "source/fuzz/instruction_descriptor.h"
  #include "test/fuzz/fuzz_test_util.h"
  
  namespace spvtools {
  namespace fuzz {
  namespace {
  
  TEST(TransformationCompositeConstructTest, ConstructArrays) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %11 "floats"
                 OpName %22 "x"
                 OpName %39 "vecs"
                 OpName %49 "bools"
                 OpName %60 "many_uvec3s"
                 OpDecorate %60 RelaxedPrecision
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeInt 32 0
            %8 = OpConstant %7 2
            %9 = OpTypeArray %6 %8
           %10 = OpTypePointer Function %9
           %12 = OpTypeInt 32 1
           %13 = OpConstant %12 0
           %14 = OpConstant %6 1
           %15 = OpTypePointer Function %6
           %17 = OpConstant %12 1
           %18 = OpConstant %6 2
           %20 = OpTypeVector %6 2
           %21 = OpTypePointer Function %20
           %32 = OpTypeBool
           %36 = OpConstant %7 3
           %37 = OpTypeArray %20 %36
           %38 = OpTypePointer Private %37
           %39 = OpVariable %38 Private
           %40 = OpConstant %6 3
           %41 = OpConstantComposite %20 %40 %40
           %42 = OpTypePointer Private %20
           %44 = OpConstant %12 2
           %47 = OpTypeArray %32 %36
           %48 = OpTypePointer Function %47
           %50 = OpConstantTrue %32
           %51 = OpTypePointer Function %32
           %56 = OpTypeVector %7 3
           %57 = OpTypeArray %56 %8
           %58 = OpTypeArray %57 %8
           %59 = OpTypePointer Function %58
           %61 = OpConstant %7 4
           %62 = OpConstantComposite %56 %61 %61 %61
           %63 = OpTypePointer Function %56
           %65 = OpConstant %7 5
           %66 = OpConstantComposite %56 %65 %65 %65
           %67 = OpConstant %7 6
           %68 = OpConstantComposite %56 %67 %67 %67
           %69 = OpConstantComposite %57 %66 %68
          %100 = OpUndef %57
           %70 = OpTypePointer Function %57
            %4 = OpFunction %2 None %3
            %5 = OpLabel
           %11 = OpVariable %10 Function
           %22 = OpVariable %21 Function
           %49 = OpVariable %48 Function
           %60 = OpVariable %59 Function
           %16 = OpAccessChain %15 %11 %13
                 OpStore %16 %14
           %19 = OpAccessChain %15 %11 %17
                 OpStore %19 %18
           %23 = OpAccessChain %15 %11 %13
           %24 = OpLoad %6 %23
           %25 = OpAccessChain %15 %11 %17
           %26 = OpLoad %6 %25
           %27 = OpCompositeConstruct %20 %24 %26
                 OpStore %22 %27
           %28 = OpAccessChain %15 %11 %13
           %29 = OpLoad %6 %28
           %30 = OpAccessChain %15 %11 %17
           %31 = OpLoad %6 %30
           %33 = OpFOrdGreaterThan %32 %29 %31
                 OpSelectionMerge %35 None
                 OpBranchConditional %33 %34 %35
           %34 = OpLabel
           %43 = OpAccessChain %42 %39 %17
                 OpStore %43 %41
           %45 = OpLoad %20 %22
           %46 = OpAccessChain %42 %39 %44
                 OpStore %46 %45
                 OpBranch %35
           %35 = OpLabel
           %52 = OpAccessChain %51 %49 %13
                 OpStore %52 %50
           %53 = OpAccessChain %51 %49 %13
           %54 = OpLoad %32 %53
           %55 = OpAccessChain %51 %49 %17
                 OpStore %55 %54
           %64 = OpAccessChain %63 %60 %13 %13
                 OpStore %64 %62
           %71 = OpAccessChain %70 %60 %17
                 OpStore %71 %69
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    // Make a vec2[3]
    TransformationCompositeConstruct make_vec2_array_length_3(
        37, {41, 45, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
        200);
    // Bad: there are too many components
    TransformationCompositeConstruct make_vec2_array_length_3_bad(
        37, {41, 45, 27, 27}, MakeInstructionDescriptor(46, SpvOpAccessChain, 0),
        200);
    ASSERT_TRUE(make_vec2_array_length_3.IsApplicable(context.get(),
                                                      transformation_context));
    ASSERT_FALSE(make_vec2_array_length_3_bad.IsApplicable(
        context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_vec2_array_length_3, context.get(),
                          &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(41, {}), MakeDataDescriptor(200, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(45, {}), MakeDataDescriptor(200, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(27, {}), MakeDataDescriptor(200, {2})));
  
    // Make a float[2]
    TransformationCompositeConstruct make_float_array_length_2(
        9, {24, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
    // Bad: %41 does not have type float
    TransformationCompositeConstruct make_float_array_length_2_bad(
        9, {41, 40}, MakeInstructionDescriptor(71, SpvOpStore, 0), 201);
    ASSERT_TRUE(make_float_array_length_2.IsApplicable(context.get(),
                                                       transformation_context));
    ASSERT_FALSE(make_float_array_length_2_bad.IsApplicable(
        context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_float_array_length_2, context.get(),
                          &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(24, {}), MakeDataDescriptor(201, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(40, {}), MakeDataDescriptor(201, {1})));
  
    // Make a bool[3]
    TransformationCompositeConstruct make_bool_array_length_3(
        47, {33, 50, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
        202);
    // Bad: %54 is not available at the desired program point.
    TransformationCompositeConstruct make_bool_array_length_3_bad(
        47, {33, 54, 50}, MakeInstructionDescriptor(33, SpvOpSelectionMerge, 0),
        202);
    ASSERT_TRUE(make_bool_array_length_3.IsApplicable(context.get(),
                                                      transformation_context));
    ASSERT_FALSE(make_bool_array_length_3_bad.IsApplicable(
        context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_bool_array_length_3, context.get(),
                          &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(33, {}), MakeDataDescriptor(202, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(50, {}), MakeDataDescriptor(202, {2})));
  
    // make a uvec3[2][2]
    TransformationCompositeConstruct make_uvec3_array_length_2_2(
        58, {69, 100}, MakeInstructionDescriptor(64, SpvOpStore, 0), 203);
    // Bad: Skip count 100 is too large.
    TransformationCompositeConstruct make_uvec3_array_length_2_2_bad(
        58, {33, 54}, MakeInstructionDescriptor(64, SpvOpStore, 100), 203);
    ASSERT_TRUE(make_uvec3_array_length_2_2.IsApplicable(context.get(),
                                                         transformation_context));
    ASSERT_FALSE(make_uvec3_array_length_2_2_bad.IsApplicable(
        context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_uvec3_array_length_2_2, context.get(),
                          &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(69, {}), MakeDataDescriptor(203, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {}), MakeDataDescriptor(203, {1})));
  
    std::string after_transformation = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %11 "floats"
                 OpName %22 "x"
                 OpName %39 "vecs"
                 OpName %49 "bools"
                 OpName %60 "many_uvec3s"
                 OpDecorate %60 RelaxedPrecision
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeInt 32 0
            %8 = OpConstant %7 2
            %9 = OpTypeArray %6 %8
           %10 = OpTypePointer Function %9
           %12 = OpTypeInt 32 1
           %13 = OpConstant %12 0
           %14 = OpConstant %6 1
           %15 = OpTypePointer Function %6
           %17 = OpConstant %12 1
           %18 = OpConstant %6 2
           %20 = OpTypeVector %6 2
           %21 = OpTypePointer Function %20
           %32 = OpTypeBool
           %36 = OpConstant %7 3
           %37 = OpTypeArray %20 %36
           %38 = OpTypePointer Private %37
           %39 = OpVariable %38 Private
           %40 = OpConstant %6 3
           %41 = OpConstantComposite %20 %40 %40
           %42 = OpTypePointer Private %20
           %44 = OpConstant %12 2
           %47 = OpTypeArray %32 %36
           %48 = OpTypePointer Function %47
           %50 = OpConstantTrue %32
           %51 = OpTypePointer Function %32
           %56 = OpTypeVector %7 3
           %57 = OpTypeArray %56 %8
           %58 = OpTypeArray %57 %8
           %59 = OpTypePointer Function %58
           %61 = OpConstant %7 4
           %62 = OpConstantComposite %56 %61 %61 %61
           %63 = OpTypePointer Function %56
           %65 = OpConstant %7 5
           %66 = OpConstantComposite %56 %65 %65 %65
           %67 = OpConstant %7 6
           %68 = OpConstantComposite %56 %67 %67 %67
           %69 = OpConstantComposite %57 %66 %68
          %100 = OpUndef %57
           %70 = OpTypePointer Function %57
            %4 = OpFunction %2 None %3
            %5 = OpLabel
           %11 = OpVariable %10 Function
           %22 = OpVariable %21 Function
           %49 = OpVariable %48 Function
           %60 = OpVariable %59 Function
           %16 = OpAccessChain %15 %11 %13
                 OpStore %16 %14
           %19 = OpAccessChain %15 %11 %17
                 OpStore %19 %18
           %23 = OpAccessChain %15 %11 %13
           %24 = OpLoad %6 %23
           %25 = OpAccessChain %15 %11 %17
           %26 = OpLoad %6 %25
           %27 = OpCompositeConstruct %20 %24 %26
                 OpStore %22 %27
           %28 = OpAccessChain %15 %11 %13
           %29 = OpLoad %6 %28
           %30 = OpAccessChain %15 %11 %17
           %31 = OpLoad %6 %30
           %33 = OpFOrdGreaterThan %32 %29 %31
          %202 = OpCompositeConstruct %47 %33 %50 %50
                 OpSelectionMerge %35 None
                 OpBranchConditional %33 %34 %35
           %34 = OpLabel
           %43 = OpAccessChain %42 %39 %17
                 OpStore %43 %41
           %45 = OpLoad %20 %22
          %200 = OpCompositeConstruct %37 %41 %45 %27
           %46 = OpAccessChain %42 %39 %44
                 OpStore %46 %45
                 OpBranch %35
           %35 = OpLabel
           %52 = OpAccessChain %51 %49 %13
                 OpStore %52 %50
           %53 = OpAccessChain %51 %49 %13
           %54 = OpLoad %32 %53
           %55 = OpAccessChain %51 %49 %17
                 OpStore %55 %54
           %64 = OpAccessChain %63 %60 %13 %13
          %203 = OpCompositeConstruct %58 %69 %100
                 OpStore %64 %62
           %71 = OpAccessChain %70 %60 %17
          %201 = OpCompositeConstruct %9 %24 %40
                 OpStore %71 %69
                 OpReturn
                 OpFunctionEnd
    )";
  
    ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
  }
  
  TEST(TransformationCompositeConstructTest, ConstructMatrices) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %9 "v1"
                 OpName %12 "v2"
                 OpName %14 "v3"
                 OpName %19 "v4"
                 OpName %26 "v5"
                 OpName %29 "v6"
                 OpName %34 "m34"
                 OpName %37 "m43"
                 OpName %43 "vecs"
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 3
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 1
           %11 = OpConstantComposite %7 %10 %10 %10
           %17 = OpTypeVector %6 4
           %18 = OpTypePointer Function %17
           %21 = OpConstant %6 2
           %32 = OpTypeMatrix %17 3
           %33 = OpTypePointer Private %32
           %34 = OpVariable %33 Private
           %35 = OpTypeMatrix %7 4
           %36 = OpTypePointer Private %35
           %37 = OpVariable %36 Private
           %38 = OpTypeVector %6 2
           %39 = OpTypeInt 32 0
           %40 = OpConstant %39 3
           %41 = OpTypeArray %38 %40
           %42 = OpTypePointer Private %41
           %43 = OpVariable %42 Private
          %100 = OpUndef %7
          %101 = OpUndef %17
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
           %12 = OpVariable %8 Function
           %14 = OpVariable %8 Function
           %19 = OpVariable %18 Function
           %26 = OpVariable %18 Function
           %29 = OpVariable %18 Function
                 OpStore %9 %11
           %13 = OpLoad %7 %9
                 OpStore %12 %13
           %15 = OpLoad %7 %12
           %16 = OpVectorShuffle %7 %15 %15 2 1 0
                 OpStore %14 %16
           %20 = OpLoad %7 %14
           %22 = OpCompositeExtract %6 %20 0
           %23 = OpCompositeExtract %6 %20 1
           %24 = OpCompositeExtract %6 %20 2
           %25 = OpCompositeConstruct %17 %22 %23 %24 %21
                 OpStore %19 %25
           %27 = OpLoad %17 %19
           %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
                 OpStore %26 %28
           %30 = OpLoad %7 %9
           %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
                 OpStore %29 %31
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    // make a mat3x4
    TransformationCompositeConstruct make_mat34(
        32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
    // Bad: %35 is mat4x3, not mat3x4.
    TransformationCompositeConstruct make_mat34_bad(
        35, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
    ASSERT_TRUE(make_mat34.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_mat34_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_mat34, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2})));
  
    // make a mat4x3
    TransformationCompositeConstruct make_mat43(
        35, {11, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
    // Bad: %25 does not match the matrix's column type.
    TransformationCompositeConstruct make_mat43_bad(
        35, {25, 13, 16, 100}, MakeInstructionDescriptor(31, SpvOpStore, 0), 201);
    ASSERT_TRUE(make_mat43.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_mat43_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_mat43, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(11, {}), MakeDataDescriptor(201, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(13, {}), MakeDataDescriptor(201, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(16, {}), MakeDataDescriptor(201, {2})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {}), MakeDataDescriptor(201, {3})));
  
    std::string after_transformation = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %9 "v1"
                 OpName %12 "v2"
                 OpName %14 "v3"
                 OpName %19 "v4"
                 OpName %26 "v5"
                 OpName %29 "v6"
                 OpName %34 "m34"
                 OpName %37 "m43"
                 OpName %43 "vecs"
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 3
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 1
           %11 = OpConstantComposite %7 %10 %10 %10
           %17 = OpTypeVector %6 4
           %18 = OpTypePointer Function %17
           %21 = OpConstant %6 2
           %32 = OpTypeMatrix %17 3
           %33 = OpTypePointer Private %32
           %34 = OpVariable %33 Private
           %35 = OpTypeMatrix %7 4
           %36 = OpTypePointer Private %35
           %37 = OpVariable %36 Private
           %38 = OpTypeVector %6 2
           %39 = OpTypeInt 32 0
           %40 = OpConstant %39 3
           %41 = OpTypeArray %38 %40
           %42 = OpTypePointer Private %41
           %43 = OpVariable %42 Private
          %100 = OpUndef %7
          %101 = OpUndef %17
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
           %12 = OpVariable %8 Function
           %14 = OpVariable %8 Function
           %19 = OpVariable %18 Function
           %26 = OpVariable %18 Function
           %29 = OpVariable %18 Function
                 OpStore %9 %11
           %13 = OpLoad %7 %9
                 OpStore %12 %13
           %15 = OpLoad %7 %12
           %16 = OpVectorShuffle %7 %15 %15 2 1 0
                 OpStore %14 %16
           %20 = OpLoad %7 %14
           %22 = OpCompositeExtract %6 %20 0
           %23 = OpCompositeExtract %6 %20 1
           %24 = OpCompositeExtract %6 %20 2
           %25 = OpCompositeConstruct %17 %22 %23 %24 %21
                 OpStore %19 %25
           %27 = OpLoad %17 %19
           %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
                 OpStore %26 %28
           %30 = OpLoad %7 %9
           %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
          %201 = OpCompositeConstruct %35 %11 %13 %16 %100
                 OpStore %29 %31
          %200 = OpCompositeConstruct %32 %25 %28 %31
                 OpReturn
                 OpFunctionEnd
    )";
  
    ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
  }
  
  TEST(TransformationCompositeConstructTest, ConstructStructs) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %9 "Inner"
                 OpMemberName %9 0 "a"
                 OpMemberName %9 1 "b"
                 OpName %11 "i1"
                 OpName %22 "i2"
                 OpName %33 "Outer"
                 OpMemberName %33 0 "c"
                 OpMemberName %33 1 "d"
                 OpMemberName %33 2 "e"
                 OpName %35 "o"
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 2
            %8 = OpTypeInt 32 1
            %9 = OpTypeStruct %7 %8
           %10 = OpTypePointer Function %9
           %12 = OpConstant %8 0
           %13 = OpConstant %6 2
           %14 = OpTypeInt 32 0
           %15 = OpConstant %14 0
           %16 = OpTypePointer Function %6
           %18 = OpConstant %8 1
           %19 = OpConstant %8 3
           %20 = OpTypePointer Function %8
           %23 = OpTypePointer Function %7
           %31 = OpConstant %14 2
           %32 = OpTypeArray %9 %31
           %33 = OpTypeStruct %32 %9 %6
           %34 = OpTypePointer Function %33
           %36 = OpConstant %6 1
           %37 = OpConstantComposite %7 %36 %13
           %38 = OpConstant %8 2
           %39 = OpConstantComposite %9 %37 %38
           %40 = OpConstant %6 3
           %41 = OpConstant %6 4
           %42 = OpConstantComposite %7 %40 %41
           %56 = OpConstant %6 5
          %100 = OpUndef %9
            %4 = OpFunction %2 None %3
            %5 = OpLabel
           %11 = OpVariable %10 Function
           %22 = OpVariable %10 Function
           %35 = OpVariable %34 Function
           %17 = OpAccessChain %16 %11 %12 %15
                 OpStore %17 %13
           %21 = OpAccessChain %20 %11 %18
                 OpStore %21 %19
           %24 = OpAccessChain %23 %11 %12
           %25 = OpLoad %7 %24
           %26 = OpAccessChain %23 %22 %12
                 OpStore %26 %25
           %27 = OpAccessChain %20 %11 %18
           %28 = OpLoad %8 %27
           %29 = OpIAdd %8 %28 %18
           %30 = OpAccessChain %20 %22 %18
                 OpStore %30 %29
           %43 = OpAccessChain %20 %11 %18
           %44 = OpLoad %8 %43
           %45 = OpCompositeConstruct %9 %42 %44
           %46 = OpCompositeConstruct %32 %39 %45
           %47 = OpLoad %9 %22
           %48 = OpCompositeConstruct %33 %46 %47 %40
                 OpStore %35 %48
           %49 = OpLoad %9 %11
           %50 = OpAccessChain %10 %35 %12 %12
                 OpStore %50 %49
           %51 = OpLoad %9 %22
           %52 = OpAccessChain %10 %35 %12 %18
                 OpStore %52 %51
           %53 = OpAccessChain %10 %35 %12 %12
           %54 = OpLoad %9 %53
           %55 = OpAccessChain %10 %35 %18
                 OpStore %55 %54
           %57 = OpAccessChain %16 %35 %38
                 OpStore %57 %56
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    // make an Inner
    TransformationCompositeConstruct make_inner(
        9, {25, 19}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
    // Bad: Too few fields to make the struct.
    TransformationCompositeConstruct make_inner_bad(
        9, {25}, MakeInstructionDescriptor(57, SpvOpAccessChain, 0), 200);
    ASSERT_TRUE(make_inner.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_inner_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_inner, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(19, {}), MakeDataDescriptor(200, {1})));
  
    // make an Outer
    TransformationCompositeConstruct make_outer(
        33, {46, 200, 56}, MakeInstructionDescriptor(200, SpvOpAccessChain, 0),
        201);
    // Bad: %200 is not available at the desired program point.
    TransformationCompositeConstruct make_outer_bad(
        33, {46, 200, 56},
        MakeInstructionDescriptor(200, SpvOpCompositeConstruct, 0), 201);
    ASSERT_TRUE(make_outer.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_outer_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_outer, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(46, {}), MakeDataDescriptor(201, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(200, {}), MakeDataDescriptor(201, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(56, {}), MakeDataDescriptor(201, {2})));
  
    std::string after_transformation = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %9 "Inner"
                 OpMemberName %9 0 "a"
                 OpMemberName %9 1 "b"
                 OpName %11 "i1"
                 OpName %22 "i2"
                 OpName %33 "Outer"
                 OpMemberName %33 0 "c"
                 OpMemberName %33 1 "d"
                 OpMemberName %33 2 "e"
                 OpName %35 "o"
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 2
            %8 = OpTypeInt 32 1
            %9 = OpTypeStruct %7 %8
           %10 = OpTypePointer Function %9
           %12 = OpConstant %8 0
           %13 = OpConstant %6 2
           %14 = OpTypeInt 32 0
           %15 = OpConstant %14 0
           %16 = OpTypePointer Function %6
           %18 = OpConstant %8 1
           %19 = OpConstant %8 3
           %20 = OpTypePointer Function %8
           %23 = OpTypePointer Function %7
           %31 = OpConstant %14 2
           %32 = OpTypeArray %9 %31
           %33 = OpTypeStruct %32 %9 %6
           %34 = OpTypePointer Function %33
           %36 = OpConstant %6 1
           %37 = OpConstantComposite %7 %36 %13
           %38 = OpConstant %8 2
           %39 = OpConstantComposite %9 %37 %38
           %40 = OpConstant %6 3
           %41 = OpConstant %6 4
           %42 = OpConstantComposite %7 %40 %41
           %56 = OpConstant %6 5
          %100 = OpUndef %9
            %4 = OpFunction %2 None %3
            %5 = OpLabel
           %11 = OpVariable %10 Function
           %22 = OpVariable %10 Function
           %35 = OpVariable %34 Function
           %17 = OpAccessChain %16 %11 %12 %15
                 OpStore %17 %13
           %21 = OpAccessChain %20 %11 %18
                 OpStore %21 %19
           %24 = OpAccessChain %23 %11 %12
           %25 = OpLoad %7 %24
           %26 = OpAccessChain %23 %22 %12
                 OpStore %26 %25
           %27 = OpAccessChain %20 %11 %18
           %28 = OpLoad %8 %27
           %29 = OpIAdd %8 %28 %18
           %30 = OpAccessChain %20 %22 %18
                 OpStore %30 %29
           %43 = OpAccessChain %20 %11 %18
           %44 = OpLoad %8 %43
           %45 = OpCompositeConstruct %9 %42 %44
           %46 = OpCompositeConstruct %32 %39 %45
           %47 = OpLoad %9 %22
           %48 = OpCompositeConstruct %33 %46 %47 %40
                 OpStore %35 %48
           %49 = OpLoad %9 %11
           %50 = OpAccessChain %10 %35 %12 %12
                 OpStore %50 %49
           %51 = OpLoad %9 %22
           %52 = OpAccessChain %10 %35 %12 %18
                 OpStore %52 %51
           %53 = OpAccessChain %10 %35 %12 %12
           %54 = OpLoad %9 %53
           %55 = OpAccessChain %10 %35 %18
                 OpStore %55 %54
          %200 = OpCompositeConstruct %9 %25 %19
          %201 = OpCompositeConstruct %33 %46 %200 %56
           %57 = OpAccessChain %16 %35 %38
                 OpStore %57 %56
                 OpReturn
                 OpFunctionEnd
    )";
  
    ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
  }
  
  TEST(TransformationCompositeConstructTest, ConstructVectors) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %9 "v2"
                 OpName %27 "v3"
                 OpName %46 "v4"
                 OpName %53 "iv2"
                 OpName %61 "uv3"
                 OpName %72 "bv4"
                 OpName %88 "uv2"
                 OpName %95 "bv3"
                 OpName %104 "bv2"
                 OpName %116 "iv3"
                 OpName %124 "iv4"
                 OpName %133 "uv4"
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 2
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 1
           %11 = OpConstant %6 2
           %12 = OpConstantComposite %7 %10 %11
           %13 = OpTypeInt 32 0
           %14 = OpConstant %13 0
           %15 = OpTypePointer Function %6
           %18 = OpConstant %13 1
           %21 = OpTypeBool
           %25 = OpTypeVector %6 3
           %26 = OpTypePointer Function %25
           %33 = OpConstant %6 3
           %34 = OpConstant %6 -0.756802499
           %38 = OpConstant %13 2
           %44 = OpTypeVector %6 4
           %45 = OpTypePointer Function %44
           %50 = OpTypeInt 32 1
           %51 = OpTypeVector %50 2
           %52 = OpTypePointer Function %51
           %57 = OpTypePointer Function %50
           %59 = OpTypeVector %13 3
           %60 = OpTypePointer Function %59
           %65 = OpConstant %13 3
           %67 = OpTypePointer Function %13
           %70 = OpTypeVector %21 4
           %71 = OpTypePointer Function %70
           %73 = OpConstantTrue %21
           %74 = OpTypePointer Function %21
           %86 = OpTypeVector %13 2
           %87 = OpTypePointer Function %86
           %93 = OpTypeVector %21 3
           %94 = OpTypePointer Function %93
          %102 = OpTypeVector %21 2
          %103 = OpTypePointer Function %102
          %111 = OpConstantFalse %21
          %114 = OpTypeVector %50 3
          %115 = OpTypePointer Function %114
          %117 = OpConstant %50 3
          %122 = OpTypeVector %50 4
          %123 = OpTypePointer Function %122
          %131 = OpTypeVector %13 4
          %132 = OpTypePointer Function %131
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
           %27 = OpVariable %26 Function
           %46 = OpVariable %45 Function
           %53 = OpVariable %52 Function
           %61 = OpVariable %60 Function
           %72 = OpVariable %71 Function
           %88 = OpVariable %87 Function
           %95 = OpVariable %94 Function
          %104 = OpVariable %103 Function
          %116 = OpVariable %115 Function
          %124 = OpVariable %123 Function
          %133 = OpVariable %132 Function
                 OpStore %9 %12
           %16 = OpAccessChain %15 %9 %14
           %17 = OpLoad %6 %16
           %19 = OpAccessChain %15 %9 %18
           %20 = OpLoad %6 %19
           %22 = OpFOrdGreaterThan %21 %17 %20
                 OpSelectionMerge %24 None
                 OpBranchConditional %22 %23 %101
           %23 = OpLabel
           %28 = OpAccessChain %15 %9 %14
           %29 = OpLoad %6 %28
           %30 = OpAccessChain %15 %9 %18
           %31 = OpLoad %6 %30
           %32 = OpFAdd %6 %29 %31
           %35 = OpCompositeConstruct %25 %32 %33 %34
                 OpStore %27 %35
           %36 = OpAccessChain %15 %27 %14
           %37 = OpLoad %6 %36
           %39 = OpAccessChain %15 %27 %38
           %40 = OpLoad %6 %39
           %41 = OpFOrdLessThan %21 %37 %40
                 OpSelectionMerge %43 None
                 OpBranchConditional %41 %42 %69
           %42 = OpLabel
           %47 = OpAccessChain %15 %9 %18
           %48 = OpLoad %6 %47
           %49 = OpAccessChain %15 %46 %14
                 OpStore %49 %48
           %54 = OpAccessChain %15 %27 %38
           %55 = OpLoad %6 %54
           %56 = OpConvertFToS %50 %55
           %58 = OpAccessChain %57 %53 %14
                 OpStore %58 %56
           %62 = OpAccessChain %15 %46 %14
           %63 = OpLoad %6 %62
           %64 = OpConvertFToU %13 %63
           %66 = OpIAdd %13 %64 %65
           %68 = OpAccessChain %67 %61 %14
                 OpStore %68 %66
                 OpBranch %43
           %69 = OpLabel
           %75 = OpAccessChain %74 %72 %14
                 OpStore %75 %73
           %76 = OpAccessChain %74 %72 %14
           %77 = OpLoad %21 %76
           %78 = OpLogicalNot %21 %77
           %79 = OpAccessChain %74 %72 %18
                 OpStore %79 %78
           %80 = OpAccessChain %74 %72 %14
           %81 = OpLoad %21 %80
           %82 = OpAccessChain %74 %72 %18
           %83 = OpLoad %21 %82
           %84 = OpLogicalAnd %21 %81 %83
           %85 = OpAccessChain %74 %72 %38
                 OpStore %85 %84
           %89 = OpAccessChain %67 %88 %14
           %90 = OpLoad %13 %89
           %91 = OpINotEqual %21 %90 %14
           %92 = OpAccessChain %74 %72 %65
                 OpStore %92 %91
                 OpBranch %43
           %43 = OpLabel
           %96 = OpLoad %70 %72
           %97 = OpCompositeExtract %21 %96 0
           %98 = OpCompositeExtract %21 %96 1
           %99 = OpCompositeExtract %21 %96 2
          %100 = OpCompositeConstruct %93 %97 %98 %99
                 OpStore %95 %100
                 OpBranch %24
          %101 = OpLabel
          %105 = OpAccessChain %67 %88 %14
          %106 = OpLoad %13 %105
          %107 = OpINotEqual %21 %106 %14
          %108 = OpCompositeConstruct %102 %107 %107
                 OpStore %104 %108
                 OpBranch %24
           %24 = OpLabel
          %109 = OpAccessChain %74 %104 %18
          %110 = OpLoad %21 %109
          %112 = OpLogicalOr %21 %110 %111
          %113 = OpAccessChain %74 %104 %14
                 OpStore %113 %112
          %118 = OpAccessChain %57 %116 %14
                 OpStore %118 %117
          %119 = OpAccessChain %57 %116 %14
          %120 = OpLoad %50 %119
          %121 = OpAccessChain %57 %53 %18
                 OpStore %121 %120
          %125 = OpAccessChain %57 %116 %14
          %126 = OpLoad %50 %125
          %127 = OpAccessChain %57 %53 %18
          %128 = OpLoad %50 %127
          %129 = OpIAdd %50 %126 %128
          %130 = OpAccessChain %57 %124 %65
                 OpStore %130 %129
          %134 = OpAccessChain %57 %116 %14
          %135 = OpLoad %50 %134
          %136 = OpBitcast %13 %135
          %137 = OpAccessChain %67 %133 %14
                 OpStore %137 %136
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    TransformationCompositeConstruct make_vec2(
        7, {17, 11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
    // Bad: not enough data for a vec2
    TransformationCompositeConstruct make_vec2_bad(
        7, {11}, MakeInstructionDescriptor(100, SpvOpStore, 0), 200);
    ASSERT_TRUE(make_vec2.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_vec2_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_vec2, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(17, {}), MakeDataDescriptor(200, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(11, {}), MakeDataDescriptor(200, {1})));
  
    TransformationCompositeConstruct make_vec3(
        25, {12, 32}, MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0),
        201);
    // Bad: too much data for a vec3
    TransformationCompositeConstruct make_vec3_bad(
        25, {12, 32, 32},
        MakeInstructionDescriptor(35, SpvOpCompositeConstruct, 0), 201);
    ASSERT_TRUE(make_vec3.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_vec3_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_vec3, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(12, {0}), MakeDataDescriptor(201, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(12, {1}), MakeDataDescriptor(201, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(32, {}), MakeDataDescriptor(201, {2})));
  
    TransformationCompositeConstruct make_vec4(
        44, {32, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
        202);
    // Bad: id 48 is not available at the insertion points
    TransformationCompositeConstruct make_vec4_bad(
        44, {48, 32, 10, 11}, MakeInstructionDescriptor(75, SpvOpAccessChain, 0),
        202);
    ASSERT_TRUE(make_vec4.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_vec4_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_vec4, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(32, {}), MakeDataDescriptor(202, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(10, {}), MakeDataDescriptor(202, {2})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(11, {}), MakeDataDescriptor(202, {3})));
  
    TransformationCompositeConstruct make_ivec2(
        51, {126, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
    // Bad: if 128 is not available at the instruction that defines 128
    TransformationCompositeConstruct make_ivec2_bad(
        51, {128, 120}, MakeInstructionDescriptor(128, SpvOpLoad, 0), 203);
    ASSERT_TRUE(make_ivec2.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_ivec2_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_ivec2, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(126, {}), MakeDataDescriptor(203, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(120, {}), MakeDataDescriptor(203, {1})));
  
    TransformationCompositeConstruct make_ivec3(
        114, {56, 117, 56}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
        204);
    // Bad because 1300 is not an id
    TransformationCompositeConstruct make_ivec3_bad(
        114, {56, 117, 1300}, MakeInstructionDescriptor(66, SpvOpAccessChain, 0),
        204);
    ASSERT_TRUE(make_ivec3.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_ivec3_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_ivec3, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(117, {}), MakeDataDescriptor(204, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(56, {}), MakeDataDescriptor(204, {2})));
  
    TransformationCompositeConstruct make_ivec4(
        122, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
        205);
    // Bad because 86 is the wrong type.
    TransformationCompositeConstruct make_ivec4_bad(
        86, {56, 117, 117, 117}, MakeInstructionDescriptor(66, SpvOpIAdd, 0),
        205);
    ASSERT_TRUE(make_ivec4.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_ivec4_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_ivec4, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(56, {}), MakeDataDescriptor(205, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {2})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(117, {}), MakeDataDescriptor(205, {3})));
  
    TransformationCompositeConstruct make_uvec2(
        86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 0), 206);
    TransformationCompositeConstruct make_uvec2_bad(
        86, {18, 38}, MakeInstructionDescriptor(133, SpvOpAccessChain, 200), 206);
    ASSERT_TRUE(make_uvec2.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_uvec2_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_uvec2, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(18, {}), MakeDataDescriptor(206, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(38, {}), MakeDataDescriptor(206, {1})));
  
    TransformationCompositeConstruct make_uvec3(
        59, {14, 18, 136}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
    // Bad because 1300 is not an id
    TransformationCompositeConstruct make_uvec3_bad(
        59, {14, 18, 1300}, MakeInstructionDescriptor(137, SpvOpReturn, 0), 207);
    ASSERT_TRUE(make_uvec3.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_uvec3_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_uvec3, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(14, {}), MakeDataDescriptor(207, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(18, {}), MakeDataDescriptor(207, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(136, {}), MakeDataDescriptor(207, {2})));
  
    TransformationCompositeConstruct make_uvec4(
        131, {14, 18, 136, 136},
        MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
    // Bad because 86 is the wrong type.
    TransformationCompositeConstruct make_uvec4_bad(
        86, {14, 18, 136, 136},
        MakeInstructionDescriptor(137, SpvOpAccessChain, 0), 208);
    ASSERT_TRUE(make_uvec4.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_uvec4_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_uvec4, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(14, {}), MakeDataDescriptor(208, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(18, {}), MakeDataDescriptor(208, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {2})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(136, {}), MakeDataDescriptor(208, {3})));
  
    TransformationCompositeConstruct make_bvec2(
        102,
        {
            111,
            41,
        },
        MakeInstructionDescriptor(75, SpvOpAccessChain, 0), 209);
    // Bad because 0 is not a valid base instruction id
    TransformationCompositeConstruct make_bvec2_bad(
        102,
        {
            111,
            41,
        },
        MakeInstructionDescriptor(0, SpvOpExtInstImport, 0), 209);
    ASSERT_TRUE(make_bvec2.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_bvec2_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_bvec2, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(111, {}), MakeDataDescriptor(209, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(41, {}), MakeDataDescriptor(209, {1})));
  
    TransformationCompositeConstruct make_bvec3(
        93, {108, 73}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
    // Bad because there are too many components for a bvec3
    TransformationCompositeConstruct make_bvec3_bad(
        93, {108, 108}, MakeInstructionDescriptor(108, SpvOpStore, 0), 210);
    ASSERT_TRUE(make_bvec3.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_bvec3_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_bvec3, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(108, {0}), MakeDataDescriptor(210, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(108, {1}), MakeDataDescriptor(210, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(73, {}), MakeDataDescriptor(210, {2})));
  
    TransformationCompositeConstruct make_bvec4(
        70, {108, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
    // Bad because 21 is a type, not a result id
    TransformationCompositeConstruct make_bvec4_bad(
        70, {21, 108}, MakeInstructionDescriptor(108, SpvOpBranch, 0), 211);
    ASSERT_TRUE(make_bvec4.IsApplicable(context.get(), transformation_context));
    ASSERT_FALSE(
        make_bvec4_bad.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(make_bvec4, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(108, {0}), MakeDataDescriptor(211, {2})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(108, {1}), MakeDataDescriptor(211, {3})));
  
    std::string after_transformation = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
                 OpName %4 "main"
                 OpName %9 "v2"
                 OpName %27 "v3"
                 OpName %46 "v4"
                 OpName %53 "iv2"
                 OpName %61 "uv3"
                 OpName %72 "bv4"
                 OpName %88 "uv2"
                 OpName %95 "bv3"
                 OpName %104 "bv2"
                 OpName %116 "iv3"
                 OpName %124 "iv4"
                 OpName %133 "uv4"
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 2
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 1
           %11 = OpConstant %6 2
           %12 = OpConstantComposite %7 %10 %11
           %13 = OpTypeInt 32 0
           %14 = OpConstant %13 0
           %15 = OpTypePointer Function %6
           %18 = OpConstant %13 1
           %21 = OpTypeBool
           %25 = OpTypeVector %6 3
           %26 = OpTypePointer Function %25
           %33 = OpConstant %6 3
           %34 = OpConstant %6 -0.756802499
           %38 = OpConstant %13 2
           %44 = OpTypeVector %6 4
           %45 = OpTypePointer Function %44
           %50 = OpTypeInt 32 1
           %51 = OpTypeVector %50 2
           %52 = OpTypePointer Function %51
           %57 = OpTypePointer Function %50
           %59 = OpTypeVector %13 3
           %60 = OpTypePointer Function %59
           %65 = OpConstant %13 3
           %67 = OpTypePointer Function %13
           %70 = OpTypeVector %21 4
           %71 = OpTypePointer Function %70
           %73 = OpConstantTrue %21
           %74 = OpTypePointer Function %21
           %86 = OpTypeVector %13 2
           %87 = OpTypePointer Function %86
           %93 = OpTypeVector %21 3
           %94 = OpTypePointer Function %93
          %102 = OpTypeVector %21 2
          %103 = OpTypePointer Function %102
          %111 = OpConstantFalse %21
          %114 = OpTypeVector %50 3
          %115 = OpTypePointer Function %114
          %117 = OpConstant %50 3
          %122 = OpTypeVector %50 4
          %123 = OpTypePointer Function %122
          %131 = OpTypeVector %13 4
          %132 = OpTypePointer Function %131
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
           %27 = OpVariable %26 Function
           %46 = OpVariable %45 Function
           %53 = OpVariable %52 Function
           %61 = OpVariable %60 Function
           %72 = OpVariable %71 Function
           %88 = OpVariable %87 Function
           %95 = OpVariable %94 Function
          %104 = OpVariable %103 Function
          %116 = OpVariable %115 Function
          %124 = OpVariable %123 Function
          %133 = OpVariable %132 Function
                 OpStore %9 %12
          %206 = OpCompositeConstruct %86 %18 %38
           %16 = OpAccessChain %15 %9 %14
           %17 = OpLoad %6 %16
           %19 = OpAccessChain %15 %9 %18
           %20 = OpLoad %6 %19
           %22 = OpFOrdGreaterThan %21 %17 %20
                 OpSelectionMerge %24 None
                 OpBranchConditional %22 %23 %101
           %23 = OpLabel
           %28 = OpAccessChain %15 %9 %14
           %29 = OpLoad %6 %28
           %30 = OpAccessChain %15 %9 %18
           %31 = OpLoad %6 %30
           %32 = OpFAdd %6 %29 %31
          %201 = OpCompositeConstruct %25 %12 %32
           %35 = OpCompositeConstruct %25 %32 %33 %34
                 OpStore %27 %35
           %36 = OpAccessChain %15 %27 %14
           %37 = OpLoad %6 %36
           %39 = OpAccessChain %15 %27 %38
           %40 = OpLoad %6 %39
           %41 = OpFOrdLessThan %21 %37 %40
                 OpSelectionMerge %43 None
                 OpBranchConditional %41 %42 %69
           %42 = OpLabel
           %47 = OpAccessChain %15 %9 %18
           %48 = OpLoad %6 %47
           %49 = OpAccessChain %15 %46 %14
                 OpStore %49 %48
           %54 = OpAccessChain %15 %27 %38
           %55 = OpLoad %6 %54
           %56 = OpConvertFToS %50 %55
           %58 = OpAccessChain %57 %53 %14
                 OpStore %58 %56
           %62 = OpAccessChain %15 %46 %14
           %63 = OpLoad %6 %62
           %64 = OpConvertFToU %13 %63
          %205 = OpCompositeConstruct %122 %56 %117 %117 %117
           %66 = OpIAdd %13 %64 %65
          %204 = OpCompositeConstruct %114 %56 %117 %56
           %68 = OpAccessChain %67 %61 %14
                 OpStore %68 %66
                 OpBranch %43
           %69 = OpLabel
          %202 = OpCompositeConstruct %44 %32 %32 %10 %11
          %209 = OpCompositeConstruct %102 %111 %41
           %75 = OpAccessChain %74 %72 %14
                 OpStore %75 %73
           %76 = OpAccessChain %74 %72 %14
           %77 = OpLoad %21 %76
           %78 = OpLogicalNot %21 %77
           %79 = OpAccessChain %74 %72 %18
                 OpStore %79 %78
           %80 = OpAccessChain %74 %72 %14
           %81 = OpLoad %21 %80
           %82 = OpAccessChain %74 %72 %18
           %83 = OpLoad %21 %82
           %84 = OpLogicalAnd %21 %81 %83
           %85 = OpAccessChain %74 %72 %38
                 OpStore %85 %84
           %89 = OpAccessChain %67 %88 %14
           %90 = OpLoad %13 %89
           %91 = OpINotEqual %21 %90 %14
           %92 = OpAccessChain %74 %72 %65
                 OpStore %92 %91
                 OpBranch %43
           %43 = OpLabel
           %96 = OpLoad %70 %72
           %97 = OpCompositeExtract %21 %96 0
           %98 = OpCompositeExtract %21 %96 1
           %99 = OpCompositeExtract %21 %96 2
          %100 = OpCompositeConstruct %93 %97 %98 %99
          %200 = OpCompositeConstruct %7 %17 %11
                 OpStore %95 %100
                 OpBranch %24
          %101 = OpLabel
          %105 = OpAccessChain %67 %88 %14
          %106 = OpLoad %13 %105
          %107 = OpINotEqual %21 %106 %14
          %108 = OpCompositeConstruct %102 %107 %107
          %210 = OpCompositeConstruct %93 %108 %73
                 OpStore %104 %108
          %211 = OpCompositeConstruct %70 %108 %108
                 OpBranch %24
           %24 = OpLabel
          %109 = OpAccessChain %74 %104 %18
          %110 = OpLoad %21 %109
          %112 = OpLogicalOr %21 %110 %111
          %113 = OpAccessChain %74 %104 %14
                 OpStore %113 %112
          %118 = OpAccessChain %57 %116 %14
                 OpStore %118 %117
          %119 = OpAccessChain %57 %116 %14
          %120 = OpLoad %50 %119
          %121 = OpAccessChain %57 %53 %18
                 OpStore %121 %120
          %125 = OpAccessChain %57 %116 %14
          %126 = OpLoad %50 %125
          %127 = OpAccessChain %57 %53 %18
          %203 = OpCompositeConstruct %51 %126 %120
          %128 = OpLoad %50 %127
          %129 = OpIAdd %50 %126 %128
          %130 = OpAccessChain %57 %124 %65
                 OpStore %130 %129
          %134 = OpAccessChain %57 %116 %14
          %135 = OpLoad %50 %134
          %136 = OpBitcast %13 %135
          %208 = OpCompositeConstruct %131 %14 %18 %136 %136
          %137 = OpAccessChain %67 %133 %14
                 OpStore %137 %136
          %207 = OpCompositeConstruct %59 %14 %18 %136
                 OpReturn
                 OpFunctionEnd
    )";
  
    ASSERT_TRUE(IsEqual(env, after_transformation, context.get()));
  }
  
  TEST(TransformationCompositeConstructTest, AddSynonymsForRelevantIds) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 3
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 1
           %11 = OpConstantComposite %7 %10 %10 %10
           %17 = OpTypeVector %6 4
           %18 = OpTypePointer Function %17
           %21 = OpConstant %6 2
           %32 = OpTypeMatrix %17 3
           %33 = OpTypePointer Private %32
           %34 = OpVariable %33 Private
           %35 = OpTypeMatrix %7 4
           %36 = OpTypePointer Private %35
           %37 = OpVariable %36 Private
           %38 = OpTypeVector %6 2
           %39 = OpTypeInt 32 0
           %40 = OpConstant %39 3
           %41 = OpTypeArray %38 %40
           %42 = OpTypePointer Private %41
           %43 = OpVariable %42 Private
          %100 = OpUndef %7
          %101 = OpUndef %17
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
           %12 = OpVariable %8 Function
           %14 = OpVariable %8 Function
           %19 = OpVariable %18 Function
           %26 = OpVariable %18 Function
           %29 = OpVariable %18 Function
                 OpStore %9 %11
           %13 = OpLoad %7 %9
                 OpStore %12 %13
           %15 = OpLoad %7 %12
           %16 = OpVectorShuffle %7 %15 %15 2 1 0
                 OpStore %14 %16
           %20 = OpLoad %7 %14
           %22 = OpCompositeExtract %6 %20 0
           %23 = OpCompositeExtract %6 %20 1
           %24 = OpCompositeExtract %6 %20 2
           %25 = OpCompositeConstruct %17 %22 %23 %24 %21
                 OpStore %19 %25
           %27 = OpLoad %17 %19
           %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
                 OpStore %26 %28
           %30 = OpLoad %7 %9
           %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
                 OpStore %29 %31
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    TransformationCompositeConstruct transformation(
        32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
    ASSERT_TRUE(
        transformation.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1})));
  }
  
  TEST(TransformationCompositeConstructTest, DontAddSynonymsForIrrelevantIds) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 310
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeFloat 32
            %7 = OpTypeVector %6 3
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 1
           %11 = OpConstantComposite %7 %10 %10 %10
           %17 = OpTypeVector %6 4
           %18 = OpTypePointer Function %17
           %21 = OpConstant %6 2
           %32 = OpTypeMatrix %17 3
           %33 = OpTypePointer Private %32
           %34 = OpVariable %33 Private
           %35 = OpTypeMatrix %7 4
           %36 = OpTypePointer Private %35
           %37 = OpVariable %36 Private
           %38 = OpTypeVector %6 2
           %39 = OpTypeInt 32 0
           %40 = OpConstant %39 3
           %41 = OpTypeArray %38 %40
           %42 = OpTypePointer Private %41
           %43 = OpVariable %42 Private
          %100 = OpUndef %7
          %101 = OpUndef %17
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
           %12 = OpVariable %8 Function
           %14 = OpVariable %8 Function
           %19 = OpVariable %18 Function
           %26 = OpVariable %18 Function
           %29 = OpVariable %18 Function
                 OpStore %9 %11
           %13 = OpLoad %7 %9
                 OpStore %12 %13
           %15 = OpLoad %7 %12
           %16 = OpVectorShuffle %7 %15 %15 2 1 0
                 OpStore %14 %16
           %20 = OpLoad %7 %14
           %22 = OpCompositeExtract %6 %20 0
           %23 = OpCompositeExtract %6 %20 1
           %24 = OpCompositeExtract %6 %20 2
           %25 = OpCompositeConstruct %17 %22 %23 %24 %21
                 OpStore %19 %25
           %27 = OpLoad %17 %19
           %28 = OpVectorShuffle %17 %27 %27 3 2 1 0
                 OpStore %26 %28
           %30 = OpLoad %7 %9
           %31 = OpVectorShuffle %17 %30 %30 0 0 1 1
                 OpStore %29 %31
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    transformation_context.GetFactManager()->AddFactIdIsIrrelevant(25);
  
    TransformationCompositeConstruct transformation(
        32, {25, 28, 31}, MakeInstructionDescriptor(31, SpvOpReturn, 0), 200);
    ASSERT_TRUE(
        transformation.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(25, {}), MakeDataDescriptor(200, {0})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(28, {}), MakeDataDescriptor(200, {1})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(31, {}), MakeDataDescriptor(200, {2})));
  }
  
  TEST(TransformationCompositeConstructTest, DontAddSynonymsInDeadBlock) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 320
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeInt 32 1
            %7 = OpTypeVector %6 2
            %8 = OpTypePointer Function %7
           %10 = OpConstant %6 0
           %11 = OpConstant %6 1
           %12 = OpConstantComposite %7 %10 %11
           %13 = OpTypeBool
           %14 = OpConstantFalse %13
            %4 = OpFunction %2 None %3
            %5 = OpLabel
            %9 = OpVariable %8 Function
                 OpStore %9 %12
                 OpSelectionMerge %16 None
                 OpBranchConditional %14 %15 %16
           %15 = OpLabel
                 OpBranch %16
           %16 = OpLabel
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    transformation_context.GetFactManager()->AddFactBlockIsDead(15);
  
    TransformationCompositeConstruct transformation(
        7, {10, 11}, MakeInstructionDescriptor(15, SpvOpBranch, 0), 100);
    ASSERT_TRUE(
        transformation.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
    ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {0}), MakeDataDescriptor(10, {})));
    ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {1}), MakeDataDescriptor(11, {})));
  }
  
  TEST(TransformationCompositeConstructTest, OneIrrelevantComponent) {
    std::string shader = R"(
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %4 "main"
                 OpExecutionMode %4 OriginUpperLeft
                 OpSource ESSL 320
            %2 = OpTypeVoid
            %3 = OpTypeFunction %2
            %6 = OpTypeInt 32 1
            %7 = OpTypeStruct %6 %6 %6
            %8 = OpConstant %6 42
            %9 = OpConstant %6 50
           %10 = OpConstant %6 51
            %4 = OpFunction %2 None %3
            %5 = OpLabel
                 OpReturn
                 OpFunctionEnd
    )";
  
    const auto env = SPV_ENV_UNIVERSAL_1_3;
    const auto consumer = nullptr;
    const auto context = BuildModule(env, consumer, shader, kFuzzAssembleOption);
    spvtools::ValidatorOptions validator_options;
    ASSERT_TRUE(fuzzerutil::IsValidAndWellFormed(context.get(), validator_options,
                                                 kConsoleMessageConsumer));
    TransformationContext transformation_context(
        MakeUnique<FactManager>(context.get()), validator_options);
    transformation_context.GetFactManager()->AddFactIdIsIrrelevant(8);
  
    TransformationCompositeConstruct transformation(
        7, {8, 9, 10}, MakeInstructionDescriptor(5, SpvOpReturn, 0), 100);
    ASSERT_TRUE(
        transformation.IsApplicable(context.get(), transformation_context));
    ApplyAndCheckFreshIds(transformation, context.get(), &transformation_context);
    ASSERT_FALSE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {0}), MakeDataDescriptor(8, {})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {1}), MakeDataDescriptor(9, {})));
    ASSERT_TRUE(transformation_context.GetFactManager()->IsSynonymous(
        MakeDataDescriptor(100, {2}), MakeDataDescriptor(10, {})));
  }
  
  }  // namespace
  }  // namespace fuzz
  }  // namespace spvtools
  