text: // 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 <memory>
  #include <string>
  #include <vector>
  
  #include "test/opt/pass_fixture.h"
  #include "test/opt/pass_utils.h"
  
  namespace spvtools {
  namespace opt {
  namespace {
  
  using InlineTest = PassTest<::testing::Test>;
  
  TEST_F(InlineTest, Simple) {
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     return bar.x + bar.y;
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //     gl_FragColor = color;
    // }
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 140",
                 "OpName %main \"main\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %color \"color\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param \"param\"",
                 "OpName %gl_FragColor \"gl_FragColor\"",
         "%void = OpTypeVoid",
           "%10 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%14 = OpTypeFunction %float %_ptr_Function_v4float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
  "%_ptr_Function_float = OpTypePointer Function %float",
       "%uint_1 = OpConstant %uint 1",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%BaseColor = OpVariable %_ptr_Input_v4float Input",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
  "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
     "%foo_vf4_ = OpFunction %float None %14",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%26 = OpLabel",
           "%27 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%28 = OpLoad %float %27",
           "%29 = OpAccessChain %_ptr_Function_float %bar %uint_1",
           "%30 = OpLoad %float %29",
           "%31 = OpFAdd %float %28 %30",
                 "OpReturnValue %31",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %10",
           "%21 = OpLabel",
        "%color = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
           "%22 = OpLoad %v4float %BaseColor",
                 "OpStore %param %22",
           "%23 = OpFunctionCall %float %foo_vf4_ %param",
           "%24 = OpCompositeConstruct %v4float %23 %23 %23 %23",
                 "OpStore %color %24",
           "%25 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %25",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %10",
           "%21 = OpLabel",
           "%32 = OpVariable %_ptr_Function_float Function",
        "%color = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
           "%22 = OpLoad %v4float %BaseColor",
                 "OpStore %param %22",
           "%34 = OpAccessChain %_ptr_Function_float %param %uint_0",
           "%35 = OpLoad %float %34",
           "%36 = OpAccessChain %_ptr_Function_float %param %uint_1",
           "%37 = OpLoad %float %36",
           "%38 = OpFAdd %float %35 %37",
                 "OpStore %32 %38",
           "%23 = OpLoad %float %32",
           "%24 = OpCompositeConstruct %v4float %23 %23 %23 %23",
                 "OpStore %color %24",
           "%25 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %25",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, Nested) {
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo2(float f, float f2)
    // {
    //     return f * f2;
    // }
    //
    // float foo(vec4 bar)
    // {
    //     return foo2(bar.x + bar.y, bar.z);
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //     gl_FragColor = color;
    // }
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 140",
                 "OpName %main \"main\"",
                 "OpName %foo2_f1_f1_ \"foo2(f1;f1;\"",
                 "OpName %f \"f\"",
                 "OpName %f2 \"f2\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %param \"param\"",
                 "OpName %param_0 \"param\"",
                 "OpName %color \"color\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param_1 \"param\"",
                 "OpName %gl_FragColor \"gl_FragColor\"",
         "%void = OpTypeVoid",
           "%15 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
  "%_ptr_Function_float = OpTypePointer Function %float",
           "%18 = OpTypeFunction %float %_ptr_Function_float %_ptr_Function_float",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%21 = OpTypeFunction %float %_ptr_Function_v4float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
       "%uint_1 = OpConstant %uint 1",
       "%uint_2 = OpConstant %uint 2",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%BaseColor = OpVariable %_ptr_Input_v4float Input",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
  "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
  "%foo2_f1_f1_ = OpFunction %float None %18",
            "%f = OpFunctionParameter %_ptr_Function_float",
           "%f2 = OpFunctionParameter %_ptr_Function_float",
           "%33 = OpLabel",
           "%34 = OpLoad %float %f",
           "%35 = OpLoad %float %f2",
           "%36 = OpFMul %float %34 %35",
                 "OpReturnValue %36",
                 "OpFunctionEnd",
     "%foo_vf4_ = OpFunction %float None %21",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%37 = OpLabel",
        "%param = OpVariable %_ptr_Function_float Function",
      "%param_0 = OpVariable %_ptr_Function_float Function",
           "%38 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%39 = OpLoad %float %38",
           "%40 = OpAccessChain %_ptr_Function_float %bar %uint_1",
           "%41 = OpLoad %float %40",
           "%42 = OpFAdd %float %39 %41",
                 "OpStore %param %42",
           "%43 = OpAccessChain %_ptr_Function_float %bar %uint_2",
           "%44 = OpLoad %float %43",
                 "OpStore %param_0 %44",
           "%45 = OpFunctionCall %float %foo2_f1_f1_ %param %param_0",
                 "OpReturnValue %45",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %15",
           "%28 = OpLabel",
        "%color = OpVariable %_ptr_Function_v4float Function",
      "%param_1 = OpVariable %_ptr_Function_v4float Function",
           "%29 = OpLoad %v4float %BaseColor",
                 "OpStore %param_1 %29",
           "%30 = OpFunctionCall %float %foo_vf4_ %param_1",
           "%31 = OpCompositeConstruct %v4float %30 %30 %30 %30",
                 "OpStore %color %31",
           "%32 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %32",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %15",
           "%28 = OpLabel",
           "%58 = OpVariable %_ptr_Function_float Function",
           "%46 = OpVariable %_ptr_Function_float Function",
           "%47 = OpVariable %_ptr_Function_float Function",
           "%48 = OpVariable %_ptr_Function_float Function",
        "%color = OpVariable %_ptr_Function_v4float Function",
      "%param_1 = OpVariable %_ptr_Function_v4float Function",
           "%29 = OpLoad %v4float %BaseColor",
                 "OpStore %param_1 %29",
           "%50 = OpAccessChain %_ptr_Function_float %param_1 %uint_0",
           "%51 = OpLoad %float %50",
           "%52 = OpAccessChain %_ptr_Function_float %param_1 %uint_1",
           "%53 = OpLoad %float %52",
           "%54 = OpFAdd %float %51 %53",
                 "OpStore %46 %54",
           "%55 = OpAccessChain %_ptr_Function_float %param_1 %uint_2",
           "%56 = OpLoad %float %55",
                 "OpStore %47 %56",
           "%60 = OpLoad %float %46",
           "%61 = OpLoad %float %47",
           "%62 = OpFMul %float %60 %61",
                 "OpStore %58 %62",
           "%57 = OpLoad %float %58",
                 "OpStore %48 %57",
           "%30 = OpLoad %float %48",
           "%31 = OpCompositeConstruct %v4float %30 %30 %30 %30",
                 "OpStore %color %31",
           "%32 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %32",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, InOutParameter) {
    // #version 400
    //
    // in vec4 Basecolor;
    //
    // void foo(inout vec4 bar)
    // {
    //     bar.z = bar.x + bar.y;
    // }
    //
    // void main()
    // {
    //     vec4 b = Basecolor;
    //     foo(b);
    //     vec4 color = vec4(b.z);
    //     gl_FragColor = color;
    // }
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %Basecolor %gl_FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 400",
                 "OpName %main \"main\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %b \"b\"",
                 "OpName %Basecolor \"Basecolor\"",
                 "OpName %param \"param\"",
                 "OpName %color \"color\"",
                 "OpName %gl_FragColor \"gl_FragColor\"",
         "%void = OpTypeVoid",
           "%11 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%15 = OpTypeFunction %void %_ptr_Function_v4float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
  "%_ptr_Function_float = OpTypePointer Function %float",
       "%uint_1 = OpConstant %uint 1",
       "%uint_2 = OpConstant %uint 2",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%Basecolor = OpVariable %_ptr_Input_v4float Input",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
  "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
     "%foo_vf4_ = OpFunction %void None %15",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%32 = OpLabel",
           "%33 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%34 = OpLoad %float %33",
           "%35 = OpAccessChain %_ptr_Function_float %bar %uint_1",
           "%36 = OpLoad %float %35",
           "%37 = OpFAdd %float %34 %36",
           "%38 = OpAccessChain %_ptr_Function_float %bar %uint_2",
                 "OpStore %38 %37",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %11",
           "%23 = OpLabel",
            "%b = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
        "%color = OpVariable %_ptr_Function_v4float Function",
           "%24 = OpLoad %v4float %Basecolor",
                 "OpStore %b %24",
           "%25 = OpLoad %v4float %b",
                 "OpStore %param %25",
           "%26 = OpFunctionCall %void %foo_vf4_ %param",
           "%27 = OpLoad %v4float %param",
                 "OpStore %b %27",
           "%28 = OpAccessChain %_ptr_Function_float %b %uint_2",
           "%29 = OpLoad %float %28",
           "%30 = OpCompositeConstruct %v4float %29 %29 %29 %29",
                 "OpStore %color %30",
           "%31 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %31",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %11",
           "%23 = OpLabel",
            "%b = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
        "%color = OpVariable %_ptr_Function_v4float Function",
           "%24 = OpLoad %v4float %Basecolor",
                 "OpStore %b %24",
           "%25 = OpLoad %v4float %b",
                 "OpStore %param %25",
           "%40 = OpAccessChain %_ptr_Function_float %param %uint_0",
           "%41 = OpLoad %float %40",
           "%42 = OpAccessChain %_ptr_Function_float %param %uint_1",
           "%43 = OpLoad %float %42",
           "%44 = OpFAdd %float %41 %43",
           "%45 = OpAccessChain %_ptr_Function_float %param %uint_2",
                 "OpStore %45 %44",
           "%27 = OpLoad %v4float %param",
                 "OpStore %b %27",
           "%28 = OpAccessChain %_ptr_Function_float %b %uint_2",
           "%29 = OpLoad %float %28",
           "%30 = OpCompositeConstruct %v4float %29 %29 %29 %29",
                 "OpStore %color %30",
           "%31 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %31",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, BranchInCallee) {
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     float r = bar.x;
    //     if (r < 0.0)
    //         r = -r;
    //     return r;
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //
    //     gl_FragColor = color;
    // }
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 140",
                 "OpName %main \"main\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %r \"r\"",
                 "OpName %color \"color\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param \"param\"",
                 "OpName %gl_FragColor \"gl_FragColor\"",
         "%void = OpTypeVoid",
           "%11 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%15 = OpTypeFunction %float %_ptr_Function_v4float",
  "%_ptr_Function_float = OpTypePointer Function %float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
      "%float_0 = OpConstant %float 0",
         "%bool = OpTypeBool",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%BaseColor = OpVariable %_ptr_Input_v4float Input",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
  "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
     "%foo_vf4_ = OpFunction %float None %15",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%28 = OpLabel",
            "%r = OpVariable %_ptr_Function_float Function",
           "%29 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%30 = OpLoad %float %29",
                 "OpStore %r %30",
           "%31 = OpLoad %float %r",
           "%32 = OpFOrdLessThan %bool %31 %float_0",
                 "OpSelectionMerge %33 None",
                 "OpBranchConditional %32 %34 %33",
           "%34 = OpLabel",
           "%35 = OpLoad %float %r",
           "%36 = OpFNegate %float %35",
                 "OpStore %r %36",
                 "OpBranch %33",
           "%33 = OpLabel",
           "%37 = OpLoad %float %r",
                 "OpReturnValue %37",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %11",
           "%23 = OpLabel",
        "%color = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
           "%24 = OpLoad %v4float %BaseColor",
                 "OpStore %param %24",
           "%25 = OpFunctionCall %float %foo_vf4_ %param",
           "%26 = OpCompositeConstruct %v4float %25 %25 %25 %25",
                 "OpStore %color %26",
           "%27 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %27",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %11",
           "%23 = OpLabel",
           "%38 = OpVariable %_ptr_Function_float Function",
           "%39 = OpVariable %_ptr_Function_float Function",
        "%color = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
           "%24 = OpLoad %v4float %BaseColor",
                 "OpStore %param %24",
           "%41 = OpAccessChain %_ptr_Function_float %param %uint_0",
           "%42 = OpLoad %float %41",
                 "OpStore %38 %42",
           "%43 = OpLoad %float %38",
           "%44 = OpFOrdLessThan %bool %43 %float_0",
                 "OpSelectionMerge %48 None",
                 "OpBranchConditional %44 %45 %48",
           "%45 = OpLabel",
           "%46 = OpLoad %float %38",
           "%47 = OpFNegate %float %46",
                 "OpStore %38 %47",
                 "OpBranch %48",
           "%48 = OpLabel",
           "%49 = OpLoad %float %38",
                 "OpStore %39 %49",
           "%25 = OpLoad %float %39",
           "%26 = OpCompositeConstruct %v4float %25 %25 %25 %25",
                 "OpStore %color %26",
           "%27 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %27",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, PhiAfterCall) {
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(float bar)
    // {
    //     float r = bar;
    //     if (r < 0.0)
    //         r = -r;
    //     return r;
    // }
    //
    // void main()
    // {
    //     vec4 color = BaseColor;
    //     if (foo(color.x) > 2.0 && foo(color.y) > 2.0)
    //         color = vec4(0.0);
    //     gl_FragColor = color;
    // }
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %gl_FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 140",
                 "OpName %main \"main\"",
                 "OpName %foo_f1_ \"foo(f1;\"",
                 "OpName %bar \"bar\"",
                 "OpName %r \"r\"",
                 "OpName %color \"color\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param \"param\"",
                 "OpName %param_0 \"param\"",
                 "OpName %gl_FragColor \"gl_FragColor\"",
         "%void = OpTypeVoid",
           "%12 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
  "%_ptr_Function_float = OpTypePointer Function %float",
           "%15 = OpTypeFunction %float %_ptr_Function_float",
      "%float_0 = OpConstant %float 0",
         "%bool = OpTypeBool",
      "%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",
       "%uint_0 = OpConstant %uint 0",
      "%float_2 = OpConstant %float 2",
       "%uint_1 = OpConstant %uint 1",
           "%25 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
  "%gl_FragColor = OpVariable %_ptr_Output_v4float Output",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
      "%foo_f1_ = OpFunction %float None %15",
          "%bar = OpFunctionParameter %_ptr_Function_float",
           "%43 = OpLabel",
            "%r = OpVariable %_ptr_Function_float Function",
           "%44 = OpLoad %float %bar",
                 "OpStore %r %44",
           "%45 = OpLoad %float %r",
           "%46 = OpFOrdLessThan %bool %45 %float_0",
                 "OpSelectionMerge %47 None",
                 "OpBranchConditional %46 %48 %47",
           "%48 = OpLabel",
           "%49 = OpLoad %float %r",
           "%50 = OpFNegate %float %49",
                 "OpStore %r %50",
                 "OpBranch %47",
           "%47 = OpLabel",
           "%51 = OpLoad %float %r",
                 "OpReturnValue %51",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %12",
           "%27 = OpLabel",
        "%color = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_float Function",
      "%param_0 = OpVariable %_ptr_Function_float Function",
           "%28 = OpLoad %v4float %BaseColor",
                 "OpStore %color %28",
           "%29 = OpAccessChain %_ptr_Function_float %color %uint_0",
           "%30 = OpLoad %float %29",
                 "OpStore %param %30",
           "%31 = OpFunctionCall %float %foo_f1_ %param",
           "%32 = OpFOrdGreaterThan %bool %31 %float_2",
                 "OpSelectionMerge %33 None",
                 "OpBranchConditional %32 %34 %33",
           "%34 = OpLabel",
           "%35 = OpAccessChain %_ptr_Function_float %color %uint_1",
           "%36 = OpLoad %float %35",
                 "OpStore %param_0 %36",
           "%37 = OpFunctionCall %float %foo_f1_ %param_0",
           "%38 = OpFOrdGreaterThan %bool %37 %float_2",
                 "OpBranch %33",
           "%33 = OpLabel",
           "%39 = OpPhi %bool %32 %27 %38 %34",
                 "OpSelectionMerge %40 None",
                 "OpBranchConditional %39 %41 %40",
           "%41 = OpLabel",
                 "OpStore %color %25",
                 "OpBranch %40",
           "%40 = OpLabel",
           "%42 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %42",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %12",
           "%27 = OpLabel",
           "%63 = OpVariable %_ptr_Function_float Function",
           "%64 = OpVariable %_ptr_Function_float Function",
           "%52 = OpVariable %_ptr_Function_float Function",
           "%53 = OpVariable %_ptr_Function_float Function",
        "%color = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_float Function",
      "%param_0 = OpVariable %_ptr_Function_float Function",
           "%28 = OpLoad %v4float %BaseColor",
                 "OpStore %color %28",
           "%29 = OpAccessChain %_ptr_Function_float %color %uint_0",
           "%30 = OpLoad %float %29",
                 "OpStore %param %30",
           "%55 = OpLoad %float %param",
                 "OpStore %52 %55",
           "%56 = OpLoad %float %52",
           "%57 = OpFOrdLessThan %bool %56 %float_0",
                 "OpSelectionMerge %61 None",
                 "OpBranchConditional %57 %58 %61",
           "%58 = OpLabel",
           "%59 = OpLoad %float %52",
           "%60 = OpFNegate %float %59",
                 "OpStore %52 %60",
                 "OpBranch %61",
           "%61 = OpLabel",
           "%62 = OpLoad %float %52",
                 "OpStore %53 %62",
           "%31 = OpLoad %float %53",
           "%32 = OpFOrdGreaterThan %bool %31 %float_2",
                 "OpSelectionMerge %33 None",
                 "OpBranchConditional %32 %34 %33",
           "%34 = OpLabel",
           "%35 = OpAccessChain %_ptr_Function_float %color %uint_1",
           "%36 = OpLoad %float %35",
                 "OpStore %param_0 %36",
           "%66 = OpLoad %float %param_0",
                 "OpStore %63 %66",
           "%67 = OpLoad %float %63",
           "%68 = OpFOrdLessThan %bool %67 %float_0",
                 "OpSelectionMerge %72 None",
                 "OpBranchConditional %68 %69 %72",
           "%69 = OpLabel",
           "%70 = OpLoad %float %63",
           "%71 = OpFNegate %float %70",
                 "OpStore %63 %71",
                 "OpBranch %72",
           "%72 = OpLabel",
           "%73 = OpLoad %float %63",
                 "OpStore %64 %73",
           "%37 = OpLoad %float %64",
           "%38 = OpFOrdGreaterThan %bool %37 %float_2",
                 "OpBranch %33",
           "%33 = OpLabel",
           "%39 = OpPhi %bool %32 %61 %38 %72",
                 "OpSelectionMerge %40 None",
                 "OpBranchConditional %39 %41 %40",
           "%41 = OpLabel",
                 "OpStore %color %25",
                 "OpBranch %40",
           "%40 = OpLabel",
           "%42 = OpLoad %v4float %color",
                 "OpStore %gl_FragColor %42",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, OpSampledImageOutOfBlock) {
    // #version 450
    //
    // uniform texture2D t2D;
    // uniform sampler samp;
    // out vec4 FragColor;
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     float r = bar.x;
    //     if (r < 0.0)
    //         r = -r;
    //     return r;
    // }
    //
    // void main()
    // {
    //     vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0));
    //     vec4 color2 = vec4(foo(BaseColor));
    //     vec4 color3 = texture(sampler2D(t2D, samp), vec2(0.5));
    //     FragColor = (color1 + color2 + color3)/3;
    // }
    //
    // Note: the before SPIR-V will need to be edited to create a use of
    // the OpSampledImage across the function call.
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 450",
                 "OpName %main \"main\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %r \"r\"",
                 "OpName %color1 \"color1\"",
                 "OpName %t2D \"t2D\"",
                 "OpName %samp \"samp\"",
                 "OpName %color2 \"color2\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param \"param\"",
                 "OpName %color3 \"color3\"",
                 "OpName %FragColor \"FragColor\"",
                 "OpDecorate %t2D DescriptorSet 0",
                 "OpDecorate %samp DescriptorSet 0",
         "%void = OpTypeVoid",
           "%15 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%19 = OpTypeFunction %float %_ptr_Function_v4float",
  "%_ptr_Function_float = OpTypePointer Function %float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
      "%float_0 = OpConstant %float 0",
         "%bool = OpTypeBool",
           "%25 = OpTypeImage %float 2D 0 0 0 1 Unknown",
  "%_ptr_UniformConstant_25 = OpTypePointer UniformConstant %25",
          "%t2D = OpVariable %_ptr_UniformConstant_25 UniformConstant",
           "%27 = OpTypeSampler",
  "%_ptr_UniformConstant_27 = OpTypePointer UniformConstant %27",
         "%samp = OpVariable %_ptr_UniformConstant_27 UniformConstant",
           "%29 = OpTypeSampledImage %25",
      "%v2float = OpTypeVector %float 2",
      "%float_1 = OpConstant %float 1",
           "%32 = OpConstantComposite %v2float %float_1 %float_1",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%BaseColor = OpVariable %_ptr_Input_v4float Input",
    "%float_0_5 = OpConstant %float 0.5",
           "%35 = OpConstantComposite %v2float %float_0_5 %float_0_5",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
    "%FragColor = OpVariable %_ptr_Output_v4float Output",
      "%float_3 = OpConstant %float 3",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
     "%foo_vf4_ = OpFunction %float None %19",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%56 = OpLabel",
            "%r = OpVariable %_ptr_Function_float Function",
           "%57 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%58 = OpLoad %float %57",
                 "OpStore %r %58",
           "%59 = OpLoad %float %r",
           "%60 = OpFOrdLessThan %bool %59 %float_0",
                 "OpSelectionMerge %61 None",
                 "OpBranchConditional %60 %62 %61",
           "%62 = OpLabel",
           "%63 = OpLoad %float %r",
           "%64 = OpFNegate %float %63",
                 "OpStore %r %64",
                 "OpBranch %61",
           "%61 = OpLabel",
           "%65 = OpLoad %float %r",
                 "OpReturnValue %65",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %15",
           "%38 = OpLabel",
       "%color1 = OpVariable %_ptr_Function_v4float Function",
       "%color2 = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
       "%color3 = OpVariable %_ptr_Function_v4float Function",
           "%39 = OpLoad %25 %t2D",
           "%40 = OpLoad %27 %samp",
           "%41 = OpSampledImage %29 %39 %40",
           "%42 = OpImageSampleImplicitLod %v4float %41 %32",
                 "OpStore %color1 %42",
           "%43 = OpLoad %v4float %BaseColor",
                 "OpStore %param %43",
           "%44 = OpFunctionCall %float %foo_vf4_ %param",
           "%45 = OpCompositeConstruct %v4float %44 %44 %44 %44",
                 "OpStore %color2 %45",
           "%46 = OpLoad %25 %t2D",
           "%47 = OpLoad %27 %samp",
           "%48 = OpImageSampleImplicitLod %v4float %41 %35",
                 "OpStore %color3 %48",
           "%49 = OpLoad %v4float %color1",
           "%50 = OpLoad %v4float %color2",
           "%51 = OpFAdd %v4float %49 %50",
           "%52 = OpLoad %v4float %color3",
           "%53 = OpFAdd %v4float %51 %52",
           "%54 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
           "%55 = OpFDiv %v4float %53 %54",
                 "OpStore %FragColor %55",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %15",
           "%38 = OpLabel",
           "%66 = OpVariable %_ptr_Function_float Function",
           "%67 = OpVariable %_ptr_Function_float Function",
       "%color1 = OpVariable %_ptr_Function_v4float Function",
       "%color2 = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
       "%color3 = OpVariable %_ptr_Function_v4float Function",
           "%39 = OpLoad %25 %t2D",
           "%40 = OpLoad %27 %samp",
           "%41 = OpSampledImage %29 %39 %40",
           "%42 = OpImageSampleImplicitLod %v4float %41 %32",
                 "OpStore %color1 %42",
           "%43 = OpLoad %v4float %BaseColor",
                 "OpStore %param %43",
           "%69 = OpAccessChain %_ptr_Function_float %param %uint_0",
           "%70 = OpLoad %float %69",
                 "OpStore %66 %70",
           "%71 = OpLoad %float %66",
           "%72 = OpFOrdLessThan %bool %71 %float_0",
                 "OpSelectionMerge %76 None",
                 "OpBranchConditional %72 %73 %76",
           "%73 = OpLabel",
           "%74 = OpLoad %float %66",
           "%75 = OpFNegate %float %74",
                 "OpStore %66 %75",
                 "OpBranch %76",
           "%76 = OpLabel",
           "%77 = OpLoad %float %66",
                 "OpStore %67 %77",
           "%44 = OpLoad %float %67",
           "%45 = OpCompositeConstruct %v4float %44 %44 %44 %44",
                 "OpStore %color2 %45",
           "%46 = OpLoad %25 %t2D",
           "%47 = OpLoad %27 %samp",
           "%78 = OpSampledImage %29 %39 %40",
           "%48 = OpImageSampleImplicitLod %v4float %78 %35",
                 "OpStore %color3 %48",
           "%49 = OpLoad %v4float %color1",
           "%50 = OpLoad %v4float %color2",
           "%51 = OpFAdd %v4float %49 %50",
           "%52 = OpLoad %v4float %color3",
           "%53 = OpFAdd %v4float %51 %52",
           "%54 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
           "%55 = OpFDiv %v4float %53 %54",
                 "OpStore %FragColor %55",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, OpImageOutOfBlock) {
    // #version 450
    //
    // uniform texture2D t2D;
    // uniform sampler samp;
    // uniform sampler samp2;
    //
    // out vec4 FragColor;
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     float r = bar.x;
    //     if (r < 0.0)
    //         r = -r;
    //     return r;
    // }
    //
    // void main()
    // {
    //     vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0));
    //     vec4 color2 = vec4(foo(BaseColor));
    //     vec4 color3 = texture(sampler2D(t2D, samp2), vec2(0.5));
    //     FragColor = (color1 + color2 + color3)/3;
    // }
    // Note: the before SPIR-V will need to be edited to create an OpImage
    // from the first OpSampledImage, place it before the call and use it
    // in the second OpSampledImage following the call.
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 450",
                 "OpName %main \"main\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %r \"r\"",
                 "OpName %color1 \"color1\"",
                 "OpName %t2D \"t2D\"",
                 "OpName %samp \"samp\"",
                 "OpName %color2 \"color2\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param \"param\"",
                 "OpName %color3 \"color3\"",
                 "OpName %samp2 \"samp2\"",
                 "OpName %FragColor \"FragColor\"",
                 "OpDecorate %t2D DescriptorSet 0",
                 "OpDecorate %samp DescriptorSet 0",
                 "OpDecorate %samp2 DescriptorSet 0",
         "%void = OpTypeVoid",
           "%16 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%20 = OpTypeFunction %float %_ptr_Function_v4float",
  "%_ptr_Function_float = OpTypePointer Function %float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
      "%float_0 = OpConstant %float 0",
         "%bool = OpTypeBool",
           "%26 = OpTypeImage %float 2D 0 0 0 1 Unknown",
  "%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26",
          "%t2D = OpVariable %_ptr_UniformConstant_26 UniformConstant",
           "%28 = OpTypeSampler",
  "%_ptr_UniformConstant_28 = OpTypePointer UniformConstant %28",
         "%samp = OpVariable %_ptr_UniformConstant_28 UniformConstant",
           "%30 = OpTypeSampledImage %26",
      "%v2float = OpTypeVector %float 2",
      "%float_1 = OpConstant %float 1",
           "%33 = OpConstantComposite %v2float %float_1 %float_1",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%BaseColor = OpVariable %_ptr_Input_v4float Input",
        "%samp2 = OpVariable %_ptr_UniformConstant_28 UniformConstant",
    "%float_0_5 = OpConstant %float 0.5",
           "%36 = OpConstantComposite %v2float %float_0_5 %float_0_5",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
    "%FragColor = OpVariable %_ptr_Output_v4float Output",
      "%float_3 = OpConstant %float 3",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
     "%foo_vf4_ = OpFunction %float None %20",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%58 = OpLabel",
            "%r = OpVariable %_ptr_Function_float Function",
           "%59 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%60 = OpLoad %float %59",
                 "OpStore %r %60",
           "%61 = OpLoad %float %r",
           "%62 = OpFOrdLessThan %bool %61 %float_0",
                 "OpSelectionMerge %63 None",
                 "OpBranchConditional %62 %64 %63",
           "%64 = OpLabel",
           "%65 = OpLoad %float %r",
           "%66 = OpFNegate %float %65",
                 "OpStore %r %66",
                 "OpBranch %63",
           "%63 = OpLabel",
           "%67 = OpLoad %float %r",
                 "OpReturnValue %67",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %16",
           "%39 = OpLabel",
       "%color1 = OpVariable %_ptr_Function_v4float Function",
       "%color2 = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
       "%color3 = OpVariable %_ptr_Function_v4float Function",
           "%40 = OpLoad %26 %t2D",
           "%41 = OpLoad %28 %samp",
           "%42 = OpSampledImage %30 %40 %41",
           "%43 = OpImageSampleImplicitLod %v4float %42 %33",
           "%44 = OpImage %26 %42",
           "%45 = OpLoad %28 %samp2",
                 "OpStore %color1 %43",
           "%46 = OpLoad %v4float %BaseColor",
                 "OpStore %param %46",
           "%47 = OpFunctionCall %float %foo_vf4_ %param",
           "%48 = OpCompositeConstruct %v4float %47 %47 %47 %47",
                 "OpStore %color2 %48",
           "%49 = OpSampledImage %30 %44 %45",
           "%50 = OpImageSampleImplicitLod %v4float %49 %36",
                 "OpStore %color3 %50",
           "%51 = OpLoad %v4float %color1",
           "%52 = OpLoad %v4float %color2",
           "%53 = OpFAdd %v4float %51 %52",
           "%54 = OpLoad %v4float %color3",
           "%55 = OpFAdd %v4float %53 %54",
           "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
           "%57 = OpFDiv %v4float %55 %56",
                 "OpStore %FragColor %57",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %16",
           "%39 = OpLabel",
           "%68 = OpVariable %_ptr_Function_float Function",
           "%69 = OpVariable %_ptr_Function_float Function",
       "%color1 = OpVariable %_ptr_Function_v4float Function",
       "%color2 = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
       "%color3 = OpVariable %_ptr_Function_v4float Function",
           "%40 = OpLoad %26 %t2D",
           "%41 = OpLoad %28 %samp",
           "%42 = OpSampledImage %30 %40 %41",
           "%43 = OpImageSampleImplicitLod %v4float %42 %33",
           "%44 = OpImage %26 %42",
           "%45 = OpLoad %28 %samp2",
                 "OpStore %color1 %43",
           "%46 = OpLoad %v4float %BaseColor",
                 "OpStore %param %46",
           "%71 = OpAccessChain %_ptr_Function_float %param %uint_0",
           "%72 = OpLoad %float %71",
                 "OpStore %68 %72",
           "%73 = OpLoad %float %68",
           "%74 = OpFOrdLessThan %bool %73 %float_0",
                 "OpSelectionMerge %78 None",
                 "OpBranchConditional %74 %75 %78",
           "%75 = OpLabel",
           "%76 = OpLoad %float %68",
           "%77 = OpFNegate %float %76",
                 "OpStore %68 %77",
                 "OpBranch %78",
           "%78 = OpLabel",
           "%79 = OpLoad %float %68",
                 "OpStore %69 %79",
           "%47 = OpLoad %float %69",
           "%48 = OpCompositeConstruct %v4float %47 %47 %47 %47",
                 "OpStore %color2 %48",
           "%80 = OpSampledImage %30 %40 %41",
           "%81 = OpImage %26 %80",
           "%49 = OpSampledImage %30 %81 %45",
           "%50 = OpImageSampleImplicitLod %v4float %49 %36",
                 "OpStore %color3 %50",
           "%51 = OpLoad %v4float %color1",
           "%52 = OpLoad %v4float %color2",
           "%53 = OpFAdd %v4float %51 %52",
           "%54 = OpLoad %v4float %color3",
           "%55 = OpFAdd %v4float %53 %54",
           "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
           "%57 = OpFDiv %v4float %55 %56",
                 "OpStore %FragColor %57",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, OpImageAndOpSampledImageOutOfBlock) {
    // #version 450
    //
    // uniform texture2D t2D;
    // uniform sampler samp;
    // uniform sampler samp2;
    //
    // out vec4 FragColor;
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     float r = bar.x;
    //     if (r < 0.0)
    //         r = -r;
    //     return r;
    // }
    //
    // void main()
    // {
    //     vec4 color1 = texture(sampler2D(t2D, samp), vec2(1.0));
    //     vec4 color2 = vec4(foo(BaseColor));
    //     vec4 color3 = texture(sampler2D(t2D, samp2), vec2(0.5));
    //     FragColor = (color1 + color2 + color3)/3;
    // }
    // Note: the before SPIR-V will need to be edited to create an OpImage
    // and subsequent OpSampledImage that is used across the function call.
    const std::vector<const char*> predefs = {
        // clang-format off
                 "OpCapability Shader",
            "%1 = OpExtInstImport \"GLSL.std.450\"",
                 "OpMemoryModel Logical GLSL450",
                 "OpEntryPoint Fragment %main \"main\" %BaseColor %FragColor",
                 "OpExecutionMode %main OriginUpperLeft",
                 "OpSource GLSL 450",
                 "OpName %main \"main\"",
                 "OpName %foo_vf4_ \"foo(vf4;\"",
                 "OpName %bar \"bar\"",
                 "OpName %r \"r\"",
                 "OpName %color1 \"color1\"",
                 "OpName %t2D \"t2D\"",
                 "OpName %samp \"samp\"",
                 "OpName %color2 \"color2\"",
                 "OpName %BaseColor \"BaseColor\"",
                 "OpName %param \"param\"",
                 "OpName %color3 \"color3\"",
                 "OpName %samp2 \"samp2\"",
                 "OpName %FragColor \"FragColor\"",
                 "OpDecorate %t2D DescriptorSet 0",
                 "OpDecorate %samp DescriptorSet 0",
                 "OpDecorate %samp2 DescriptorSet 0",
         "%void = OpTypeVoid",
           "%16 = OpTypeFunction %void",
        "%float = OpTypeFloat 32",
      "%v4float = OpTypeVector %float 4",
  "%_ptr_Function_v4float = OpTypePointer Function %v4float",
           "%20 = OpTypeFunction %float %_ptr_Function_v4float",
  "%_ptr_Function_float = OpTypePointer Function %float",
         "%uint = OpTypeInt 32 0",
       "%uint_0 = OpConstant %uint 0",
      "%float_0 = OpConstant %float 0",
         "%bool = OpTypeBool",
           "%26 = OpTypeImage %float 2D 0 0 0 1 Unknown",
  "%_ptr_UniformConstant_26 = OpTypePointer UniformConstant %26",
          "%t2D = OpVariable %_ptr_UniformConstant_26 UniformConstant",
           "%28 = OpTypeSampler",
  "%_ptr_UniformConstant_28 = OpTypePointer UniformConstant %28",
         "%samp = OpVariable %_ptr_UniformConstant_28 UniformConstant",
           "%30 = OpTypeSampledImage %26",
      "%v2float = OpTypeVector %float 2",
      "%float_1 = OpConstant %float 1",
           "%33 = OpConstantComposite %v2float %float_1 %float_1",
  "%_ptr_Input_v4float = OpTypePointer Input %v4float",
    "%BaseColor = OpVariable %_ptr_Input_v4float Input",
        "%samp2 = OpVariable %_ptr_UniformConstant_28 UniformConstant",
    "%float_0_5 = OpConstant %float 0.5",
           "%36 = OpConstantComposite %v2float %float_0_5 %float_0_5",
  "%_ptr_Output_v4float = OpTypePointer Output %v4float",
    "%FragColor = OpVariable %_ptr_Output_v4float Output",
      "%float_3 = OpConstant %float 3",
        // clang-format on
    };
  
    const std::vector<const char*> nonEntryFuncs = {
        // clang-format off
     "%foo_vf4_ = OpFunction %float None %20",
          "%bar = OpFunctionParameter %_ptr_Function_v4float",
           "%58 = OpLabel",
            "%r = OpVariable %_ptr_Function_float Function",
           "%59 = OpAccessChain %_ptr_Function_float %bar %uint_0",
           "%60 = OpLoad %float %59",
                 "OpStore %r %60",
           "%61 = OpLoad %float %r",
           "%62 = OpFOrdLessThan %bool %61 %float_0",
                 "OpSelectionMerge %63 None",
                 "OpBranchConditional %62 %64 %63",
           "%64 = OpLabel",
           "%65 = OpLoad %float %r",
           "%66 = OpFNegate %float %65",
                 "OpStore %r %66",
                 "OpBranch %63",
           "%63 = OpLabel",
           "%67 = OpLoad %float %r",
                 "OpReturnValue %67",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> before = {
        // clang-format off
         "%main = OpFunction %void None %16",
           "%39 = OpLabel",
       "%color1 = OpVariable %_ptr_Function_v4float Function",
       "%color2 = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
       "%color3 = OpVariable %_ptr_Function_v4float Function",
           "%40 = OpLoad %26 %t2D",
           "%41 = OpLoad %28 %samp",
           "%42 = OpSampledImage %30 %40 %41",
           "%43 = OpImageSampleImplicitLod %v4float %42 %33",
           "%44 = OpImage %26 %42",
           "%45 = OpLoad %28 %samp2",
           "%46 = OpSampledImage %30 %44 %45",
                 "OpStore %color1 %43",
           "%47 = OpLoad %v4float %BaseColor",
                 "OpStore %param %47",
           "%48 = OpFunctionCall %float %foo_vf4_ %param",
           "%49 = OpCompositeConstruct %v4float %48 %48 %48 %48",
                 "OpStore %color2 %49",
           "%50 = OpImageSampleImplicitLod %v4float %46 %36",
                 "OpStore %color3 %50",
           "%51 = OpLoad %v4float %color1",
           "%52 = OpLoad %v4float %color2",
           "%53 = OpFAdd %v4float %51 %52",
           "%54 = OpLoad %v4float %color3",
           "%55 = OpFAdd %v4float %53 %54",
           "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
           "%57 = OpFDiv %v4float %55 %56",
                 "OpStore %FragColor %57",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
  
    const std::vector<const char*> after = {
        // clang-format off
         "%main = OpFunction %void None %16",
           "%39 = OpLabel",
           "%68 = OpVariable %_ptr_Function_float Function",
           "%69 = OpVariable %_ptr_Function_float Function",
       "%color1 = OpVariable %_ptr_Function_v4float Function",
       "%color2 = OpVariable %_ptr_Function_v4float Function",
        "%param = OpVariable %_ptr_Function_v4float Function",
       "%color3 = OpVariable %_ptr_Function_v4float Function",
           "%40 = OpLoad %26 %t2D",
           "%41 = OpLoad %28 %samp",
           "%42 = OpSampledImage %30 %40 %41",
           "%43 = OpImageSampleImplicitLod %v4float %42 %33",
           "%44 = OpImage %26 %42",
           "%45 = OpLoad %28 %samp2",
           "%46 = OpSampledImage %30 %44 %45",
                 "OpStore %color1 %43",
           "%47 = OpLoad %v4float %BaseColor",
                 "OpStore %param %47",
           "%71 = OpAccessChain %_ptr_Function_float %param %uint_0",
           "%72 = OpLoad %float %71",
                 "OpStore %68 %72",
           "%73 = OpLoad %float %68",
           "%74 = OpFOrdLessThan %bool %73 %float_0",
                 "OpSelectionMerge %78 None",
                 "OpBranchConditional %74 %75 %78",
           "%75 = OpLabel",
           "%76 = OpLoad %float %68",
           "%77 = OpFNegate %float %76",
                 "OpStore %68 %77",
                 "OpBranch %78",
           "%78 = OpLabel",
           "%79 = OpLoad %float %68",
                 "OpStore %69 %79",
           "%48 = OpLoad %float %69",
           "%49 = OpCompositeConstruct %v4float %48 %48 %48 %48",
                 "OpStore %color2 %49",
           "%80 = OpSampledImage %30 %40 %41",
           "%81 = OpImage %26 %80",
           "%82 = OpSampledImage %30 %81 %45",
           "%50 = OpImageSampleImplicitLod %v4float %82 %36",
                 "OpStore %color3 %50",
           "%51 = OpLoad %v4float %color1",
           "%52 = OpLoad %v4float %color2",
           "%53 = OpFAdd %v4float %51 %52",
           "%54 = OpLoad %v4float %color3",
           "%55 = OpFAdd %v4float %53 %54",
           "%56 = OpCompositeConstruct %v4float %float_3 %float_3 %float_3 %float_3",
           "%57 = OpFDiv %v4float %55 %56",
                 "OpStore %FragColor %57",
                 "OpReturn",
                 "OpFunctionEnd",
        // clang-format on
    };
    SinglePassRunAndCheck<InlineExhaustivePass>(
        JoinAllInsts(Concat(Concat(predefs, before), nonEntryFuncs)),
        JoinAllInsts(Concat(Concat(predefs, after), nonEntryFuncs)),
        /* skip_nop = */ false, /* do_validate = */ true);
  }
  
  TEST_F(InlineTest, EarlyReturnInLoopIsNotInlined) {
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     while (true) {
    //         if (bar.x < 0.0)
    //             return 0.0;
    //         return bar.x;
    //     }
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //     gl_FragColor = color;
    // }
  
    const std::string assembly =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 140
  OpName %main "main"
  OpName %foo_vf4_ "foo(vf4;"
  OpName %bar "bar"
  OpName %color "color"
  OpName %BaseColor "BaseColor"
  OpName %param "param"
  OpName %gl_FragColor "gl_FragColor"
  %void = OpTypeVoid
  %10 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %v4float = OpTypeVector %float 4
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %14 = OpTypeFunction %float %_ptr_Function_v4float
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_float = OpTypePointer Function %float
  %float_0 = OpConstant %float 0
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %BaseColor = OpVariable %_ptr_Input_v4float Input
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output
  %main = OpFunction %void None %10
  %23 = OpLabel
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %24 = OpLoad %v4float %BaseColor
  OpStore %param %24
  %25 = OpFunctionCall %float %foo_vf4_ %param
  %26 = OpCompositeConstruct %v4float %25 %25 %25 %25
  OpStore %color %26
  %27 = OpLoad %v4float %color
  OpStore %gl_FragColor %27
  OpReturn
  OpFunctionEnd
  %foo_vf4_ = OpFunction %float None %14
  %bar = OpFunctionParameter %_ptr_Function_v4float
  %28 = OpLabel
  OpBranch %29
  %29 = OpLabel
  OpLoopMerge %30 %31 None
  OpBranch %32
  %32 = OpLabel
  OpBranchConditional %true %33 %30
  %33 = OpLabel
  %34 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %35 = OpLoad %float %34
  %36 = OpFOrdLessThan %bool %35 %float_0
  OpSelectionMerge %37 None
  OpBranchConditional %36 %38 %37
  %38 = OpLabel
  OpReturnValue %float_0
  %37 = OpLabel
  %39 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %40 = OpLoad %float %39
  OpReturnValue %40
  %31 = OpLabel
  OpBranch %29
  %30 = OpLabel
  %41 = OpUndef %float
  OpReturnValue %41
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(assembly, assembly, false, true);
  }
  
  TEST_F(InlineTest, ExternalFunctionIsNotInlined) {
    // In particular, don't crash.
    // See report https://github.com/KhronosGroup/SPIRV-Tools/issues/605
    const std::string assembly =
        R"(OpCapability Addresses
  OpCapability Kernel
  OpCapability Linkage
  OpMemoryModel Physical32 OpenCL
  OpEntryPoint Kernel %1 "entry_pt"
  OpDecorate %2 LinkageAttributes "external" Import
  %void = OpTypeVoid
  %4 = OpTypeFunction %void
  %2 = OpFunction %void None %4
  OpFunctionEnd
  %1 = OpFunction %void None %4
  %5 = OpLabel
  %6 = OpFunctionCall %void %2
  OpReturn
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(assembly, assembly, false, true);
  }
  
  TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCallee) {
    // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/787
    //
    // CFG structure is:
    //    foo:
    //       fooentry -> fooexit
    //
    //    main:
    //       entry -> loop
    //       loop -> loop, merge
    //         loop calls foo()
    //       merge
    //
    // Since the callee has multiple blocks, it will split the calling block
    // into at least two, resulting in a new "back-half" block that contains
    // the instructions after the inlined function call.  If the calling block
    // has an OpLoopMerge that points back to the calling block itself, then
    // the OpLoopMerge can't remain in the back-half block, but must be
    // moved to the end of the original calling block, and it continue target
    // operand updated to point to the back-half block.
  
    const std::string predefs =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint GLCompute %1 "main"
  OpSource OpenCL_C 120
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %void = OpTypeVoid
  %5 = OpTypeFunction %void
  )";
  
    const std::string nonEntryFuncs =
        R"(%6 = OpFunction %void None %5
  %7 = OpLabel
  OpBranch %8
  %8 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%1 = OpFunction %void None %5
  %9 = OpLabel
  OpBranch %10
  %10 = OpLabel
  %11 = OpFunctionCall %void %6
  OpLoopMerge %12 %10 None
  OpBranchConditional %true %10 %12
  %12 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(%1 = OpFunction %void None %5
  %9 = OpLabel
  OpBranch %10
  %10 = OpLabel
  OpLoopMerge %12 %10 None
  OpBranch %14
  %14 = OpLabel
  OpBranchConditional %true %10 %12
  %12 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
                                                predefs + nonEntryFuncs + after,
                                                false, true);
  }
  
  TEST_F(InlineTest, MultiBlockLoopHeaderCallsMultiBlockCallee) {
    // Like SingleBlockLoopCallsMultiBlockCallee but the loop has several
    // blocks, but the function call still occurs in the loop header.
    // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/800
  
    const std::string predefs =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint GLCompute %1 "main"
  OpSource OpenCL_C 120
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %int = OpTypeInt 32 1
  %int_1 = OpConstant %int 1
  %int_2 = OpConstant %int 2
  %int_3 = OpConstant %int 3
  %int_4 = OpConstant %int 4
  %int_5 = OpConstant %int 5
  %void = OpTypeVoid
  %11 = OpTypeFunction %void
  )";
  
    const std::string nonEntryFuncs =
        R"(%12 = OpFunction %void None %11
  %13 = OpLabel
  %14 = OpCopyObject %int %int_1
  OpBranch %15
  %15 = OpLabel
  %16 = OpCopyObject %int %int_2
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%1 = OpFunction %void None %11
  %17 = OpLabel
  OpBranch %18
  %18 = OpLabel
  %19 = OpCopyObject %int %int_3
  %20 = OpFunctionCall %void %12
  %21 = OpCopyObject %int %int_4
  OpLoopMerge %22 %23 None
  OpBranchConditional %true %23 %22
  %23 = OpLabel
  %24 = OpCopyObject %int %int_5
  OpBranchConditional %true %18 %22
  %22 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(%1 = OpFunction %void None %11
  %17 = OpLabel
  OpBranch %18
  %18 = OpLabel
  %19 = OpCopyObject %int %int_3
  %26 = OpCopyObject %int %int_1
  OpLoopMerge %22 %23 None
  OpBranch %27
  %27 = OpLabel
  %28 = OpCopyObject %int %int_2
  %21 = OpCopyObject %int %int_4
  OpBranchConditional %true %23 %22
  %23 = OpLabel
  %24 = OpCopyObject %int %int_5
  OpBranchConditional %true %18 %22
  %22 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
                                                predefs + nonEntryFuncs + after,
                                                false, true);
  }
  
  TEST_F(InlineTest, SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge) {
    // This is similar to SingleBlockLoopCallsMultiBlockCallee except
    // that calleee block also has a merge instruction in its first block.
    // That merge instruction must be an OpSelectionMerge (because the entry
    // block of a function can't be the header of a loop since the entry
    // block can't be the target of a branch).
    //
    // In this case the OpLoopMerge can't be placed in the same block as
    // the OpSelectionMerge, so inlining must create a new block to contain
    // the callee contents.
    //
    // Additionally, we have two extra OpCopyObject instructions to prove that
    // the OpLoopMerge is moved to the right location.
    //
    // Also ensure that OpPhis within the cloned callee code are valid.
    // We need to test that the predecessor blocks are remapped correctly so that
    // dominance rules are satisfied
  
    const std::string predefs =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint GLCompute %1 "main"
  OpSource OpenCL_C 120
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %false = OpConstantFalse %bool
  %void = OpTypeVoid
  %6 = OpTypeFunction %void
  )";
  
    // This callee has multiple blocks, and an OpPhi in the last block
    // that references a value from the first block.  This tests that
    // cloned block IDs are remapped appropriately.  The OpPhi dominance
    // requires that the remapped %9 must be in a block that dominates
    // the remapped %8.
    const std::string nonEntryFuncs =
        R"(%7 = OpFunction %void None %6
  %8 = OpLabel
  %9 = OpCopyObject %bool %true
  OpSelectionMerge %10 None
  OpBranchConditional %true %10 %10
  %10 = OpLabel
  %11 = OpPhi %bool %9 %8
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%1 = OpFunction %void None %6
  %12 = OpLabel
  OpBranch %13
  %13 = OpLabel
  %14 = OpCopyObject %bool %false
  %15 = OpFunctionCall %void %7
  OpLoopMerge %16 %13 None
  OpBranchConditional %true %13 %16
  %16 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    // Note the remapped Phi uses %17 as the parent instead
    // of %13, demonstrating that the parent block has been remapped
    // correctly.
    const std::string after =
        R"(%1 = OpFunction %void None %6
  %12 = OpLabel
  OpBranch %13
  %13 = OpLabel
  %14 = OpCopyObject %bool %false
  OpLoopMerge %16 %13 None
  OpBranch %17
  %17 = OpLabel
  %19 = OpCopyObject %bool %true
  OpSelectionMerge %20 None
  OpBranchConditional %true %20 %20
  %20 = OpLabel
  %21 = OpPhi %bool %19 %17
  OpBranchConditional %true %13 %16
  %16 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
    SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
                                                predefs + nonEntryFuncs + after,
                                                false, true);
  }
  
  TEST_F(InlineTest,
         MultiBlockLoopHeaderCallsFromToMultiBlockCalleeHavingSelectionMerge) {
    // This is similar to SingleBlockLoopCallsMultiBlockCalleeHavingSelectionMerge
    // but the call is in the header block of a multi block loop.
  
    const std::string predefs =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint GLCompute %1 "main"
  OpSource OpenCL_C 120
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %int = OpTypeInt 32 1
  %int_1 = OpConstant %int 1
  %int_2 = OpConstant %int 2
  %int_3 = OpConstant %int 3
  %int_4 = OpConstant %int 4
  %int_5 = OpConstant %int 5
  %void = OpTypeVoid
  %11 = OpTypeFunction %void
  )";
  
    const std::string nonEntryFuncs =
        R"(%12 = OpFunction %void None %11
  %13 = OpLabel
  %14 = OpCopyObject %int %int_1
  OpSelectionMerge %15 None
  OpBranchConditional %true %15 %15
  %15 = OpLabel
  %16 = OpCopyObject %int %int_2
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%1 = OpFunction %void None %11
  %17 = OpLabel
  OpBranch %18
  %18 = OpLabel
  %19 = OpCopyObject %int %int_3
  %20 = OpFunctionCall %void %12
  %21 = OpCopyObject %int %int_4
  OpLoopMerge %22 %23 None
  OpBranchConditional %true %23 %22
  %23 = OpLabel
  %24 = OpCopyObject %int %int_5
  OpBranchConditional %true %18 %22
  %22 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(%1 = OpFunction %void None %11
  %17 = OpLabel
  OpBranch %18
  %18 = OpLabel
  %19 = OpCopyObject %int %int_3
  OpLoopMerge %22 %23 None
  OpBranch %25
  %25 = OpLabel
  %27 = OpCopyObject %int %int_1
  OpSelectionMerge %28 None
  OpBranchConditional %true %28 %28
  %28 = OpLabel
  %29 = OpCopyObject %int %int_2
  %21 = OpCopyObject %int %int_4
  OpBranchConditional %true %23 %22
  %23 = OpLabel
  %24 = OpCopyObject %int %int_5
  OpBranchConditional %true %18 %22
  %22 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(predefs + nonEntryFuncs + before,
                                                predefs + nonEntryFuncs + after,
                                                false, true);
  }
  
  TEST_F(InlineTest, NonInlinableCalleeWithSingleReturn) {
    // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018
    //
    // The callee has a single return, but cannot be inlined because the
    // return is inside a loop.
  
    const std::string predefs =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %_GLF_color
  OpExecutionMode %main OriginUpperLeft
  OpSource ESSL 310
  OpName %main "main"
  OpName %f_ "f("
  OpName %i "i"
  OpName %_GLF_color "_GLF_color"
  OpDecorate %_GLF_color Location 0
  %void = OpTypeVoid
  %7 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %9 = OpTypeFunction %float
  %float_1 = OpConstant %float 1
  %bool = OpTypeBool
  %false = OpConstantFalse %bool
  %int = OpTypeInt 32 1
  %_ptr_Function_int = OpTypePointer Function %int
  %int_0 = OpConstant %int 0
  %int_1 = OpConstant %int 1
  %v4float = OpTypeVector %float 4
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %_GLF_color = OpVariable %_ptr_Output_v4float Output
  %float_0 = OpConstant %float 0
  %20 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
  %21 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1
  )";
  
    const std::string caller =
        R"(%main = OpFunction %void None %7
  %22 = OpLabel
  %i = OpVariable %_ptr_Function_int Function
  OpStore %i %int_0
  OpBranch %23
  %23 = OpLabel
  OpLoopMerge %24 %25 None
  OpBranch %26
  %26 = OpLabel
  %27 = OpLoad %int %i
  %28 = OpSLessThan %bool %27 %int_1
  OpBranchConditional %28 %29 %24
  %29 = OpLabel
  OpStore %_GLF_color %20
  %30 = OpFunctionCall %float %f_
  OpBranch %25
  %25 = OpLabel
  %31 = OpLoad %int %i
  %32 = OpIAdd %int %31 %int_1
  OpStore %i %32
  OpBranch %23
  %24 = OpLabel
  OpStore %_GLF_color %21
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string callee =
        R"(%f_ = OpFunction %float None %9
  %33 = OpLabel
  OpBranch %34
  %34 = OpLabel
  OpLoopMerge %35 %36 None
  OpBranch %37
  %37 = OpLabel
  OpReturnValue %float_1
  %36 = OpLabel
  OpBranch %34
  %35 = OpLabel
  OpUnreachable
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(
        predefs + caller + callee, predefs + caller + callee, false, true);
  }
  
  TEST_F(InlineTest, Decorated1) {
    // Same test as Simple with the difference
    // that OpFAdd in the outlined function is
    // decorated with RelaxedPrecision
    // Expected result is an equal decoration
    // of the corresponding inlined instruction
    //
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     return bar.x + bar.y;
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //     gl_FragColor = color;
    // }
  
    const std::string predefs =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 140
  OpName %main "main"
  OpName %foo_vf4_ "foo(vf4;"
  OpName %bar "bar"
  OpName %color "color"
  OpName %BaseColor "BaseColor"
  OpName %param "param"
  OpName %gl_FragColor "gl_FragColor"
  OpDecorate %9 RelaxedPrecision
  )";
  
    const std::string before =
        R"(%void = OpTypeVoid
  %11 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %v4float = OpTypeVector %float 4
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %15 = OpTypeFunction %float %_ptr_Function_v4float
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_float = OpTypePointer Function %float
  %uint_1 = OpConstant %uint 1
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %BaseColor = OpVariable %_ptr_Input_v4float Input
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output
  %main = OpFunction %void None %11
  %22 = OpLabel
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %23 = OpLoad %v4float %BaseColor
  OpStore %param %23
  %24 = OpFunctionCall %float %foo_vf4_ %param
  %25 = OpCompositeConstruct %v4float %24 %24 %24 %24
  OpStore %color %25
  %26 = OpLoad %v4float %color
  OpStore %gl_FragColor %26
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(OpDecorate %38 RelaxedPrecision
  %void = OpTypeVoid
  %11 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %v4float = OpTypeVector %float 4
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %15 = OpTypeFunction %float %_ptr_Function_v4float
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_float = OpTypePointer Function %float
  %uint_1 = OpConstant %uint 1
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %BaseColor = OpVariable %_ptr_Input_v4float Input
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output
  %main = OpFunction %void None %11
  %22 = OpLabel
  %32 = OpVariable %_ptr_Function_float Function
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %23 = OpLoad %v4float %BaseColor
  OpStore %param %23
  %34 = OpAccessChain %_ptr_Function_float %param %uint_0
  %35 = OpLoad %float %34
  %36 = OpAccessChain %_ptr_Function_float %param %uint_1
  %37 = OpLoad %float %36
  %38 = OpFAdd %float %35 %37
  OpStore %32 %38
  %24 = OpLoad %float %32
  %25 = OpCompositeConstruct %v4float %24 %24 %24 %24
  OpStore %color %25
  %26 = OpLoad %v4float %color
  OpStore %gl_FragColor %26
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string nonEntryFuncs =
        R"(%foo_vf4_ = OpFunction %float None %15
  %bar = OpFunctionParameter %_ptr_Function_v4float
  %27 = OpLabel
  %28 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %29 = OpLoad %float %28
  %30 = OpAccessChain %_ptr_Function_float %bar %uint_1
  %31 = OpLoad %float %30
  %9 = OpFAdd %float %29 %31
  OpReturnValue %9
  OpFunctionEnd
  )";
    SinglePassRunAndCheck<InlineExhaustivePass>(predefs + before + nonEntryFuncs,
                                                predefs + after + nonEntryFuncs,
                                                false, true);
  }
  
  TEST_F(InlineTest, Decorated2) {
    // Same test as Simple with the difference
    // that the Result <id> of the outlined OpFunction
    // is decorated with RelaxedPrecision
    // Expected result is an equal decoration
    // of the created return variable
    //
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     return bar.x + bar.y;
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //     gl_FragColor = color;
    // }
  
    const std::string predefs =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 140
  OpName %main "main"
  OpName %foo_vf4_ "foo(vf4;"
  OpName %bar "bar"
  OpName %color "color"
  OpName %BaseColor "BaseColor"
  OpName %param "param"
  OpName %gl_FragColor "gl_FragColor"
  OpDecorate %foo_vf4_ RelaxedPrecision
  )";
  
    const std::string before =
        R"(%void = OpTypeVoid
  %10 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %v4float = OpTypeVector %float 4
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %14 = OpTypeFunction %float %_ptr_Function_v4float
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_float = OpTypePointer Function %float
  %uint_1 = OpConstant %uint 1
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %BaseColor = OpVariable %_ptr_Input_v4float Input
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output
  %main = OpFunction %void None %10
  %21 = OpLabel
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %22 = OpLoad %v4float %BaseColor
  OpStore %param %22
  %23 = OpFunctionCall %float %foo_vf4_ %param
  %24 = OpCompositeConstruct %v4float %23 %23 %23 %23
  OpStore %color %24
  %25 = OpLoad %v4float %color
  OpStore %gl_FragColor %25
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(OpDecorate %32 RelaxedPrecision
  %void = OpTypeVoid
  %10 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %v4float = OpTypeVector %float 4
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %14 = OpTypeFunction %float %_ptr_Function_v4float
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_float = OpTypePointer Function %float
  %uint_1 = OpConstant %uint 1
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %BaseColor = OpVariable %_ptr_Input_v4float Input
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output
  %main = OpFunction %void None %10
  %21 = OpLabel
  %32 = OpVariable %_ptr_Function_float Function
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %22 = OpLoad %v4float %BaseColor
  OpStore %param %22
  %34 = OpAccessChain %_ptr_Function_float %param %uint_0
  %35 = OpLoad %float %34
  %36 = OpAccessChain %_ptr_Function_float %param %uint_1
  %37 = OpLoad %float %36
  %38 = OpFAdd %float %35 %37
  OpStore %32 %38
  %23 = OpLoad %float %32
  %24 = OpCompositeConstruct %v4float %23 %23 %23 %23
  OpStore %color %24
  %25 = OpLoad %v4float %color
  OpStore %gl_FragColor %25
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string nonEntryFuncs =
        R"(%foo_vf4_ = OpFunction %float None %14
  %bar = OpFunctionParameter %_ptr_Function_v4float
  %26 = OpLabel
  %27 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %28 = OpLoad %float %27
  %29 = OpAccessChain %_ptr_Function_float %bar %uint_1
  %30 = OpLoad %float %29
  %31 = OpFAdd %float %28 %30
  OpReturnValue %31
  OpFunctionEnd
  )";
    SinglePassRunAndCheck<InlineExhaustivePass>(predefs + before + nonEntryFuncs,
                                                predefs + after + nonEntryFuncs,
                                                false, true);
  }
  
  TEST_F(InlineTest, DeleteName) {
    // Test that the name of the result id of the call is deleted.
    const std::string before =
        R"(
                 OpCapability Shader
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Vertex %main "main"
                 OpName %main "main"
                 OpName %main_entry "main_entry"
                 OpName %foo_result "foo_result"
                 OpName %void_fn "void_fn"
                 OpName %foo "foo"
                 OpName %foo_entry "foo_entry"
         %void = OpTypeVoid
      %void_fn = OpTypeFunction %void
          %foo = OpFunction %void None %void_fn
    %foo_entry = OpLabel
                 OpReturn
                 OpFunctionEnd
         %main = OpFunction %void None %void_fn
   %main_entry = OpLabel
   %foo_result = OpFunctionCall %void %foo
                 OpReturn
                 OpFunctionEnd
  )";
  
    const std::string after =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint Vertex %main "main"
  OpName %main "main"
  OpName %main_entry "main_entry"
  OpName %void_fn "void_fn"
  OpName %foo "foo"
  OpName %foo_entry "foo_entry"
  %void = OpTypeVoid
  %void_fn = OpTypeFunction %void
  %foo = OpFunction %void None %void_fn
  %foo_entry = OpLabel
  OpReturn
  OpFunctionEnd
  %main = OpFunction %void None %void_fn
  %main_entry = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
  }
  
  TEST_F(InlineTest, SetParent) {
    // Test that after inlining all basic blocks have the correct parent.
    const std::string text =
        R"(
                 OpCapability Shader
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Vertex %main "main"
                 OpName %main "main"
                 OpName %main_entry "main_entry"
                 OpName %foo_result "foo_result"
                 OpName %void_fn "void_fn"
                 OpName %foo "foo"
                 OpName %foo_entry "foo_entry"
         %void = OpTypeVoid
      %void_fn = OpTypeFunction %void
          %foo = OpFunction %void None %void_fn
    %foo_entry = OpLabel
                 OpReturn
                 OpFunctionEnd
         %main = OpFunction %void None %void_fn
   %main_entry = OpLabel
   %foo_result = OpFunctionCall %void %foo
                 OpReturn
                 OpFunctionEnd
  )";
  
    std::unique_ptr<IRContext> context =
        BuildModule(SPV_ENV_UNIVERSAL_1_2, nullptr, text);
    InlineExhaustivePass pass;
    pass.Run(context.get());
  
    for (Function& func : *context->module()) {
      for (BasicBlock& bb : func) {
        EXPECT_TRUE(bb.GetParent() == &func);
      }
    }
  }
  
  TEST_F(InlineTest, OpVariableWithInit) {
    // Check that there is a store that corresponds to the initializer.  This
    // test makes sure that is a store to the variable in the loop and before any
    // load.
    const std::string text = R"(
  ; CHECK: OpFunction
  ; CHECK-NOT: OpFunctionEnd
  ; CHECK: [[var:%\w+]] = OpVariable %_ptr_Function_float Function %float_0
  ; CHECK: OpLoopMerge [[outer_merge:%\w+]]
  ; CHECK-NOT: OpLoad %float [[var]]
  ; CHECK: OpStore [[var]] %float_0
  ; CHECK: OpFunctionEnd
                 OpCapability Shader
            %1 = OpExtInstImport "GLSL.std.450"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %main "main" %o
                 OpExecutionMode %main OriginUpperLeft
                 OpSource GLSL 450
                 OpDecorate %o Location 0
         %void = OpTypeVoid
            %3 = OpTypeFunction %void
        %float = OpTypeFloat 32
            %7 = OpTypeFunction %float
  %_ptr_Function_float = OpTypePointer Function %float
      %float_0 = OpConstant %float 0
         %bool = OpTypeBool
      %float_1 = OpConstant %float 1
  %_ptr_Output_float = OpTypePointer Output %float
            %o = OpVariable %_ptr_Output_float Output
          %int = OpTypeInt 32 1
  %_ptr_Function_int = OpTypePointer Function %int
  %_ptr_Input_int = OpTypePointer Input %int
        %int_0 = OpConstant %int 0
        %int_1 = OpConstant %int 1
        %int_2 = OpConstant %int 2
         %main = OpFunction %void None %3
            %5 = OpLabel
                 OpStore %o %float_0
                 OpBranch %34
           %34 = OpLabel
           %39 = OpPhi %int %int_0 %5 %47 %37
                 OpLoopMerge %36 %37 None
                 OpBranch %38
           %38 = OpLabel
           %41 = OpSLessThan %bool %39 %int_2
                 OpBranchConditional %41 %35 %36
           %35 = OpLabel
           %42 = OpFunctionCall %float %foo_
           %43 = OpLoad %float %o
           %44 = OpFAdd %float %43 %42
                 OpStore %o %44
                 OpBranch %37
           %37 = OpLabel
           %47 = OpIAdd %int %39 %int_1
                 OpBranch %34
           %36 = OpLabel
                 OpReturn
                 OpFunctionEnd
         %foo_ = OpFunction %float None %7
            %9 = OpLabel
            %n = OpVariable %_ptr_Function_float Function %float_0
           %13 = OpLoad %float %n
           %15 = OpFOrdEqual %bool %13 %float_0
                 OpSelectionMerge %17 None
                 OpBranchConditional %15 %16 %17
           %16 = OpLabel
           %19 = OpLoad %float %n
           %20 = OpFAdd %float %19 %float_1
                 OpStore %n %20
                 OpBranch %17
           %17 = OpLabel
           %21 = OpLoad %float %n
                 OpReturnValue %21
                 OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DontInlineDirectlyRecursiveFunc) {
    // Test that the name of the result id of the call is deleted.
    const std::string test =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %1 "main"
  OpExecutionMode %1 OriginUpperLeft
  %void = OpTypeVoid
  %4 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %_struct_6 = OpTypeStruct %float %float
  %15 = OpConstantNull %_struct_6
  %7 = OpTypeFunction %_struct_6
  %1 = OpFunction %void Pure|Const %4
  %8 = OpLabel
  %2 = OpFunctionCall %_struct_6 %9
  OpKill
  OpFunctionEnd
  %9 = OpFunction %_struct_6 None %7
  %10 = OpLabel
  %11 = OpFunctionCall %_struct_6 %9
  OpReturnValue %15
  OpFunctionEnd
  )";
  
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
  }
  
  TEST_F(InlineTest, DontInlineInDirectlyRecursiveFunc) {
    // Test that the name of the result id of the call is deleted.
    const std::string test =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %1 "main"
  OpExecutionMode %1 OriginUpperLeft
  %void = OpTypeVoid
  %4 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %_struct_6 = OpTypeStruct %float %float
  %15 = OpConstantNull %_struct_6
  %7 = OpTypeFunction %_struct_6
  %1 = OpFunction %void Pure|Const %4
  %8 = OpLabel
  %2 = OpFunctionCall %_struct_6 %9
  OpKill
  OpFunctionEnd
  %9 = OpFunction %_struct_6 None %7
  %10 = OpLabel
  %11 = OpFunctionCall %_struct_6 %12
  OpReturnValue %15
  OpFunctionEnd
  %12 = OpFunction %_struct_6 None %7
  %13 = OpLabel
  %14 = OpFunctionCall %_struct_6 %9
  OpReturnValue %15
  OpFunctionEnd
  )";
  
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
  }
  
  TEST_F(InlineTest, DontInlineFuncWithOpKillInContinue) {
    const std::string test =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 330
  OpName %main "main"
  OpName %kill_ "kill("
  %void = OpTypeVoid
  %3 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %3
  %5 = OpLabel
  OpBranch %9
  %9 = OpLabel
  OpLoopMerge %11 %12 None
  OpBranch %13
  %13 = OpLabel
  OpBranchConditional %true %10 %11
  %10 = OpLabel
  OpBranch %12
  %12 = OpLabel
  %16 = OpFunctionCall %void %kill_
  OpBranch %9
  %11 = OpLabel
  OpReturn
  OpFunctionEnd
  %kill_ = OpFunction %void None %3
  %7 = OpLabel
  OpKill
  OpFunctionEnd
  )";
  
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
  }
  
  TEST_F(InlineTest, DontInlineFuncWithDontInline) {
    // Check that the function with DontInline flag is not inlined.
    const std::string text = R"(
  ; CHECK: %foo = OpFunction %int DontInline
  ; CHECK: OpReturnValue %int_0
  
  OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource HLSL 600
  OpName %main "main"
  OpName %foo "foo"
  %int = OpTypeInt 32 1
  %int_0 = OpConstant %int 0
  %void = OpTypeVoid
  %6 = OpTypeFunction %void
  %7 = OpTypeFunction %int
  %main = OpFunction %void None %6
  %8 = OpLabel
  %9 = OpFunctionCall %int %foo
  OpReturn
  OpFunctionEnd
  %foo = OpFunction %int DontInline %7
  %10 = OpLabel
  OpReturnValue %int_0
  OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, InlineFuncWithOpKillNotInContinue) {
    const std::string before =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 330
  OpName %main "main"
  OpName %kill_ "kill("
  %void = OpTypeVoid
  %3 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %3
  %5 = OpLabel
  %16 = OpFunctionCall %void %kill_
  OpReturn
  OpFunctionEnd
  %kill_ = OpFunction %void None %3
  %7 = OpLabel
  OpKill
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 330
  OpName %main "main"
  OpName %kill_ "kill("
  %void = OpTypeVoid
  %3 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %3
  %5 = OpLabel
  OpKill
  %18 = OpLabel
  OpReturn
  OpFunctionEnd
  %kill_ = OpFunction %void None %3
  %7 = OpLabel
  OpKill
  OpFunctionEnd
  )";
  
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
  }
  
  TEST_F(InlineTest, DontInlineFuncWithOpTerminateInvocationInContinue) {
    const std::string test =
        R"(OpCapability Shader
  OpExtension "SPV_KHR_terminate_invocation"
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 330
  OpName %main "main"
  OpName %kill_ "kill("
  %void = OpTypeVoid
  %3 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %3
  %5 = OpLabel
  OpBranch %9
  %9 = OpLabel
  OpLoopMerge %11 %12 None
  OpBranch %13
  %13 = OpLabel
  OpBranchConditional %true %10 %11
  %10 = OpLabel
  OpBranch %12
  %12 = OpLabel
  %16 = OpFunctionCall %void %kill_
  OpBranch %9
  %11 = OpLabel
  OpReturn
  OpFunctionEnd
  %kill_ = OpFunction %void None %3
  %7 = OpLabel
  OpTerminateInvocation
  OpFunctionEnd
  )";
  
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(test, test, false, true);
  }
  
  TEST_F(InlineTest, InlineFuncWithOpTerminateInvocationNotInContinue) {
    const std::string before =
        R"(OpCapability Shader
  OpExtension "SPV_KHR_terminate_invocation"
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 330
  OpName %main "main"
  OpName %kill_ "kill("
  %void = OpTypeVoid
  %3 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %3
  %5 = OpLabel
  %16 = OpFunctionCall %void %kill_
  OpReturn
  OpFunctionEnd
  %kill_ = OpFunction %void None %3
  %7 = OpLabel
  OpTerminateInvocation
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(OpCapability Shader
  OpExtension "SPV_KHR_terminate_invocation"
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 330
  OpName %main "main"
  OpName %kill_ "kill("
  %void = OpTypeVoid
  %3 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %3
  %5 = OpLabel
  OpTerminateInvocation
  %18 = OpLabel
  OpReturn
  OpFunctionEnd
  %kill_ = OpFunction %void None %3
  %7 = OpLabel
  OpTerminateInvocation
  OpFunctionEnd
  )";
  
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
  }
  
  TEST_F(InlineTest, InlineForLinkage) {
    const std::string before =
        R"(OpCapability SampledBuffer
  OpCapability ImageBuffer
  OpCapability Shader
  OpCapability Linkage
  OpMemoryModel Logical GLSL450
  OpSource HLSL 630
  OpName %type_buffer_image "type.buffer.image"
  OpName %output "output"
  OpName %main "main"
  OpName %color "color"
  OpName %bb_entry "bb.entry"
  OpName %param_var_color "param.var.color"
  OpName %fn "fn"
  OpName %color_0 "color"
  OpName %bb_entry_0 "bb.entry"
  OpName %v "v"
  OpDecorate %main LinkageAttributes "main" Export
  OpDecorate %output DescriptorSet 0
  OpDecorate %output Binding 1
  %float = OpTypeFloat 32
  %float_0_200000003 = OpConstant %float 0.200000003
  %v4float = OpTypeVector %float 4
  %6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003
  %int = OpTypeInt 32 1
  %int_0 = OpConstant %int 0
  %type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
  %_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %11 = OpTypeFunction %float %_ptr_Function_v4float
  %_ptr_Function_float = OpTypePointer Function %float
  %output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
  %main = OpFunction %float None %11
  %color = OpFunctionParameter %_ptr_Function_v4float
  %bb_entry = OpLabel
  %param_var_color = OpVariable %_ptr_Function_v4float Function
  %16 = OpLoad %v4float %color
  OpStore %param_var_color %16
  %17 = OpFunctionCall %float %fn %param_var_color
  OpReturnValue %17
  OpFunctionEnd
  %fn = OpFunction %float None %11
  %color_0 = OpFunctionParameter %_ptr_Function_v4float
  %bb_entry_0 = OpLabel
  %v = OpVariable %_ptr_Function_v4float Function
  %22 = OpLoad %v4float %color_0
  OpStore %v %22
  %23 = OpLoad %v4float %v
  %24 = OpFMul %v4float %23 %6
  OpStore %v %24
  %26 = OpAccessChain %_ptr_Function_float %v %int_0
  %27 = OpLoad %float %26
  OpReturnValue %27
  OpFunctionEnd
        )";
  
    const std::string after =
        R"(OpCapability SampledBuffer
  OpCapability ImageBuffer
  OpCapability Shader
  OpCapability Linkage
  OpMemoryModel Logical GLSL450
  OpSource HLSL 630
  OpName %type_buffer_image "type.buffer.image"
  OpName %output "output"
  OpName %main "main"
  OpName %color "color"
  OpName %bb_entry "bb.entry"
  OpName %param_var_color "param.var.color"
  OpName %fn "fn"
  OpName %color_0 "color"
  OpName %bb_entry_0 "bb.entry"
  OpName %v "v"
  OpDecorate %main LinkageAttributes "main" Export
  OpDecorate %output DescriptorSet 0
  OpDecorate %output Binding 1
  %float = OpTypeFloat 32
  %float_0_200000003 = OpConstant %float 0.200000003
  %v4float = OpTypeVector %float 4
  %6 = OpConstantComposite %v4float %float_0_200000003 %float_0_200000003 %float_0_200000003 %float_0_200000003
  %int = OpTypeInt 32 1
  %int_0 = OpConstant %int 0
  %type_buffer_image = OpTypeImage %float Buffer 2 0 0 2 Rgba32f
  %_ptr_UniformConstant_type_buffer_image = OpTypePointer UniformConstant %type_buffer_image
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %11 = OpTypeFunction %float %_ptr_Function_v4float
  %_ptr_Function_float = OpTypePointer Function %float
  %output = OpVariable %_ptr_UniformConstant_type_buffer_image UniformConstant
  %main = OpFunction %float None %11
  %color = OpFunctionParameter %_ptr_Function_v4float
  %bb_entry = OpLabel
  %28 = OpVariable %_ptr_Function_v4float Function
  %29 = OpVariable %_ptr_Function_float Function
  %param_var_color = OpVariable %_ptr_Function_v4float Function
  %16 = OpLoad %v4float %color
  OpStore %param_var_color %16
  %31 = OpLoad %v4float %param_var_color
  OpStore %28 %31
  %32 = OpLoad %v4float %28
  %33 = OpFMul %v4float %32 %6
  OpStore %28 %33
  %34 = OpAccessChain %_ptr_Function_float %28 %int_0
  %35 = OpLoad %float %34
  OpStore %29 %35
  %17 = OpLoad %float %29
  OpReturnValue %17
  OpFunctionEnd
  %fn = OpFunction %float None %11
  %color_0 = OpFunctionParameter %_ptr_Function_v4float
  %bb_entry_0 = OpLabel
  %v = OpVariable %_ptr_Function_v4float Function
  %22 = OpLoad %v4float %color_0
  OpStore %v %22
  %23 = OpLoad %v4float %v
  %24 = OpFMul %v4float %23 %6
  OpStore %v %24
  %26 = OpAccessChain %_ptr_Function_float %v %int_0
  %27 = OpLoad %float %26
  OpReturnValue %27
  OpFunctionEnd
  )";
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndCheck<InlineExhaustivePass>(before, after, false, true);
  }
  
  TEST_F(InlineTest, InlineFuncWithOpTerminateRayNotInContinue) {
    const std::string text =
        R"(
                 OpCapability RayTracingKHR
                 OpExtension "SPV_KHR_ray_tracing"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint AnyHitKHR %MyAHitMain2 "MyAHitMain2" %a
                 OpSource HLSL 630
                 OpName %a "a"
                 OpName %MyAHitMain2 "MyAHitMain2"
                 OpName %param_var_a "param.var.a"
                 OpName %src_MyAHitMain2 "src.MyAHitMain2"
                 OpName %a_0 "a"
                 OpName %bb_entry "bb.entry"
          %int = OpTypeInt 32 1
  %_ptr_IncomingRayPayloadKHR_int = OpTypePointer IncomingRayPayloadKHR %int
         %void = OpTypeVoid
            %6 = OpTypeFunction %void
  %_ptr_Function_int = OpTypePointer Function %int
           %14 = OpTypeFunction %void %_ptr_Function_int
            %a = OpVariable %_ptr_IncomingRayPayloadKHR_int IncomingRayPayloadKHR
  %MyAHitMain2 = OpFunction %void None %6
            %7 = OpLabel
  %param_var_a = OpVariable %_ptr_Function_int Function
           %10 = OpLoad %int %a
                 OpStore %param_var_a %10
           %11 = OpFunctionCall %void %src_MyAHitMain2 %param_var_a
           %13 = OpLoad %int %param_var_a
                 OpStore %a %13
                 OpReturn
                 OpFunctionEnd
  %src_MyAHitMain2 = OpFunction %void None %14
          %a_0 = OpFunctionParameter %_ptr_Function_int
     %bb_entry = OpLabel
           %17 = OpLoad %int %a_0
                 OpStore %a %17
                 OpTerminateRayKHR
                 OpFunctionEnd
  
  ; CHECK:      %MyAHitMain2 = OpFunction %void None
  ; CHECK-NEXT: OpLabel
  ; CHECK-NEXT:   %param_var_a = OpVariable %_ptr_Function_int Function
  ; CHECK-NEXT:   OpLoad %int %a
  ; CHECK-NEXT:   OpStore %param_var_a {{%\d+}}
  ; CHECK-NEXT:   OpLoad %int %param_var_a
  ; CHECK-NEXT:   OpStore %a {{%\d+}}
  ; CHECK-NEXT:   OpTerminateRayKHR
  ; CHECK-NEXT: OpLabel
  ; CHECK-NEXT:   OpLoad %int %param_var_a
  ; CHECK-NEXT:   OpStore %a %16
  ; CHECK-NEXT:   OpReturn
  ; CHECK-NEXT: OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, false);
  }
  
  TEST_F(InlineTest, EarlyReturnFunctionInlined) {
    // #version 140
    //
    // in vec4 BaseColor;
    //
    // float foo(vec4 bar)
    // {
    //     if (bar.x < 0.0)
    //         return 0.0;
    //     return bar.x;
    // }
    //
    // void main()
    // {
    //     vec4 color = vec4(foo(BaseColor));
    //     gl_FragColor = color;
    // }
  
    const std::string predefs =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %BaseColor %gl_FragColor
  OpExecutionMode %main OriginUpperLeft
  OpSource GLSL 140
  OpName %main "main"
  OpName %foo_vf4_ "foo(vf4;"
  OpName %bar "bar"
  OpName %color "color"
  OpName %BaseColor "BaseColor"
  OpName %param "param"
  OpName %gl_FragColor "gl_FragColor"
  %void = OpTypeVoid
  %10 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %v4float = OpTypeVector %float 4
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %14 = OpTypeFunction %float %_ptr_Function_v4float
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_float = OpTypePointer Function %float
  %float_0 = OpConstant %float 0
  %bool = OpTypeBool
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %BaseColor = OpVariable %_ptr_Input_v4float Input
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %gl_FragColor = OpVariable %_ptr_Output_v4float Output
  )";
  
    const std::string foo =
        R"(%foo_vf4_ = OpFunction %float None %14
  %bar = OpFunctionParameter %_ptr_Function_v4float
  %27 = OpLabel
  %28 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %29 = OpLoad %float %28
  %30 = OpFOrdLessThan %bool %29 %float_0
  OpSelectionMerge %31 None
  OpBranchConditional %30 %32 %31
  %32 = OpLabel
  OpReturnValue %float_0
  %31 = OpLabel
  %33 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %34 = OpLoad %float %33
  OpReturnValue %34
  OpFunctionEnd
  )";
  
    const std::string fooMergeReturn =
        R"(%foo_vf4_ = OpFunction %float None %14
  %bar = OpFunctionParameter %_ptr_Function_v4float
  %27 = OpLabel
  %41 = OpVariable %_ptr_Function_bool Function %false
  %36 = OpVariable %_ptr_Function_float Function
  OpSelectionMerge %35 None
  OpSwitch %uint_0 %38
  %38 = OpLabel
  %28 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %29 = OpLoad %float %28
  %30 = OpFOrdLessThan %bool %29 %float_0
  OpSelectionMerge %31 None
  OpBranchConditional %30 %32 %31
  %32 = OpLabel
  OpStore %41 %true
  OpStore %36 %float_0
  OpBranch %35
  %31 = OpLabel
  %33 = OpAccessChain %_ptr_Function_float %bar %uint_0
  %34 = OpLoad %float %33
  OpStore %41 %true
  OpStore %36 %34
  OpBranch %35
  %35 = OpLabel
  %37 = OpLoad %float %36
  OpReturnValue %37
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%main = OpFunction %void None %10
  %22 = OpLabel
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %23 = OpLoad %v4float %BaseColor
  OpStore %param %23
  %24 = OpFunctionCall %float %foo_vf4_ %param
  %25 = OpCompositeConstruct %v4float %24 %24 %24 %24
  OpStore %color %25
  %26 = OpLoad %v4float %color
  OpStore %gl_FragColor %26
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(%false = OpConstantFalse %bool
  %_ptr_Function_bool = OpTypePointer Function %bool
  %true = OpConstantTrue %bool
  %main = OpFunction %void None %10
  %22 = OpLabel
  %43 = OpVariable %_ptr_Function_bool Function %false
  %44 = OpVariable %_ptr_Function_float Function
  %45 = OpVariable %_ptr_Function_float Function
  %color = OpVariable %_ptr_Function_v4float Function
  %param = OpVariable %_ptr_Function_v4float Function
  %23 = OpLoad %v4float %BaseColor
  OpStore %param %23
  OpStore %43 %false
  OpSelectionMerge %55 None
  OpSwitch %uint_0 %47
  %47 = OpLabel
  %48 = OpAccessChain %_ptr_Function_float %param %uint_0
  %49 = OpLoad %float %48
  %50 = OpFOrdLessThan %bool %49 %float_0
  OpSelectionMerge %52 None
  OpBranchConditional %50 %51 %52
  %51 = OpLabel
  OpStore %43 %true
  OpStore %44 %float_0
  OpBranch %55
  %52 = OpLabel
  %53 = OpAccessChain %_ptr_Function_float %param %uint_0
  %54 = OpLoad %float %53
  OpStore %43 %true
  OpStore %44 %54
  OpBranch %55
  %55 = OpLabel
  %56 = OpLoad %float %44
  OpStore %45 %56
  %24 = OpLoad %float %45
  %25 = OpCompositeConstruct %v4float %24 %24 %24 %24
  OpStore %color %25
  %26 = OpLoad %v4float %color
  OpStore %gl_FragColor %26
  OpReturn
  OpFunctionEnd
  )";
  
    // The early return case must be handled by merge-return first.
    AddPass<MergeReturnPass>();
    AddPass<InlineExhaustivePass>();
    RunAndCheck(predefs + before + foo, predefs + after + fooMergeReturn);
  }
  
  TEST_F(InlineTest, EarlyReturnNotAppearingLastInFunctionInlined) {
    // Example from https://github.com/KhronosGroup/SPIRV-Tools/issues/755
    //
    // Original example is derived from:
    //
    // #version 450
    //
    // float foo() {
    //     if (true) {
    //     }
    // }
    //
    // void main() { foo(); }
    //
    // But the order of basic blocks in foo is changed so that the return
    // block is listed second-last.  There is only one return in the callee
    // but it does not appear last.
  
    const std::string predefs =
        R"(OpCapability Shader
  OpMemoryModel Logical GLSL450
  OpEntryPoint Vertex %main "main"
  OpSource GLSL 450
  OpName %main "main"
  OpName %foo_ "foo("
  %void = OpTypeVoid
  %4 = OpTypeFunction %void
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  )";
  
    const std::string foo =
        R"(%foo_ = OpFunction %void None %4
  %7 = OpLabel
  OpSelectionMerge %8 None
  OpBranchConditional %true %9 %8
  %8 = OpLabel
  OpReturn
  %9 = OpLabel
  OpBranch %8
  OpFunctionEnd
  )";
  
    const std::string fooMergeReturn =
        R"(%uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %false = OpConstantFalse %bool
  %_ptr_Function_bool = OpTypePointer Function %bool
  %foo_ = OpFunction %void None %4
  %7 = OpLabel
  %18 = OpVariable %_ptr_Function_bool Function %false
  OpSelectionMerge %12 None
  OpSwitch %uint_0 %13
  %13 = OpLabel
  OpSelectionMerge %8 None
  OpBranchConditional %true %9 %8
  %8 = OpLabel
  OpStore %18 %true
  OpBranch %12
  %9 = OpLabel
  OpBranch %8
  %12 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%main = OpFunction %void None %4
  %10 = OpLabel
  %11 = OpFunctionCall %void %foo_
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(%main = OpFunction %void None %4
  %10 = OpLabel
  %19 = OpVariable %_ptr_Function_bool Function %false
  OpStore %19 %false
  OpSelectionMerge %24 None
  OpSwitch %uint_0 %21
  %21 = OpLabel
  OpSelectionMerge %22 None
  OpBranchConditional %true %23 %22
  %22 = OpLabel
  OpStore %19 %true
  OpBranch %24
  %23 = OpLabel
  OpBranch %22
  %24 = OpLabel
  OpReturn
  OpFunctionEnd
  )";
  
    // The early return case must be handled by merge-return first.
    AddPass<MergeReturnPass>();
    AddPass<InlineExhaustivePass>();
    RunAndCheck(predefs + foo + before, predefs + fooMergeReturn + after);
  }
  
  TEST_F(InlineTest, CalleeWithSingleReturnNeedsSingleTripLoopWrapper) {
    // The case from https://github.com/KhronosGroup/SPIRV-Tools/issues/2018
    //
    // The callee has a single return, but needs single-trip loop wrapper
    // to be inlined because the return is in a selection structure.
  
    const std::string predefs =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %_GLF_color
  OpExecutionMode %main OriginUpperLeft
  OpSource ESSL 310
  OpName %main "main"
  OpName %f_ "f("
  OpName %i "i"
  OpName %_GLF_color "_GLF_color"
  OpDecorate %_GLF_color Location 0
  %void = OpTypeVoid
  %7 = OpTypeFunction %void
  %float = OpTypeFloat 32
  %9 = OpTypeFunction %float
  %float_1 = OpConstant %float 1
  %bool = OpTypeBool
  %false = OpConstantFalse %bool
  %true = OpConstantTrue %bool
  %int = OpTypeInt 32 1
  %_ptr_Function_int = OpTypePointer Function %int
  %int_0 = OpConstant %int 0
  %int_1 = OpConstant %int 1
  %v4float = OpTypeVector %float 4
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %_GLF_color = OpVariable %_ptr_Output_v4float Output
  %float_0 = OpConstant %float 0
  %21 = OpConstantComposite %v4float %float_0 %float_0 %float_0 %float_0
  %22 = OpConstantComposite %v4float %float_0 %float_1 %float_0 %float_1
  )";
  
    const std::string new_predefs =
        R"(%_ptr_Function_float = OpTypePointer Function %float
  %uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %_ptr_Function_bool = OpTypePointer Function %bool
  )";
  
    const std::string main_before =
        R"(%main = OpFunction %void None %7
  %23 = OpLabel
  %i = OpVariable %_ptr_Function_int Function
  OpStore %i %int_0
  OpBranch %24
  %24 = OpLabel
  OpLoopMerge %25 %26 None
  OpBranch %27
  %27 = OpLabel
  %28 = OpLoad %int %i
  %29 = OpSLessThan %bool %28 %int_1
  OpBranchConditional %29 %30 %25
  %30 = OpLabel
  OpStore %_GLF_color %21
  %31 = OpFunctionCall %float %f_
  OpBranch %26
  %26 = OpLabel
  %32 = OpLoad %int %i
  %33 = OpIAdd %int %32 %int_1
  OpStore %i %33
  OpBranch %24
  %25 = OpLabel
  OpStore %_GLF_color %22
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string main_after =
        R"(%main = OpFunction %void None %7
  %23 = OpLabel
  %46 = OpVariable %_ptr_Function_bool Function %false
  %47 = OpVariable %_ptr_Function_float Function
  %48 = OpVariable %_ptr_Function_float Function
  %i = OpVariable %_ptr_Function_int Function
  OpStore %i %int_0
  OpBranch %24
  %24 = OpLabel
  OpLoopMerge %25 %26 None
  OpBranch %27
  %27 = OpLabel
  %28 = OpLoad %int %i
  %29 = OpSLessThan %bool %28 %int_1
  OpBranchConditional %29 %30 %25
  %30 = OpLabel
  OpStore %_GLF_color %21
  OpStore %46 %false
  OpSelectionMerge %53 None
  OpSwitch %uint_0 %50
  %50 = OpLabel
  OpSelectionMerge %52 None
  OpBranchConditional %true %51 %52
  %51 = OpLabel
  OpStore %46 %true
  OpStore %47 %float_1
  OpBranch %53
  %52 = OpLabel
  OpStore %46 %true
  OpStore %47 %float_1
  OpBranch %53
  %53 = OpLabel
  %54 = OpLoad %float %47
  OpStore %48 %54
  %31 = OpLoad %float %48
  OpBranch %26
  %26 = OpLabel
  %32 = OpLoad %int %i
  %33 = OpIAdd %int %32 %int_1
  OpStore %i %33
  OpBranch %24
  %25 = OpLabel
  OpStore %_GLF_color %22
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string callee =
        R"(%f_ = OpFunction %float None %9
  %34 = OpLabel
  OpSelectionMerge %35 None
  OpBranchConditional %true %36 %35
  %36 = OpLabel
  OpReturnValue %float_1
  %35 = OpLabel
  OpReturnValue %float_1
  OpFunctionEnd
  )";
  
    const std::string calleeMergeReturn =
        R"(%f_ = OpFunction %float None %9
  %34 = OpLabel
  %45 = OpVariable %_ptr_Function_bool Function %false
  %39 = OpVariable %_ptr_Function_float Function
  OpSelectionMerge %37 None
  OpSwitch %uint_0 %41
  %41 = OpLabel
  OpSelectionMerge %35 None
  OpBranchConditional %true %36 %35
  %36 = OpLabel
  OpStore %45 %true
  OpStore %39 %float_1
  OpBranch %37
  %35 = OpLabel
  OpStore %45 %true
  OpStore %39 %float_1
  OpBranch %37
  %37 = OpLabel
  %40 = OpLoad %float %39
  OpReturnValue %40
  OpFunctionEnd
  )";
  
    // The early return case must be handled by merge-return first.
    AddPass<MergeReturnPass>();
    AddPass<InlineExhaustivePass>();
    RunAndCheck(predefs + main_before + callee,
                predefs + new_predefs + main_after + calleeMergeReturn);
  }
  
  TEST_F(InlineTest, ForwardReferencesInPhiInlined) {
    // The basic structure of the test case is like this:
    //
    // int foo() {
    //   int result = 1;
    //   if (true) {
    //      result = 1;
    //   }
    //   return result;
    // }
    //
    // void main() {
    //  int x = foo();
    // }
    //
    // but with modifications: Using Phi instead of load/store, and the
    // return block in foo appears before the "then" block.
  
    const std::string predefs =
        R"(OpCapability Shader
  %1 = OpExtInstImport "GLSL.std.450"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Vertex %main "main"
  OpSource GLSL 450
  OpName %main "main"
  OpName %foo_ "foo("
  OpName %x "x"
  %void = OpTypeVoid
  %6 = OpTypeFunction %void
  %int = OpTypeInt 32 1
  %8 = OpTypeFunction %int
  %bool = OpTypeBool
  %true = OpConstantTrue %bool
  %int_0 = OpConstant %int 0
  %_ptr_Function_int = OpTypePointer Function %int
  )";
  
    const std::string callee =
        R"(%foo_ = OpFunction %int None %8
  %13 = OpLabel
  %14 = OpCopyObject %int %int_0
  OpSelectionMerge %15 None
  OpBranchConditional %true %16 %15
  %15 = OpLabel
  %17 = OpPhi %int %14 %13 %18 %16
  OpReturnValue %17
  %16 = OpLabel
  %18 = OpCopyObject %int %int_0
  OpBranch %15
  OpFunctionEnd
  )";
  
    const std::string calleeMergeReturn =
        R"(%uint = OpTypeInt 32 0
  %uint_0 = OpConstant %uint 0
  %false = OpConstantFalse %bool
  %_ptr_Function_bool = OpTypePointer Function %bool
  %foo_ = OpFunction %int None %8
  %13 = OpLabel
  %29 = OpVariable %_ptr_Function_bool Function %false
  %22 = OpVariable %_ptr_Function_int Function
  OpSelectionMerge %21 None
  OpSwitch %uint_0 %24
  %24 = OpLabel
  %14 = OpCopyObject %int %int_0
  OpSelectionMerge %15 None
  OpBranchConditional %true %16 %15
  %15 = OpLabel
  %17 = OpPhi %int %14 %24 %18 %16
  OpStore %29 %true
  OpStore %22 %17
  OpBranch %21
  %16 = OpLabel
  %18 = OpCopyObject %int %int_0
  OpBranch %15
  %21 = OpLabel
  %23 = OpLoad %int %22
  OpReturnValue %23
  OpFunctionEnd
  )";
  
    const std::string before =
        R"(%main = OpFunction %void None %6
  %19 = OpLabel
  %x = OpVariable %_ptr_Function_int Function
  %20 = OpFunctionCall %int %foo_
  OpStore %x %20
  OpReturn
  OpFunctionEnd
  )";
  
    const std::string after =
        R"(%main = OpFunction %void None %6
  %19 = OpLabel
  %30 = OpVariable %_ptr_Function_bool Function %false
  %31 = OpVariable %_ptr_Function_int Function
  %32 = OpVariable %_ptr_Function_int Function
  %x = OpVariable %_ptr_Function_int Function
  OpStore %30 %false
  OpSelectionMerge %40 None
  OpSwitch %uint_0 %34
  %34 = OpLabel
  %35 = OpCopyObject %int %int_0
  OpSelectionMerge %36 None
  OpBranchConditional %true %38 %36
  %36 = OpLabel
  %37 = OpPhi %int %35 %34 %39 %38
  OpStore %30 %true
  OpStore %31 %37
  OpBranch %40
  %38 = OpLabel
  %39 = OpCopyObject %int %int_0
  OpBranch %36
  %40 = OpLabel
  %41 = OpLoad %int %31
  OpStore %32 %41
  %20 = OpLoad %int %32
  OpStore %x %20
  OpReturn
  OpFunctionEnd
  )";
  
    AddPass<MergeReturnPass>();
    AddPass<InlineExhaustivePass>();
    RunAndCheck(predefs + callee + before, predefs + calleeMergeReturn + after);
  }
  
  TEST_F(InlineTest, DebugSimple) {
    // Check that it correctly generates DebugInlinedAt and maps it to DebugScope
    // for the inlined function foo().
    const std::string text = R"(
  ; CHECK: [[main_name:%\d+]] = OpString "main"
  ; CHECK: [[foo_name:%\d+]] = OpString "foo"
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} 4 1 {{%\d+}} [[main_name]] FlagIsProtected|FlagIsPrivate 4 [[main:%\d+]]
  ; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} 1 1 {{%\d+}} [[foo_name]] FlagIsProtected|FlagIsPrivate 1 [[foo:%\d+]]
  ; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} 1 14 [[dbg_foo]]
  ; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt 4 [[dbg_main]]
  ; CHECK: [[main]] = OpFunction %void None
  ; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]]
  ; CHECK: [[foo]] = OpFunction %v4float None
                 OpCapability Shader
            %1 = OpExtInstImport "OpenCL.DebugInfo.100"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %main "main" %3 %4
                 OpExecutionMode %main OriginUpperLeft
            %5 = OpString "ps.hlsl"
                 OpSource HLSL 600 %5
            %6 = OpString "float"
    %main_name = OpString "main"
     %foo_name = OpString "foo"
                 OpDecorate %3 Location 0
                 OpDecorate %4 Location 0
         %uint = OpTypeInt 32 0
      %uint_32 = OpConstant %uint 32
        %float = OpTypeFloat 32
      %float_1 = OpConstant %float 1
      %v4float = OpTypeVector %float 4
           %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
         %void = OpTypeVoid
           %18 = OpTypeFunction %void
           %19 = OpTypeFunction %v4float
            %3 = OpVariable %_ptr_Input_v4float Input
            %4 = OpVariable %_ptr_Output_v4float Output
           %20 = OpExtInst %void %1 DebugSource %5
           %21 = OpExtInst %void %1 DebugCompilationUnit 1 4 %20 HLSL
           %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
           %23 = OpExtInst %void %1 DebugTypeVector %22 4
           %24 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23 %23
           %25 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %23
     %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 4 1 %21 %main_name FlagIsProtected|FlagIsPrivate 4 %main
      %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 1 1 %21 %foo_name FlagIsProtected|FlagIsPrivate 1 %foo
           %29 = OpExtInst %void %1 DebugLexicalBlock %20 1 14 %dbg_foo
         %main = OpFunction %void None %18
           %30 = OpLabel
           %31 = OpExtInst %void %1 DebugScope %dbg_main
           %32 = OpFunctionCall %v4float %foo
           %33 = OpLoad %v4float %3
           %34 = OpFAdd %v4float %32 %33
                 OpStore %4 %34
                 OpReturn
                 OpFunctionEnd
          %foo = OpFunction %v4float None %19
           %35 = OpExtInst %void %1 DebugScope %dbg_foo
           %36 = OpLabel
           %37 = OpExtInst %void %1 DebugScope %29
                 OpReturnValue %14
                 OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, ShaderDebugSimple) {
    // Same as DebugSimple but for NonSemantic.Shader.DebugInfo.100.
    const std::string text = R"(
  ; CHECK: [[main_name:%\d+]] = OpString "main"
  ; CHECK: [[foo_name:%\d+]] = OpString "foo"
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[main_name]] {{%\d+}} {{%\d+}} %uint_4 %uint_1 {{%\d+}} [[main_name]] %uint_3 %uint_4
  ; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void {{%\d+}} DebugFunction [[foo_name]] {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} [[foo_name]] %uint_3 %uint_1
  ; CHECK: [[foo_bb:%\d+]] = OpExtInst %void {{%\d+}} DebugLexicalBlock {{%\d+}} %uint_1 %uint_14 [[dbg_foo]]
  ; CHECK: [[inlined_at:%\d+]] = OpExtInst %void {{%\d+}} DebugInlinedAt %uint_4 [[dbg_main]]
  ; CHECK: [[main:%\d+]] = OpFunction %void None
  ; CHECK: {{%\d+}} = OpExtInst %void {{%\d+}} DebugScope [[foo_bb]] [[inlined_at]]
  ; CHECK: [[foo:%\d+]] = OpFunction %v4float None
                 OpCapability Shader
                 OpExtension "SPV_KHR_non_semantic_info"
            %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %main "main" %3 %4
                 OpExecutionMode %main OriginUpperLeft
            %5 = OpString "ps.hlsl"
                 OpSource HLSL 600 %5
            %6 = OpString "float"
    %main_name = OpString "main"
     %foo_name = OpString "foo"
                 OpDecorate %3 Location 0
                 OpDecorate %4 Location 0
         %uint = OpTypeInt 32 0
       %uint_0 = OpConstant %uint 0
       %uint_1 = OpConstant %uint 1
       %uint_2 = OpConstant %uint 2
       %uint_3 = OpConstant %uint 3
       %uint_4 = OpConstant %uint 4
       %uint_5 = OpConstant %uint 5
      %uint_14 = OpConstant %uint 14
      %uint_32 = OpConstant %uint 32
        %float = OpTypeFloat 32
      %float_1 = OpConstant %float 1
      %v4float = OpTypeVector %float 4
           %14 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
         %void = OpTypeVoid
           %18 = OpTypeFunction %void
           %19 = OpTypeFunction %v4float
            %3 = OpVariable %_ptr_Input_v4float Input
            %4 = OpVariable %_ptr_Output_v4float Output
           %20 = OpExtInst %void %1 DebugSource %5
           %21 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %20 %uint_5
           %22 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 %uint_3 %uint_0
           %23 = OpExtInst %void %1 DebugTypeVector %22 %uint_4
           %24 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23 %23
           %25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %23
     %dbg_main = OpExtInst %void %1 DebugFunction %main_name %24 %20 %uint_4 %uint_1 %21 %main_name %uint_3 %uint_4
      %dbg_foo = OpExtInst %void %1 DebugFunction %foo_name %25 %20 %uint_1 %uint_1 %21 %foo_name %uint_3 %uint_1
           %29 = OpExtInst %void %1 DebugLexicalBlock %20 %uint_1 %uint_14 %dbg_foo
         %main = OpFunction %void None %18
           %30 = OpLabel
  %dbg_main_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_main %main
           %31 = OpExtInst %void %1 DebugScope %dbg_main
           %32 = OpFunctionCall %v4float %foo
           %33 = OpLoad %v4float %3
           %34 = OpFAdd %v4float %32 %33
                 OpStore %4 %34
                 OpReturn
                 OpFunctionEnd
          %foo = OpFunction %v4float None %19
           %36 = OpLabel
  %dbg_foo_def = OpExtInst %void %1 DebugFunctionDefinition %dbg_foo %foo
           %35 = OpExtInst %void %1 DebugScope %dbg_foo
           %37 = OpExtInst %void %1 DebugScope %29
                 OpReturnValue %14
                 OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DebugNested) {
    // When function main() calls function zoo() and function zoo() calls
    // function bar() and function bar() calls function foo(), check that
    // the inline pass correctly generates DebugInlinedAt instructions
    // for the nested function calls.
    const std::string text = R"(
  ; CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  ; CHECK: [[v4f2:%\d+]] = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  ; CHECK: [[v4f3:%\d+]] = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
  ; CHECK: [[color:%\d+]] = OpVariable %_ptr_Input_v4float Input
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 10 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 10 [[main:%\d+]]
  ; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 [[foo:%\d+]]
  ; CHECK: [[dbg_bar:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 4 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 4 [[bar:%\d+]]
  ; CHECK: [[dbg_zoo:%\d+]] = OpExtInst %void [[ext]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 7 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 7 [[zoo:%\d+]]
  ; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 600 [[dbg_main]]
  ; CHECK: [[inlined_to_zoo:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 700 [[dbg_zoo]] [[inlined_to_main]]
  ; CHECK: [[inlined_to_bar:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 300 [[dbg_bar]] [[inlined_to_zoo]]
  ; CHECK: [[main]] = OpFunction %void None
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_foo]] [[inlined_to_bar]]
  ; CHECK-NEXT: OpLine {{%\d+}} 100 0
  ; CHECK-NEXT: OpStore {{%\d+}} [[v4f1]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_bar]] [[inlined_to_zoo]]
  ; CHECK-NEXT: OpLine {{%\d+}} 300 0
  ; CHECK-NEXT: [[foo_ret:%\d+]] = OpLoad %v4float
  ; CHECK-NEXT: OpLine {{%\d+}} 400 0
  ; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[foo_ret]] [[v4f2]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_zoo]] [[inlined_to_main]]
  ; CHECK-NEXT: OpLine {{%\d+}} 700 0
  ; CHECK-NEXT: [[bar_ret:%\d+]] = OpLoad %v4float
  ; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[bar_ret]] [[v4f3]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
  ; CHECK-NEXT: OpLine {{%\d+}} 600 0
  ; CHECK-NEXT: [[zoo_ret:%\d+]] = OpLoad %v4float
  ; CHECK-NEXT: [[color_val:%\d+]] = OpLoad %v4float [[color]]
  ; CHECK-NEXT: {{%\d+}} = OpFAdd %v4float [[zoo_ret]] [[color_val]]
  ; CHECK: [[foo]] = OpFunction %v4float None
  ; CHECK: [[bar]] = OpFunction %v4float None
  ; CHECK: [[zoo]] = OpFunction %v4float None
                 OpCapability Shader
            %1 = OpExtInstImport "OpenCL.DebugInfo.100"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %main "main" %3 %4
                 OpExecutionMode %main OriginUpperLeft
            %5 = OpString "ps.hlsl"
                 OpSource HLSL 600 %5
            %6 = OpString "float"
            %7 = OpString "main"
            %8 = OpString "foo"
            %9 = OpString "bar"
           %10 = OpString "zoo"
                 OpDecorate %3 Location 0
                 OpDecorate %4 Location 0
         %uint = OpTypeInt 32 0
      %uint_32 = OpConstant %uint 32
        %float = OpTypeFloat 32
      %float_1 = OpConstant %float 1
      %float_2 = OpConstant %float 2
      %float_3 = OpConstant %float 3
      %v4float = OpTypeVector %float 4
           %18 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
           %19 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
           %20 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
         %void = OpTypeVoid
           %24 = OpTypeFunction %void
           %25 = OpTypeFunction %v4float
            %3 = OpVariable %_ptr_Input_v4float Input
            %4 = OpVariable %_ptr_Output_v4float Output
           %26 = OpExtInst %void %1 DebugSource %5
           %27 = OpExtInst %void %1 DebugCompilationUnit 1 4 %26 HLSL
           %28 = OpExtInst %void %1 DebugTypeBasic %6 %uint_32 Float
           %29 = OpExtInst %void %1 DebugTypeVector %28 4
           %30 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %29 %29
           %31 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %29
           %32 = OpExtInst %void %1 DebugFunction %7 %30 %26 10 1 %27 %7 FlagIsProtected|FlagIsPrivate 10 %main
           %33 = OpExtInst %void %1 DebugFunction %8 %31 %26 1 1 %27 %8 FlagIsProtected|FlagIsPrivate 1 %foo
           %35 = OpExtInst %void %1 DebugFunction %9 %31 %26 4 1 %27 %9 FlagIsProtected|FlagIsPrivate 4 %bar
           %37 = OpExtInst %void %1 DebugFunction %10 %31 %26 7 1 %27 %10 FlagIsProtected|FlagIsPrivate 7 %zoo
         %main = OpFunction %void None %24
           %39 = OpLabel
           %40 = OpExtInst %void %1 DebugScope %32
                 OpLine %5 600 0
           %41 = OpFunctionCall %v4float %zoo
           %42 = OpLoad %v4float %3
           %43 = OpFAdd %v4float %41 %42
                 OpStore %4 %43
                 OpReturn
                 OpFunctionEnd
          %foo = OpFunction %v4float None %25
           %44 = OpExtInst %void %1 DebugScope %33
           %45 = OpLabel
                 OpLine %5 100 0
                 OpReturnValue %18
                 OpFunctionEnd
                 OpLine %5 200 0
          %bar = OpFunction %v4float None %25
           %46 = OpExtInst %void %1 DebugScope %35
           %47 = OpLabel
                 OpLine %5 300 0
           %48 = OpFunctionCall %v4float %foo
                 OpLine %5 400 0
           %49 = OpFAdd %v4float %48 %19
                 OpLine %5 500 0
                 OpReturnValue %49
                 OpFunctionEnd
          %zoo = OpFunction %v4float None %25
           %50 = OpExtInst %void %1 DebugScope %37
           %51 = OpLabel
                 OpLine %5 700 0
           %52 = OpFunctionCall %v4float %bar
           %53 = OpFAdd %v4float %52 %20
                 OpReturnValue %53
                 OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DebugSimpleHLSLPixelShader) {
    const std::string text = R"(
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} 1 1 {{%\d+}} {{%\d+}} FlagIsProtected|FlagIsPrivate 1 %src_main
  ; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} 1 47 [[dbg_main]]
  ; CHECK: %main = OpFunction %void None
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]]
  ; CHECK: OpLine {{%\d+}} 2 10
  ; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color
  ; CHECK: OpLine {{%\d+}} 2 3
  ; CHECK: OpFunctionEnd
  ; CHECK: %src_main = OpFunction %v4float None
                 OpCapability Shader
            %1 = OpExtInstImport "OpenCL.DebugInfo.100"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
                 OpExecutionMode %main OriginUpperLeft
            %5 = OpString "ps.hlsl"
                 OpSource HLSL 600 %5
           %14 = OpString "#line 1 \"ps.hlsl\"
  float4 main(float4 color : COLOR) : SV_TARGET {
    return color;
  }
  "
           %17 = OpString "float"
           %21 = OpString "src.main"
           %24 = OpString "color"
                 OpName %in_var_COLOR "in.var.COLOR"
                 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
                 OpName %main "main"
                 OpName %param_var_color "param.var.color"
                 OpName %src_main "src.main"
                 OpName %color "color"
                 OpName %bb_entry "bb.entry"
                 OpDecorate %in_var_COLOR Location 0
                 OpDecorate %out_var_SV_TARGET Location 0
         %uint = OpTypeInt 32 0
      %uint_32 = OpConstant %uint 32
        %float = OpTypeFloat 32
      %v4float = OpTypeVector %float 4
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
         %void = OpTypeVoid
           %27 = OpTypeFunction %void
  %_ptr_Function_v4float = OpTypePointer Function %v4float
           %33 = OpTypeFunction %v4float %_ptr_Function_v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
           %13 = OpExtInst %void %1 DebugExpression
           %15 = OpExtInst %void %1 DebugSource %5 %14
           %16 = OpExtInst %void %1 DebugCompilationUnit 1 4 %15 HLSL
           %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 Float
           %19 = OpExtInst %void %1 DebugTypeVector %18 4
           %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %19 %19
           %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %src_main
           %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 1 20 %22 FlagIsLocal 0
           %26 = OpExtInst %void %1 DebugLexicalBlock %15 1 47 %22
         %main = OpFunction %void None %27
           %28 = OpLabel
  %param_var_color = OpVariable %_ptr_Function_v4float Function
           %31 = OpLoad %v4float %in_var_COLOR
                 OpStore %param_var_color %31
           %32 = OpFunctionCall %v4float %src_main %param_var_color
                 OpStore %out_var_SV_TARGET %32
                 OpReturn
                 OpFunctionEnd
                 OpLine %5 1 1
     %src_main = OpFunction %v4float None %33
           %34 = OpExtInst %void %1 DebugScope %22
        %color = OpFunctionParameter %_ptr_Function_v4float
           %36 = OpExtInst %void %1 DebugDeclare %25 %color %13
     %bb_entry = OpLabel
           %38 = OpExtInst %void %1 DebugScope %26
                 OpLine %5 2 10
           %39 = OpLoad %v4float %color
                 OpLine %5 2 3
                 OpReturnValue %39
                 OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, ShaderDebugSimpleHLSLPixelShader) {
    // Same as DebugSimpleHLSLPixelShader but for
    // NonSemantic.Shader.DebugInfo.100.
    const std::string text = R"(
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction {{%\d+}} {{%\d+}} {{%\d+}} %uint_1 %uint_1 {{%\d+}} {{%\d+}} %uint_3 %uint_1
  ; CHECK: [[lex_blk:%\d+]] = OpExtInst %void [[ext]] DebugLexicalBlock {{%\d+}} %uint_1 %uint_47 [[dbg_main]]
  ; CHECK: %main = OpFunction %void None
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare {{%\d+}} %param_var_color
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[lex_blk]]
  ; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_10 %uint_10
  ; CHECK: {{%\d+}} = OpLoad %v4float %param_var_color
  ; CHECK: {{%\d+}} = OpExtInst %void %1 DebugLine {{%\d+}} %uint_2 %uint_2 %uint_3 %uint_3
  ; CHECK: OpFunctionEnd
  ; CHECK: %src_main = OpFunction %v4float None
                 OpCapability Shader
                 OpExtension "SPV_KHR_non_semantic_info"
            %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
                 OpMemoryModel Logical GLSL450
                 OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
                 OpExecutionMode %main OriginUpperLeft
            %5 = OpString "ps.hlsl"
                 OpSource HLSL 600 %5
           %14 = OpString "#line 1 \"ps.hlsl\"
  float4 main(float4 color : COLOR) : SV_TARGET {
    return color;
  }
  "
           %17 = OpString "float"
           %21 = OpString "src.main"
           %24 = OpString "color"
                 OpName %in_var_COLOR "in.var.COLOR"
                 OpName %out_var_SV_TARGET "out.var.SV_TARGET"
                 OpName %main "main"
                 OpName %param_var_color "param.var.color"
                 OpName %src_main "src.main"
                 OpName %color "color"
                 OpName %bb_entry "bb.entry"
                 OpDecorate %in_var_COLOR Location 0
                 OpDecorate %out_var_SV_TARGET Location 0
         %uint = OpTypeInt 32 0
       %uint_0 = OpConstant %uint 0
       %uint_1 = OpConstant %uint 1
       %uint_2 = OpConstant %uint 2
       %uint_3 = OpConstant %uint 3
       %uint_4 = OpConstant %uint 4
       %uint_5 = OpConstant %uint 5
      %uint_10 = OpConstant %uint 10
      %uint_20 = OpConstant %uint 20
      %uint_32 = OpConstant %uint 32
      %uint_47 = OpConstant %uint 47
        %float = OpTypeFloat 32
      %v4float = OpTypeVector %float 4
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
         %void = OpTypeVoid
           %27 = OpTypeFunction %void
  %_ptr_Function_v4float = OpTypePointer Function %v4float
           %33 = OpTypeFunction %v4float %_ptr_Function_v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
           %13 = OpExtInst %void %1 DebugExpression
           %15 = OpExtInst %void %1 DebugSource %5 %14
           %16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5
           %18 = OpExtInst %void %1 DebugTypeBasic %17 %uint_32 %uint_3 %uint_0
           %19 = OpExtInst %void %1 DebugTypeVector %18 %uint_4
           %20 = OpExtInst %void %1 DebugTypeFunction %uint_3 %19 %19
           %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 %uint_1 %uint_1 %16 %21 %uint_3 %uint_1
           %25 = OpExtInst %void %1 DebugLocalVariable %24 %19 %15 %uint_1 %uint_20 %22 %uint_4 %uint_0
           %26 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_1 %uint_47 %22
         %main = OpFunction %void None %27
           %28 = OpLabel
  %param_var_color = OpVariable %_ptr_Function_v4float Function
           %31 = OpLoad %v4float %in_var_COLOR
                 OpStore %param_var_color %31
           %32 = OpFunctionCall %v4float %src_main %param_var_color
                 OpStore %out_var_SV_TARGET %32
                 OpReturn
                 OpFunctionEnd
     %src_main = OpFunction %v4float None %33
        %color = OpFunctionParameter %_ptr_Function_v4float
     %bb_entry = OpLabel
          %140 = OpExtInst %void %1 DebugFunctionDefinition %22 %src_main
          %141 = OpExtInst %void %1 DebugLine %5 %uint_1 %uint_1 %uint_1 %uint_1
           %34 = OpExtInst %void %1 DebugScope %22
           %36 = OpExtInst %void %1 DebugDeclare %25 %color %13
           %38 = OpExtInst %void %1 DebugScope %26
          %142 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_10 %uint_10
           %39 = OpLoad %v4float %color
          %143 = OpExtInst %void %1 DebugLine %5 %uint_2 %uint_2 %uint_3 %uint_3
                 OpReturnValue %39
                 OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DebugDeclareForCalleeFunctionParam) {
    // Check that InlinePass correctly generates DebugDeclare instructions
    // for callee function's parameters and maps them to corresponding
    // local variables of caller function.
    const std::string text = R"(
  ; CHECK: [[add:%\d+]] = OpString "add"
  ; CHECK: [[a:%\d+]] = OpString "a"
  ; CHECK: [[b:%\d+]] = OpString "b"
  ; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
  ; CHECK: [[dbg_a:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[a]]
  ; CHECK: [[dbg_b:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[b]]
  ; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5
  ; CHECK: OpStore [[param_a:%\d+]]
  ; CHECK: OpStore [[param_b:%\d+]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_a]] [[param_a]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_b]] [[param_b]]
  
  OpCapability Shader
  %ext = OpExtInstImport "OpenCL.DebugInfo.100"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
  OpExecutionMode %main OriginUpperLeft
  %file_name = OpString "ps.hlsl"
  OpSource HLSL 600 %file_name
  %float_name = OpString "float"
  %main_name = OpString "main"
  %add_name = OpString "add"
  %a_name = OpString "a"
  %b_name = OpString "b"
  OpDecorate %in_var_COLOR Location 0
  OpDecorate %out_var_SV_TARGET Location 0
  %uint = OpTypeInt 32 0
  %uint_32 = OpConstant %uint 32
  %float = OpTypeFloat 32
  %float_1 = OpConstant %float 1
  %float_2 = OpConstant %float 2
  %v4float = OpTypeVector %float 4
  %v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
  %void = OpTypeVoid
  %void_fn_type = OpTypeFunction %void
  %v4f_fn_type = OpTypeFunction %v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
  %null_expr = OpExtInst %void %ext DebugExpression
  %src = OpExtInst %void %ext DebugSource %file_name
  %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
  %dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
  %dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
  %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
  %add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
  %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
  %dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
  %dbg_a = OpExtInst %void %ext DebugLocalVariable %a_name %dbg_v4f %src 1 13 %dbg_add FlagIsLocal 0
  %dbg_b = OpExtInst %void %ext DebugLocalVariable %b_name %dbg_v4f %src 1 20 %dbg_add FlagIsLocal 1
  %add_lb = OpExtInst %void %ext DebugLexicalBlock %src 1 23 %dbg_add
  %main = OpFunction %void None %void_fn_type
  %main_bb = OpLabel
  %param_a = OpVariable %_ptr_Function_v4float Function
  %param_b = OpVariable %_ptr_Function_v4float Function
  %scope0 = OpExtInst %void %ext DebugScope %dbg_main
  OpStore %param_a %v4f1
  OpStore %param_b %v4f2
  %result = OpFunctionCall %v4float %add %param_a %param_b
  OpStore %out_var_SV_TARGET %result
  OpReturn
  OpFunctionEnd
  %add = OpFunction %v4float None %add_fn_type
  %scope1 = OpExtInst %void %ext DebugScope %dbg_add
  %a = OpFunctionParameter %_ptr_Function_v4float
  %b = OpFunctionParameter %_ptr_Function_v4float
  %decl0 = OpExtInst %void %ext DebugDeclare %dbg_a %a %null_expr
  %decl1 = OpExtInst %void %ext DebugDeclare %dbg_b %b %null_expr
  %add_bb = OpLabel
  %scope2 = OpExtInst %void %ext DebugScope %add_lb
  %a_val = OpLoad %v4float %a
  %b_val = OpLoad %v4float %b
  %res = OpFAdd %v4float %a_val %b_val
  OpReturnValue %res
  OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DebugDeclareForCalleeLocalVar) {
    // Check that InlinePass correctly generates DebugDeclare instructions
    // for callee function's local variables and maps them to corresponding
    // local variables of caller function.
    const std::string text = R"(
  ; CHECK: [[add:%\d+]] = OpString "add"
  ; CHECK: [[foo:%\d+]] = OpString "foo"
  ; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
  ; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[foo]] {{%\d+}} {{%\d+}} 2 2 [[dbg_add]]
  ; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5
  
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
  ; CHECK: [[new_foo:%\d+]] = OpVariable %_ptr_Function_v4float Function
  
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
  ; CHECK: [[a_val:%\d+]] = OpLoad %v4float
  ; CHECK: [[b_val:%\d+]] = OpLoad %v4float
  ; CHECK: [[res:%\d+]] = OpFAdd %v4float [[a_val]] [[b_val]]
  ; CHECK: OpStore [[new_foo]] [[res]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_foo]] [[new_foo]]
  
  OpCapability Shader
  %ext = OpExtInstImport "OpenCL.DebugInfo.100"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
  OpExecutionMode %main OriginUpperLeft
  %file_name = OpString "ps.hlsl"
  OpSource HLSL 600 %file_name
  %float_name = OpString "float"
  %main_name = OpString "main"
  %add_name = OpString "add"
  %foo_name = OpString "foo"
  OpDecorate %in_var_COLOR Location 0
  OpDecorate %out_var_SV_TARGET Location 0
  %uint = OpTypeInt 32 0
  %uint_32 = OpConstant %uint 32
  %float = OpTypeFloat 32
  %float_1 = OpConstant %float 1
  %float_2 = OpConstant %float 2
  %v4float = OpTypeVector %float 4
  %v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
  %void = OpTypeVoid
  %void_fn_type = OpTypeFunction %void
  %v4f_fn_type = OpTypeFunction %v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
  %null_expr = OpExtInst %void %ext DebugExpression
  %src = OpExtInst %void %ext DebugSource %file_name
  %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
  %dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
  %dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
  %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
  %add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
  %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
  %dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
  %dbg_foo = OpExtInst %void %ext DebugLocalVariable %foo_name %dbg_v4f %src 2 2 %dbg_add FlagIsLocal
  %main = OpFunction %void None %void_fn_type
  %main_bb = OpLabel
  %param_a = OpVariable %_ptr_Function_v4float Function
  %param_b = OpVariable %_ptr_Function_v4float Function
  %scope0 = OpExtInst %void %ext DebugScope %dbg_main
  OpStore %param_a %v4f1
  OpStore %param_b %v4f2
  %result = OpFunctionCall %v4float %add %param_a %param_b
  OpStore %out_var_SV_TARGET %result
  OpReturn
  OpFunctionEnd
  %add = OpFunction %v4float None %add_fn_type
  %scope1 = OpExtInst %void %ext DebugScope %dbg_add
  %a = OpFunctionParameter %_ptr_Function_v4float
  %b = OpFunctionParameter %_ptr_Function_v4float
  %add_bb = OpLabel
  %foo = OpVariable %_ptr_Function_v4float Function
  %a_val = OpLoad %v4float %a
  %b_val = OpLoad %v4float %b
  %res = OpFAdd %v4float %a_val %b_val
  OpStore %foo %res
  %decl = OpExtInst %void %ext DebugDeclare %dbg_foo %foo %null_expr
  %foo_val = OpLoad %v4float %foo
  OpReturnValue %foo_val
  OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DebugDeclareMultiple) {
    // Check that InlinePass correctly generates DebugDeclare instructions
    // for callee function's parameters and maps them to corresponding
    // local variables of caller function.
    const std::string text = R"(
  ; CHECK: [[add:%\d+]] = OpString "add"
  ; CHECK: [[a:%\d+]] = OpString "a"
  ; CHECK: [[b:%\d+]] = OpString "b"
  ; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
  ; CHECK: [[dbg_a:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[a]]
  ; CHECK: [[dbg_b:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[b]]
  ; CHECK: OpFunction
  ; CHECK-NOT: OpFunctionEnd
  ; CHECK: OpStore [[param_a:%\d+]]
  ; CHECK: OpStore [[param_b:%\d+]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_a]] [[param_a]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugDeclare [[dbg_b]] [[param_b]]
  ; CHECK: [[a_val:%\d+]] = OpLoad %v4float [[param_a]]
  ; CHECK: OpStore [[foo:%\d+]] [[a_val]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugValue [[dbg_a]] [[foo]]
  
  OpCapability Shader
  %ext = OpExtInstImport "OpenCL.DebugInfo.100"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
  OpExecutionMode %main OriginUpperLeft
  %file_name = OpString "ps.hlsl"
  OpSource HLSL 600 %file_name
  %float_name = OpString "float"
  %main_name = OpString "main"
  %add_name = OpString "add"
  %a_name = OpString "a"
  %b_name = OpString "b"
  OpDecorate %in_var_COLOR Location 0
  OpDecorate %out_var_SV_TARGET Location 0
  %uint = OpTypeInt 32 0
  %uint_32 = OpConstant %uint 32
  %float = OpTypeFloat 32
  %float_1 = OpConstant %float 1
  %float_2 = OpConstant %float 2
  %v4float = OpTypeVector %float 4
  %v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
  %void = OpTypeVoid
  %void_fn_type = OpTypeFunction %void
  %v4f_fn_type = OpTypeFunction %v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
  %null_expr = OpExtInst %void %ext DebugExpression
  %src = OpExtInst %void %ext DebugSource %file_name
  %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
  %dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
  %dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
  %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
  %add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
  %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
  %dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
  %dbg_a = OpExtInst %void %ext DebugLocalVariable %a_name %dbg_v4f %src 1 13 %dbg_add FlagIsLocal 0
  %dbg_b = OpExtInst %void %ext DebugLocalVariable %b_name %dbg_v4f %src 1 20 %dbg_add FlagIsLocal 1
  %main = OpFunction %void None %void_fn_type
  %main_bb = OpLabel
  %param_a = OpVariable %_ptr_Function_v4float Function
  %param_b = OpVariable %_ptr_Function_v4float Function
  %scope0 = OpExtInst %void %ext DebugScope %dbg_main
  OpStore %param_a %v4f1
  OpStore %param_b %v4f2
  %result = OpFunctionCall %v4float %add %param_a %param_b
  OpStore %out_var_SV_TARGET %result
  OpReturn
  OpFunctionEnd
  %add = OpFunction %v4float None %add_fn_type
  %scope1 = OpExtInst %void %ext DebugScope %dbg_add
  %a = OpFunctionParameter %_ptr_Function_v4float
  %b = OpFunctionParameter %_ptr_Function_v4float
  %decl0 = OpExtInst %void %ext DebugDeclare %dbg_a %a %null_expr
  %add_bb = OpLabel
  %decl1 = OpExtInst %void %ext DebugDeclare %dbg_b %b %null_expr
  %foo = OpVariable %_ptr_Function_v4float Function
  %a_val = OpLoad %v4float %a
  OpStore %foo %a_val
  %dbg_val = OpExtInst %void %ext DebugValue %dbg_a %foo %null_expr
  %b_val = OpLoad %v4float %b
  %res = OpFAdd %v4float %a_val %b_val
  OpReturnValue %res
  OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, DebugValueForFunctionCallReturn) {
    // Check that InlinePass correctly generates DebugValue instruction
    // for function call's return value and maps it to a corresponding
    // value in the caller function.
    const std::string text = R"(
  ; CHECK: [[main:%\d+]] = OpString "main"
  ; CHECK: [[add:%\d+]] = OpString "add"
  ; CHECK: [[result:%\d+]] = OpString "result"
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[main]]
  ; CHECK: [[dbg_add:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[add]]
  ; CHECK: [[dbg_result:%\d+]] = OpExtInst %void [[ext]] DebugLocalVariable [[result]] {{%\d+}} {{%\d+}} 6 2 [[dbg_main]]
  ; CHECK: [[inlinedat:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 5
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_add]] [[inlinedat]]
  ; CHECK: [[a_val:%\d+]] = OpLoad %v4float
  ; CHECK: [[b_val:%\d+]] = OpLoad %v4float
  ; CHECK: [[res:%\d+]] = OpFAdd %v4float [[a_val]] [[b_val]]
  ; CHECK: OpStore [[new_result:%\d+]] [[res]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
  ; CHECK: [[result_val:%\d+]] = OpLoad %v4float [[new_result]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugValue [[dbg_result]] [[result_val]]
  
  OpCapability Shader
  %ext = OpExtInstImport "OpenCL.DebugInfo.100"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
  OpExecutionMode %main OriginUpperLeft
  %file_name = OpString "ps.hlsl"
  OpSource HLSL 600 %file_name
  %float_name = OpString "float"
  %main_name = OpString "main"
  %add_name = OpString "add"
  %result_name = OpString "result"
  OpDecorate %in_var_COLOR Location 0
  OpDecorate %out_var_SV_TARGET Location 0
  %uint = OpTypeInt 32 0
  %uint_32 = OpConstant %uint 32
  %float = OpTypeFloat 32
  %float_1 = OpConstant %float 1
  %float_2 = OpConstant %float 2
  %v4float = OpTypeVector %float 4
  %v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %_ptr_Function_v4float = OpTypePointer Function %v4float
  %add_fn_type = OpTypeFunction %v4float %_ptr_Function_v4float %_ptr_Function_v4float
  %void = OpTypeVoid
  %void_fn_type = OpTypeFunction %void
  %v4f_fn_type = OpTypeFunction %v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
  %null_expr = OpExtInst %void %ext DebugExpression
  %src = OpExtInst %void %ext DebugSource %file_name
  %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
  %dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
  %dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
  %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
  %add_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f %dbg_v4f
  %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 5 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
  %dbg_add = OpExtInst %void %ext DebugFunction %add_name %add_ty %src 1 1 %cu %add_name FlagIsProtected|FlagIsPrivate 1 %add
  %dbg_result = OpExtInst %void %ext DebugLocalVariable %result_name %dbg_v4f %src 6 2 %dbg_main FlagIsLocal
  %main = OpFunction %void None %void_fn_type
  %main_bb = OpLabel
  %param_a = OpVariable %_ptr_Function_v4float Function
  %param_b = OpVariable %_ptr_Function_v4float Function
  %scope0 = OpExtInst %void %ext DebugScope %dbg_main
  OpStore %param_a %v4f1
  OpStore %param_b %v4f2
  %result = OpFunctionCall %v4float %add %param_a %param_b
  %value = OpExtInst %void %ext DebugValue %dbg_result %result %null_expr
  OpStore %out_var_SV_TARGET %result
  OpReturn
  OpFunctionEnd
  %add = OpFunction %v4float None %add_fn_type
  %scope1 = OpExtInst %void %ext DebugScope %dbg_add
  %a = OpFunctionParameter %_ptr_Function_v4float
  %b = OpFunctionParameter %_ptr_Function_v4float
  %add_bb = OpLabel
  %a_val = OpLoad %v4float %a
  %b_val = OpLoad %v4float %b
  %res = OpFAdd %v4float %a_val %b_val
  OpReturnValue %res
  OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, NestedWithAnExistingDebugInlinedAt) {
    // When a DebugScope instruction in a callee function already has a
    // DebugInlinedAt information, we have to create a recursive
    // DebugInlinedAt chain. See inlined_to_zoo and inlined_to_bar in
    // the following code.
    const std::string text = R"(
  ; CHECK: [[main:%\d+]] = OpString "main"
  ; CHECK: [[foo:%\d+]] = OpString "foo"
  ; CHECK: [[bar:%\d+]] = OpString "bar"
  ; CHECK: [[zoo:%\d+]] = OpString "zoo"
  ; CHECK: [[v4f1:%\d+]] = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  ; CHECK: [[v4f2:%\d+]] = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  ; CHECK: [[v4f3:%\d+]] = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
  ; CHECK: [[dbg_main:%\d+]] = OpExtInst %void [[ext:%\d+]] DebugFunction [[main]]
  ; CHECK: [[dbg_foo:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[foo]]
  ; CHECK: [[dbg_bar:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[bar]]
  ; CHECK: [[dbg_zoo:%\d+]] = OpExtInst %void [[ext]] DebugFunction [[zoo]]
  ; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 10 [[dbg_main]]
  ; CHECK: [[inlined_to_zoo:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 7 [[dbg_zoo]] [[inlined_to_main]]
  ; CHECK: [[inlined_to_main:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 10 [[dbg_main]]
  ; CHECK: [[inlined_to_bar:%\d+]] = OpExtInst %void [[ext]] DebugInlinedAt 4 [[dbg_bar]] [[inlined_to_zoo]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_foo]] [[inlined_to_bar]]
  ; CHECK: OpStore [[foo_ret:%\d+]] [[v4f1]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_bar]] [[inlined_to_zoo]]
  ; CHECK: [[foo_ret_val:%\d+]] = OpLoad %v4float [[foo_ret]]
  ; CHECK: [[bar_ret:%\d+]] = OpFAdd %v4float [[foo_ret_val]] [[v4f2]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_zoo]] [[inlined_to_main]]
  ; CHECK: [[zoo_result:%\d+]] = OpFAdd %v4float [[bar_ret]] [[v4f3]]
  ; CHECK: OpStore [[zoo_ret:%\d+]] [[zoo_result]]
  ; CHECK: {{%\d+}} = OpExtInst %void [[ext]] DebugScope [[dbg_main]]
  ; CHECK: [[zoo_ret_val:%\d+]] = OpLoad %v4float [[zoo_ret]]
  ; CHECK: {{%\d+}} = OpFAdd %v4float [[zoo_ret_val]] {{%\d+}}
  
  OpCapability Shader
  %ext = OpExtInstImport "OpenCL.DebugInfo.100"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main" %in_var_COLOR %out_var_SV_TARGET
  OpExecutionMode %main OriginUpperLeft
  %file_name = OpString "ps.hlsl"
  OpSource HLSL 600 %file_name
  %float_name = OpString "float"
  %main_name = OpString "main"
  %foo_name = OpString "foo"
  %bar_name = OpString "bar"
  %zoo_name = OpString "zoo"
  OpDecorate %in_var_COLOR Location 0
  OpDecorate %out_var_SV_TARGET Location 0
  %uint = OpTypeInt 32 0
  %uint_32 = OpConstant %uint 32
  %float = OpTypeFloat 32
  %float_1 = OpConstant %float 1
  %float_2 = OpConstant %float 2
  %float_3 = OpConstant %float 3
  %v4float = OpTypeVector %float 4
  %v4f1 = OpConstantComposite %v4float %float_1 %float_1 %float_1 %float_1
  %v4f2 = OpConstantComposite %v4float %float_2 %float_2 %float_2 %float_2
  %v4f3 = OpConstantComposite %v4float %float_3 %float_3 %float_3 %float_3
  %_ptr_Input_v4float = OpTypePointer Input %v4float
  %_ptr_Output_v4float = OpTypePointer Output %v4float
  %void = OpTypeVoid
  %void_fn_type = OpTypeFunction %void
  %v4f_fn_type = OpTypeFunction %v4float
  %in_var_COLOR = OpVariable %_ptr_Input_v4float Input
  %out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
  %src = OpExtInst %void %ext DebugSource %file_name
  %cu = OpExtInst %void %ext DebugCompilationUnit 1 4 %src HLSL
  %dbg_f = OpExtInst %void %ext DebugTypeBasic %float_name %uint_32 Float
  %dbg_v4f = OpExtInst %void %ext DebugTypeVector %dbg_f 4
  %main_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f %dbg_v4f
  %foo_ty = OpExtInst %void %ext DebugTypeFunction FlagIsProtected|FlagIsPrivate %dbg_v4f
  %dbg_main = OpExtInst %void %ext DebugFunction %main_name %main_ty %src 10 1 %cu %main_name FlagIsProtected|FlagIsPrivate 10 %main
  %dbg_foo = OpExtInst %void %ext DebugFunction %foo_name %foo_ty %src 1 1 %cu %foo_name FlagIsProtected|FlagIsPrivate 1 %foo
  %dbg_bar = OpExtInst %void %ext DebugFunction %bar_name %foo_ty %src 4 1 %cu %bar_name FlagIsProtected|FlagIsPrivate 4 %bar
  %dbg_zoo = OpExtInst %void %ext DebugFunction %zoo_name %foo_ty %src 7 1 %cu %zoo_name FlagIsProtected|FlagIsPrivate 7 %zoo
  %inlined_to_zoo = OpExtInst %void %ext DebugInlinedAt 7 %dbg_zoo
  %main = OpFunction %void None %void_fn_type
  %main_bb = OpLabel
  %scope0 = OpExtInst %void %ext DebugScope %dbg_main
  %zoo_val = OpFunctionCall %v4float %zoo
  %color = OpLoad %v4float %in_var_COLOR
  %result = OpFAdd %v4float %zoo_val %color
  OpStore %out_var_SV_TARGET %result
  OpReturn
  OpFunctionEnd
  %foo = OpFunction %v4float None %v4f_fn_type
  %scope1 = OpExtInst %void %ext DebugScope %dbg_foo
  %foo_bb = OpLabel
  OpReturnValue %v4f1
  OpFunctionEnd
  %zoo = OpFunction %v4float None %v4f_fn_type
  %scope3 = OpExtInst %void %ext DebugScope %dbg_zoo
  %zoo_bb = OpLabel
  %scope2 = OpExtInst %void %ext DebugScope %dbg_bar %inlined_to_zoo
  %foo_val = OpFunctionCall %v4float %foo
  %bar_val = OpFAdd %v4float %foo_val %v4f2
  %scope4 = OpExtInst %void %ext DebugScope %dbg_zoo
  %zoo_ret = OpFAdd %v4float %bar_val %v4f3
  OpReturnValue %zoo_ret
  OpFunctionEnd
  %bar = OpFunction %v4float None %v4f_fn_type
  %scope5 = OpExtInst %void %ext DebugScope %dbg_bar
  %bar_bb = OpLabel
  %foo_val0 = OpFunctionCall %v4float %foo
  %bar_ret = OpFAdd %v4float %foo_val0 %v4f2
  OpReturnValue %bar_ret
  OpFunctionEnd
  )";
  
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  TEST_F(InlineTest, CreateConstantForInlinedAt) {
    // This shader causes CreateDebugInlinedAt to generate a constant.
    // Using the Constant manager would attempt to build the invalidated
    // DefUse manager during inlining which could cause an assert because
    // the function is in an inconsistant state. This test verifies that
    // CreateDebugInlinedAt detects that the DefUse manager is disabled
    // and creates a duplicate constant safely without the Constant manager.
    //
    // int function1() {
    //   return 1;
    // }
    //
    // void main() {
    //   function1();
    // }
  
    const std::string text = R"(OpCapability Shader
  ; CHECK: %uint_7 = OpConstant %uint 7
  ; CHECK: %uint_7_0 = OpConstant %uint 7
  ; CHECK: OpExtInst %void %1 DebugInlinedAt %uint_7_0
  OpExtension "SPV_KHR_non_semantic_info"
  %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
  OpMemoryModel Logical GLSL450
  OpEntryPoint Fragment %main "main"
  OpExecutionMode %main OriginUpperLeft
  %3 = OpString "parent3.hlsl"
  %8 = OpString "int"
  %19 = OpString "function1"
  %20 = OpString ""
  %26 = OpString "main"
  OpName %main "main"
  OpName %src_main "src.main"
  OpName %bb_entry "bb.entry"
  OpName %function1 "function1"
  OpName %bb_entry_0 "bb.entry"
  %int = OpTypeInt 32 1
  %int_1 = OpConstant %int 1
  %uint = OpTypeInt 32 0
  %uint_32 = OpConstant %uint 32
  %void = OpTypeVoid
  %uint_4 = OpConstant %uint 4
  %uint_0 = OpConstant %uint 0
  %uint_3 = OpConstant %uint 3
  %uint_1 = OpConstant %uint 1
  %uint_5 = OpConstant %uint 5
  %uint_2 = OpConstant %uint 2
  %uint_17 = OpConstant %uint 17
  %uint_6 = OpConstant %uint 6
  %uint_13 = OpConstant %uint 13
  %uint_7 = OpConstant %uint 7
  %31 = OpTypeFunction %void
  %42 = OpTypeFunction %int
  %10 = OpExtInst %void %1 DebugTypeBasic %8 %uint_32 %uint_4 %uint_0
  %13 = OpExtInst %void %1 DebugTypeFunction %uint_3 %10
  %15 = OpExtInst %void %1 DebugSource %3
  %16 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %15 %uint_5
  %21 = OpExtInst %void %1 DebugFunction %19 %13 %15 %uint_2 %uint_1 %16 %20 %uint_3 %uint_2
  %23 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_2 %uint_17 %21
  %25 = OpExtInst %void %1 DebugTypeFunction %uint_3 %void
  %27 = OpExtInst %void %1 DebugFunction %26 %25 %15 %uint_6 %uint_1 %16 %20 %uint_3 %uint_6
  %29 = OpExtInst %void %1 DebugLexicalBlock %15 %uint_6 %uint_13 %27
  %main = OpFunction %void None %31
  %32 = OpLabel
  %33 = OpFunctionCall %void %src_main
  OpLine %3 8 1
  OpReturn
  OpFunctionEnd
  OpLine %3 6 1
  %src_main = OpFunction %void None %31
  OpNoLine
  %bb_entry = OpLabel
  %47 = OpExtInst %void %1 DebugScope %27
  %37 = OpExtInst %void %1 DebugFunctionDefinition %27 %src_main
  %48 = OpExtInst %void %1 DebugScope %29
  OpLine %3 7 3
  %39 = OpFunctionCall %int %function1
  %49 = OpExtInst %void %1 DebugScope %27
  OpLine %3 8 1
  OpReturn
  %50 = OpExtInst %void %1 DebugNoScope
  OpFunctionEnd
  OpLine %3 2 1
  %function1 = OpFunction %int None %42
  OpNoLine
  %bb_entry_0 = OpLabel
  %51 = OpExtInst %void %1 DebugScope %21
  %45 = OpExtInst %void %1 DebugFunctionDefinition %21 %function1
  %52 = OpExtInst %void %1 DebugScope %23
  OpLine %3 3 3
  OpReturnValue %int_1
  %53 = OpExtInst %void %1 DebugNoScope
  OpFunctionEnd
  )";
  
    SetTargetEnv(SPV_ENV_VULKAN_1_2);
    SetAssembleOptions(SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
    SinglePassRunAndMatch<InlineExhaustivePass>(text, true);
  }
  
  // TODO(greg-lunarg): Add tests to verify handling of these cases:
  //
  //    Empty modules
  //    Modules without function definitions
  //    Modules in which all functions do not call other functions
  //    Caller and callee both accessing the same global variable
  //    Functions with OpLine & OpNoLine
  //    Others?
  
  // TODO(dneto): Test suggestions from code review
  // https://github.com/KhronosGroup/SPIRV-Tools/pull/534
  //
  //    Callee function returns a value generated outside the callee,
  //      e.g. a constant value. This might exercise some logic not yet
  //      exercised by the current tests: the false branch in the "if"
  //      inside the SpvOpReturnValue case in InlinePass::GenInlineCode?
  //    SampledImage before function call, but callee is only single block.
  //      Then the SampledImage instruction is not cloned. Documents existing
  //      behaviour.
  //    SampledImage after function call. It is not cloned or changed.
  
  }  // namespace
  }  // namespace opt
  }  // namespace spvtools
  