| /* |
| * Copyright (c) 2016-2017 Advanced Micro Devices, Inc. |
| * All rights reserved. |
| * |
| * For use for simulation and test purposes only |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, |
| * this list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * 3. Neither the name of the copyright holder nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| * |
| * Authors: Anthony Gutierrez |
| */ |
| |
| #ifndef __ARCH_GCN3_INSTS_OP_ENCODINGS_HH__ |
| #define __ARCH_GCN3_INSTS_OP_ENCODINGS_HH__ |
| |
| #include "arch/gcn3/gpu_decoder.hh" |
| #include "arch/gcn3/gpu_mem_helpers.hh" |
| #include "arch/gcn3/insts/gpu_static_inst.hh" |
| #include "arch/gcn3/operand.hh" |
| #include "debug/GCN3.hh" |
| #include "debug/GPUExec.hh" |
| #include "mem/ruby/system/RubySystem.hh" |
| |
| namespace Gcn3ISA |
| { |
| struct BufferRsrcDescriptor |
| { |
| uint64_t baseAddr : 48; |
| uint32_t stride : 14; |
| uint32_t cacheSwizzle : 1; |
| uint32_t swizzleEn : 1; |
| uint32_t numRecords : 32; |
| uint32_t dstSelX : 3; |
| uint32_t dstSelY : 3; |
| uint32_t dstSelZ : 3; |
| uint32_t dstSelW : 3; |
| uint32_t numFmt : 3; |
| uint32_t dataFmt : 4; |
| uint32_t elemSize : 2; |
| uint32_t idxStride : 2; |
| uint32_t addTidEn : 1; |
| uint32_t atc : 1; |
| uint32_t hashEn : 1; |
| uint32_t heap : 1; |
| uint32_t mType : 3; |
| uint32_t type : 2; |
| }; |
| |
| // --- purely virtual instruction classes --- |
| |
| class Inst_SOP2 : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_SOP2(InFmt_SOP2*, const std::string &opcode); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_SOP2 instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_SOP2 *); |
| }; // Inst_SOP2 |
| |
| class Inst_SOPK : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_SOPK(InFmt_SOPK*, const std::string &opcode); |
| ~Inst_SOPK(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_SOPK instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_SOPK *); |
| }; // Inst_SOPK |
| |
| class Inst_SOP1 : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_SOP1(InFmt_SOP1*, const std::string &opcode); |
| ~Inst_SOP1(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_SOP1 instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_SOP1 *); |
| }; // Inst_SOP1 |
| |
| class Inst_SOPC : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_SOPC(InFmt_SOPC*, const std::string &opcode); |
| ~Inst_SOPC(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_SOPC instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_SOPC *); |
| }; // Inst_SOPC |
| |
| class Inst_SOPP : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_SOPP(InFmt_SOPP*, const std::string &opcode); |
| ~Inst_SOPP(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_SOPP instData; |
| }; // Inst_SOPP |
| |
| class Inst_SMEM : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_SMEM(InFmt_SMEM*, const std::string &opcode); |
| ~Inst_SMEM(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| /** |
| * initiate a memory read access for N dwords |
| */ |
| template<int N> |
| void |
| initMemRead(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqScalarHelper<ScalarRegU32, N>(gpuDynInst, |
| MemCmd::ReadReq); |
| } |
| |
| /** |
| * initiate a memory write access for N dwords |
| */ |
| template<int N> |
| void |
| initMemWrite(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqScalarHelper<ScalarRegU32, N>(gpuDynInst, |
| MemCmd::WriteReq); |
| } |
| |
| /** |
| * For normal s_load_dword/s_store_dword instruction addresses. |
| */ |
| void |
| calcAddr(GPUDynInstPtr gpu_dyn_inst, ConstScalarOperandU64 &addr, |
| ScalarRegU32 offset) |
| { |
| Addr vaddr = ((addr.rawData() + offset) & ~0x3); |
| gpu_dyn_inst->scalarAddr = vaddr; |
| } |
| |
| /** |
| * For s_buffer_load_dword/s_buffer_store_dword instruction addresses. |
| * The s_buffer instructions use the same buffer resource descriptor |
| * as the MUBUF instructions. |
| */ |
| void |
| calcAddr(GPUDynInstPtr gpu_dyn_inst, |
| ConstScalarOperandU128 &s_rsrc_desc, ScalarRegU32 offset) |
| { |
| BufferRsrcDescriptor rsrc_desc; |
| ScalarRegU32 clamped_offset(offset); |
| std::memcpy((void*)&rsrc_desc, s_rsrc_desc.rawDataPtr(), |
| sizeof(BufferRsrcDescriptor)); |
| |
| /** |
| * The address is clamped if: |
| * Stride is zero: clamp if offset >= num_records |
| * Stride is non-zero: clamp if offset > (stride * num_records) |
| */ |
| if (!rsrc_desc.stride && offset >= rsrc_desc.numRecords) { |
| clamped_offset = rsrc_desc.numRecords; |
| } else if (rsrc_desc.stride && offset |
| > (rsrc_desc.stride * rsrc_desc.numRecords)) { |
| clamped_offset = (rsrc_desc.stride * rsrc_desc.numRecords); |
| } |
| |
| Addr vaddr = ((rsrc_desc.baseAddr + clamped_offset) & ~0x3); |
| gpu_dyn_inst->scalarAddr = vaddr; |
| } |
| |
| // first instruction DWORD |
| InFmt_SMEM instData; |
| // second instruction DWORD |
| InFmt_SMEM_1 extData; |
| }; // Inst_SMEM |
| |
| class Inst_VOP2 : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_VOP2(InFmt_VOP2*, const std::string &opcode); |
| ~Inst_VOP2(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_VOP2 instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_VOP2 *); |
| }; // Inst_VOP2 |
| |
| class Inst_VOP1 : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_VOP1(InFmt_VOP1*, const std::string &opcode); |
| ~Inst_VOP1(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_VOP1 instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_VOP1 *); |
| }; // Inst_VOP1 |
| |
| class Inst_VOPC : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_VOPC(InFmt_VOPC*, const std::string &opcode); |
| ~Inst_VOPC(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_VOPC instData; |
| // possible second DWORD |
| InstFormat extData; |
| uint32_t varSize; |
| |
| private: |
| bool hasSecondDword(InFmt_VOPC *); |
| }; // Inst_VOPC |
| |
| class Inst_VINTRP : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_VINTRP(InFmt_VINTRP*, const std::string &opcode); |
| ~Inst_VINTRP(); |
| |
| int instSize() const override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_VINTRP instData; |
| }; // Inst_VINTRP |
| |
| class Inst_VOP3 : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_VOP3(InFmt_VOP3*, const std::string &opcode, bool sgpr_dst); |
| ~Inst_VOP3(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_VOP3 instData; |
| // second instruction DWORD |
| InFmt_VOP3_1 extData; |
| |
| private: |
| bool hasSecondDword(InFmt_VOP3 *); |
| /** |
| * the v_cmp and readlane instructions in the VOP3 |
| * encoding are unique because they are the only |
| * instructions that use the VDST field to specify |
| * a scalar register destination. for VOP3::V_CMP insts |
| * VDST specifies the arbitrary SGPR pair used to write |
| * VCC. for V_READLANE VDST specifies the SGPR to return |
| * the value of the selected lane in the source VGPR |
| * from which we are reading. |
| */ |
| const bool sgprDst; |
| }; // Inst_VOP3 |
| |
| class Inst_VOP3_SDST_ENC : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_VOP3_SDST_ENC(InFmt_VOP3_SDST_ENC*, const std::string &opcode); |
| ~Inst_VOP3_SDST_ENC(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_VOP3_SDST_ENC instData; |
| // second instruction DWORD |
| InFmt_VOP3_1 extData; |
| |
| private: |
| bool hasSecondDword(InFmt_VOP3_SDST_ENC *); |
| }; // Inst_VOP3_SDST_ENC |
| |
| class Inst_DS : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_DS(InFmt_DS*, const std::string &opcode); |
| ~Inst_DS(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| template<typename T> |
| void |
| initMemRead(GPUDynInstPtr gpuDynInst, Addr offset) |
| { |
| Wavefront *wf = gpuDynInst->wavefront(); |
| |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (gpuDynInst->exec_mask[lane]) { |
| Addr vaddr = gpuDynInst->addr[lane] + offset; |
| |
| (reinterpret_cast<T*>(gpuDynInst->d_data))[lane] |
| = wf->ldsChunk->read<T>(vaddr); |
| } |
| } |
| } |
| |
| template<typename T> |
| void |
| initDualMemRead(GPUDynInstPtr gpuDynInst, Addr offset0, Addr offset1) |
| { |
| Wavefront *wf = gpuDynInst->wavefront(); |
| |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (gpuDynInst->exec_mask[lane]) { |
| Addr vaddr0 = gpuDynInst->addr[lane] + offset0; |
| Addr vaddr1 = gpuDynInst->addr[lane] + offset1; |
| |
| (reinterpret_cast<T*>(gpuDynInst->d_data))[lane * 2] |
| = wf->ldsChunk->read<T>(vaddr0); |
| (reinterpret_cast<T*>(gpuDynInst->d_data))[lane * 2 + 1] |
| = wf->ldsChunk->read<T>(vaddr1); |
| } |
| } |
| } |
| |
| template<typename T> |
| void |
| initMemWrite(GPUDynInstPtr gpuDynInst, Addr offset) |
| { |
| Wavefront *wf = gpuDynInst->wavefront(); |
| |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (gpuDynInst->exec_mask[lane]) { |
| Addr vaddr = gpuDynInst->addr[lane] + offset; |
| wf->ldsChunk->write<T>(vaddr, |
| (reinterpret_cast<T*>(gpuDynInst->d_data))[lane]); |
| } |
| } |
| } |
| |
| template<typename T> |
| void |
| initDualMemWrite(GPUDynInstPtr gpuDynInst, Addr offset0, Addr offset1) |
| { |
| Wavefront *wf = gpuDynInst->wavefront(); |
| |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (gpuDynInst->exec_mask[lane]) { |
| Addr vaddr0 = gpuDynInst->addr[lane] + offset0; |
| Addr vaddr1 = gpuDynInst->addr[lane] + offset1; |
| wf->ldsChunk->write<T>(vaddr0, (reinterpret_cast<T*>( |
| gpuDynInst->d_data))[lane * 2]); |
| wf->ldsChunk->write<T>(vaddr1, (reinterpret_cast<T*>( |
| gpuDynInst->d_data))[lane * 2 + 1]); |
| } |
| } |
| } |
| |
| void |
| calcAddr(GPUDynInstPtr gpuDynInst, ConstVecOperandU32 &addr) |
| { |
| Wavefront *wf = gpuDynInst->wavefront(); |
| |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (wf->execMask(lane)) { |
| gpuDynInst->addr.at(lane) = (Addr)addr[lane]; |
| } |
| } |
| } |
| |
| // first instruction DWORD |
| InFmt_DS instData; |
| // second instruction DWORD |
| InFmt_DS_1 extData; |
| }; // Inst_DS |
| |
| class Inst_MUBUF : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_MUBUF(InFmt_MUBUF*, const std::string &opcode); |
| ~Inst_MUBUF(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| template<typename T> |
| void |
| initMemRead(GPUDynInstPtr gpuDynInst) |
| { |
| // temporarily modify exec_mask to supress memory accesses to oob |
| // regions. Only issue memory requests for lanes that have their |
| // exec_mask set and are not out of bounds. |
| VectorMask old_exec_mask = gpuDynInst->exec_mask; |
| gpuDynInst->exec_mask &= ~oobMask; |
| initMemReqHelper<T, 1>(gpuDynInst, MemCmd::ReadReq); |
| gpuDynInst->exec_mask = old_exec_mask; |
| } |
| |
| |
| template<int N> |
| void |
| initMemRead(GPUDynInstPtr gpuDynInst) |
| { |
| // temporarily modify exec_mask to supress memory accesses to oob |
| // regions. Only issue memory requests for lanes that have their |
| // exec_mask set and are not out of bounds. |
| VectorMask old_exec_mask = gpuDynInst->exec_mask; |
| gpuDynInst->exec_mask &= ~oobMask; |
| initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::ReadReq); |
| gpuDynInst->exec_mask = old_exec_mask; |
| } |
| |
| template<typename T> |
| void |
| initMemWrite(GPUDynInstPtr gpuDynInst) |
| { |
| // temporarily modify exec_mask to supress memory accesses to oob |
| // regions. Only issue memory requests for lanes that have their |
| // exec_mask set and are not out of bounds. |
| VectorMask old_exec_mask = gpuDynInst->exec_mask; |
| gpuDynInst->exec_mask &= ~oobMask; |
| initMemReqHelper<T, 1>(gpuDynInst, MemCmd::WriteReq); |
| gpuDynInst->exec_mask = old_exec_mask; |
| } |
| |
| template<int N> |
| void |
| initMemWrite(GPUDynInstPtr gpuDynInst) |
| { |
| // temporarily modify exec_mask to supress memory accesses to oob |
| // regions. Only issue memory requests for lanes that have their |
| // exec_mask set and are not out of bounds. |
| VectorMask old_exec_mask = gpuDynInst->exec_mask; |
| gpuDynInst->exec_mask &= ~oobMask; |
| initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::WriteReq); |
| gpuDynInst->exec_mask = old_exec_mask; |
| } |
| |
| void |
| injectGlobalMemFence(GPUDynInstPtr gpuDynInst) |
| { |
| // create request and set flags |
| gpuDynInst->resetEntireStatusVector(); |
| gpuDynInst->setStatusVector(0, 1); |
| RequestPtr req = std::make_shared<Request>(0, 0, 0, |
| gpuDynInst->computeUnit()-> |
| requestorId(), 0, |
| gpuDynInst->wfDynId); |
| gpuDynInst->setRequestFlags(req); |
| gpuDynInst->computeUnit()-> |
| injectGlobalMemFence(gpuDynInst, false, req); |
| } |
| |
| /** |
| * MUBUF insructions calculate their addresses as follows: |
| * |
| * index = (IDXEN ? vgpr_idx : 0) + (const_add_tid_en ? TID : 0) |
| * offset = (OFFEN ? vgpr_off : 0) + inst_off |
| * |
| * / ====================== LINEAR ADDRESSING ====================== / |
| * VADDR = base + sgpr_off + offset + stride * index |
| * |
| * / ===================== SWIZZLED ADDRESSING ===================== / |
| * index_msb = index / const_index_stride |
| * index_lsb = index % const_index_stride |
| * offset_msb = offset / const_element_size |
| * offset_lsb = offset % const_element_size |
| * buffer_offset = ((index_msb * stride + offset_msb * |
| * const_element_size) * const_index_stride + |
| * index_lsb * const_element_size + offset_lsb) |
| * |
| * VADDR = base + sgpr_off + buffer_offset |
| */ |
| template<typename VOFF, typename VIDX, typename SRSRC, typename SOFF> |
| void |
| calcAddr(GPUDynInstPtr gpuDynInst, VOFF v_off, VIDX v_idx, |
| SRSRC s_rsrc_desc, SOFF s_offset, int inst_offset) |
| { |
| Addr vaddr = 0; |
| Addr base_addr = 0; |
| Addr stride = 0; |
| Addr buf_idx = 0; |
| Addr buf_off = 0; |
| BufferRsrcDescriptor rsrc_desc; |
| |
| std::memcpy((void*)&rsrc_desc, s_rsrc_desc.rawDataPtr(), |
| sizeof(BufferRsrcDescriptor)); |
| |
| base_addr = rsrc_desc.baseAddr; |
| |
| stride = rsrc_desc.addTidEn ? ((rsrc_desc.dataFmt << 14) |
| + rsrc_desc.stride) : rsrc_desc.stride; |
| |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (gpuDynInst->exec_mask[lane]) { |
| vaddr = base_addr + s_offset.rawData(); |
| /** |
| * first we calculate the buffer's index and offset. |
| * these will be used for either linear or swizzled |
| * buffers. |
| */ |
| buf_idx = v_idx[lane] + (rsrc_desc.addTidEn ? lane : 0); |
| |
| buf_off = v_off[lane] + inst_offset; |
| |
| |
| /** |
| * Range check behavior causes out of range accesses to |
| * to be treated differently. Out of range accesses return |
| * 0 for loads and are ignored for stores. For |
| * non-formatted accesses, this is done on a per-lane |
| * basis. |
| */ |
| if (stride == 0 || !rsrc_desc.swizzleEn) { |
| if (buf_off + stride * buf_idx >= |
| rsrc_desc.numRecords - s_offset.rawData()) { |
| DPRINTF(GCN3, "mubuf out-of-bounds condition 1: " |
| "lane = %d, buffer_offset = %llx, " |
| "const_stride = %llx, " |
| "const_num_records = %llx\n", |
| lane, buf_off + stride * buf_idx, |
| stride, rsrc_desc.numRecords); |
| oobMask.set(lane); |
| continue; |
| } |
| } |
| |
| if (stride != 0 && rsrc_desc.swizzleEn) { |
| if (buf_idx >= rsrc_desc.numRecords || |
| buf_off >= stride) { |
| DPRINTF(GCN3, "mubuf out-of-bounds condition 2: " |
| "lane = %d, offset = %llx, " |
| "index = %llx, " |
| "const_num_records = %llx\n", |
| lane, buf_off, buf_idx, |
| rsrc_desc.numRecords); |
| oobMask.set(lane); |
| continue; |
| } |
| } |
| |
| if (rsrc_desc.swizzleEn) { |
| Addr idx_stride = 8 << rsrc_desc.idxStride; |
| Addr elem_size = 2 << rsrc_desc.elemSize; |
| Addr idx_msb = buf_idx / idx_stride; |
| Addr idx_lsb = buf_idx % idx_stride; |
| Addr off_msb = buf_off / elem_size; |
| Addr off_lsb = buf_off % elem_size; |
| DPRINTF(GCN3, "mubuf swizzled lane %d: " |
| "idx_stride = %llx, elem_size = %llx, " |
| "idx_msb = %llx, idx_lsb = %llx, " |
| "off_msb = %llx, off_lsb = %llx\n", |
| lane, idx_stride, elem_size, idx_msb, idx_lsb, |
| off_msb, off_lsb); |
| |
| vaddr += ((idx_msb * stride + off_msb * elem_size) |
| * idx_stride + idx_lsb * elem_size + off_lsb); |
| } else { |
| vaddr += buf_off + stride * buf_idx; |
| } |
| |
| DPRINTF(GCN3, "Calculating mubuf address for lane %d: " |
| "vaddr = %llx, base_addr = %llx, " |
| "stride = %llx, buf_idx = %llx, buf_off = %llx\n", |
| lane, vaddr, base_addr, stride, |
| buf_idx, buf_off); |
| gpuDynInst->addr.at(lane) = vaddr; |
| } |
| } |
| } |
| |
| // first instruction DWORD |
| InFmt_MUBUF instData; |
| // second instruction DWORD |
| InFmt_MUBUF_1 extData; |
| // Mask of lanes with out-of-bounds accesses. Needs to be tracked |
| // seperately from the exec_mask so that we remember to write zero |
| // to the registers associated with out of bounds lanes. |
| VectorMask oobMask; |
| }; // Inst_MUBUF |
| |
| class Inst_MTBUF : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_MTBUF(InFmt_MTBUF*, const std::string &opcode); |
| ~Inst_MTBUF(); |
| |
| int instSize() const override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_MTBUF instData; |
| // second instruction DWORD |
| InFmt_MTBUF_1 extData; |
| |
| private: |
| bool hasSecondDword(InFmt_MTBUF *); |
| }; // Inst_MTBUF |
| |
| class Inst_MIMG : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_MIMG(InFmt_MIMG*, const std::string &opcode); |
| ~Inst_MIMG(); |
| |
| int instSize() const override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_MIMG instData; |
| // second instruction DWORD |
| InFmt_MIMG_1 extData; |
| }; // Inst_MIMG |
| |
| class Inst_EXP : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_EXP(InFmt_EXP*, const std::string &opcode); |
| ~Inst_EXP(); |
| |
| int instSize() const override; |
| |
| protected: |
| // first instruction DWORD |
| InFmt_EXP instData; |
| // second instruction DWORD |
| InFmt_EXP_1 extData; |
| }; // Inst_EXP |
| |
| class Inst_FLAT : public GCN3GPUStaticInst |
| { |
| public: |
| Inst_FLAT(InFmt_FLAT*, const std::string &opcode); |
| ~Inst_FLAT(); |
| |
| int instSize() const override; |
| void generateDisassembly() override; |
| |
| bool isScalarRegister(int opIdx) override; |
| bool isVectorRegister(int opIdx) override; |
| int getRegisterIndex(int opIdx, GPUDynInstPtr gpuDynInst) override; |
| |
| protected: |
| template<typename T> |
| void |
| initMemRead(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqHelper<T, 1>(gpuDynInst, MemCmd::ReadReq); |
| } |
| |
| template<int N> |
| void |
| initMemRead(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::ReadReq); |
| } |
| |
| template<typename T> |
| void |
| initMemWrite(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqHelper<T, 1>(gpuDynInst, MemCmd::WriteReq); |
| } |
| |
| template<int N> |
| void |
| initMemWrite(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqHelper<VecElemU32, N>(gpuDynInst, MemCmd::WriteReq); |
| } |
| |
| template<typename T> |
| void |
| initAtomicAccess(GPUDynInstPtr gpuDynInst) |
| { |
| initMemReqHelper<T, 1>(gpuDynInst, MemCmd::SwapReq, true); |
| } |
| |
| void |
| calcAddr(GPUDynInstPtr gpuDynInst, ConstVecOperandU64 &addr) |
| { |
| for (int lane = 0; lane < NumVecElemPerVecReg; ++lane) { |
| if (gpuDynInst->exec_mask[lane]) { |
| gpuDynInst->addr.at(lane) = addr[lane]; |
| } |
| } |
| gpuDynInst->resolveFlatSegment(gpuDynInst->exec_mask); |
| } |
| |
| // first instruction DWORD |
| InFmt_FLAT instData; |
| // second instruction DWORD |
| InFmt_FLAT_1 extData; |
| }; // Inst_FLAT |
| } // namespace Gcn3ISA |
| |
| #endif // __ARCH_GCN3_INSTS_OP_ENCODINGS_HH__ |