// Copyright (c) 2018 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 <memory>
#include <unordered_set>
#include <vector>

#include "gmock/gmock.h"
#include "source/opt/register_pressure.h"
#include "test/opt/assembly_builder.h"
#include "test/opt/function_utils.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"

namespace spvtools {
namespace opt {
namespace {

using ::testing::UnorderedElementsAre;
using PassClassTest = PassTest<::testing::Test>;

void CompareSets(const std::unordered_set<Instruction*>& computed,
                 const std::unordered_set<uint32_t>& expected) {
  for (Instruction* insn : computed) {
    EXPECT_TRUE(expected.count(insn->result_id()))
        << "Unexpected instruction in live set: " << *insn;
  }
  EXPECT_EQ(computed.size(), expected.size());
}

/*
Generated from the following GLSL

#version 330
in vec4 BaseColor;
flat in int Count;
void main()
{
  vec4 color = BaseColor;
  vec4 acc;
  if (Count == 0) {
    acc = color;
  }
  else {
    acc = color + vec4(0,1,2,0);
  }
  gl_FragColor = acc + color;
}
*/
TEST_F(PassClassTest, LivenessWithIf) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %4 "main" %11 %15 %32
               OpExecutionMode %4 OriginLowerLeft
               OpSource GLSL 330
               OpName %4 "main"
               OpName %11 "BaseColor"
               OpName %15 "Count"
               OpName %32 "gl_FragColor"
               OpDecorate %11 Location 0
               OpDecorate %15 Flat
               OpDecorate %15 Location 0
               OpDecorate %32 Location 0
          %2 = OpTypeVoid
          %3 = OpTypeFunction %2
          %6 = OpTypeFloat 32
          %7 = OpTypeVector %6 4
         %10 = OpTypePointer Input %7
         %11 = OpVariable %10 Input
         %13 = OpTypeInt 32 1
         %14 = OpTypePointer Input %13
         %15 = OpVariable %14 Input
         %17 = OpConstant %13 0
         %18 = OpTypeBool
         %26 = OpConstant %6 0
         %27 = OpConstant %6 1
         %28 = OpConstant %6 2
         %29 = OpConstantComposite %7 %26 %27 %28 %26
         %31 = OpTypePointer Output %7
         %32 = OpVariable %31 Output
          %4 = OpFunction %2 None %3
          %5 = OpLabel
         %12 = OpLoad %7 %11
         %16 = OpLoad %13 %15
         %19 = OpIEqual %18 %16 %17
               OpSelectionMerge %21 None
               OpBranchConditional %19 %20 %24
         %20 = OpLabel
               OpBranch %21
         %24 = OpLabel
         %30 = OpFAdd %7 %12 %29
               OpBranch %21
         %21 = OpLabel
         %36 = OpPhi %7 %12 %20 %30 %24
         %35 = OpFAdd %7 %36 %12
               OpStore %32 %35
               OpReturn
               OpFunctionEnd
  )";
  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  Module* module = context->module();
  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                             << text << std::endl;
  Function* f = &*module->begin();
  LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
  const RegisterLiveness* register_liveness = liveness_analysis->Get(f);
  {
    SCOPED_TRACE("Block 5");
    auto live_sets = register_liveness->Get(5);
    std::unordered_set<uint32_t> live_in{
        11,  // %11 = OpVariable %10 Input
        15,  // %15 = OpVariable %14 Input
        32,  // %32 = OpVariable %31 Output
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        12,  // %12 = OpLoad %7 %11
        32,  // %32 = OpVariable %31 Output
    };
    CompareSets(live_sets->live_out_, live_out);
  }
  {
    SCOPED_TRACE("Block 20");
    auto live_sets = register_liveness->Get(20);
    std::unordered_set<uint32_t> live_inout{
        12,  // %12 = OpLoad %7 %11
        32,  // %32 = OpVariable %31 Output
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);
  }
  {
    SCOPED_TRACE("Block 24");
    auto live_sets = register_liveness->Get(24);
    std::unordered_set<uint32_t> live_in{
        12,  // %12 = OpLoad %7 %11
        32,  // %32 = OpVariable %31 Output
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        12,  // %12 = OpLoad %7 %11
        30,  // %30 = OpFAdd %7 %12 %29
        32,  // %32 = OpVariable %31 Output
    };
    CompareSets(live_sets->live_out_, live_out);
  }
  {
    SCOPED_TRACE("Block 21");
    auto live_sets = register_liveness->Get(21);
    std::unordered_set<uint32_t> live_in{
        12,  // %12 = OpLoad %7 %11
        32,  // %32 = OpVariable %31 Output
        36,  // %36 = OpPhi %7 %12 %20 %30 %24
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{};
    CompareSets(live_sets->live_out_, live_out);
  }
}

/*
Generated from the following GLSL
#version 330
in vec4 bigColor;
in vec4 BaseColor;
in float f;
flat in int Count;
flat in uvec4 v4;
void main()
{
    vec4 color = BaseColor;
    for (int i = 0; i < Count; ++i)
        color += bigColor;
    float sum = 0.0;
    for (int i = 0; i < 4; ++i) {
      float acc = 0.0;
      if (sum == 0.0) {
        acc = v4[i];
      }
      else {
        acc = BaseColor[i];
      }
      sum += acc + v4[i];
    }
    vec4 tv4;
    for (int i = 0; i < 4; ++i)
        tv4[i] = v4[i] * 4u;
    color += vec4(sum) + tv4;
    vec4 r;
    r.xyz = BaseColor.xyz;
    for (int i = 0; i < Count; ++i)
        r.w = f;
    color.xyz += r.xyz;
    for (int i = 0; i < 16; i += 4)
      for (int j = 0; j < 4; j++)
        color *= f;
    gl_FragColor = color + tv4;
}
*/
TEST_F(PassClassTest, RegisterLiveness) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %4 "main" %11 %24 %28 %55 %124 %176
               OpExecutionMode %4 OriginLowerLeft
               OpSource GLSL 330
               OpName %4 "main"
               OpName %11 "BaseColor"
               OpName %24 "Count"
               OpName %28 "bigColor"
               OpName %55 "v4"
               OpName %84 "tv4"
               OpName %124 "f"
               OpName %176 "gl_FragColor"
               OpDecorate %11 Location 0
               OpDecorate %24 Flat
               OpDecorate %24 Location 0
               OpDecorate %28 Location 0
               OpDecorate %55 Flat
               OpDecorate %55 Location 0
               OpDecorate %124 Location 0
               OpDecorate %176 Location 0
          %2 = OpTypeVoid
          %3 = OpTypeFunction %2
          %6 = OpTypeFloat 32
          %7 = OpTypeVector %6 4
          %8 = OpTypePointer Function %7
         %10 = OpTypePointer Input %7
         %11 = OpVariable %10 Input
         %13 = OpTypeInt 32 1
         %16 = OpConstant %13 0
         %23 = OpTypePointer Input %13
         %24 = OpVariable %23 Input
         %26 = OpTypeBool
         %28 = OpVariable %10 Input
         %33 = OpConstant %13 1
         %35 = OpTypePointer Function %6
         %37 = OpConstant %6 0
         %45 = OpConstant %13 4
         %52 = OpTypeInt 32 0
         %53 = OpTypeVector %52 4
         %54 = OpTypePointer Input %53
         %55 = OpVariable %54 Input
         %57 = OpTypePointer Input %52
         %63 = OpTypePointer Input %6
         %89 = OpConstant %52 4
        %102 = OpTypeVector %6 3
        %124 = OpVariable %63 Input
        %158 = OpConstant %13 16
        %175 = OpTypePointer Output %7
        %176 = OpVariable %175 Output
        %195 = OpUndef %7
          %4 = OpFunction %2 None %3
          %5 = OpLabel
         %84 = OpVariable %8 Function
         %12 = OpLoad %7 %11
               OpBranch %17
         %17 = OpLabel
        %191 = OpPhi %7 %12 %5 %31 %18
        %184 = OpPhi %13 %16 %5 %34 %18
         %25 = OpLoad %13 %24
         %27 = OpSLessThan %26 %184 %25
               OpLoopMerge %19 %18 None
               OpBranchConditional %27 %18 %19
         %18 = OpLabel
         %29 = OpLoad %7 %28
         %31 = OpFAdd %7 %191 %29
         %34 = OpIAdd %13 %184 %33
               OpBranch %17
         %19 = OpLabel
               OpBranch %39
         %39 = OpLabel
        %188 = OpPhi %6 %37 %19 %73 %51
        %185 = OpPhi %13 %16 %19 %75 %51
         %46 = OpSLessThan %26 %185 %45
               OpLoopMerge %41 %51 None
               OpBranchConditional %46 %40 %41
         %40 = OpLabel
         %49 = OpFOrdEqual %26 %188 %37
               OpSelectionMerge %51 None
               OpBranchConditional %49 %50 %61
         %50 = OpLabel
         %58 = OpAccessChain %57 %55 %185
         %59 = OpLoad %52 %58
         %60 = OpConvertUToF %6 %59
               OpBranch %51
         %61 = OpLabel
         %64 = OpAccessChain %63 %11 %185
         %65 = OpLoad %6 %64
               OpBranch %51
         %51 = OpLabel
        %210 = OpPhi %6 %60 %50 %65 %61
         %68 = OpAccessChain %57 %55 %185
         %69 = OpLoad %52 %68
         %70 = OpConvertUToF %6 %69
         %71 = OpFAdd %6 %210 %70
         %73 = OpFAdd %6 %188 %71
         %75 = OpIAdd %13 %185 %33
               OpBranch %39
         %41 = OpLabel
               OpBranch %77
         %77 = OpLabel
        %186 = OpPhi %13 %16 %41 %94 %78
         %83 = OpSLessThan %26 %186 %45
               OpLoopMerge %79 %78 None
               OpBranchConditional %83 %78 %79
         %78 = OpLabel
         %87 = OpAccessChain %57 %55 %186
         %88 = OpLoad %52 %87
         %90 = OpIMul %52 %88 %89
         %91 = OpConvertUToF %6 %90
         %92 = OpAccessChain %35 %84 %186
               OpStore %92 %91
         %94 = OpIAdd %13 %186 %33
               OpBranch %77
         %79 = OpLabel
         %96 = OpCompositeConstruct %7 %188 %188 %188 %188
         %97 = OpLoad %7 %84
         %98 = OpFAdd %7 %96 %97
        %100 = OpFAdd %7 %191 %98
        %104 = OpVectorShuffle %102 %12 %12 0 1 2
        %106 = OpVectorShuffle %7 %195 %104 4 5 6 3
               OpBranch %108
        %108 = OpLabel
        %197 = OpPhi %7 %106 %79 %208 %133
        %196 = OpPhi %13 %16 %79 %143 %133
        %115 = OpSLessThan %26 %196 %25
               OpLoopMerge %110 %133 None
               OpBranchConditional %115 %109 %110
        %109 = OpLabel
               OpBranch %117
        %117 = OpLabel
        %209 = OpPhi %7 %197 %109 %181 %118
        %204 = OpPhi %13 %16 %109 %129 %118
        %123 = OpSLessThan %26 %204 %45
               OpLoopMerge %119 %118 None
               OpBranchConditional %123 %118 %119
        %118 = OpLabel
        %125 = OpLoad %6 %124
        %181 = OpCompositeInsert %7 %125 %209 3
        %129 = OpIAdd %13 %204 %33
               OpBranch %117
        %119 = OpLabel
               OpBranch %131
        %131 = OpLabel
        %208 = OpPhi %7 %209 %119 %183 %132
        %205 = OpPhi %13 %16 %119 %141 %132
        %137 = OpSLessThan %26 %205 %45
               OpLoopMerge %133 %132 None
               OpBranchConditional %137 %132 %133
        %132 = OpLabel
        %138 = OpLoad %6 %124
        %183 = OpCompositeInsert %7 %138 %208 3
        %141 = OpIAdd %13 %205 %33
               OpBranch %131
        %133 = OpLabel
        %143 = OpIAdd %13 %196 %33
               OpBranch %108
        %110 = OpLabel
        %145 = OpVectorShuffle %102 %197 %197 0 1 2
        %147 = OpVectorShuffle %102 %100 %100 0 1 2
        %148 = OpFAdd %102 %147 %145
        %150 = OpVectorShuffle %7 %100 %148 4 5 6 3
               OpBranch %152
        %152 = OpLabel
        %200 = OpPhi %7 %150 %110 %203 %163
        %199 = OpPhi %13 %16 %110 %174 %163
        %159 = OpSLessThan %26 %199 %158
               OpLoopMerge %154 %163 None
               OpBranchConditional %159 %153 %154
        %153 = OpLabel
               OpBranch %161
        %161 = OpLabel
        %203 = OpPhi %7 %200 %153 %170 %162
        %201 = OpPhi %13 %16 %153 %172 %162
        %167 = OpSLessThan %26 %201 %45
               OpLoopMerge %163 %162 None
               OpBranchConditional %167 %162 %163
        %162 = OpLabel
        %168 = OpLoad %6 %124
        %170 = OpVectorTimesScalar %7 %203 %168
        %172 = OpIAdd %13 %201 %33
               OpBranch %161
        %163 = OpLabel
        %174 = OpIAdd %13 %199 %45
               OpBranch %152
        %154 = OpLabel
        %178 = OpLoad %7 %84
        %179 = OpFAdd %7 %200 %178
               OpStore %176 %179
               OpReturn
               OpFunctionEnd
  )";
  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  Module* module = context->module();
  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                             << text << std::endl;
  Function* f = &*module->begin();
  LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
  const RegisterLiveness* register_liveness = liveness_analysis->Get(f);
  LoopDescriptor& ld = *context->GetLoopDescriptor(f);

  {
    SCOPED_TRACE("Block 5");
    auto live_sets = register_liveness->Get(5);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        24,   // %24 = OpVariable %23 Input
        28,   // %28 = OpVariable %10 Input
        55,   // %55 = OpVariable %54 Input
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        24,   // %24 = OpVariable %23 Input
        28,   // %28 = OpVariable %10 Input
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 8u);
  }
  {
    SCOPED_TRACE("Block 17");
    auto live_sets = register_liveness->Get(17);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        24,   // %24 = OpVariable %23 Input
        28,   // %28 = OpVariable %10 Input
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        184,  // %184 = OpPhi %13 %16 %5 %34 %18
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        28,   // %28 = OpVariable %10 Input
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        184,  // %184 = OpPhi %13 %16 %5 %34 %18
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 11u);
  }
  {
    SCOPED_TRACE("Block 18");
    auto live_sets = register_liveness->Get(18);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        24,   // %24 = OpVariable %23 Input
        28,   // %28 = OpVariable %10 Input
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        184,  // %184 = OpPhi %13 %16 %5 %34 %18
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        24,   // %24 = OpVariable %23 Input
        28,   // %28 = OpVariable %10 Input
        31,   // %31 = OpFAdd %7 %191 %29
        34,   // %34 = OpIAdd %13 %184 %33
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 12u);
  }
  {
    SCOPED_TRACE("Block 19");
    auto live_sets = register_liveness->Get(19);
    std::unordered_set<uint32_t> live_inout{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 8u);
  }
  {
    SCOPED_TRACE("Block 39");
    auto live_sets = register_liveness->Get(39);
    std::unordered_set<uint32_t> live_inout{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 11u);
  }
  {
    SCOPED_TRACE("Block 40");
    auto live_sets = register_liveness->Get(40);
    std::unordered_set<uint32_t> live_inout{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 11u);
  }
  {
    SCOPED_TRACE("Block 50");
    auto live_sets = register_liveness->Get(50);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        60,   // %60 = OpConvertUToF %6 %59
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 12u);
  }
  {
    SCOPED_TRACE("Block 61");
    auto live_sets = register_liveness->Get(61);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        65,   // %65 = OpLoad %6 %64
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 12u);
  }
  {
    SCOPED_TRACE("Block 51");
    auto live_sets = register_liveness->Get(51);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
        210,  // %210 = OpPhi %6 %60 %50 %65 %61
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        73,   // %73 = OpFAdd %6 %188 %71
        75,   // %75 = OpIAdd %13 %185 %33
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 13u);
  }
  {
    SCOPED_TRACE("Block 41");
    auto live_sets = register_liveness->Get(41);
    std::unordered_set<uint32_t> live_inout{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 8u);
  }
  {
    SCOPED_TRACE("Block 77");
    auto live_sets = register_liveness->Get(77);
    std::unordered_set<uint32_t> live_inout{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        186,  // %186 = OpPhi %13 %16 %41 %94 %78
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 10u);
  }
  {
    SCOPED_TRACE("Block 78");
    auto live_sets = register_liveness->Get(78);
    std::unordered_set<uint32_t> live_in{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        186,  // %186 = OpPhi %13 %16 %41 %94 %78
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        94,   // %94 = OpIAdd %13 %186 %33
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 11u);
  }
  {
    SCOPED_TRACE("Block 79");
    auto live_sets = register_liveness->Get(79);
    std::unordered_set<uint32_t> live_in{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        106,  // %106 = OpVectorShuffle %7 %195 %104 4 5 6 3
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 9u);
  }
  {
    SCOPED_TRACE("Block 108");
    auto live_sets = register_liveness->Get(108);
    std::unordered_set<uint32_t> live_in{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        197,  // %197 = OpPhi %7 %106 %79 %208 %133
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        197,  // %197 = OpPhi %7 %106 %79 %208 %133
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 8u);
  }
  {
    SCOPED_TRACE("Block 109");
    auto live_sets = register_liveness->Get(109);
    std::unordered_set<uint32_t> live_inout{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        197,  // %197 = OpPhi %7 %106 %79 %208 %133
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 7u);
  }
  {
    SCOPED_TRACE("Block 117");
    auto live_sets = register_liveness->Get(117);
    std::unordered_set<uint32_t> live_inout{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        204,  // %204 = OpPhi %13 %16 %109 %129 %118
        209,  // %209 = OpPhi %7 %197 %109 %181 %118
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 9u);
  }
  {
    SCOPED_TRACE("Block 118");
    auto live_sets = register_liveness->Get(118);
    std::unordered_set<uint32_t> live_in{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        204,  // %204 = OpPhi %13 %16 %109 %129 %118
        209,  // %209 = OpPhi %7 %197 %109 %181 %118
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        129,  // %129 = OpIAdd %13 %204 %33
        176,  // %176 = OpVariable %175 Output
        181,  // %181 = OpCompositeInsert %7 %125 %209 3
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 10u);
  }
  {
    SCOPED_TRACE("Block 119");
    auto live_sets = register_liveness->Get(119);
    std::unordered_set<uint32_t> live_inout{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        209,  // %209 = OpPhi %7 %197 %109 %181 %118
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 7u);
  }
  {
    SCOPED_TRACE("Block 131");
    auto live_sets = register_liveness->Get(131);
    std::unordered_set<uint32_t> live_inout{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        205,  // %205 = OpPhi %13 %16 %119 %141 %132
        208,  // %208 = OpPhi %7 %209 %119 %183 %132
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 9u);
  }
  {
    SCOPED_TRACE("Block 132");
    auto live_sets = register_liveness->Get(132);
    std::unordered_set<uint32_t> live_in{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        205,  // %205 = OpPhi %13 %16 %119 %141 %132
        208,  // %208 = OpPhi %7 %209 %119 %183 %132
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        141,  // %141 = OpIAdd %13 %205 %33
        176,  // %176 = OpVariable %175 Output
        183,  // %183 = OpCompositeInsert %7 %138 %208 3
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 10u);
  }
  {
    SCOPED_TRACE("Block 133");
    auto live_sets = register_liveness->Get(133);
    std::unordered_set<uint32_t> live_in{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        196,  // %196 = OpPhi %13 %16 %79 %143 %133
        208,  // %208 = OpPhi %7 %209 %119 %183 %132
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        25,   // %25 = OpLoad %13 %24
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        143,  // %143 = OpIAdd %13 %196 %33
        176,  // %176 = OpVariable %175 Output
        208,  // %208 = OpPhi %7 %209 %119 %183 %132
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 8u);
  }
  {
    SCOPED_TRACE("Block 110");
    auto live_sets = register_liveness->Get(110);
    std::unordered_set<uint32_t> live_in{
        84,   // %84 = OpVariable %8 Function
        100,  // %100 = OpFAdd %7 %191 %98
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        197,  // %197 = OpPhi %7 %106 %79 %208 %133
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        150,  // %150 = OpVectorShuffle %7 %100 %148 4 5 6 3
        176,  // %176 = OpVariable %175 Output
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 7u);
  }
  {
    SCOPED_TRACE("Block 152");
    auto live_sets = register_liveness->Get(152);
    std::unordered_set<uint32_t> live_inout{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        199,  // %199 = OpPhi %13 %16 %110 %174 %163
        200,  // %200 = OpPhi %7 %150 %110 %203 %163
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 6u);
  }
  {
    SCOPED_TRACE("Block 153");
    auto live_sets = register_liveness->Get(153);
    std::unordered_set<uint32_t> live_inout{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        199,  // %199 = OpPhi %13 %16 %110 %174 %163
        200,  // %200 = OpPhi %7 %150 %110 %203 %163
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 5u);
  }
  {
    SCOPED_TRACE("Block 161");
    auto live_sets = register_liveness->Get(161);
    std::unordered_set<uint32_t> live_inout{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        199,  // %199 = OpPhi %13 %16 %110 %174 %163
        201,  // %201 = OpPhi %13 %16 %153 %172 %162
        203,  // %203 = OpPhi %7 %200 %153 %170 %162
    };
    CompareSets(live_sets->live_in_, live_inout);
    CompareSets(live_sets->live_out_, live_inout);

    EXPECT_EQ(live_sets->used_registers_, 7u);
  }
  {
    SCOPED_TRACE("Block 162");
    auto live_sets = register_liveness->Get(162);
    std::unordered_set<uint32_t> live_in{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        199,  // %199 = OpPhi %13 %16 %110 %174 %163
        201,  // %201 = OpPhi %13 %16 %153 %172 %162
        203,  // %203 = OpPhi %7 %200 %153 %170 %162
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        170,  // %170 = OpVectorTimesScalar %7 %203 %168
        172,  // %172 = OpIAdd %13 %201 %33
        176,  // %176 = OpVariable %175 Output
        199,  // %199 = OpPhi %13 %16 %110 %174 %163
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 8u);
  }
  {
    SCOPED_TRACE("Block 163");
    auto live_sets = register_liveness->Get(163);
    std::unordered_set<uint32_t> live_in{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        199,  // %199 = OpPhi %13 %16 %110 %174 %163
        203,  // %203 = OpPhi %7 %200 %153 %170 %162
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        174,  // %174 = OpIAdd %13 %199 %45
        176,  // %176 = OpVariable %175 Output
        203,  // %203 = OpPhi %7 %200 %153 %170 %162
    };
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 6u);
  }
  {
    SCOPED_TRACE("Block 154");
    auto live_sets = register_liveness->Get(154);
    std::unordered_set<uint32_t> live_in{
        84,   // %84 = OpVariable %8 Function
        176,  // %176 = OpVariable %175 Output
        200,  // %200 = OpPhi %7 %150 %110 %203 %163
    };
    CompareSets(live_sets->live_in_, live_in);

    std::unordered_set<uint32_t> live_out{};
    CompareSets(live_sets->live_out_, live_out);

    EXPECT_EQ(live_sets->used_registers_, 4u);
  }

  {
    SCOPED_TRACE("Compute loop pressure");
    RegisterLiveness::RegionRegisterLiveness loop_reg_pressure;
    register_liveness->ComputeLoopRegisterPressure(*ld[39], &loop_reg_pressure);
    // Generate(*context->cfg()->block(39), &loop_reg_pressure);
    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(loop_reg_pressure.live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(loop_reg_pressure.live_out_, live_out);

    EXPECT_EQ(loop_reg_pressure.used_registers_, 13u);
  }

  {
    SCOPED_TRACE("Loop Fusion simulation");
    RegisterLiveness::RegionRegisterLiveness simulation_resut;
    register_liveness->SimulateFusion(*ld[17], *ld[39], &simulation_resut);

    std::unordered_set<uint32_t> live_in{
        11,   // %11 = OpVariable %10 Input
        12,   // %12 = OpLoad %7 %11
        24,   // %24 = OpVariable %23 Input
        25,   // %25 = OpLoad %13 %24
        28,   // %28 = OpVariable %10 Input
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        184,  // %184 = OpPhi %13 %16 %5 %34 %18
        185,  // %185 = OpPhi %13 %16 %19 %75 %51
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(simulation_resut.live_in_, live_in);

    std::unordered_set<uint32_t> live_out{
        12,   // %12 = OpLoad %7 %11
        25,   // %25 = OpLoad %13 %24
        55,   // %55 = OpVariable %54 Input
        84,   // %84 = OpVariable %8 Function
        124,  // %124 = OpVariable %63 Input
        176,  // %176 = OpVariable %175 Output
        188,  // %188 = OpPhi %6 %37 %19 %73 %51
        191,  // %191 = OpPhi %7 %12 %5 %31 %18
    };
    CompareSets(simulation_resut.live_out_, live_out);

    EXPECT_EQ(simulation_resut.used_registers_, 17u);
  }
}

TEST_F(PassClassTest, FissionSimulation) {
  const std::string source = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %2 "main"
               OpExecutionMode %2 OriginUpperLeft
               OpSource GLSL 430
               OpName %2 "main"
               OpName %3 "i"
               OpName %4 "A"
               OpName %5 "B"
          %6 = OpTypeVoid
          %7 = OpTypeFunction %6
          %8 = OpTypeInt 32 1
          %9 = OpTypePointer Function %8
         %10 = OpConstant %8 0
         %11 = OpConstant %8 10
         %12 = OpTypeBool
         %13 = OpTypeFloat 32
         %14 = OpTypeInt 32 0
         %15 = OpConstant %14 10
         %16 = OpTypeArray %13 %15
         %17 = OpTypePointer Function %16
         %18 = OpTypePointer Function %13
         %19 = OpConstant %8 1
          %2 = OpFunction %6 None %7
         %20 = OpLabel
          %3 = OpVariable %9 Function
          %4 = OpVariable %17 Function
          %5 = OpVariable %17 Function
               OpBranch %21
         %21 = OpLabel
         %22 = OpPhi %8 %10 %20 %23 %24
               OpLoopMerge %25 %24 None
               OpBranch %26
         %26 = OpLabel
         %27 = OpSLessThan %12 %22 %11
               OpBranchConditional %27 %28 %25
         %28 = OpLabel
         %29 = OpAccessChain %18 %5 %22
         %30 = OpLoad %13 %29
         %31 = OpAccessChain %18 %4 %22
               OpStore %31 %30
         %32 = OpAccessChain %18 %4 %22
         %33 = OpLoad %13 %32
         %34 = OpAccessChain %18 %5 %22
               OpStore %34 %33
               OpBranch %24
         %24 = OpLabel
         %23 = OpIAdd %8 %22 %19
               OpBranch %21
         %25 = OpLabel
               OpStore %3 %22
               OpReturn
               OpFunctionEnd
    )";
  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, source,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  Module* module = context->module();
  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                             << source << std::endl;
  Function* f = &*module->begin();
  LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
  const RegisterLiveness* register_liveness = liveness_analysis->Get(f);
  LoopDescriptor& ld = *context->GetLoopDescriptor(f);
  analysis::DefUseManager& def_use_mgr = *context->get_def_use_mgr();

  {
    RegisterLiveness::RegionRegisterLiveness l1_sim_resut;
    RegisterLiveness::RegionRegisterLiveness l2_sim_resut;
    std::unordered_set<Instruction*> moved_instructions{
        def_use_mgr.GetDef(29), def_use_mgr.GetDef(30), def_use_mgr.GetDef(31),
        def_use_mgr.GetDef(31)->NextNode()};
    std::unordered_set<Instruction*> copied_instructions{
        def_use_mgr.GetDef(22), def_use_mgr.GetDef(27),
        def_use_mgr.GetDef(27)->NextNode(), def_use_mgr.GetDef(23)};

    register_liveness->SimulateFission(*ld[21], moved_instructions,
                                       copied_instructions, &l1_sim_resut,
                                       &l2_sim_resut);
    {
      SCOPED_TRACE("L1 simulation");
      std::unordered_set<uint32_t> live_in{
          3,   // %3 = OpVariable %9 Function
          4,   // %4 = OpVariable %17 Function
          5,   // %5 = OpVariable %17 Function
          22,  // %22 = OpPhi %8 %10 %20 %23 %24
      };
      CompareSets(l1_sim_resut.live_in_, live_in);

      std::unordered_set<uint32_t> live_out{
          3,   // %3 = OpVariable %9 Function
          4,   // %4 = OpVariable %17 Function
          5,   // %5 = OpVariable %17 Function
          22,  // %22 = OpPhi %8 %10 %20 %23 %24
      };
      CompareSets(l1_sim_resut.live_out_, live_out);

      EXPECT_EQ(l1_sim_resut.used_registers_, 6u);
    }
    {
      SCOPED_TRACE("L2 simulation");
      std::unordered_set<uint32_t> live_in{
          3,   // %3 = OpVariable %9 Function
          4,   // %4 = OpVariable %17 Function
          5,   // %5 = OpVariable %17 Function
          22,  // %22 = OpPhi %8 %10 %20 %23 %24
      };
      CompareSets(l2_sim_resut.live_in_, live_in);

      std::unordered_set<uint32_t> live_out{
          3,   // %3 = OpVariable %9 Function
          22,  // %22 = OpPhi %8 %10 %20 %23 %24
      };
      CompareSets(l2_sim_resut.live_out_, live_out);

      EXPECT_EQ(l2_sim_resut.used_registers_, 6u);
    }
  }
}

// Test that register liveness does not fail when there is an unreachable block.
// We are not testing if the liveness is computed correctly because the specific
// results do not matter for unreachable blocks.
TEST_F(PassClassTest, RegisterLivenessWithUnreachableBlock) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %2 "main"
               OpExecutionMode %2 OriginLowerLeft
               OpSource GLSL 330
               OpSourceExtension "GL_ARB_shading_language_420pack"
       %void = OpTypeVoid
          %4 = OpTypeFunction %void
          %2 = OpFunction %void None %4
          %5 = OpLabel
               OpBranch %6
          %6 = OpLabel
               OpLoopMerge %7 %8 None
               OpBranch %9
          %9 = OpLabel
               OpBranch %7
          %8 = OpLabel
               OpBranch %6
          %7 = OpLabel
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  Module* module = context->module();
  EXPECT_NE(nullptr, module) << "Assembling failed for shader:\n"
                             << text << std::endl;
  Function* f = &*module->begin();
  LivenessAnalysis* liveness_analysis = context->GetLivenessAnalysis();
  liveness_analysis->Get(f);
}

}  // namespace
}  // namespace opt
}  // namespace spvtools
