// Copyright (c) 2022 Advanced Micro Devices, 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 "gmock/gmock.h"
#include "test/opt/pass_fixture.h"
#include "test/opt/pass_utils.h"

namespace spvtools {
namespace opt {
namespace {

using FixFuncCallArgumentsTest = PassTest<::testing::Test>;
TEST_F(FixFuncCallArgumentsTest, Simple) {
  const std::string text = R"(
;
; CHECK: [[v0:%\w+]] = OpVariable %_ptr_Function_float Function
; CHECK: [[v1:%\w+]] = OpVariable %_ptr_Function_float Function
; CHECK: [[v2:%\w+]] = OpVariable %_ptr_Function_T Function
; CHECK: [[ac0:%\w+]] = OpAccessChain %_ptr_Function_float %t %int_0
; CHECK: [[ac1:%\w+]] = OpAccessChain %_ptr_Uniform_float %r1 %int_0 %uint_0
; CHECK: [[ld0:%\w+]] = OpLoad %float [[ac0]]
; CHECK:                OpStore [[v1]] [[ld0]]
; CHECK: [[ld1:%\w+]] = OpLoad %float [[ac1]]
; CHECK:                OpStore [[v0]] [[ld1]]
; CHECK: [[func:%\w+]] = OpFunctionCall %void %fn [[v1]] [[v0]]
; CHECK: [[ld2:%\w+]] = OpLoad %float [[v0]]
; CHECK: OpStore [[ac1]] [[ld2]]
; CHECK: [[ld3:%\w+]] = OpLoad %float [[v1]]
; CHECK: OpStore [[ac0]] [[ld3]]
;
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpSource HLSL 630
OpName %type_RWStructuredBuffer_float "type.RWStructuredBuffer.float"
OpName %r1 "r1"
OpName %type_ACSBuffer_counter "type.ACSBuffer.counter"
OpMemberName %type_ACSBuffer_counter 0 "counter"
OpName %counter_var_r1 "counter.var.r1"
OpName %main "main"
OpName %bb_entry "bb.entry"
OpName %T "T"
OpMemberName %T 0 "t0"
OpName %t "t"
OpName %fn "fn"
OpName %p0 "p0"
OpName %p2 "p2"
OpName %bb_entry_0 "bb.entry"
OpDecorate %main LinkageAttributes "main" Export
OpDecorate %r1 DescriptorSet 0
OpDecorate %r1 Binding 0
OpDecorate %counter_var_r1 DescriptorSet 0
OpDecorate %counter_var_r1 Binding 1
OpDecorate %_runtimearr_float ArrayStride 4
OpMemberDecorate %type_RWStructuredBuffer_float 0 Offset 0
OpDecorate %type_RWStructuredBuffer_float BufferBlock
OpMemberDecorate %type_ACSBuffer_counter 0 Offset 0
OpDecorate %type_ACSBuffer_counter BufferBlock
%int = OpTypeInt 32 1
%int_0 = OpConstant %int 0
%uint = OpTypeInt 32 0
%uint_0 = OpConstant %uint 0
%int_1 = OpConstant %int 1
%float = OpTypeFloat 32
%_runtimearr_float = OpTypeRuntimeArray %float
%type_RWStructuredBuffer_float = OpTypeStruct %_runtimearr_float
%_ptr_Uniform_type_RWStructuredBuffer_float = OpTypePointer Uniform %type_RWStructuredBuffer_float
%type_ACSBuffer_counter = OpTypeStruct %int
%_ptr_Uniform_type_ACSBuffer_counter = OpTypePointer Uniform %type_ACSBuffer_counter
%15 = OpTypeFunction %int
%T = OpTypeStruct %float
%_ptr_Function_T = OpTypePointer Function %T
%_ptr_Function_float = OpTypePointer Function %float
%_ptr_Uniform_float = OpTypePointer Uniform %float
%void = OpTypeVoid
%27 = OpTypeFunction %void %_ptr_Function_float %_ptr_Function_float
%r1 = OpVariable %_ptr_Uniform_type_RWStructuredBuffer_float Uniform
%counter_var_r1 = OpVariable %_ptr_Uniform_type_ACSBuffer_counter Uniform
%main = OpFunction %int None %15
%bb_entry = OpLabel
%t = OpVariable %_ptr_Function_T Function
%21 = OpAccessChain %_ptr_Function_float %t %int_0
%23 = OpAccessChain %_ptr_Uniform_float %r1 %int_0 %uint_0
%25 = OpFunctionCall %void %fn %21 %23
OpReturnValue %int_1
OpFunctionEnd
%fn = OpFunction %void DontInline %27
%p0 = OpFunctionParameter %_ptr_Function_float
%p2 = OpFunctionParameter %_ptr_Function_float
%bb_entry_0 = OpLabel
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndMatch<FixFuncCallArgumentsPass>(text, true);
}

TEST_F(FixFuncCallArgumentsTest, NotAccessChainInput) {
  const std::string text = R"(
;
; CHECK: [[o:%\w+]] = OpCopyObject %_ptr_Function_float %t
; CHECK: [[func:%\w+]] = OpFunctionCall %void %fn [[o]]
;
OpCapability Shader
OpCapability Linkage
OpMemoryModel Logical GLSL450
OpSource HLSL 630
OpName %main "main"
OpName %bb_entry "bb.entry"
OpName %t "t"
OpName %fn "fn"
OpName %p0 "p0"
OpName %bb_entry_0 "bb.entry"
OpDecorate %main LinkageAttributes "main" Export
%int = OpTypeInt 32 1
%int_1 = OpConstant %int 1
%4 = OpTypeFunction %int
%float = OpTypeFloat 32
%_ptr_Function_float = OpTypePointer Function %float
%void = OpTypeVoid
%12 = OpTypeFunction %void %_ptr_Function_float
%main = OpFunction %int None %4
%bb_entry = OpLabel
%t = OpVariable %_ptr_Function_float Function
%t1 = OpCopyObject %_ptr_Function_float %t
%10 = OpFunctionCall %void %fn %t1
OpReturnValue %int_1
OpFunctionEnd
%fn = OpFunction %void DontInline %12
%p0 = OpFunctionParameter %_ptr_Function_float
%bb_entry_0 = OpLabel
OpReturn
OpFunctionEnd
)";

  SinglePassRunAndMatch<FixFuncCallArgumentsPass>(text, false);
}

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