| /* |
| * Copyright (c) 2015-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. |
| */ |
| |
| #include "gpu-compute/gpu_dyn_inst.hh" |
| |
| #include "debug/GPUInst.hh" |
| #include "debug/GPUMem.hh" |
| #include "gpu-compute/gpu_static_inst.hh" |
| #include "gpu-compute/scalar_register_file.hh" |
| #include "gpu-compute/shader.hh" |
| #include "gpu-compute/wavefront.hh" |
| |
| namespace gem5 |
| { |
| |
| GPUDynInst::GPUDynInst(ComputeUnit *_cu, Wavefront *_wf, |
| GPUStaticInst *static_inst, InstSeqNum instSeqNum) |
| : GPUExecContext(_cu, _wf), scalarAddr(0), addr(computeUnit()->wfSize(), |
| (Addr)0), numScalarReqs(0), isSaveRestore(false), |
| _staticInst(static_inst), _seqNum(instSeqNum), |
| maxSrcVecRegOpSize(-1), maxSrcScalarRegOpSize(-1) |
| { |
| _staticInst->initOperandInfo(); |
| statusVector.assign(TheGpuISA::NumVecElemPerVecReg, 0); |
| tlbHitLevel.assign(computeUnit()->wfSize(), -1); |
| // vector instructions can have up to 4 source/destination operands |
| d_data = new uint8_t[computeUnit()->wfSize() * 4 * sizeof(double)]; |
| a_data = new uint8_t[computeUnit()->wfSize() * 8]; |
| x_data = new uint8_t[computeUnit()->wfSize() * 8]; |
| // scalar loads can read up to 16 Dwords of data (see publicly |
| // available GCN3 ISA manual) |
| scalar_data = new uint8_t[16 * sizeof(uint32_t)]; |
| for (int i = 0; i < (16 * sizeof(uint32_t)); ++i) { |
| scalar_data[i] = 0; |
| } |
| for (int i = 0; i < (computeUnit()->wfSize() * 8); ++i) { |
| a_data[i] = 0; |
| x_data[i] = 0; |
| } |
| for (int i = 0; i < (computeUnit()->wfSize() * 4 * sizeof(double)); ++i) { |
| d_data[i] = 0; |
| } |
| time = 0; |
| |
| cu_id = _cu->cu_id; |
| if (_wf) { |
| simdId = _wf->simdId; |
| wfDynId = _wf->wfDynId; |
| kern_id = _wf->kernId; |
| wg_id = _wf->wgId; |
| wfSlotId = _wf->wfSlotId; |
| } else { |
| simdId = -1; |
| wfDynId = -1; |
| kern_id = -1; |
| wg_id = -1; |
| wfSlotId = -1; |
| } |
| |
| |
| DPRINTF(GPUInst, "%s: generating operand info for %d operands\n", |
| disassemble(), getNumOperands()); |
| |
| _staticInst->initDynOperandInfo(wavefront(), computeUnit()); |
| |
| } |
| |
| GPUDynInst::~GPUDynInst() |
| { |
| delete[] d_data; |
| delete[] a_data; |
| delete[] x_data; |
| delete[] scalar_data; |
| delete _staticInst; |
| } |
| |
| void |
| GPUDynInst::execute(GPUDynInstPtr gpuDynInst) |
| { |
| _staticInst->execute(gpuDynInst); |
| } |
| |
| const std::vector<OperandInfo>& |
| GPUDynInst::srcVecRegOperands() const |
| { |
| return _staticInst->srcVecRegOperands(); |
| } |
| |
| const std::vector<OperandInfo>& |
| GPUDynInst::dstVecRegOperands() const |
| { |
| return _staticInst->dstVecRegOperands(); |
| } |
| |
| const std::vector<OperandInfo>& |
| GPUDynInst::srcScalarRegOperands() const |
| { |
| return _staticInst->srcScalarRegOperands(); |
| } |
| |
| const std::vector<OperandInfo>& |
| GPUDynInst::dstScalarRegOperands() const |
| { |
| return _staticInst->dstScalarRegOperands(); |
| } |
| |
| int |
| GPUDynInst::numSrcRegOperands() |
| { |
| return _staticInst->numSrcRegOperands(); |
| } |
| |
| int |
| GPUDynInst::numDstRegOperands() |
| { |
| return _staticInst->numDstRegOperands(); |
| } |
| |
| int |
| GPUDynInst::numSrcVecRegOperands() const |
| { |
| return _staticInst->numSrcVecOperands(); |
| } |
| |
| int |
| GPUDynInst::numDstVecRegOperands() const |
| { |
| return _staticInst->numDstVecOperands(); |
| } |
| |
| int |
| GPUDynInst::maxSrcVecRegOperandSize() |
| { |
| if (maxSrcVecRegOpSize != -1) |
| return maxSrcVecRegOpSize; |
| |
| maxSrcVecRegOpSize = 0; |
| for (const auto& srcVecOp : srcVecRegOperands()) |
| if (srcVecOp.sizeInDWords() > maxSrcVecRegOpSize) |
| maxSrcVecRegOpSize = srcVecOp.sizeInDWords(); |
| |
| return maxSrcVecRegOpSize; |
| } |
| |
| int |
| GPUDynInst::numSrcVecDWords() |
| { |
| return _staticInst->numSrcVecDWords(); |
| } |
| |
| int |
| GPUDynInst::numDstVecDWords() |
| { |
| return _staticInst->numDstVecDWords(); |
| } |
| |
| int |
| GPUDynInst::numSrcScalarRegOperands() const |
| { |
| return _staticInst->numSrcScalarOperands(); |
| } |
| |
| int |
| GPUDynInst::numDstScalarRegOperands() const |
| { |
| return _staticInst->numDstScalarOperands(); |
| } |
| |
| int |
| GPUDynInst::maxSrcScalarRegOperandSize() |
| { |
| if (maxSrcScalarRegOpSize != -1) |
| return maxSrcScalarRegOpSize; |
| |
| maxSrcScalarRegOpSize = 0; |
| for (const auto& srcScOp : srcScalarRegOperands()) |
| if (srcScOp.sizeInDWords() > maxSrcScalarRegOpSize) |
| maxSrcScalarRegOpSize = srcScOp.sizeInDWords(); |
| |
| return maxSrcScalarRegOpSize; |
| } |
| |
| int |
| GPUDynInst::numSrcScalarDWords() |
| { |
| return _staticInst->numSrcScalarDWords(); |
| } |
| |
| int |
| GPUDynInst::numDstScalarDWords() |
| { |
| return _staticInst->numDstScalarDWords(); |
| } |
| |
| int |
| GPUDynInst::maxOperandSize() |
| { |
| return _staticInst->maxOperandSize(); |
| } |
| |
| int |
| GPUDynInst::getNumOperands() const |
| { |
| return _staticInst->getNumOperands(); |
| } |
| |
| bool |
| GPUDynInst::hasSourceVgpr() const |
| { |
| return !srcVecRegOperands().empty(); |
| } |
| |
| bool |
| GPUDynInst::hasDestinationVgpr() const |
| { |
| return !dstVecRegOperands().empty(); |
| } |
| |
| bool |
| GPUDynInst::hasSourceSgpr() const |
| { |
| return !srcScalarRegOperands().empty(); |
| } |
| |
| bool |
| GPUDynInst::hasDestinationSgpr() const |
| { |
| return !dstScalarRegOperands().empty(); |
| } |
| |
| bool |
| GPUDynInst::isOpcode(const std::string& opcodeStr, |
| const std::string& extStr) const |
| { |
| return _staticInst->opcode().find(opcodeStr) != std::string::npos && |
| _staticInst->opcode().find(extStr) != std::string::npos; |
| } |
| |
| bool |
| GPUDynInst::isOpcode(const std::string& opcodeStr) const |
| { |
| return _staticInst->opcode().find(opcodeStr) != std::string::npos; |
| } |
| |
| const std::string& |
| GPUDynInst::disassemble() const |
| { |
| return _staticInst->disassemble(); |
| } |
| |
| InstSeqNum |
| GPUDynInst::seqNum() const |
| { |
| return _seqNum; |
| } |
| |
| enums::StorageClassType |
| GPUDynInst::executedAs() |
| { |
| return _staticInst->executed_as; |
| } |
| |
| // Process a memory instruction and (if necessary) submit timing request |
| void |
| GPUDynInst::initiateAcc(GPUDynInstPtr gpuDynInst) |
| { |
| DPRINTF(GPUMem, "CU%d: WF[%d][%d]: mempacket status bitvector=%#x\n", |
| cu->cu_id, simdId, wfSlotId, exec_mask); |
| |
| _staticInst->initiateAcc(gpuDynInst); |
| } |
| |
| void |
| GPUDynInst::completeAcc(GPUDynInstPtr gpuDynInst) |
| { |
| DPRINTF(GPUMem, "CU%d: WF[%d][%d]: mempacket status bitvector=" |
| "%#x\n complete", |
| cu->cu_id, simdId, wfSlotId, exec_mask); |
| |
| _staticInst->completeAcc(gpuDynInst); |
| } |
| |
| /** |
| * accessor methods for the attributes of |
| * the underlying GPU static instruction |
| */ |
| bool |
| GPUDynInst::isALU() const |
| { |
| return _staticInst->isALU(); |
| } |
| |
| bool |
| GPUDynInst::isBranch() const |
| { |
| return _staticInst->isBranch(); |
| } |
| |
| bool |
| GPUDynInst::isCondBranch() const |
| { |
| return _staticInst->isCondBranch(); |
| } |
| |
| bool |
| GPUDynInst::isNop() const |
| { |
| return _staticInst->isNop(); |
| } |
| |
| bool |
| GPUDynInst::isEndOfKernel() const |
| { |
| return _staticInst->isEndOfKernel(); |
| } |
| |
| bool |
| GPUDynInst::isKernelLaunch() const |
| { |
| return _staticInst->isKernelLaunch(); |
| } |
| |
| bool |
| GPUDynInst::isSDWAInst() const |
| { |
| return _staticInst->isSDWAInst(); |
| } |
| |
| bool |
| GPUDynInst::isDPPInst() const |
| { |
| return _staticInst->isDPPInst(); |
| } |
| |
| bool |
| GPUDynInst::isReturn() const |
| { |
| return _staticInst->isReturn(); |
| } |
| |
| bool |
| GPUDynInst::isUnconditionalJump() const |
| { |
| return _staticInst->isUnconditionalJump(); |
| } |
| |
| bool |
| GPUDynInst::isSpecialOp() const |
| { |
| return _staticInst->isSpecialOp(); |
| } |
| |
| bool |
| GPUDynInst::isWaitcnt() const |
| { |
| return _staticInst->isWaitcnt(); |
| } |
| |
| bool |
| GPUDynInst::isSleep() const |
| { |
| return _staticInst->isSleep(); |
| } |
| |
| bool |
| GPUDynInst::isBarrier() const |
| { |
| return _staticInst->isBarrier(); |
| } |
| |
| bool |
| GPUDynInst::isMemSync() const |
| { |
| return _staticInst->isMemSync(); |
| } |
| |
| bool |
| GPUDynInst::isMemRef() const |
| { |
| return _staticInst->isMemRef(); |
| } |
| |
| bool |
| GPUDynInst::isFlat() const |
| { |
| return _staticInst->isFlat(); |
| } |
| |
| bool |
| GPUDynInst::isLoad() const |
| { |
| return _staticInst->isLoad(); |
| } |
| |
| bool |
| GPUDynInst::isStore() const |
| { |
| return _staticInst->isStore(); |
| } |
| |
| bool |
| GPUDynInst::isAtomic() const |
| { |
| return _staticInst->isAtomic(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicNoRet() const |
| { |
| return _staticInst->isAtomicNoRet(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicRet() const |
| { |
| return _staticInst->isAtomicRet(); |
| } |
| |
| bool |
| GPUDynInst::isVector() const |
| { |
| return !_staticInst->isScalar(); |
| } |
| |
| bool |
| GPUDynInst::isScalar() const |
| { |
| return _staticInst->isScalar(); |
| } |
| |
| bool |
| GPUDynInst::readsSCC() const |
| { |
| return _staticInst->readsSCC(); |
| } |
| |
| bool |
| GPUDynInst::writesSCC() const |
| { |
| return _staticInst->writesSCC(); |
| } |
| |
| bool |
| GPUDynInst::readsVCC() const |
| { |
| for (const auto& srcOp : _staticInst->srcOperands()) |
| if (srcOp.isVcc()) |
| return true; |
| |
| return _staticInst->readsVCC(); |
| } |
| |
| bool |
| GPUDynInst::writesVCC() const |
| { |
| for (const auto& dstOp : _staticInst->dstOperands()) |
| if (dstOp.isVcc()) |
| return true; |
| |
| return _staticInst->writesVCC(); |
| } |
| |
| bool |
| GPUDynInst::readsMode() const |
| { |
| return _staticInst->readsMode(); |
| } |
| |
| bool |
| GPUDynInst::writesMode() const |
| { |
| return _staticInst->writesMode(); |
| } |
| |
| bool |
| GPUDynInst::readsExec() const |
| { |
| return _staticInst->readsEXEC(); |
| } |
| |
| bool |
| GPUDynInst::writesExec() const |
| { |
| return _staticInst->writesEXEC(); |
| } |
| |
| bool |
| GPUDynInst::ignoreExec() const |
| { |
| return _staticInst->ignoreExec(); |
| } |
| |
| bool |
| GPUDynInst::writesExecMask() const |
| { |
| for (const auto& dstOp : _staticInst->dstOperands()) |
| if (dstOp.isExec()) |
| return true; |
| |
| return _staticInst->writesEXEC(); |
| } |
| |
| bool |
| GPUDynInst::readsExecMask() const |
| { |
| for (const auto& srcOp : _staticInst->srcOperands()) |
| if (srcOp.isExec()) |
| return true; |
| |
| return _staticInst->readsEXEC(); |
| } |
| |
| bool |
| GPUDynInst::writesFlatScratch() const |
| { |
| for (const auto& dstScalarOp : dstScalarRegOperands()) |
| if (dstScalarOp.isFlatScratch()) |
| return true; |
| |
| return false; |
| } |
| |
| bool |
| GPUDynInst::readsFlatScratch() const |
| { |
| for (const auto& srcScalarOp : srcScalarRegOperands()) |
| if (srcScalarOp.isFlatScratch()) |
| return true; |
| |
| return false; |
| } |
| |
| bool |
| GPUDynInst::isAtomicAnd() const |
| { |
| return _staticInst->isAtomicAnd(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicOr() const |
| { |
| return _staticInst->isAtomicOr(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicXor() const |
| { |
| return _staticInst->isAtomicXor(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicCAS() const |
| { |
| return _staticInst->isAtomicCAS(); |
| } |
| |
| bool GPUDynInst::isAtomicExch() const |
| { |
| return _staticInst->isAtomicExch(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicAdd() const |
| { |
| return _staticInst->isAtomicAdd(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicSub() const |
| { |
| return _staticInst->isAtomicSub(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicInc() const |
| { |
| return _staticInst->isAtomicInc(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicDec() const |
| { |
| return _staticInst->isAtomicDec(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicMax() const |
| { |
| return _staticInst->isAtomicMax(); |
| } |
| |
| bool |
| GPUDynInst::isAtomicMin() const |
| { |
| return _staticInst->isAtomicMin(); |
| } |
| |
| bool |
| GPUDynInst::isArgLoad() const |
| { |
| return _staticInst->isArgLoad(); |
| } |
| |
| bool |
| GPUDynInst::isGlobalMem() const |
| { |
| return _staticInst->isGlobalMem(); |
| } |
| |
| bool |
| GPUDynInst::isLocalMem() const |
| { |
| return _staticInst->isLocalMem(); |
| } |
| |
| bool |
| GPUDynInst::isArgSeg() const |
| { |
| return _staticInst->isArgSeg(); |
| } |
| |
| bool |
| GPUDynInst::isGlobalSeg() const |
| { |
| return _staticInst->isGlobalSeg(); |
| } |
| |
| bool |
| GPUDynInst::isGroupSeg() const |
| { |
| return _staticInst->isGroupSeg(); |
| } |
| |
| bool |
| GPUDynInst::isKernArgSeg() const |
| { |
| return _staticInst->isKernArgSeg(); |
| } |
| |
| bool |
| GPUDynInst::isPrivateSeg() const |
| { |
| return _staticInst->isPrivateSeg(); |
| } |
| |
| bool |
| GPUDynInst::isReadOnlySeg() const |
| { |
| return _staticInst->isReadOnlySeg(); |
| } |
| |
| bool |
| GPUDynInst::isSpillSeg() const |
| { |
| return _staticInst->isSpillSeg(); |
| } |
| |
| bool |
| GPUDynInst::isGloballyCoherent() const |
| { |
| return _staticInst->isGloballyCoherent(); |
| } |
| |
| bool |
| GPUDynInst::isSystemCoherent() const |
| { |
| return _staticInst->isSystemCoherent(); |
| } |
| |
| bool |
| GPUDynInst::isF16() const |
| { |
| return _staticInst->isF16(); |
| } |
| |
| bool |
| GPUDynInst::isF32() const |
| { |
| return _staticInst->isF32(); |
| } |
| |
| bool |
| GPUDynInst::isF64() const |
| { |
| return _staticInst->isF64(); |
| } |
| |
| bool |
| GPUDynInst::isFMA() const |
| { |
| return _staticInst->isFMA(); |
| } |
| |
| bool |
| GPUDynInst::isMAC() const |
| { |
| return _staticInst->isMAC(); |
| } |
| |
| bool |
| GPUDynInst::isMAD() const |
| { |
| return _staticInst->isMAD(); |
| } |
| |
| void |
| GPUDynInst::doApertureCheck(const VectorMask &mask) |
| { |
| assert(mask.any()); |
| // find the segment of the first active address, after |
| // that we check that all other active addresses also |
| // fall within the same APE |
| for (int lane = 0; lane < computeUnit()->wfSize(); ++lane) { |
| if (mask[lane]) { |
| if (computeUnit()->shader->isLdsApe(addr[lane])) { |
| // group segment |
| staticInstruction()->executed_as = enums::SC_GROUP; |
| break; |
| } else if (computeUnit()->shader->isScratchApe(addr[lane])) { |
| // private segment |
| staticInstruction()->executed_as = enums::SC_PRIVATE; |
| break; |
| } else if (computeUnit()->shader->isGpuVmApe(addr[lane])) { |
| // we won't support GPUVM |
| fatal("flat access is in GPUVM APE\n"); |
| } else if (bits(addr[lane], 63, 47) != 0x1FFFF && |
| bits(addr[lane], 63, 47)) { |
| // we are in the "hole", this is a memory violation |
| fatal("flat access at addr %#x has a memory violation\n", |
| addr[lane]); |
| } else { |
| // global memory segment |
| staticInstruction()->executed_as = enums::SC_GLOBAL; |
| break; |
| } |
| } |
| } |
| |
| // we should have found the segment |
| assert(executedAs() != enums::SC_NONE); |
| |
| // flat accesses should not straddle multiple APEs so we |
| // must check that all addresses fall within the same APE |
| if (executedAs() == enums::SC_GROUP) { |
| for (int lane = 0; lane < computeUnit()->wfSize(); ++lane) { |
| if (mask[lane]) { |
| // if the first valid addr we found above was LDS, |
| // all the rest should be |
| assert(computeUnit()->shader->isLdsApe(addr[lane])); |
| } |
| } |
| } else if (executedAs() == enums::SC_PRIVATE) { |
| for (int lane = 0; lane < computeUnit()->wfSize(); ++lane) { |
| if (mask[lane]) { |
| // if the first valid addr we found above was private, |
| // all the rest should be |
| assert(computeUnit()->shader->isScratchApe(addr[lane])); |
| } |
| } |
| } else { |
| for (int lane = 0; lane < computeUnit()->wfSize(); ++lane) { |
| if (mask[lane]) { |
| // if the first valid addr we found above was global, |
| // all the rest should be. because we don't have an |
| // explicit range of the global segment, we just make |
| // sure that the address fall in no other APE and that |
| // it is not a memory violation |
| assert(!computeUnit()->shader->isLdsApe(addr[lane])); |
| assert(!computeUnit()->shader->isScratchApe(addr[lane])); |
| assert(!computeUnit()->shader->isGpuVmApe(addr[lane])); |
| assert(!(bits(addr[lane], 63, 47) != 0x1FFFF |
| && bits(addr[lane], 63, 47))); |
| } |
| } |
| } |
| } |
| |
| void |
| GPUDynInst::resolveFlatSegment(const VectorMask &mask) |
| { |
| doApertureCheck(mask); |
| |
| |
| // Now that we know the aperature, do the following: |
| // 1. Transform the flat address to its segmented equivalent. |
| // 2. Set the execUnitId based an the aperture check. |
| // 3. Decrement any extra resources that were reserved. Other |
| // resources are released as normal, below. |
| if (executedAs() == enums::SC_GLOBAL) { |
| // no transormation for global segment |
| wavefront()->execUnitId = wavefront()->flatGmUnitId; |
| if (isLoad()) { |
| wavefront()->rdLmReqsInPipe--; |
| } else if (isStore()) { |
| wavefront()->wrLmReqsInPipe--; |
| } else if (isAtomic() || isMemSync()) { |
| wavefront()->wrLmReqsInPipe--; |
| wavefront()->rdLmReqsInPipe--; |
| } else { |
| panic("Invalid memory operation!\n"); |
| } |
| } else if (executedAs() == enums::SC_GROUP) { |
| for (int lane = 0; lane < wavefront()->computeUnit->wfSize(); ++lane) { |
| if (mask[lane]) { |
| // flat address calculation goes here. |
| // addr[lane] = segmented address |
| panic("Flat group memory operation is unimplemented!\n"); |
| } |
| } |
| wavefront()->execUnitId = wavefront()->flatLmUnitId; |
| wavefront()->decVMemInstsIssued(); |
| if (isLoad()) { |
| wavefront()->rdGmReqsInPipe--; |
| } else if (isStore()) { |
| wavefront()->wrGmReqsInPipe--; |
| } else if (isAtomic() || isMemSync()) { |
| wavefront()->rdGmReqsInPipe--; |
| wavefront()->wrGmReqsInPipe--; |
| } else { |
| panic("Invalid memory operation!\n"); |
| } |
| } else if (executedAs() == enums::SC_PRIVATE) { |
| /** |
| * Flat instructions may resolve to the private segment (scratch), |
| * which is backed by main memory and provides per-lane scratch |
| * memory. Flat addressing uses apertures - registers that specify |
| * the address range in the VA space where LDS/private memory is |
| * mapped. The value of which is set by the kernel mode driver. |
| * These apertures use addresses that are not used by x86 CPUs. |
| * When the address of a Flat operation falls into one of the |
| * apertures, the Flat operation is redirected to either LDS or |
| * to the private memory segment. |
| * |
| * For private memory the SW runtime will allocate some space in |
| * the VA space for each AQL queue. The base address of which is |
| * stored in scalar registers per the AMD GPU ABI. The amd_queue_t |
| * scratch_backing_memory_location provides the base address in |
| * memory for the queue's private segment. Various other fields |
| * loaded into register state during kernel launch specify per-WF |
| * and per-work-item offsets so that individual lanes may access |
| * their private segment allocation. |
| * |
| * For more details about flat addressing see: |
| * http://rocm-documentation.readthedocs.io/en/latest/ |
| * ROCm_Compiler_SDK/ROCm-Native-ISA.html#flat-scratch |
| * |
| * https://github.com/ROCm-Developer-Tools/ |
| * ROCm-ComputeABI-Doc/blob/master/AMDGPU-ABI.md |
| * #flat-addressing |
| */ |
| |
| uint32_t numSgprs = wavefront()->maxSgprs; |
| uint32_t physSgprIdx = |
| wavefront()->computeUnit->registerManager->mapSgpr(wavefront(), |
| numSgprs - 3); |
| uint32_t offset = |
| wavefront()->computeUnit->srf[simdId]->read(physSgprIdx); |
| physSgprIdx = |
| wavefront()->computeUnit->registerManager->mapSgpr(wavefront(), |
| numSgprs - 4); |
| uint32_t size = |
| wavefront()->computeUnit->srf[simdId]->read(physSgprIdx); |
| for (int lane = 0; lane < wavefront()->computeUnit->wfSize(); ++lane) { |
| if (mask[lane]) { |
| addr[lane] = addr[lane] + lane * size + offset + |
| wavefront()->computeUnit->shader->getHiddenPrivateBase() - |
| wavefront()->computeUnit->shader->getScratchBase(); |
| } |
| } |
| wavefront()->execUnitId = wavefront()->flatLmUnitId; |
| wavefront()->decLGKMInstsIssued(); |
| if (isLoad()) { |
| wavefront()->rdGmReqsInPipe--; |
| } else if (isStore()) { |
| wavefront()->wrGmReqsInPipe--; |
| } else if (isAtomic() || isMemSync()) { |
| wavefront()->rdGmReqsInPipe--; |
| wavefront()->wrGmReqsInPipe--; |
| } else { |
| panic("Invalid memory operation!\n"); |
| } |
| } else { |
| for (int lane = 0; lane < wavefront()->computeUnit->wfSize(); ++lane) { |
| if (mask[lane]) { |
| panic("flat addr %#llx maps to bad segment %d\n", |
| addr[lane], executedAs()); |
| } |
| } |
| } |
| } |
| |
| TheGpuISA::ScalarRegU32 |
| GPUDynInst::srcLiteral() const |
| { |
| return _staticInst->srcLiteral(); |
| } |
| |
| void |
| GPUDynInst::updateStats() |
| { |
| if (_staticInst->isLocalMem()) { |
| // access to LDS (shared) memory |
| cu->stats.dynamicLMemInstrCnt++; |
| } else if (_staticInst->isFlat()) { |
| cu->stats.dynamicFlatMemInstrCnt++; |
| } else { |
| // access to global memory |
| |
| // update PageDivergence histogram |
| int number_pages_touched = cu->pagesTouched.size(); |
| assert(number_pages_touched); |
| cu->stats.pageDivergenceDist.sample(number_pages_touched); |
| |
| std::pair<ComputeUnit::pageDataStruct::iterator, bool> ret; |
| |
| for (auto it : cu->pagesTouched) { |
| // see if this page has been touched before. if not, this also |
| // inserts the page into the table. |
| ret = cu->pageAccesses |
| .insert(ComputeUnit::pageDataStruct::value_type(it.first, |
| std::make_pair(1, it.second))); |
| |
| // if yes, then update the stats |
| if (!ret.second) { |
| ret.first->second.first++; |
| ret.first->second.second += it.second; |
| } |
| } |
| |
| cu->pagesTouched.clear(); |
| |
| // total number of memory instructions (dynamic) |
| // Atomics are counted as a single memory instruction. |
| // this is # memory instructions per wavefronts, not per workitem |
| cu->stats.dynamicGMemInstrCnt++; |
| } |
| } |
| |
| void |
| GPUDynInst::profileRoundTripTime(Tick currentTime, int hopId) |
| { |
| // Only take the first measurement in the case of coalescing |
| if (roundTripTime.size() > hopId) |
| return; |
| |
| roundTripTime.push_back(currentTime); |
| } |
| |
| void |
| GPUDynInst::profileLineAddressTime(Addr addr, Tick currentTime, int hopId) |
| { |
| if (lineAddressTime.count(addr)) { |
| if (lineAddressTime[addr].size() > hopId) { |
| return; |
| } |
| |
| lineAddressTime[addr].push_back(currentTime); |
| } else if (hopId == 0) { |
| auto addressTimeVec = std::vector<Tick> { currentTime }; |
| lineAddressTime.insert(std::make_pair(addr, addressTimeVec)); |
| } |
| } |
| |
| } // namespace gem5 |