// MIT License
//
// Copyright (c) 2017-2025 Advanced Micro Devices, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

#ifndef SRC_PM4_GFX11_CMD_BUILDER_H_
#define SRC_PM4_GFX11_CMD_BUILDER_H_

#include <assert.h>
#include <string.h>

#include <iomanip>
#include <iostream>
#include <sstream>

#include "pm4/cmd_builder.h"
#include "def/gfx11_def.h"

namespace pm4_builder {

/// @brief class Gfx11CmdBuilder implements the virtual class CmdBuilder
/// for GFX11 chipsets
class Gfx11CmdBuilder : public CmdBuilder {
 private:
  // @brief Return a Type 3 PM4 packet header
  static uint32_t MakePacket3Header(uint32_t opcode, size_t packet_size) {
    uint32_t count = packet_size / sizeof(uint32_t) - 2;
    uint32_t header = PACKET3(opcode, count);
    return header;
  }

 public:
  Gfx11CmdBuilder(const reg_base_offset_table* _table) : CmdBuilder(_table){};
  static constexpr bool IsPrivilegedConfigReg(uint32_t addr) {
    return ((addr >= CONFIG_SPACE_START) && (addr <= CONFIG_SPACE_END));
  }

  virtual void BuildBarrierCommand(CmdBuffer* cmdBuf) {
    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_EVENT_WRITE, 2 * sizeof(uint32_t));

    uint32_t dword2 =
        PACKET3_EVENT_WRITE__EVENT_TYPE(CS_PARTIAL_FLUSH) |
        PACKET3_EVENT_WRITE__EVENT_INDEX(PACKET3_EVENT_WRITE__EVENT_INDEX__CS_PARTIAL_FLUSH);

    // build the pm4mec_event_write command which has 2 Dwords
    uint32_t pm4mec_event_write_cmd[2] = {header, dword2};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdBuf, pm4mec_event_write_cmd);
  }

  void BuildWriteWaitIdlePacket(CmdBuffer* cmdbuf) { BuildBarrierCommand(cmdbuf); }

  void BuildCacheFlushPacket(CmdBuffer* cmdbuf, size_t addr, size_t size) {
    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_ACQUIRE_MEM, 8 * sizeof(uint32_t));

    // Specify the base address of memory to invalidate.
    // The address must be 256 byte aligned.
    uint32_t dword5 = PACKET3_ACQUIRE_MEM__COHER_BASE_LO((uint32_t(addr >> 8)));
    uint32_t dword6 = PACKET3_ACQUIRE_MEM__COHER_BASE_HI((uint8_t(addr >> 40)));

    // Specify the size of memory to invalidate.
    // Size is specified in terms of 256 byte chunks. A coher_size
    // of 0xFFFFFFFF actually specified 0xFFFFFFFF00 (40 bits)
    // of memory. The field coher_size_hi specifies memory from
    // bits 40-64 for a total of 256 TB.
    size = ((addr % 256 + size) >> 8) + ((size + 0xFF) >> 8) - (size >> 8);
    uint32_t dword3 = PACKET3_ACQUIRE_MEM__COHER_SIZE((uint32_t(size)));
    uint32_t dword4 = PACKET3_ACQUIRE_MEM__COHER_SIZE_HI((uint32_t(size >> 32)));

    // Specify the poll interval for determing if operation is complete
    uint32_t dword7 = PACKET3_ACQUIRE_MEM__POLL_INTERVAL(0x10);

    // Program GCR Control Register. Initialize L2 Cache flush
    // for Non-Coherent memory blocks
    uint32_t dword8 = PACKET3_ACQUIRE_MEM__GCR_CNTL(
        ((GCR_CNTL__SEQ_FORWARD & GCR_CNTL__SEQ_MASK) | GCR_CNTL__GL2_WB_MASK));

    // build the pm4mec_acquire_mem command which has 8 Dwords
    uint32_t pm4mec_acquire_mem_cmd[8] = {header, 0,      dword3, dword4,
                                          dword5, dword6, dword7, dword8};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_acquire_mem_cmd);
  }

  void BuildWaitRegMemCommand(CmdBuffer* cmdbuf, bool mem_space, uint64_t wait_addr, bool func_eq,
                              uint32_t mask_val, uint32_t wait_val) override {
    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_WAIT_REG_MEM, 7 * sizeof(uint32_t));

    uint32_t dword7 = PACKET3_WAIT_REG_MEM__POLL_INTERVAL(0x10);
    uint32_t dword2_operation =
        PACKET3_WAIT_REG_MEM__OPERATION(PACKET3_WAIT_REG_MEM__OPERATION__WAIT_REG_MEM);

    // Apply the space to which addr belongs
    uint32_t dword2_mem_space =
        mem_space
            ? PACKET3_WAIT_REG_MEM__MEM_SPACE(PACKET3_WAIT_REG_MEM__MEM_SPACE__MEMORY_SPACE)
            : PACKET3_WAIT_REG_MEM__MEM_SPACE(PACKET3_WAIT_REG_MEM__MEM_SPACE__REGISTER_SPACE);

    // Apply the function - equal / not equal desired by user
    uint32_t dword2_function =
        func_eq ? PACKET3_WAIT_REG_MEM__FUNCTION(
                      PACKET3_WAIT_REG_MEM__FUNCTION__EQUAL_TO_THE_REFERENCE_VALUE)
                : PACKET3_WAIT_REG_MEM__FUNCTION(
                      PACKET3_WAIT_REG_MEM__FUNCTION__NOT_EQUAL_REFERENCE_VALUE);

    uint32_t dword2 = dword2_operation | dword2_mem_space | dword2_function;

    // Apply the mask on value at address/register
    uint32_t dword6 = PACKET3_WAIT_REG_MEM__MASK(mask_val);

    // Value to use in applying equal / not equal function
    uint32_t dword5 = PACKET3_WAIT_REG_MEM__REFERENCE(wait_val);

    // The address to poll should be DWord (4 byte) aligned
    // Update upper 32 bit address if addr is not a register
    uint32_t dword3 = 0;
    uint32_t dword4 = 0;
    if (mem_space) {
      assert(!(wait_addr & 0x3) && "WaitRegMem address must be 4 byte aligned");
      dword3 = PACKET3_WAIT_REG_MEM__MEM_POLL_ADDR_LO((Low32(wait_addr) >> 2));
      dword4 = PACKET3_WAIT_REG_MEM__MEM_POLL_ADDR_HI(High32(wait_addr));
    } else
      dword3 = PACKET3_WAIT_REG_MEM__REG_POLL_ADDR(wait_addr);

    // build the pm4mec_wait_reg_mem command which has 7 Dwords
    uint32_t pm4mec_wait_reg_mem_cmd[7] = {header, dword2, dword3, dword4, dword5, dword6, dword7};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_wait_reg_mem_cmd);
  }

  void BuildWriteShRegPacket(CmdBuffer* cmdbuf, uint32_t addr, uint32_t value) override {
    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_SET_SH_REG, 2 * sizeof(uint32_t) + sizeof(value));
    uint32_t dword2 = PACKET3_SET_SH_REG__REG_OFFSET((addr - PERSISTENT_SPACE_START)) |
                      PACKET3_SET_SH_REG__INDEX(PACKET3_SET_SH_REG__INDEX__DEFAULT);

    // build the pm4mec_set_sh_reg command which has 3 Dwords
    uint32_t pm4mec_set_sh_reg_cmd[3] = {header, dword2, value};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_set_sh_reg_cmd);
  }

  void BuildWriteUConfigRegPacket(CmdBuffer* cmdbuf, uint32_t addr, uint32_t value) override {
    // Initialize the command header
    uint32_t header =
        MakePacket3Header(PACKET3_SET_UCONFIG_REG, 2 * sizeof(uint32_t) + sizeof(value));
    uint32_t dword2 = PACKET3_SET_UCONFIG_REG__REG_OFFSET((addr - UCONFIG_SPACE_START));

    // build the pm4mec_set_uconfig_reg command which has 3 Dwords
    uint32_t pm4mec_set_uconfig_reg_cmd[3] = {header, dword2, value};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_set_uconfig_reg_cmd);
  }

  void BuildWritePConfigRegPacket(CmdBuffer* cmdbuf, uint32_t addr, uint32_t value) {
    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_COPY_DATA, 6 * sizeof(uint32_t));

    uint32_t dword2 = 0;
    dword2 |= PACKET3_COPY_DATA__SRC_SEL(PACKET3_COPY_DATA__SRC_SEL__IMMEDIATE_DATA);
    dword2 |= PACKET3_COPY_DATA__SRC_CACHE_POLICY(PACKET3_COPY_DATA__SRC_CACHE_POLICY__LRU);

    dword2 |= IsPrivilegedConfigReg(addr)
                  ? PACKET3_COPY_DATA__DST_SEL(PACKET3_COPY_DATA__DST_SEL__PERFCOUNTERS)
                  : PACKET3_COPY_DATA__DST_SEL(PACKET3_COPY_DATA__DST_SEL__MEM_MAPPED_REGISTER);

    dword2 |= PACKET3_COPY_DATA__DST_CACHE_POLICY(PACKET3_COPY_DATA__DST_CACHE_POLICY__LRU);

    dword2 |=
        PACKET3_COPY_DATA__WR_CONFIRM(PACKET3_COPY_DATA__WR_CONFIRM__DO_NOT_WAIT_FOR_CONFIRMATION);
    dword2 |= PACKET3_COPY_DATA__COUNT_SEL(PACKET3_COPY_DATA__COUNT_SEL__32_BITS_OF_DATA);

    uint32_t dword3 = PACKET3_COPY_DATA__IMM_DATA(value);

    uint32_t dword5 = PACKET3_COPY_DATA__DST_REG_OFFSET(addr);

    // build the pm4mec_copy_data command which has 6 Dwords
    uint32_t pm4mec_copy_data_cmd[6] = {header, dword2, dword3, 0, dword5, 0};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_copy_data_cmd);
  }

  void BuildWriteConfigRegPacket(CmdBuffer* cmdbuf, uint32_t addr, uint32_t value) {
    return IsPrivilegedConfigReg(addr) ? BuildWritePConfigRegPacket(cmdbuf, addr, value)
                                       : BuildWriteUConfigRegPacket(cmdbuf, addr, value);
  }

  void BuildCopyRegDataPacket(CmdBuffer* cmdbuf, uint32_t src_reg_addr, const void* dst_addr,
                              uint32_t size, bool wait) override {
    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_COPY_DATA, 6 * sizeof(uint32_t));

    uint32_t dword2 = 0;
    dword2 |= IsPrivilegedConfigReg(src_reg_addr)
                  ? PACKET3_COPY_DATA__SRC_SEL(PACKET3_COPY_DATA__SRC_SEL__PERFCOUNTERS)
                  : PACKET3_COPY_DATA__SRC_SEL(PACKET3_COPY_DATA__SRC_SEL__MEM_MAPPED_REGISTER);

    dword2 |= PACKET3_COPY_DATA__SRC_CACHE_POLICY(PACKET3_COPY_DATA__SRC_CACHE_POLICY__LRU);
    dword2 |= PACKET3_COPY_DATA__DST_SEL(PACKET3_COPY_DATA__DST_SEL__TC_L2);
    dword2 |= PACKET3_COPY_DATA__DST_CACHE_POLICY(PACKET3_COPY_DATA__DST_CACHE_POLICY__LRU);

    dword2 |=
        wait ? PACKET3_COPY_DATA__WR_CONFIRM(PACKET3_COPY_DATA__WR_CONFIRM__WAIT_FOR_CONFIRMATION)
             : PACKET3_COPY_DATA__WR_CONFIRM(
                   PACKET3_COPY_DATA__WR_CONFIRM__DO_NOT_WAIT_FOR_CONFIRMATION);

    dword2 |= (size == 0)
                  ? PACKET3_COPY_DATA__COUNT_SEL(PACKET3_COPY_DATA__COUNT_SEL__32_BITS_OF_DATA)
                  : PACKET3_COPY_DATA__COUNT_SEL(PACKET3_COPY_DATA__COUNT_SEL__64_BITS_OF_DATA);

    // Specify the source register offset
    uint32_t dword3 = PACKET3_COPY_DATA__SRC_REG_OFFSET(src_reg_addr);

    // Specify the destination memory address
    uint32_t dword6 = PACKET3_COPY_DATA__DST_ADDR_HI(PtrHigh32(dst_addr));

    uint32_t dword5 = 0;
    if (size == 0) {
      dword5 |= PACKET3_COPY_DATA__DST_32B_ADDR_LO((PtrLow32(dst_addr) >> 2));
    } else {
      dword5 |= PACKET3_COPY_DATA__DST_64B_ADDR_LO((PtrLow32(dst_addr) >> 3));
    }

    // build the pm4mec_copy_data command which has 6 Dwords
    uint32_t pm4mec_copy_data_cmd[6] = {header, dword2, dword3, 0, dword5, dword6};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_copy_data_cmd);
  }

  uint32_t BuildCopyCounterDataPacket(CmdBuffer* cmdbuf, uint32_t src_reg_addr_lo,
                                      uint32_t src_reg_addr_hi, const void* dst_addr,
                                      uint32_t dw_mask) {
    uint32_t read_counter = 0;
    if (dw_mask & 0x1) {
      BuildCopyRegDataPacket(cmdbuf, src_reg_addr_lo, (uint32_t*)dst_addr + read_counter,
                             PACKET3_COPY_DATA__COUNT_SEL__32_BITS_OF_DATA, false);
      ++read_counter;
    }
    if (dw_mask & 0x2) {
      BuildCopyRegDataPacket(cmdbuf, src_reg_addr_hi, (uint32_t*)dst_addr + read_counter,
                             PACKET3_COPY_DATA__COUNT_SEL__32_BITS_OF_DATA, false);
      ++read_counter;
    }
    return read_counter;
  }

  void BuildWriteRegDataPacket(CmdBuffer* cmdbuf, uint32_t dst_reg_addr, const uint32_t* data,
                               uint32_t count, bool wait) {
    // Initialize the command header
    uint32_t header =
        MakePacket3Header(PACKET3_WRITE_DATA, 4 * sizeof(uint32_t) + count * sizeof(data[0]));

    // ordinal2
    uint32_t dword2 = 0;
    dword2 |= PACKET3_WRITE_DATA__DST_SEL(
        PACKET3_WRITE_DATA__DST_SEL__MEM_MAPPED_REGISTER);  // mem-mapped reg
    dword2 |= PACKET3_WRITE_DATA__ADDR_INCR(
        PACKET3_WRITE_DATA__ADDR_INCR__DO_NOT_INCREMENT_ADDRESS);  // not increment address
    dword2 |= wait ? PACKET3_WRITE_DATA__WR_CONFIRM(
                         PACKET3_WRITE_DATA__WR_CONFIRM__WAIT_FOR_WRITE_CONFIRMATION)
                   : PACKET3_WRITE_DATA__WR_CONFIRM(
                         PACKET3_WRITE_DATA__WR_CONFIRM__DO_NOT_WAIT_FOR_WRITE_CONFIRMATION);

    // ordinal3
    uint32_t dword3 = PACKET3_WRITE_DATA__DST_MMREG_ADDR(dst_reg_addr);  // mem-mapped reg

    // ordinal4
    uint32_t dword4 = PACKET3_WRITE_DATA__DST_MEM_ADDR_HI(0);  // mem-mapped reg

    // build the pm4mec_write_data command which has 4 Dwords
    uint32_t pm4mec_write_data_cmd[4] = {header, dword2, dword3, dword4};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_write_data_cmd);

    // appending data dwords
    for (uint32_t i = 0; i < count; ++i) {
      APPEND_COMMAND_WRAPPER(cmdbuf, data[i]);
    }

    if (count & 1) {
      // Insert a NOP spacer
      BuildNopPacket(cmdbuf, 1);
    }
  }

  void BuildNopPacket(CmdBuffer* cmdbuf, uint32_t num_dwords) {
    uint32_t header = MakePacket3Header(PACKET3_NOP, num_dwords * sizeof(uint32_t));
    APPEND_COMMAND_WRAPPER(cmdbuf, header);
    if (num_dwords > 1) {
      std::vector<uint32_t> data_block((num_dwords - 1), 0);
      APPEND_COMMAND_WRAPPER(cmdbuf, data_block.data(), (num_dwords - 1));
    }
  }

  void BuildThreadTraceEventFinish(CmdBuffer* cmdBuf) {}

  void BuildIndirectBufferCmd(CmdBuffer* cmdbuf, const void* cmd_addr, std::size_t cmd_size) {
    // Verify the address is 4-byte aligned
    assert(!(uintptr_t(cmd_addr) & 0x3) && "IndirectBuffer address must be 4 byte aligned");

    // Initialize the command header
    uint32_t header = MakePacket3Header(PACKET3_INDIRECT_BUFFER, 4 * sizeof(uint32_t));

    // Specify the address of indirect buffer encoding cmd stream
    uint32_t dword2 = PACKET3_INDIRECT_BUFFER__IB_BASE_LO((PtrLow32(cmd_addr) >> 2));
    uint32_t dword3 = PACKET3_INDIRECT_BUFFER__IB_BASE_HI(PtrHigh32(cmd_addr));

    // Specify the size of indirect buffer and cache policy to set
    // upon executing the cmds of indirect buffer
    uint32_t dword4 = 0;
    dword4 |= PACKET3_INDIRECT_BUFFER__VALID(1);
    dword4 |= PACKET3_INDIRECT_BUFFER__IB_SIZE((cmd_size / sizeof(uint32_t)));
    dword4 |= PACKET3_INDIRECT_BUFFER__CACHE_POLICY(PACKET3_INDIRECT_BUFFER__CACHE_POLICY__STREAM);

    // build the pm4mec_indirect_buffer command which has 4 Dwords
    uint32_t pm4mec_indirect_buffer_cmd[4] = {header, dword2, dword3, dword4};
    // Append the built command into output Command Buffer
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4mec_indirect_buffer_cmd);
  }
  void BuildPredExecPacket(CmdBuffer* cmdbuf, uint32_t xcc_id = 0, uint32_t exec_count = 0) {}

  void BuildMutexAcquirePacket(CmdBuffer* cmdbuf, size_t addr) override {
    constexpr uint32_t GL2_OP_ATOMIC_CMPSWAP_RTN_32 = 8;
    uint32_t header = MakePacket3Header(PACKET3_ATOMIC_MEM, 9 * sizeof(uint32_t));

    uint32_t dword2 =
        PACKET3_ATOMIC_MEM__COMMAND(PACKET3_ATOMIC_MEM__COMMAND__LOOP_UNTIL_COMPARE_SATISFIED);
    dword2 |= PACKET3_ATOMIC_MEM__ATOMIC(GL2_OP_ATOMIC_CMPSWAP_RTN_32);
    uint32_t dword9 = PACKET3_ATOMIC_MEM__LOOP_INTERVAL(16);

    uint32_t dword3 = PACKET3_ATOMIC_MEM__ADDR_LO(uint32_t(addr));
    uint32_t dword4 = PACKET3_ATOMIC_MEM__ADDR_HI((addr >> 32));
    uint32_t dword5 = PACKET3_ATOMIC_MEM__SRC_DATA_LO(MakeMutexSlot());
    uint32_t dword6 = PACKET3_ATOMIC_MEM__CMP_DATA_LO(0);

    // build the pm4mec_atomic_mem command which has 9 Dwords
    uint32_t pm4_mec_atomic_mem_cmd[9] = {header, dword2, dword3, dword4, dword5,
                                          dword6, 0,      0,      dword9};
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4_mec_atomic_mem_cmd);
  }

  void BuildMutexReleasePacket(CmdBuffer* cmdbuf, size_t addr) override {
    constexpr uint32_t GL2_OP_ATOMIC_SWAP_RTN_32 = 7;
    uint32_t header = MakePacket3Header(PACKET3_ATOMIC_MEM, 9 * sizeof(uint32_t));

    uint32_t dword2 = PACKET3_ATOMIC_MEM__COMMAND(PACKET3_ATOMIC_MEM__COMMAND__SINGLE_PASS_ATOMIC);
    dword2 |= PACKET3_ATOMIC_MEM__ATOMIC(GL2_OP_ATOMIC_SWAP_RTN_32);

    uint32_t dword3 = PACKET3_ATOMIC_MEM__ADDR_LO(uint32_t(addr));
    uint32_t dword4 = PACKET3_ATOMIC_MEM__ADDR_HI((addr >> 32));
    uint32_t dword5 = PACKET3_ATOMIC_MEM__SRC_DATA_LO(0);

    // build the pm4mec_atomic_mem command which has 9 Dwords
    uint32_t pm4_mec_atomic_mem_cmd[9] = {header, dword2, dword3, dword4, dword5, 0, 0, 0, 0};
    APPEND_COMMAND_WRAPPER(cmdbuf, pm4_mec_atomic_mem_cmd);
  }

  void BuildPrimeL2(CmdBuffer* cmdBuf, uint64_t addr) override
  {
#ifdef PM4_MEC_PRIME_UTCL2_DEFINED
    PM4_MEC_PRIME_UTCL2 prime{};
    prime.header = MakePacket3Header(IT_PRIME_UTCL2, sizeof(prime));
    prime.bitfields2.cache_perm = cache_perm__mec_prime_utcl2__write;
    prime.bitfields2.prime_mode = prime_mode__mec_prime_utcl2__dont_wait_for_xack;

    prime.bitfields5.requested_pages = 15;
    prime.addr_lo = (uint32_t)addr;
    prime.addr_hi = addr >> 32;
#endif
  }

  void BuildWriteShRegPacket(CmdBuffer* cmd, const Register& reg, uint32_t value) {
    BuildWriteShRegPacket(cmd, get_addr(reg), value);
  }

  void BuildWriteUConfigRegPacket(CmdBuffer* cmd, const Register& reg, uint32_t value) {
    BuildWriteUConfigRegPacket(cmd, get_addr(reg), value);
  }

  void BuildWritePConfigRegPacket(CmdBuffer* cmd, const Register& reg, uint32_t value) {
    BuildWritePConfigRegPacket(cmd, get_addr(reg), value);
  }

  void BuildCopyCounterDataPacket(CmdBuffer* cmd, const Register& reg_lo, const Register& reg_hi,
                                  const void* dst_addr, uint32_t mask) {
    BuildCopyCounterDataPacket(cmd, (mask & 1 << 0) ? get_addr(reg_lo) : 0,
                               (mask & 1 << 1) ? get_addr(reg_hi) : 0, dst_addr, mask);
  }

  void BuildWriteConfigRegPacket(CmdBuffer* cmd, const Register& reg, uint32_t value) {
    BuildWriteConfigRegPacket(cmd, get_addr(reg), value);
  }

  void BuildWaitRegMemCommand(CmdBuffer* cmd, bool mem_space, const Register& wait_addr,
                              bool func_eq, uint32_t mask_val, uint32_t wait_val) {
    BuildWaitRegMemCommand(cmd, mem_space, get_addr(wait_addr), func_eq, mask_val, wait_val);
  }

  void BuildWriteRegDataPacket(CmdBuffer* cmd, const Register& reg, const uint32_t* data,
                               uint32_t count, bool wait) {
    BuildWriteRegDataPacket(cmd, get_addr(reg), data, count, wait);
  }

  void BuildCopyRegDataPacket(CmdBuffer* cmd, const Register& reg, const void* dst_addr,
                              uint32_t size, bool wait) {
    BuildCopyRegDataPacket(cmd, get_addr(reg), dst_addr, size, wait);
  }
};

}  // namespace pm4_builder

#endif  //  SRC_PM4_GFX11_CMD_BUILDER_H_
