// Copyright (c) 2017 Valve Corporation
// Copyright (c) 2017 LunarG Inc.
//
// 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 "pass_fixture.h"

namespace {

using namespace spvtools;

using CommonUniformElimTest = PassTest<::testing::Test>;

TEST_F(CommonUniformElimTest, Basic1) {
  // Note: This test exemplifies the following:
  // - Common uniform (%_) load floated to nearest non-controlled block
  // - Common extract (g_F) floated to non-controlled block
  // - Non-common extract (g_F2) not floated, but common uniform load shared
  //
  // #version 140
  // in vec4 BaseColor;
  // in float fi;
  //
  // layout(std140) uniform U_t
  // {
  //     float g_F;
  //     float g_F2;
  // } ;
  //
  // void main()
  // {
  //     vec4 v = BaseColor;
  //     if (fi > 0) {
  //       v = v * g_F;
  //     }
  //     else {
  //       float f2 = g_F2 - g_F;
  //       v = v * f2;
  //     }
  //     gl_FragColor = v;
  // }

  const std::string predefs =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpName %gl_FragColor "gl_FragColor"
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%U_t = OpTypeStruct %float %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";

  const std::string before =
      R"(%main = OpFunction %void None %11
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpLoad %float %fi
%29 = OpFOrdGreaterThan %bool %28 %float_0
OpSelectionMerge %30 None
OpBranchConditional %29 %31 %32
%31 = OpLabel
%33 = OpLoad %v4float %v
%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%35 = OpLoad %float %34
%36 = OpVectorTimesScalar %v4float %33 %35
OpStore %v %36
OpBranch %30
%32 = OpLabel
%37 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%38 = OpLoad %float %37
%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%40 = OpLoad %float %39
%41 = OpFSub %float %38 %40
OpStore %f2 %41
%42 = OpLoad %v4float %v
%43 = OpLoad %float %f2
%44 = OpVectorTimesScalar %v4float %42 %43
OpStore %v %44
OpBranch %30
%30 = OpLabel
%45 = OpLoad %v4float %v
OpStore %gl_FragColor %45
OpReturn
OpFunctionEnd
)";

  const std::string after =
      R"(%main = OpFunction %void None %11
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%52 = OpLoad %U_t %_
%53 = OpCompositeExtract %float %52 0
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpLoad %float %fi
%29 = OpFOrdGreaterThan %bool %28 %float_0
OpSelectionMerge %30 None
OpBranchConditional %29 %31 %32
%31 = OpLabel
%33 = OpLoad %v4float %v
%36 = OpVectorTimesScalar %v4float %33 %53
OpStore %v %36
OpBranch %30
%32 = OpLabel
%49 = OpCompositeExtract %float %52 1
%41 = OpFSub %float %49 %53
OpStore %f2 %41
%42 = OpLoad %v4float %v
%43 = OpLoad %float %f2
%44 = OpVectorTimesScalar %v4float %42 %43
OpStore %v %44
OpBranch %30
%30 = OpLabel
%45 = OpLoad %v4float %v
OpStore %gl_FragColor %45
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndCheck<opt::CommonUniformElimPass>(
      predefs + before, predefs + after, true, true);
}

TEST_F(CommonUniformElimTest, Basic2) {
  // Note: This test exemplifies the following:
  // - Common uniform (%_) load floated to nearest non-controlled block
  // - Common extract (g_F) floated to non-controlled block
  // - Non-common extract (g_F2) not floated, but common uniform load shared
  //
  // #version 140
  // in vec4 BaseColor;
  // in float fi;
  // in float fi2;
  //
  // layout(std140) uniform U_t
  // {
  //     float g_F;
  //     float g_F2;
  // } ;
  //
  // void main()
  // {
  //     float f = fi;
  //     if (f < 0)
  //       f = -f;
  //     if (fi2 > 0) {
  //       f = f * g_F;
  //     }
  //     else {
  //       f = g_F2 - g_F;
  //     }
  //     gl_FragColor = f * BaseColor;
  // }

  const std::string predefs =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %fi %fi2 %gl_FragColor %BaseColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %f "f"
OpName %fi "fi"
OpName %fi2 "fi2"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %gl_FragColor "gl_FragColor"
OpName %BaseColor "BaseColor"
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%_ptr_Function_float = OpTypePointer Function %float
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%fi2 = OpVariable %_ptr_Input_float Input
%U_t = OpTypeStruct %float %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%int_1 = OpConstant %int 1
%v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
)";

  const std::string before =
      R"(%main = OpFunction %void None %11
%25 = OpLabel
%f = OpVariable %_ptr_Function_float Function
%26 = OpLoad %float %fi
OpStore %f %26
%27 = OpLoad %float %f
%28 = OpFOrdLessThan %bool %27 %float_0
OpSelectionMerge %29 None
OpBranchConditional %28 %30 %29
%30 = OpLabel
%31 = OpLoad %float %f
%32 = OpFNegate %float %31
OpStore %f %32
OpBranch %29
%29 = OpLabel
%33 = OpLoad %float %fi2
%34 = OpFOrdGreaterThan %bool %33 %float_0
OpSelectionMerge %35 None
OpBranchConditional %34 %36 %37
%36 = OpLabel
%38 = OpLoad %float %f
%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%40 = OpLoad %float %39
%41 = OpFMul %float %38 %40
OpStore %f %41
OpBranch %35
%37 = OpLabel
%42 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%43 = OpLoad %float %42
%44 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%45 = OpLoad %float %44
%46 = OpFSub %float %43 %45
OpStore %f %46
OpBranch %35
%35 = OpLabel
%47 = OpLoad %v4float %BaseColor
%48 = OpLoad %float %f
%49 = OpVectorTimesScalar %v4float %47 %48
OpStore %gl_FragColor %49
OpReturn
OpFunctionEnd
)";

  const std::string after =
      R"(%main = OpFunction %void None %11
%25 = OpLabel
%f = OpVariable %_ptr_Function_float Function
%26 = OpLoad %float %fi
OpStore %f %26
%27 = OpLoad %float %f
%28 = OpFOrdLessThan %bool %27 %float_0
OpSelectionMerge %29 None
OpBranchConditional %28 %30 %29
%30 = OpLabel
%31 = OpLoad %float %f
%32 = OpFNegate %float %31
OpStore %f %32
OpBranch %29
%29 = OpLabel
%56 = OpLoad %U_t %_
%57 = OpCompositeExtract %float %56 0
%33 = OpLoad %float %fi2
%34 = OpFOrdGreaterThan %bool %33 %float_0
OpSelectionMerge %35 None
OpBranchConditional %34 %36 %37
%36 = OpLabel
%38 = OpLoad %float %f
%41 = OpFMul %float %38 %57
OpStore %f %41
OpBranch %35
%37 = OpLabel
%53 = OpCompositeExtract %float %56 1
%46 = OpFSub %float %53 %57
OpStore %f %46
OpBranch %35
%35 = OpLabel
%47 = OpLoad %v4float %BaseColor
%48 = OpLoad %float %f
%49 = OpVectorTimesScalar %v4float %47 %48
OpStore %gl_FragColor %49
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndCheck<opt::CommonUniformElimPass>(
      predefs + before, predefs + after, true, true);
}

TEST_F(CommonUniformElimTest, Basic3) {
  // Note: This test exemplifies the following:
  // - Existing common uniform (%_) load kept in place and shared
  //
  // #version 140
  // in vec4 BaseColor;
  // in float fi;
  //
  // layout(std140) uniform U_t
  // {
  //     bool g_B;
  //     float g_F;
  // } ;
  //
  // void main()
  // {
  //     vec4 v = BaseColor;
  //     if (g_B)
  //       v = v * g_F;
  //     gl_FragColor = v;
  // }

  const std::string predefs =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor %fi
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_B"
OpMemberName %U_t 1 "g_F"
OpName %_ ""
OpName %gl_FragColor "gl_FragColor"
OpName %fi "fi"
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%10 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%uint = OpTypeInt 32 0
%U_t = OpTypeStruct %uint %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
%bool = OpTypeBool
%uint_0 = OpConstant %uint 0
%int_1 = OpConstant %int 1
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
)";

  const std::string before =
      R"(%main = OpFunction %void None %10
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpAccessChain %_ptr_Uniform_uint %_ %int_0
%29 = OpLoad %uint %28
%30 = OpINotEqual %bool %29 %uint_0
OpSelectionMerge %31 None
OpBranchConditional %30 %32 %31
%32 = OpLabel
%33 = OpLoad %v4float %v
%34 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%35 = OpLoad %float %34
%36 = OpVectorTimesScalar %v4float %33 %35
OpStore %v %36
OpBranch %31
%31 = OpLabel
%37 = OpLoad %v4float %v
OpStore %gl_FragColor %37
OpReturn
OpFunctionEnd
)";

  const std::string after =
      R"(%main = OpFunction %void None %10
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%38 = OpLoad %U_t %_
%39 = OpCompositeExtract %uint %38 0
%30 = OpINotEqual %bool %39 %uint_0
OpSelectionMerge %31 None
OpBranchConditional %30 %32 %31
%32 = OpLabel
%33 = OpLoad %v4float %v
%41 = OpCompositeExtract %float %38 1
%36 = OpVectorTimesScalar %v4float %33 %41
OpStore %v %36
OpBranch %31
%31 = OpLabel
%37 = OpLoad %v4float %v
OpStore %gl_FragColor %37
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndCheck<opt::CommonUniformElimPass>(
      predefs + before, predefs + after, true, true);
}

TEST_F(CommonUniformElimTest, Loop) {
  // Note: This test exemplifies the following:
  // - Common extract (g_F) shared between two loops
  // #version 140
  // in vec4 BC;
  // in vec4 BC2;
  //
  // layout(std140) uniform U_t
  // {
  //     float g_F;
  // } ;
  //
  // void main()
  // {
  //     vec4 v = BC;
  //     for (int i = 0; i < 4; i++)
  //       v[i] = v[i] / g_F;
  //     vec4 v2 = BC2;
  //     for (int i = 0; i < 4; i++)
  //       v2[i] = v2[i] * g_F;
  //     gl_FragColor = v + v2;
  // }

  const std::string predefs =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BC %BC2 %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BC "BC"
OpName %i "i"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpName %_ ""
OpName %v2 "v2"
OpName %BC2 "BC2"
OpName %i_0 "i"
OpName %gl_FragColor "gl_FragColor"
OpMemberDecorate %U_t 0 Offset 0
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%13 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BC = OpVariable %_ptr_Input_v4float Input
%int = OpTypeInt 32 1
%_ptr_Function_int = OpTypePointer Function %int
%int_0 = OpConstant %int 0
%int_4 = OpConstant %int 4
%bool = OpTypeBool
%_ptr_Function_float = OpTypePointer Function %float
%U_t = OpTypeStruct %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%_ptr_Uniform_float = OpTypePointer Uniform %float
%int_1 = OpConstant %int 1
%BC2 = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";

  const std::string before =
      R"(%main = OpFunction %void None %13
%28 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%i = OpVariable %_ptr_Function_int Function
%v2 = OpVariable %_ptr_Function_v4float Function
%i_0 = OpVariable %_ptr_Function_int Function
%29 = OpLoad %v4float %BC
OpStore %v %29
OpStore %i %int_0
OpBranch %30
%30 = OpLabel
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
%34 = OpLoad %int %i
%35 = OpSLessThan %bool %34 %int_4
OpBranchConditional %35 %36 %31
%36 = OpLabel
%37 = OpLoad %int %i
%38 = OpLoad %int %i
%39 = OpAccessChain %_ptr_Function_float %v %38
%40 = OpLoad %float %39
%41 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%42 = OpLoad %float %41
%43 = OpFDiv %float %40 %42
%44 = OpAccessChain %_ptr_Function_float %v %37
OpStore %44 %43
OpBranch %32
%32 = OpLabel
%45 = OpLoad %int %i
%46 = OpIAdd %int %45 %int_1
OpStore %i %46
OpBranch %30
%31 = OpLabel
%47 = OpLoad %v4float %BC2
OpStore %v2 %47
OpStore %i_0 %int_0
OpBranch %48
%48 = OpLabel
OpLoopMerge %49 %50 None
OpBranch %51
%51 = OpLabel
%52 = OpLoad %int %i_0
%53 = OpSLessThan %bool %52 %int_4
OpBranchConditional %53 %54 %49
%54 = OpLabel
%55 = OpLoad %int %i_0
%56 = OpLoad %int %i_0
%57 = OpAccessChain %_ptr_Function_float %v2 %56
%58 = OpLoad %float %57
%59 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%60 = OpLoad %float %59
%61 = OpFMul %float %58 %60
%62 = OpAccessChain %_ptr_Function_float %v2 %55
OpStore %62 %61
OpBranch %50
%50 = OpLabel
%63 = OpLoad %int %i_0
%64 = OpIAdd %int %63 %int_1
OpStore %i_0 %64
OpBranch %48
%49 = OpLabel
%65 = OpLoad %v4float %v
%66 = OpLoad %v4float %v2
%67 = OpFAdd %v4float %65 %66
OpStore %gl_FragColor %67
OpReturn
OpFunctionEnd
)";

  const std::string after =
      R"(%main = OpFunction %void None %13
%28 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%i = OpVariable %_ptr_Function_int Function
%v2 = OpVariable %_ptr_Function_v4float Function
%i_0 = OpVariable %_ptr_Function_int Function
%72 = OpLoad %U_t %_
%73 = OpCompositeExtract %float %72 0
%29 = OpLoad %v4float %BC
OpStore %v %29
OpStore %i %int_0
OpBranch %30
%30 = OpLabel
OpLoopMerge %31 %32 None
OpBranch %33
%33 = OpLabel
%34 = OpLoad %int %i
%35 = OpSLessThan %bool %34 %int_4
OpBranchConditional %35 %36 %31
%36 = OpLabel
%37 = OpLoad %int %i
%38 = OpLoad %int %i
%39 = OpAccessChain %_ptr_Function_float %v %38
%40 = OpLoad %float %39
%43 = OpFDiv %float %40 %73
%44 = OpAccessChain %_ptr_Function_float %v %37
OpStore %44 %43
OpBranch %32
%32 = OpLabel
%45 = OpLoad %int %i
%46 = OpIAdd %int %45 %int_1
OpStore %i %46
OpBranch %30
%31 = OpLabel
%47 = OpLoad %v4float %BC2
OpStore %v2 %47
OpStore %i_0 %int_0
OpBranch %48
%48 = OpLabel
OpLoopMerge %49 %50 None
OpBranch %51
%51 = OpLabel
%52 = OpLoad %int %i_0
%53 = OpSLessThan %bool %52 %int_4
OpBranchConditional %53 %54 %49
%54 = OpLabel
%55 = OpLoad %int %i_0
%56 = OpLoad %int %i_0
%57 = OpAccessChain %_ptr_Function_float %v2 %56
%58 = OpLoad %float %57
%61 = OpFMul %float %58 %73
%62 = OpAccessChain %_ptr_Function_float %v2 %55
OpStore %62 %61
OpBranch %50
%50 = OpLabel
%63 = OpLoad %int %i_0
%64 = OpIAdd %int %63 %int_1
OpStore %i_0 %64
OpBranch %48
%49 = OpLabel
%65 = OpLoad %v4float %v
%66 = OpLoad %v4float %v2
%67 = OpFAdd %v4float %65 %66
OpStore %gl_FragColor %67
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndCheck<opt::CommonUniformElimPass>(
      predefs + before, predefs + after, true, true);
}

TEST_F(CommonUniformElimTest, Volatile1) {
  // Note: This test exemplifies the following:
  // - Same test as Basic1 with the exception that
  //   the Load of g_F in else-branch is volatile
  // - Common uniform (%_) load floated to nearest non-controlled block
  //
  // #version 140
  // in vec4 BaseColor;
  // in float fi;
  //
  // layout(std140) uniform U_t
  // {
  //     float g_F;
  //     float g_F2;
  // } ;
  //
  // void main()
  // {
  //     vec4 v = BaseColor;
  //     if (fi > 0) {
  //       v = v * g_F;
  //     }
  //     else {
  //       float f2 = g_F2 - g_F;
  //       v = v * f2;
  //     }
  //     gl_FragColor = v;
  // }

  const std::string predefs =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi %gl_FragColor
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 140
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpName %gl_FragColor "gl_FragColor"
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t Block
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%11 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%U_t = OpTypeStruct %float %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%_ptr_Output_v4float = OpTypePointer Output %v4float
%gl_FragColor = OpVariable %_ptr_Output_v4float Output
)";

  const std::string before =
      R"(%main = OpFunction %void None %11
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpLoad %float %fi
%29 = OpFOrdGreaterThan %bool %28 %float_0
OpSelectionMerge %30 None
OpBranchConditional %29 %31 %32
%31 = OpLabel
%33 = OpLoad %v4float %v
%34 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%35 = OpLoad %float %34
%36 = OpVectorTimesScalar %v4float %33 %35
OpStore %v %36
OpBranch %30
%32 = OpLabel
%37 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%38 = OpLoad %float %37
%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%40 = OpLoad %float %39 Volatile
%41 = OpFSub %float %38 %40
OpStore %f2 %41
%42 = OpLoad %v4float %v
%43 = OpLoad %float %f2
%44 = OpVectorTimesScalar %v4float %42 %43
OpStore %v %44
OpBranch %30
%30 = OpLabel
%45 = OpLoad %v4float %v
OpStore %gl_FragColor %45
OpReturn
OpFunctionEnd
)";

  const std::string after =
      R"(%main = OpFunction %void None %11
%26 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%50 = OpLoad %U_t %_
%27 = OpLoad %v4float %BaseColor
OpStore %v %27
%28 = OpLoad %float %fi
%29 = OpFOrdGreaterThan %bool %28 %float_0
OpSelectionMerge %30 None
OpBranchConditional %29 %31 %32
%31 = OpLabel
%33 = OpLoad %v4float %v
%47 = OpCompositeExtract %float %50 0
%36 = OpVectorTimesScalar %v4float %33 %47
OpStore %v %36
OpBranch %30
%32 = OpLabel
%49 = OpCompositeExtract %float %50 1
%39 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%40 = OpLoad %float %39 Volatile
%41 = OpFSub %float %49 %40
OpStore %f2 %41
%42 = OpLoad %v4float %v
%43 = OpLoad %float %f2
%44 = OpVectorTimesScalar %v4float %42 %43
OpStore %v %44
OpBranch %30
%30 = OpLabel
%45 = OpLoad %v4float %v
OpStore %gl_FragColor %45
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndCheck<opt::CommonUniformElimPass>(
      predefs + before, predefs + after, true, true);
}

TEST_F(CommonUniformElimTest, Volatile2) {
  // Note: This test exemplifies the following:
  // - Same test as Basic1 with the exception that
  //   U_t is Volatile.
  // - No optimizations are applied
  //
  // #version 430
  // in vec4 BaseColor;
  // in float fi;
  //
  // layout(std430) volatile buffer U_t
  // {
  //   float g_F;
  //   float g_F2;
  // };
  //
  //
  // void main(void)
  // {
  //   vec4 v = BaseColor;
  //   if (fi > 0) {
  //     v = v * g_F;
  //   } else {
  //     float f2 = g_F2 - g_F;
  //     v = v * f2;
  //   }
  // }

  const std::string text =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpDecorate %BaseColor Location 0
OpDecorate %fi Location 0
OpMemberDecorate %U_t 0 Volatile
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Volatile
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t BufferBlock
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%U_t = OpTypeStruct %float %float
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%12 = OpLoad %v4float %BaseColor
OpStore %v %12
%15 = OpLoad %float %fi
%18 = OpFOrdGreaterThan %bool %15 %float_0
OpSelectionMerge %20 None
OpBranchConditional %18 %19 %31
%19 = OpLabel
%21 = OpLoad %v4float %v
%28 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%29 = OpLoad %float %28
%30 = OpVectorTimesScalar %v4float %21 %29
OpStore %v %30
OpBranch %20
%31 = OpLabel
%35 = OpAccessChain %_ptr_Uniform_float %_ %int_1
%36 = OpLoad %float %35
%37 = OpAccessChain %_ptr_Uniform_float %_ %int_0
%38 = OpLoad %float %37
%39 = OpFSub %float %36 %38
OpStore %f2 %39
%40 = OpLoad %v4float %v
%41 = OpLoad %float %f2
%42 = OpVectorTimesScalar %v4float %40 %41
OpStore %v %42
OpBranch %20
%20 = OpLabel
OpReturn
OpFunctionEnd
)";

  opt::Pass::Status res =
      std::get<1>(SinglePassRunAndDisassemble<opt::CommonUniformElimPass>(
          text, true, false));
  EXPECT_EQ(res, opt::Pass::Status::SuccessWithoutChange);
}

TEST_F(CommonUniformElimTest, Volatile3) {
  // Note: This test exemplifies the following:
  // - Same test as Volatile2 with the exception that
  //   the nested struct S is volatile
  // - No optimizations are applied
  //
  // #version 430
  // in vec4 BaseColor;
  // in float fi;
  //
  // struct S {
  //   volatile float a;
  // };
  //
  // layout(std430) buffer U_t
  // {
  //   S g_F;
  //   S g_F2;
  // };
  //
  //
  // void main(void)
  // {
  //   vec4 v = BaseColor;
  //   if (fi > 0) {
  //     v = v * g_F.a;
  //   } else {
  //     float f2 = g_F2.a - g_F.a;
  //     v = v * f2;
  //   }
  // }

  const std::string text =
      R"(OpCapability Shader
%1 = OpExtInstImport "GLSL.std.450"
OpMemoryModel Logical GLSL450
OpEntryPoint Fragment %main "main" %BaseColor %fi
OpExecutionMode %main OriginUpperLeft
OpSource GLSL 430
OpName %main "main"
OpName %v "v"
OpName %BaseColor "BaseColor"
OpName %fi "fi"
OpName %S "S"
OpMemberName %S 0 "a"
OpName %U_t "U_t"
OpMemberName %U_t 0 "g_F"
OpMemberName %U_t 1 "g_F2"
OpName %_ ""
OpName %f2 "f2"
OpDecorate %BaseColor Location 0
OpDecorate %fi Location 0
OpMemberDecorate %S 0 Offset 0
OpMemberDecorate %S 0 Volatile
OpMemberDecorate %U_t 0 Offset 0
OpMemberDecorate %U_t 1 Offset 4
OpDecorate %U_t BufferBlock
OpDecorate %_ DescriptorSet 0
%void = OpTypeVoid
%3 = OpTypeFunction %void
%float = OpTypeFloat 32
%v4float = OpTypeVector %float 4
%_ptr_Function_v4float = OpTypePointer Function %v4float
%_ptr_Input_v4float = OpTypePointer Input %v4float
%BaseColor = OpVariable %_ptr_Input_v4float Input
%_ptr_Input_float = OpTypePointer Input %float
%fi = OpVariable %_ptr_Input_float Input
%float_0 = OpConstant %float 0
%bool = OpTypeBool
%S = OpTypeStruct %float
%U_t = OpTypeStruct %S %S
%_ptr_Uniform_U_t = OpTypePointer Uniform %U_t
%_ = OpVariable %_ptr_Uniform_U_t Uniform
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%_ptr_Uniform_float = OpTypePointer Uniform %float
%_ptr_Function_float = OpTypePointer Function %float
%int_1 = OpConstant %int 1
%main = OpFunction %void None %3
%5 = OpLabel
%v = OpVariable %_ptr_Function_v4float Function
%f2 = OpVariable %_ptr_Function_float Function
%12 = OpLoad %v4float %BaseColor
OpStore %v %12
%15 = OpLoad %float %fi
%18 = OpFOrdGreaterThan %bool %15 %float_0
OpSelectionMerge %20 None
OpBranchConditional %18 %19 %32
%19 = OpLabel
%21 = OpLoad %v4float %v
%29 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0
%30 = OpLoad %float %29
%31 = OpVectorTimesScalar %v4float %21 %30
OpStore %v %31
OpBranch %20
%32 = OpLabel
%36 = OpAccessChain %_ptr_Uniform_float %_ %int_1 %int_0
%37 = OpLoad %float %36
%38 = OpAccessChain %_ptr_Uniform_float %_ %int_0 %int_0
%39 = OpLoad %float %38
%40 = OpFSub %float %37 %39
OpStore %f2 %40
%41 = OpLoad %v4float %v
%42 = OpLoad %float %f2
%43 = OpVectorTimesScalar %v4float %41 %42
OpStore %v %43
OpBranch %20
%20 = OpLabel
OpReturn
OpFunctionEnd
)";

  opt::Pass::Status res =
      std::get<1>(SinglePassRunAndDisassemble<opt::CommonUniformElimPass>(
          text, true, false));
  EXPECT_EQ(res, opt::Pass::Status::SuccessWithoutChange);
}

#ifdef SPIRV_EFFCEE
TEST_F(CommonUniformElimTest, MixedConstantAndNonConstantIndexes) {
  const std::string text = R"(
; CHECK: [[var:%\w+]] = OpVariable {{%\w+}} Uniform
; CHECK: %501 = OpLabel
; CHECK: [[ld:%\w+]] = OpLoad
; CHECK-NOT: OpCompositeExtract {{%\w+}} {{%\w+}} 0 2 484
; CHECK: OpAccessChain {{%\w+}} [[var]] %int_0 %int_2 [[ld]]
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %4 "ringeffectLayer_px" %gl_FragCoord %178 %182
               OpExecutionMode %4 OriginUpperLeft
               OpSource HLSL 500
               OpDecorate %_arr_v4float_uint_10 ArrayStride 16
               OpMemberDecorate %_struct_20 0 Offset 0
               OpMemberDecorate %_struct_20 1 Offset 16
               OpMemberDecorate %_struct_20 2 Offset 32
               OpMemberDecorate %_struct_21 0 Offset 0
               OpDecorate %_struct_21 Block
               OpDecorate %23 DescriptorSet 0
               OpDecorate %gl_FragCoord BuiltIn FragCoord
               OpDecorate %178 Location 0
               OpDecorate %182 Location 0
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
    %v2float = OpTypeVector %float 2
%_ptr_Function_v2float = OpTypePointer Function %v2float
       %uint = OpTypeInt 32 0
    %uint_10 = OpConstant %uint 10
%_arr_v4float_uint_10 = OpTypeArray %v4float %uint_10
 %_struct_20 = OpTypeStruct %v4float %v4float %_arr_v4float_uint_10
 %_struct_21 = OpTypeStruct %_struct_20
%_ptr_Uniform__struct_21 = OpTypePointer Uniform %_struct_21
         %23 = OpVariable %_ptr_Uniform__struct_21 Uniform
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
%_ptr_Uniform_v4float = OpTypePointer Uniform %v4float
%_ptr_Uniform_float = OpTypePointer Uniform %float
     %uint_3 = OpConstant %uint 3
%_ptr_Function_v4float = OpTypePointer Function %v4float
    %float_0 = OpConstant %float 0
         %43 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
%_ptr_Function_int = OpTypePointer Function %int
      %int_5 = OpConstant %int 5
       %bool = OpTypeBool
      %int_1 = OpConstant %int 1
      %int_2 = OpConstant %int 2
     %uint_5 = OpConstant %uint 5
%_arr_v2float_uint_5 = OpTypeArray %v2float %uint_5
%_ptr_Function__arr_v2float_uint_5 = OpTypePointer Function %_arr_v2float_uint_5
         %82 = OpTypeImage %float 2D 0 0 0 1 Unknown
%_ptr_UniformConstant_82 = OpTypePointer UniformConstant %82
         %86 = OpTypeSampler
%_ptr_UniformConstant_86 = OpTypePointer UniformConstant %86
         %90 = OpTypeSampledImage %82
    %v3float = OpTypeVector %float 3
%_ptr_Input_v4float = OpTypePointer Input %v4float
%gl_FragCoord = OpVariable %_ptr_Input_v4float Input
        %178 = OpVariable %_ptr_Input_v4float Input
%_ptr_Output_v4float = OpTypePointer Output %v4float
        %182 = OpVariable %_ptr_Output_v4float Output
          %4 = OpFunction %void None %3
          %5 = OpLabel
        %483 = OpVariable %_ptr_Function_v4float Function
        %484 = OpVariable %_ptr_Function_int Function
        %486 = OpVariable %_ptr_Function__arr_v2float_uint_5 Function
        %179 = OpLoad %v4float %178
        %493 = OpAccessChain %_ptr_Uniform_float %23 %int_0 %int_0 %uint_3
        %494 = OpLoad %float %493
               OpStore %483 %43
               OpStore %484 %int_0
               OpBranch %495
        %495 = OpLabel
               OpLoopMerge %496 %497 None
               OpBranch %498
        %498 = OpLabel
        %499 = OpLoad %int %484
        %500 = OpSLessThan %bool %499 %int_5
               OpBranchConditional %500 %501 %496
        %501 = OpLabel
        %504 = OpVectorShuffle %v2float %179 %179 0 1
        %505 = OpLoad %int %484
        %506 = OpAccessChain %_ptr_Uniform_v4float %23 %int_0 %int_2 %505
        %507 = OpLoad %v4float %506
        %508 = OpVectorShuffle %v2float %507 %507 0 1
        %509 = OpFAdd %v2float %504 %508
        %512 = OpAccessChain %_ptr_Uniform_v4float %23 %int_0 %int_1
        %513 = OpLoad %v4float %512
        %514 = OpVectorShuffle %v2float %513 %513 0 1
        %517 = OpVectorShuffle %v2float %513 %513 2 3
        %518 = OpExtInst %v2float %1 FClamp %509 %514 %517
        %519 = OpAccessChain %_ptr_Function_v2float %486 %505
               OpStore %519 %518
               OpBranch %497
        %497 = OpLabel
        %520 = OpLoad %int %484
        %521 = OpIAdd %int %520 %int_1
               OpStore %484 %521
               OpBranch %495
        %496 = OpLabel
               OpReturn
               OpFunctionEnd
)";

  SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  SinglePassRunAndMatch<opt::CommonUniformElimPass>(text, true);
}
#endif  //  SPIRV_EFFCEE
// TODO(greg-lunarg): Add tests to verify handling of these cases:
//
//    Disqualifying cases: extensions, decorations, non-logical addressing,
//      non-structured control flow
//    Others?

}  // anonymous namespace
