// Copyright (c) 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "source/opt/debug_info_manager.h"

#include <memory>
#include <vector>

#include "gtest/gtest.h"
#include "source/opt/build_module.h"
#include "source/opt/instruction.h"
#include "spirv-tools/libspirv.hpp"

// Constants for OpenCL.DebugInfo.100 extension instructions.

static const uint32_t kDebugFunctionOperandFunctionIndex = 13;
static const uint32_t kDebugInlinedAtOperandLineIndex = 4;
static const uint32_t kDebugInlinedAtOperandScopeIndex = 5;
static const uint32_t kDebugInlinedAtOperandInlinedIndex = 6;
static const uint32_t kOpLineInOperandFileIndex = 0;
static const uint32_t kOpLineInOperandLineIndex = 1;
static const uint32_t kOpLineInOperandColumnIndex = 2;

namespace spvtools {
namespace opt {
namespace analysis {
namespace {

TEST(DebugInfoManager, GetDebugInlinedAt) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
        %100 = OpExtInst %void %1 DebugInlinedAt 7 %22
       %main = OpFunction %void None %27
         %28 = OpLabel
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  EXPECT_EQ(manager.GetDebugInlinedAt(150), nullptr);
  EXPECT_EQ(manager.GetDebugInlinedAt(31), nullptr);
  EXPECT_EQ(manager.GetDebugInlinedAt(22), nullptr);

  auto* inst = manager.GetDebugInlinedAt(100);
  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7);
  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22);
}

TEST(DebugInfoManager, CreateDebugInlinedAt) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
        %100 = OpExtInst %void %1 DebugInlinedAt 7 %22
       %main = OpFunction %void None %27
         %28 = OpLabel
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
               OpReturn
               OpFunctionEnd
  )";

  DebugScope scope(22U, 0U);

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  uint32_t inlined_at_id = manager.CreateDebugInlinedAt(nullptr, scope);
  auto* inlined_at = manager.GetDebugInlinedAt(inlined_at_id);
  EXPECT_NE(inlined_at, nullptr);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
            1);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex),
            22);
  EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);

  const uint32_t line_number = 77U;
  Instruction line(context.get(), spv::Op::OpLine);
  line.SetInOperands({
      {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}},
      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}},
  });

  inlined_at_id = manager.CreateDebugInlinedAt(&line, scope);
  inlined_at = manager.GetDebugInlinedAt(inlined_at_id);
  EXPECT_NE(inlined_at, nullptr);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
            line_number);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex),
            22);
  EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);

  scope.SetInlinedAt(100U);
  inlined_at_id = manager.CreateDebugInlinedAt(&line, scope);
  inlined_at = manager.GetDebugInlinedAt(inlined_at_id);
  EXPECT_NE(inlined_at, nullptr);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
            line_number);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex),
            22);
  EXPECT_EQ(inlined_at->NumOperands(), kDebugInlinedAtOperandInlinedIndex + 1);
  EXPECT_EQ(
      inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandInlinedIndex),
      100U);
}

TEST(DebugInfoManager, CreateDebugInlinedAtWithConstantManager) {
  // Show that CreateDebugInlinedAt will use the Constant manager to generate
  // its line operand if the Constant and DefUse managers are valid. This is
  // proven by checking that the id for the line operand 7 is the same as the
  // existing constant 7.
  //
  // int function1() {
  //   return 1;
  // }
  //
  // void main() {
  //   function1();
  // }
  const std::string text = R"(OpCapability Shader
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
%100 = 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
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);

  const uint32_t line_number = 7U;
  Instruction line(context.get(), spv::Op::OpLine);
  line.SetInOperands({
      {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {5U}},
      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {line_number}},
      {spv_operand_type_t::SPV_OPERAND_TYPE_LITERAL_INTEGER, {0U}},
  });

  DebugScope scope(29U, 0U);

  auto db_manager = context.get()->get_debug_info_mgr();
  auto du_manager = context.get()->get_def_use_mgr();
  auto c_manager = context.get()->get_constant_mgr();

  (void)du_manager;
  (void)c_manager;

  uint32_t inlined_at_id = db_manager->CreateDebugInlinedAt(&line, scope);
  auto* inlined_at = db_manager->GetDebugInlinedAt(inlined_at_id);
  EXPECT_NE(inlined_at, nullptr);
  EXPECT_EQ(inlined_at->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex),
            100);
}

TEST(DebugInfoManager, GetDebugInfoNone) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
         %12 = OpExtInst %void %1 DebugInfoNone
         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
       %main = OpFunction %void None %27
         %28 = OpLabel
        %100 = OpVariable %_ptr_Function_float Function
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
         %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  Instruction* debug_info_none_inst = manager.GetDebugInfoNone();
  EXPECT_NE(debug_info_none_inst, nullptr);
  EXPECT_EQ(debug_info_none_inst->GetOpenCL100DebugOpcode(),
            OpenCLDebugInfo100DebugInfoNone);
  EXPECT_EQ(debug_info_none_inst->PreviousNode(), nullptr);
}

TEST(DebugInfoManager, CreateDebugInfoNone) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
       %main = OpFunction %void None %27
         %28 = OpLabel
        %100 = OpVariable %_ptr_Function_float Function
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
         %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  Instruction* debug_info_none_inst = manager.GetDebugInfoNone();
  EXPECT_NE(debug_info_none_inst, nullptr);
  EXPECT_EQ(debug_info_none_inst->GetOpenCL100DebugOpcode(),
            OpenCLDebugInfo100DebugInfoNone);
  EXPECT_EQ(debug_info_none_inst->PreviousNode(), nullptr);
}

TEST(DebugInfoManager, GetDebugFunction) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %200 "200" %in_var_COLOR
               OpExecutionMode %200 OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void 200(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "200"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %200 "200"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %200
         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
       %200 = OpFunction %void None %27
         %28 = OpLabel
        %100 = OpVariable %_ptr_Function_float Function
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
         %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  EXPECT_EQ(manager.GetDebugFunction(100), nullptr);
  EXPECT_EQ(manager.GetDebugFunction(150), nullptr);

  Instruction* dbg_fn = manager.GetDebugFunction(200);

  EXPECT_EQ(dbg_fn->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugFunction);
  EXPECT_EQ(dbg_fn->GetSingleWordOperand(kDebugFunctionOperandFunctionIndex),
            200);
}

TEST(DebugInfoManager, GetDebugFunction_InlinedAway) {
  // struct PS_INPUT
  // {
  //   float4 iColor : COLOR;
  // };
  //
  // struct PS_OUTPUT
  // {
  //   float4 oColor : SV_Target0;
  // };
  //
  // float4 foo(float4 ic)
  // {
  //   float4 c = ic / 2.0;
  //   return c;
  // }
  //
  // PS_OUTPUT MainPs(PS_INPUT i)
  // {
  //   PS_OUTPUT ps_output;
  //   float4 ic = i.iColor;
  //   ps_output.oColor = foo(ic);
  //   return ps_output;
  // }
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %MainPs "MainPs" %in_var_COLOR %out_var_SV_Target0
               OpExecutionMode %MainPs OriginUpperLeft
         %15 = OpString "foo2.frag"
         %19 = OpString "PS_OUTPUT"
         %23 = OpString "float"
         %26 = OpString "oColor"
         %28 = OpString "PS_INPUT"
         %31 = OpString "iColor"
         %33 = OpString "foo"
         %37 = OpString "c"
         %39 = OpString "ic"
         %42 = OpString "src.MainPs"
         %47 = OpString "ps_output"
         %50 = OpString "i"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %out_var_SV_Target0 "out.var.SV_Target0"
               OpName %MainPs "MainPs"
               OpDecorate %in_var_COLOR Location 0
               OpDecorate %out_var_SV_Target0 Location 0
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
      %float = OpTypeFloat 32
    %v4float = OpTypeVector %float 4
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
%_ptr_Input_v4float = OpTypePointer Input %v4float
%_ptr_Output_v4float = OpTypePointer Output %v4float
       %void = OpTypeVoid
   %uint_128 = OpConstant %uint 128
     %uint_0 = OpConstant %uint 0
         %52 = OpTypeFunction %void
%in_var_COLOR = OpVariable %_ptr_Input_v4float Input
%out_var_SV_Target0 = OpVariable %_ptr_Output_v4float Output
  %float_0_5 = OpConstant %float 0.5
        %130 = OpConstantComposite %v4float %float_0_5 %float_0_5 %float_0_5 %float_0_5
        %115 = OpExtInst %void %1 DebugInfoNone
         %49 = OpExtInst %void %1 DebugExpression
         %17 = OpExtInst %void %1 DebugSource %15
         %18 = OpExtInst %void %1 DebugCompilationUnit 1 4 %17 HLSL
         %21 = OpExtInst %void %1 DebugTypeComposite %19 Structure %17 6 1 %18 %19 %uint_128 FlagIsProtected|FlagIsPrivate %22
         %24 = OpExtInst %void %1 DebugTypeBasic %23 %uint_32 Float
         %25 = OpExtInst %void %1 DebugTypeVector %24 4
         %22 = OpExtInst %void %1 DebugTypeMember %26 %25 %17 8 5 %21 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
         %29 = OpExtInst %void %1 DebugTypeComposite %28 Structure %17 1 1 %18 %28 %uint_128 FlagIsProtected|FlagIsPrivate %30
         %30 = OpExtInst %void %1 DebugTypeMember %31 %25 %17 3 5 %29 %uint_0 %uint_128 FlagIsProtected|FlagIsPrivate
         %32 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %25 %25
         %34 = OpExtInst %void %1 DebugFunction %33 %32 %17 11 1 %18 %33 FlagIsProtected|FlagIsPrivate 12 %115
         %36 = OpExtInst %void %1 DebugLexicalBlock %17 12 1 %34
         %38 = OpExtInst %void %1 DebugLocalVariable %37 %25 %17 13 12 %36 FlagIsLocal
         %41 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %21 %29
         %43 = OpExtInst %void %1 DebugFunction %42 %41 %17 17 1 %18 %42 FlagIsProtected|FlagIsPrivate 18 %115
         %45 = OpExtInst %void %1 DebugLexicalBlock %17 18 1 %43
         %46 = OpExtInst %void %1 DebugLocalVariable %39 %25 %17 20 12 %45 FlagIsLocal
         %48 = OpExtInst %void %1 DebugLocalVariable %47 %21 %17 19 15 %45 FlagIsLocal
        %107 = OpExtInst %void %1 DebugInlinedAt 21 %45
     %MainPs = OpFunction %void None %52
         %53 = OpLabel
         %57 = OpLoad %v4float %in_var_COLOR
        %131 = OpExtInst %void %1 DebugScope %45
               OpLine %15 20 12
        %117 = OpExtInst %void %1 DebugValue %46 %57 %49
        %132 = OpExtInst %void %1 DebugScope %36 %107
               OpLine %15 13 19
        %112 = OpFMul %v4float %57 %130
               OpLine %15 13 12
        %116 = OpExtInst %void %1 DebugValue %38 %112 %49
        %133 = OpExtInst %void %1 DebugScope %45
        %128 = OpExtInst %void %1 DebugValue %48 %112 %49 %int_0
        %134 = OpExtInst %void %1 DebugNoScope
               OpStore %out_var_SV_Target0 %112
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  EXPECT_EQ(manager.GetDebugFunction(115), nullptr);
}

TEST(DebugInfoManager, CloneDebugInlinedAt) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
        %100 = OpExtInst %void %1 DebugInlinedAt 7 %22
       %main = OpFunction %void None %27
         %28 = OpLabel
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  DebugInfoManager manager(context.get());

  EXPECT_EQ(manager.CloneDebugInlinedAt(150), nullptr);
  EXPECT_EQ(manager.CloneDebugInlinedAt(22), nullptr);

  auto* inst = manager.CloneDebugInlinedAt(100);
  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7);
  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22);
  EXPECT_EQ(inst->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);

  Instruction* before_100 = nullptr;
  for (auto it = context->module()->ext_inst_debuginfo_begin();
       it != context->module()->ext_inst_debuginfo_end(); ++it) {
    if (it->result_id() == 100) break;
    before_100 = &*it;
  }
  EXPECT_NE(inst, before_100);

  inst = manager.CloneDebugInlinedAt(100, manager.GetDebugInlinedAt(100));
  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandLineIndex), 7);
  EXPECT_EQ(inst->GetSingleWordOperand(kDebugInlinedAtOperandScopeIndex), 22);
  EXPECT_EQ(inst->NumOperands(), kDebugInlinedAtOperandScopeIndex + 1);

  before_100 = nullptr;
  for (auto it = context->module()->ext_inst_debuginfo_begin();
       it != context->module()->ext_inst_debuginfo_end(); ++it) {
    if (it->result_id() == 100) break;
    before_100 = &*it;
  }
  EXPECT_EQ(inst, before_100);
}

TEST(DebugInfoManager, KillDebugDeclares) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
         %12 = OpExtInst %void %1 DebugInfoNone
         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
       %main = OpFunction %void None %27
         %28 = OpLabel
        %100 = OpVariable %_ptr_Function_float Function
         %31 = OpLoad %float %in_var_COLOR
               OpStore %100 %31
         %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
         %37 = OpExtInst %void %1 DebugDeclare %25 %100 %13
         %38 = OpExtInst %void %1 DebugDeclare %25 %100 %13
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  auto* dbg_info_mgr = context->get_debug_info_mgr();
  auto* def_use_mgr = context->get_def_use_mgr();

  EXPECT_TRUE(dbg_info_mgr->IsVariableDebugDeclared(100));
  EXPECT_EQ(def_use_mgr->GetDef(36)->GetOpenCL100DebugOpcode(),
            OpenCLDebugInfo100DebugDeclare);
  EXPECT_EQ(def_use_mgr->GetDef(37)->GetOpenCL100DebugOpcode(),
            OpenCLDebugInfo100DebugDeclare);
  EXPECT_EQ(def_use_mgr->GetDef(38)->GetOpenCL100DebugOpcode(),
            OpenCLDebugInfo100DebugDeclare);

  dbg_info_mgr->KillDebugDeclares(100);
  EXPECT_EQ(def_use_mgr->GetDef(36), nullptr);
  EXPECT_EQ(def_use_mgr->GetDef(37), nullptr);
  EXPECT_EQ(def_use_mgr->GetDef(38), nullptr);
  EXPECT_FALSE(dbg_info_mgr->IsVariableDebugDeclared(100));
}

TEST(DebugInfoManager, AddDebugValueForDecl) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "OpenCL.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %in_var_COLOR
               OpExecutionMode %main OriginUpperLeft
          %5 = OpString "ps.hlsl"
         %14 = OpString "#line 1 \"ps.hlsl\"
void main(float in_var_color : COLOR) {
  float color = in_var_color;
}
"
         %17 = OpString "float"
         %21 = OpString "main"
         %24 = OpString "color"
               OpName %in_var_COLOR "in.var.COLOR"
               OpName %main "main"
               OpDecorate %in_var_COLOR Location 0
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
      %float = OpTypeFloat 32
%_ptr_Input_float = OpTypePointer Input %float
       %void = OpTypeVoid
         %27 = OpTypeFunction %void
%_ptr_Function_float = OpTypePointer Function %float
%in_var_COLOR = OpVariable %_ptr_Input_float Input
         %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
         %20 = OpExtInst %void %1 DebugTypeFunction FlagIsProtected|FlagIsPrivate %18 %18
         %22 = OpExtInst %void %1 DebugFunction %21 %20 %15 1 1 %16 %21 FlagIsProtected|FlagIsPrivate 1 %main
         %12 = OpExtInst %void %1 DebugInfoNone
         %25 = OpExtInst %void %1 DebugLocalVariable %24 %18 %15 1 20 %22 FlagIsLocal 0
       %main = OpFunction %void None %27
         %28 = OpLabel
        %100 = OpVariable %_ptr_Function_float Function
         %31 = OpLoad %float %in_var_COLOR
        %101 = OpExtInst %void %1 DebugScope %22
               OpLine %5 13 7
               OpStore %100 %31
         %36 = OpExtInst %void %1 DebugDeclare %25 %100 %13
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  auto* def_use_mgr = context->get_def_use_mgr();
  auto* dbg_decl = def_use_mgr->GetDef(36);
  EXPECT_EQ(dbg_decl->GetOpenCL100DebugOpcode(),
            OpenCLDebugInfo100DebugDeclare);

  auto* dbg_info_mgr = context->get_debug_info_mgr();
  Instruction* store = dbg_decl->PreviousNode();
  auto* dbg_value =
      dbg_info_mgr->AddDebugValueForDecl(dbg_decl, 100, dbg_decl, store);

  EXPECT_EQ(dbg_value->GetOpenCL100DebugOpcode(), OpenCLDebugInfo100DebugValue);
  EXPECT_EQ(dbg_value->dbg_line_inst()->GetSingleWordInOperand(
                kOpLineInOperandFileIndex),
            5);
  EXPECT_EQ(dbg_value->dbg_line_inst()->GetSingleWordInOperand(
                kOpLineInOperandLineIndex),
            13);
  EXPECT_EQ(dbg_value->dbg_line_inst()->GetSingleWordInOperand(
                kOpLineInOperandColumnIndex),
            7);
}

TEST(DebugInfoManager, ConvertGlobalToLocal) {
  const std::string text = R"(
               OpCapability Shader
          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %2 "PSMain" %3
               OpExecutionMode %2 OriginUpperLeft
          %4 = OpString "C:\\local\\Temp\\2528091a-6811-4e62-9ed5-02f1547c2016.hlsl"
          %5 = OpString "float"
          %6 = OpString "Pi"
      %float = OpTypeFloat 32
%float_3_1415 = OpConstant %float 3.1415
       %uint = OpTypeInt 32 0
    %uint_32 = OpConstant %uint 32
%_ptr_Private_float = OpTypePointer Private %float
%_ptr_Function_float = OpTypePointer Function %float
       %void = OpTypeVoid
     %uint_3 = OpConstant %uint 3
     %uint_0 = OpConstant %uint 0
     %uint_4 = OpConstant %uint 4
     %uint_1 = OpConstant %uint 1
     %uint_5 = OpConstant %uint 5
     %uint_8 = OpConstant %uint 8
     %uint_6 = OpConstant %uint 6
    %uint_20 = OpConstant %uint 20
         %25 = OpTypeFunction %void
    %uint_11 = OpConstant %uint 11
          %3 = OpVariable %_ptr_Private_float Private
          %8 = OpExtInst %void %1 DebugTypeBasic %5 %uint_32 %uint_3 %uint_0
         %12 = OpExtInst %void %1 DebugSource %4
         %13 = OpExtInst %void %1 DebugCompilationUnit %uint_1 %uint_4 %12 %uint_5
         %17 = OpExtInst %void %1 DebugGlobalVariable %6 %8 %12 %uint_6 %uint_20 %13 %6 %3 %uint_8
          %2 = OpFunction %void None %25
         %27 = OpLabel
         %29 = OpVariable %_ptr_Function_float Function
               OpStore %3 %float_3_1415
         %28 = OpExtInst %void %1 DebugLine %12 %uint_11 %uint_11 %uint_1 %uint_1
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  auto* def_use_mgr = context->get_def_use_mgr();
  auto* dbg_var = def_use_mgr->GetDef(17);
  EXPECT_EQ(dbg_var->GetCommonDebugOpcode(),
            OpenCLDebugInfo100DebugGlobalVariable);
  EXPECT_EQ(dbg_var->NumInOperands(), 11);

  std::vector<Operand> originalOperands;
  for (uint32_t i = 0; i < dbg_var->NumInOperands(); ++i) {
    originalOperands.emplace_back(dbg_var->GetInOperand((i)));
  }

  auto* local_var = def_use_mgr->GetDef(29);
  auto* dbg_info_mgr = context->get_debug_info_mgr();
  dbg_info_mgr->ConvertDebugGlobalToLocalVariable(dbg_var, local_var);

  EXPECT_EQ(dbg_var->NumInOperands(), 9);

  // This checks that the first two inoperands are correct.
  EXPECT_EQ(dbg_var->GetCommonDebugOpcode(),
            OpenCLDebugInfo100DebugLocalVariable);

  // Then next 6 operands should be the same as the original instruction.
  EXPECT_EQ(dbg_var->GetInOperand(2), originalOperands[2]);
  EXPECT_EQ(dbg_var->GetInOperand(3), originalOperands[3]);
  EXPECT_EQ(dbg_var->GetInOperand(4), originalOperands[4]);
  EXPECT_EQ(dbg_var->GetInOperand(5), originalOperands[5]);
  EXPECT_EQ(dbg_var->GetInOperand(6), originalOperands[6]);
  EXPECT_EQ(dbg_var->GetInOperand(7), originalOperands[7]);

  // The flags operand should have shifted because operand 8 and 9 in the global
  // instruction are not relevant.
  EXPECT_EQ(dbg_var->GetInOperand(8), originalOperands[10]);
}

TEST(DebugInfoManagerTest, DebugInstructionWithForwardRefsKHR) {
  const std::string text = R"(
               OpCapability Shader
               OpExtension "SPV_KHR_non_semantic_info"
               OpExtension "SPV_KHR_relaxed_extended_instruction"
          %1 = OpExtInstImport "NonSemantic.Shader.DebugInfo.100"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main"
               OpExecutionMode %main OriginUpperLeft
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
     %uint_3 = OpConstant %uint 3
         %10 = OpExtInstWithForwardRefsKHR %void %1 DebugTypeFunction %uint_3 %11
         %11 = OpExtInst %void %1 DebugInfoNone
       %main = OpFunction %void None %3
          %5 = OpLabel
               OpReturn
               OpFunctionEnd
  )";

  std::unique_ptr<IRContext> context =
      BuildModule(SPV_ENV_UNIVERSAL_1_1, nullptr, text,
                  SPV_TEXT_TO_BINARY_OPTION_PRESERVE_NUMERIC_IDS);
  ASSERT_NE(nullptr, context);

  Instruction* forward_ref_inst = context->get_def_use_mgr()->GetDef(10);
  ASSERT_NE(nullptr, forward_ref_inst);
  EXPECT_EQ(forward_ref_inst->opcode(), spv::Op::OpExtInstWithForwardRefsKHR);

  EXPECT_EQ(forward_ref_inst->GetShaderDebugOpcode(),
            NonSemanticShaderDebugInfoDebugTypeFunction);

  EXPECT_EQ(forward_ref_inst->GetCommonDebugOpcode(),
            CommonDebugInfoDebugTypeFunction);
}

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