// Copyright (c) 2025 Huawei Technologies Co., Ltd.
// This program is free software, you can redistribute it and/or modify it under the terms and conditions of
// CANN Open Software License Agreement Version 2.0 (the "License").
// Please refer to the License for details. You may not use this file except in compliance with the License.
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
// See LICENSE in the root of the software repository for the full text of the License.

// RUN: ascir-translate -mlir-to-ascendc %s | FileCheck %s

// CHECK-LABEL:void emit_pipe() {
// CHECK-NEXT:   AscendC::TPipe v1;
// CHECK-NEXT:   v2 = v1.AllocEventID<AscendC::HardEvent::MTE2_V>();
// CHECK-NEXT:   AscendC::SetFlag<AscendC::HardEvent::MTE2_V>(v2)
// CHECK-NEXT:   AscendC::WaitFlag<AscendC::HardEvent::MTE2_V>(v2)
// CHECK-NEXT:   v1.ReleaseEventID<AscendC::HardEvent::MTE2_V>(v2);
// CHECK-NEXT:   v3 = v1.FetchEventID<AscendC::HardEvent::V_MTE3>();
// CHECK-NEXT:   AscendC::SetFlag<AscendC::HardEvent::V_MTE3>(v3)
// CHECK-NEXT:   AscendC::WaitFlag<AscendC::HardEvent::V_MTE3>(v3)
// CHECK-NEXT:   v1.Init();
// CHECK-NEXT:   v1.Reset();
// CHECK-NEXT:   v1.Destroy();
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_pipe() {
  %0 = ascendc.pipe
  %id0 = ascendc.pipe.alloc_event_id %0, mte2_v : i32
  ascendc.set_flag mte2_v, %id0 : i32
  ascendc.wait_flag mte2_v, %id0 : i32
  ascendc.pipe.release_event_id %0, %id0 {event = 4 : i32} : !ascendc.pipe, i32
  %id1 = ascendc.pipe.fetch_event_id %0, v_mte3 : i32
  ascendc.set_flag v_mte3, %id1 : i32
  ascendc.wait_flag v_mte3, %id1 : i32
  ascendc.pipe.init %0 : !ascendc.pipe 
  ascendc.pipe.reset %0 : !ascendc.pipe 
  ascendc.pipe.destroy %0 : !ascendc.pipe 
  return
}

// CHECK-LABEL:void emit_tbuf_pool(uint32_t v1) {
// CHECK-NEXT:   AscendC::TPipe v2;
// CHECK-NEXT:   AscendC::TBufPool<AscendC::TPosition::VECIN, 4> v3;
// CHECK-NEXT:   AscendC::TBufPool<AscendC::TPosition::VECIN, 4> v4;
// CHECK-NEXT:   bool v5 = v2.InitBufPool(v4, v1);
// CHECK-NEXT:   bool v6 = v2.InitBufPool(v4, v1, v3);
// CHECK-NEXT:   AscendC::TBufPool<AscendC::TPosition::VECIN, 4> v7;
// CHECK-NEXT:   bool v8 = v3.InitBufPool(v4, v1);
// CHECK-NEXT:   bool v9 = v3.InitBufPool(v4, v1, v7);
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_tbuf_pool(%arg0 : ui32) {
  %0 = ascendc.pipe
  %1 = ascendc.tBufPool : <vecin, 4> 
  %2 = ascendc.tBufPool : <vecin, 4> 
  %4 = ascendc.pipe.init_buf_pool(%0, %2, %arg0) : !ascendc.pipe, !ascendc.tbuf_pool<vecin, 4>, ui32 -> i1
  %5 = ascendc.pipe.init_buf_pool(%0, %2, %arg0), %1 : !ascendc.pipe, !ascendc.tbuf_pool<vecin, 4>, ui32 , !ascendc.tbuf_pool<vecin, 4> -> i1
  %6 = ascendc.tBufPool : <vecin, 4> 
  %7 = ascendc.tbuf_pool.init_buf_pool(%1, %2, %arg0) : !ascendc.tbuf_pool<vecin, 4>, !ascendc.tbuf_pool<vecin, 4>, ui32 -> i1
  %8 = ascendc.tbuf_pool.init_buf_pool(%1, %2, %arg0), %6 : !ascendc.tbuf_pool<vecin, 4>, !ascendc.tbuf_pool<vecin, 4>, ui32 , !ascendc.tbuf_pool<vecin, 4> -> i1
  return
}

// CHECK-LABEL:void emit_tbuf_pool_init_buffer(int32_t v1) {
// CHECK-NEXT:   AscendC::TBufPool<AscendC::TPosition::VECIN, 4> v2;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::GM> v3;
// CHECK-NEXT:   v2.InitBuffer(v3, v1);
// CHECK-NEXT:   AscendC::TQue<AscendC::TPosition::GM, 1> v4;
// CHECK-NEXT:   v2.InitBuffer(v4, v1, v1);
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_tbuf_pool_init_buffer(%arg0 : i32) {
  %0 = ascendc.tBufPool : <vecin, 4> 
  %buffer = ascendc.tbuf : !ascendc.tbuf<gm>
  ascendc.tbuf_pool.init_buffer %0, %buffer, %arg0 : !ascendc.tbuf_pool<vecin, 4>, !ascendc.tbuf<gm> , i32
  %queue = ascendc.queue : !ascendc.queue<gm, 1>
  ascendc.tbuf_pool.init_queue %0, %queue, %arg0, %arg0 : !ascendc.tbuf_pool<vecin, 4>, !ascendc.queue<gm, 1>, i32, i32
  return
}

// CHECK-LABEL:void emit_buffer() {
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::GM> v1;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::A1> v2;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::A2> v3;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::B1> v4;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::B2> v5;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::C1> v6;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::C2> v7;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::CO1> v8;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::CO2> v9;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::VECIN> v10;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::VECOUT> v11;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::VECCALC> v12;
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_buffer() {
  %0 = ascendc.tbuf : !ascendc.tbuf<gm>
  %1 = ascendc.tbuf : !ascendc.tbuf<a1>
  %2 = ascendc.tbuf : !ascendc.tbuf<a2>
  %3 = ascendc.tbuf : !ascendc.tbuf<b1>
  %4 = ascendc.tbuf : !ascendc.tbuf<b2>
  %5 = ascendc.tbuf : !ascendc.tbuf<c1>
  %6 = ascendc.tbuf : !ascendc.tbuf<c2>
  %7 = ascendc.tbuf : !ascendc.tbuf<co1>
  %8 = ascendc.tbuf : !ascendc.tbuf<co2>
  %9 = ascendc.tbuf : !ascendc.tbuf<vecin>
  %10 = ascendc.tbuf : !ascendc.tbuf<vecout>
  %11 = ascendc.tbuf : !ascendc.tbuf<veccalc>
  return
}

// CHECK-LABEL:void emit_queue() {
// CHECK-NEXT:   AscendC::TQue<AscendC::TPosition::GM, 1> v1;
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_queue() {
  %queue = ascendc.queue : !ascendc.queue<gm, 1>
  return
}

// CHECK-LABEL:void emit_local_tensor_slice(AscendC::LocalTensor<float> v1, uint32_t v2) {
// CHECK-NEXT:   AscendC::LocalTensor<float> v3 = v1[v2];
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_local_tensor_slice(%arg0: !ascendc.local_tensor<1024xf32>, %arg1: index) {
  %subindex = ascendc.local_tensor.subindex %arg0[%arg1] : !ascendc.local_tensor<1024xf32>, index, !ascendc.local_tensor<1024xf32>
  return
}


// CHECK-LABEL:void emit_init_buffer(int32_t v1) {
// CHECK-NEXT:   AscendC::TPipe v2;
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::GM> v3;
// CHECK-NEXT:   v2.InitBuffer(v3, v1);
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_init_buffer(%length : i32) {
  %pipe = ascendc.pipe
  %buffer = ascendc.tbuf : !ascendc.tbuf<gm>
  ascendc.pipe.init_buffer %pipe, %buffer, %length : !ascendc.tbuf<gm> , i32
  return
}

// CHECK-LABEL:void emit_get_tensor() {
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::GM> v1;
// CHECK-NEXT:   AscendC::LocalTensor<int32_t> v2 = v1.Get<int32_t>();
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_get_tensor() {
  %buffer = ascendc.tbuf : !ascendc.tbuf<gm>
  %local_tensor = ascendc.tbuf.get_tensor %buffer : !ascendc.tbuf<gm>, !ascendc.local_tensor<i32>
  return
}

// CHECK-LABEL:void emit_get_with_offset(uint32_t v1) {
// CHECK-NEXT:   AscendC::TBuf<AscendC::TPosition::GM> v2;
// CHECK-NEXT:   AscendC::LocalTensor<int32_t> v3 = v2.GetWithOffset<int32_t>(v1, v1);
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_get_with_offset(%arg0 : ui32) {
  %buffer = ascendc.tbuf : !ascendc.tbuf<gm>
  %local_tensor = ascendc.tbuf.get_with_offset %buffer, %arg0, %arg0 : !ascendc.tbuf<gm>, ui32, ui32, !ascendc.local_tensor<*xi32>
  return
}

// CHECK-LABEL:void emit_init_queue(int32_t v1, int32_t v2) {
// CHECK-NEXT:   AscendC::TPipe v3;
// CHECK-NEXT:   AscendC::TQue<AscendC::TPosition::GM, 1> v4;
// CHECK-NEXT:   v3.InitBuffer(v4, v1, v2);
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_init_queue(%num : i32, %length : i32) {
  %pipe = ascendc.pipe
  %queue = ascendc.queue : !ascendc.queue<gm, 1>
  ascendc.pipe.init_queue %pipe, %queue, %num, %length : !ascendc.queue<gm, 1>, i32, i32
  return
}

// CHECK-LABEL:void emit_base_queue_operations(AscendC::TQue<AscendC::TPosition::GM, 1> v1) {
// CHECK-NEXT:   AscendC::LocalTensor<int32_t> v2 = v1.AllocTensor<int32_t>();
// CHECK-NEXT:   v1.EnQue(v2);
// CHECK-NEXT:   v1.EnQue<AscendC::TPosition::GM, AscendC::TPosition::VECIN>(v2);
// CHECK-NEXT:   AscendC::LocalTensor<int32_t> v3 = v1.DeQue<int32_t>();
// CHECK-NEXT:   v1.FreeTensor(v2);
// CHECK-NEXT:   AscendC::TQueBind<AscendC::TPosition::GM, AscendC::TPosition::VECCALC, 1>& v4 = v1;
// CHECK-NEXT:   int32_t v5 = v1.GetTensorCountInQue();
// CHECK-NEXT:   bool v6 = v1.HasIdleBuffer();
// CHECK-NEXT:   bool v7 = v1.HasTensorInQue();
// CHECK-NEXT:   bool v8 = v1.VacantInQue();
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_base_queue_operations(%queue : !ascendc.queue<gm, 1>) {
  %local_tensor = ascendc.que_bind.alloc_tensor %queue : !ascendc.queue<gm, 1>, !ascendc.local_tensor<i32>
  ascendc.que_bind.enque_tensor %queue, %local_tensor : !ascendc.queue<gm, 1> , !ascendc.local_tensor<i32>
  ascendc.que_bind.enque_tensor_pos %queue, %local_tensor, gm, vecin : !ascendc.queue<gm, 1> , !ascendc.local_tensor<i32>
  %deque_tensor = ascendc.que_bind.deque_tensor %queue : !ascendc.queue<gm, 1> , !ascendc.local_tensor<i32>
  ascendc.que_bind.free_tensor %queue, %local_tensor : !ascendc.queue<gm, 1>, !ascendc.local_tensor<i32>
  %que_bind = ascendc.to_que_bind %queue : !ascendc.queue<gm, 1>, !ascendc.que_bind<gm, veccalc, 1>
  %0 = ascendc.que_bind.get_tensor_count_in_que %queue :  !ascendc.queue<gm, 1>, i32
  %1 = ascendc.que_bind.has_idle_buffer %queue :  !ascendc.queue<gm, 1>, i1
  %2 = ascendc.que_bind.has_tensor_in_que %queue :  !ascendc.queue<gm, 1>, i1
  %3 = ascendc.que_bind.vacant_in_que %queue :  !ascendc.queue<gm, 1>, i1
  return
}

// CHECK-LABEL:void emit_que_bind_in_place(uint32_t v1, uint32_t v2) {
// CHECK-NEXT:   AscendC::TQueBind<AscendC::TPosition::VECIN, AscendC::TPosition::VECIN, 0> v3;
// CHECK-NEXT:   AscendC::LocalTensor<float> v4 = AscendC::LocalTensor<float>(AscendC::TPosition::VECIN, v1, v2);
// CHECK-NEXT:   v3.AllocTensor<float>(v4);
// CHECK-NEXT:   v3.DeQue<float>(v4);
// CHECK-NEXT:   AscendC::LocalTensor<float> v5 = v3.DeQue<AscendC::TPosition::GM, AscendC::TPosition::VECIN, float>();
// CHECK-NEXT:   return;
// CHECK-NEXT: }
func.func @emit_que_bind_in_place(%arg0 : ui32, %arg1 : ui32) {
  %que_bind = ascendc.que_bind : <vecin, vecin, 0>
  %tensor = ascendc.local_tensor_v2 vecin, %arg0, %arg1 : !ascendc.local_tensor<*xf32>
  ascendc.que_bind.alloc_tensor_in_place %que_bind, %tensor : !ascendc.que_bind<vecin, vecin, 0>, !ascendc.local_tensor<*xf32>
  ascendc.que_bind.deque_tensor_in_place %que_bind, %tensor : !ascendc.que_bind<vecin, vecin, 0>, !ascendc.local_tensor<*xf32>
  %tensor0 = ascendc.que_bind.deque_tensor_pos %que_bind, gm, vecin : !ascendc.que_bind<vecin, vecin, 0>, !ascendc.local_tensor<*xf32>
  return
}