| /* | |
| * Copyright (c) 2011-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: Lisa Hsu | |
| */ | |
| #ifndef __WAVEFRONT_HH__ | |
| #define __WAVEFRONT_HH__ | |
| #include <cassert> | |
| #include <deque> | |
| #include <memory> | |
| #include <stack> | |
| #include <vector> | |
| #include "arch/gpu_isa.hh" | |
| #include "base/logging.hh" | |
| #include "base/types.hh" | |
| #include "config/the_gpu_isa.hh" | |
| #include "gpu-compute/condition_register_state.hh" | |
| #include "gpu-compute/lds_state.hh" | |
| #include "gpu-compute/misc.hh" | |
| #include "gpu-compute/ndrange.hh" | |
| #include "params/Wavefront.hh" | |
| #include "sim/sim_object.hh" | |
| static const int MAX_NUM_INSTS_PER_WF = 12; | |
| /** | |
| * A reconvergence stack entry conveys the necessary state to implement | |
| * control flow divergence. | |
| */ | |
| struct ReconvergenceStackEntry { | |
| /** | |
| * PC of current instruction. | |
| */ | |
| uint32_t pc; | |
| /** | |
| * PC of the immediate post-dominator instruction, i.e., the value of | |
| * @a pc for the first instruction that will be executed by the wavefront | |
| * when a reconvergence point is reached. | |
| */ | |
| uint32_t rpc; | |
| /** | |
| * Execution mask. | |
| */ | |
| VectorMask execMask; | |
| }; | |
| /* | |
| * Arguments for the hsail opcode call, are user defined and variable length. | |
| * The hardware/finalizer can support arguments in hardware or use memory to | |
| * pass arguments. For now, let's assume that an unlimited number of arguments | |
| * are supported in hardware (the compiler inlines functions whenver it can | |
| * anyways, so unless someone is interested in the implications of linking/ | |
| * library functions, I think this is a reasonable assumption given the typical | |
| * size of an OpenCL kernel). | |
| * | |
| * Note that call args are different than kernel arguments: | |
| * * All work-items in a kernel refer the same set of kernel arguments | |
| * * Each work-item has it's on set of call args. So a call argument at | |
| * address 0x4 is different for work-item 0 and work-item 1. | |
| * | |
| * Ok, the table below shows an example of how we organize the call arguments in | |
| * the CallArgMem class. | |
| * | |
| * int foo(int arg1, double arg2) | |
| * ___________________________________________________ | |
| * | 0: return.0 | 4: return.1 | ... | 252: return.63 | | |
| * |---------------------------------------------------| | |
| * | 256: arg1.0 | 260: arg1.1 | ... | 508: arg1.63 | | |
| * |---------------------------------------------------| | |
| * | 512: arg2.0 | 520: arg2.1 | ... | 1016: arg2.63 | | |
| * ___________________________________________________ | |
| */ | |
| class CallArgMem | |
| { | |
| public: | |
| // pointer to buffer for storing function arguments | |
| uint8_t *mem; | |
| int wfSize; | |
| // size of function args | |
| int funcArgsSizePerItem; | |
| template<typename CType> | |
| int | |
| getLaneOffset(int lane, int addr) | |
| { | |
| return addr * wfSize + sizeof(CType) * lane; | |
| } | |
| CallArgMem(int func_args_size_per_item, int wf_size) | |
| : wfSize(wf_size), funcArgsSizePerItem(func_args_size_per_item) | |
| { | |
| mem = (uint8_t*)malloc(funcArgsSizePerItem * wfSize); | |
| } | |
| ~CallArgMem() | |
| { | |
| free(mem); | |
| } | |
| template<typename CType> | |
| uint8_t* | |
| getLaneAddr(int lane, int addr) | |
| { | |
| return mem + getLaneOffset<CType>(lane, addr); | |
| } | |
| template<typename CType> | |
| void | |
| setLaneAddr(int lane, int addr, CType val) | |
| { | |
| *((CType*)(mem + getLaneOffset<CType>(lane, addr))) = val; | |
| } | |
| }; | |
| class Wavefront : public SimObject | |
| { | |
| public: | |
| enum itype_e {I_ALU,I_GLOBAL,I_SHARED,I_FLAT,I_PRIVATE}; | |
| enum status_e {S_STOPPED,S_RETURNING,S_RUNNING}; | |
| // Base pointer for array of instruction pointers | |
| uint64_t basePtr; | |
| uint32_t oldBarrierCnt; | |
| uint32_t barrierCnt; | |
| uint32_t barrierId; | |
| uint32_t barrierSlots; | |
| status_e status; | |
| // HW slot id where the WF is mapped to inside a SIMD unit | |
| int wfSlotId; | |
| int kernId; | |
| // SIMD unit where the WV has been scheduled | |
| int simdId; | |
| // pointer to parent CU | |
| ComputeUnit *computeUnit; | |
| std::deque<GPUDynInstPtr> instructionBuffer; | |
| bool pendingFetch; | |
| bool dropFetch; | |
| // Condition Register State (for HSAIL simulations only) | |
| class ConditionRegisterState *condRegState; | |
| // number of single precision VGPRs required by WF | |
| uint32_t maxSpVgprs; | |
| // number of double precision VGPRs required by WF | |
| uint32_t maxDpVgprs; | |
| // map virtual to physical vector register | |
| uint32_t remap(uint32_t vgprIndex, uint32_t size, uint8_t mode=0); | |
| void resizeRegFiles(int num_cregs, int num_sregs, int num_dregs); | |
| bool isGmInstruction(GPUDynInstPtr ii); | |
| bool isLmInstruction(GPUDynInstPtr ii); | |
| bool isOldestInstGMem(); | |
| bool isOldestInstLMem(); | |
| bool isOldestInstPrivMem(); | |
| bool isOldestInstFlatMem(); | |
| bool isOldestInstALU(); | |
| bool isOldestInstBarrier(); | |
| // used for passing spill address to DDInstGPU | |
| std::vector<Addr> lastAddr; | |
| std::vector<uint32_t> workItemId[3]; | |
| std::vector<uint32_t> workItemFlatId; | |
| /* kernel launch parameters */ | |
| uint32_t workGroupId[3]; | |
| uint32_t workGroupSz[3]; | |
| uint32_t gridSz[3]; | |
| uint32_t wgId; | |
| uint32_t wgSz; | |
| /* the actual WG size can differ than the maximum size */ | |
| uint32_t actualWgSz[3]; | |
| uint32_t actualWgSzTotal; | |
| void computeActualWgSz(NDRange *ndr); | |
| // wavefront id within a workgroup | |
| uint32_t wfId; | |
| uint32_t maxDynWaveId; | |
| uint32_t dispatchId; | |
| // outstanding global+local memory requests | |
| uint32_t outstandingReqs; | |
| // memory requests between scoreboard | |
| // and execute stage not yet executed | |
| uint32_t memReqsInPipe; | |
| // outstanding global memory write requests | |
| uint32_t outstandingReqsWrGm; | |
| // outstanding local memory write requests | |
| uint32_t outstandingReqsWrLm; | |
| // outstanding global memory read requests | |
| uint32_t outstandingReqsRdGm; | |
| // outstanding local memory read requests | |
| uint32_t outstandingReqsRdLm; | |
| uint32_t rdLmReqsInPipe; | |
| uint32_t rdGmReqsInPipe; | |
| uint32_t wrLmReqsInPipe; | |
| uint32_t wrGmReqsInPipe; | |
| int memTraceBusy; | |
| uint64_t lastTrace; | |
| // number of vector registers reserved by WF | |
| int reservedVectorRegs; | |
| // Index into the Vector Register File's namespace where the WF's registers | |
| // will live while the WF is executed | |
| uint32_t startVgprIndex; | |
| // Old value of destination gpr (for trace) | |
| std::vector<uint32_t> oldVgpr; | |
| // Id of destination gpr (for trace) | |
| uint32_t oldVgprId; | |
| // Tick count of last old_vgpr copy | |
| uint64_t oldVgprTcnt; | |
| // Old value of destination gpr (for trace) | |
| std::vector<uint64_t> oldDgpr; | |
| // Id of destination gpr (for trace) | |
| uint32_t oldDgprId; | |
| // Tick count of last old_vgpr copy | |
| uint64_t oldDgprTcnt; | |
| // Execution mask at wavefront start | |
| VectorMask initMask; | |
| // number of barriers this WF has joined | |
| std::vector<int> barCnt; | |
| int maxBarCnt; | |
| // Flag to stall a wave on barrier | |
| bool stalledAtBarrier; | |
| // a pointer to the fraction of the LDS allocated | |
| // to this workgroup (thus this wavefront) | |
| LdsChunk *ldsChunk; | |
| // A pointer to the spill area | |
| Addr spillBase; | |
| // The size of the spill area | |
| uint32_t spillSizePerItem; | |
| // The vector width of the spill area | |
| uint32_t spillWidth; | |
| // A pointer to the private memory area | |
| Addr privBase; | |
| // The size of the private memory area | |
| uint32_t privSizePerItem; | |
| // A pointer ot the read-only memory area | |
| Addr roBase; | |
| // size of the read-only memory area | |
| uint32_t roSize; | |
| // pointer to buffer for storing kernel arguments | |
| uint8_t *kernelArgs; | |
| // unique WF id over all WFs executed across all CUs | |
| uint64_t wfDynId; | |
| // number of times instruction issue for this wavefront is blocked | |
| // due to VRF port availability | |
| Stats::Scalar numTimesBlockedDueVrfPortAvail; | |
| // number of times an instruction of a WF is blocked from being issued | |
| // due to WAR and WAW dependencies | |
| Stats::Scalar numTimesBlockedDueWAXDependencies; | |
| // number of times an instruction of a WF is blocked from being issued | |
| // due to WAR and WAW dependencies | |
| Stats::Scalar numTimesBlockedDueRAWDependencies; | |
| // distribution of executed instructions based on their register | |
| // operands; this is used to highlight the load on the VRF | |
| Stats::Distribution srcRegOpDist; | |
| Stats::Distribution dstRegOpDist; | |
| // Functions to operate on call argument memory | |
| // argument memory for hsail call instruction | |
| CallArgMem *callArgMem; | |
| void | |
| initCallArgMem(int func_args_size_per_item, int wf_size) | |
| { | |
| callArgMem = new CallArgMem(func_args_size_per_item, wf_size); | |
| } | |
| template<typename CType> | |
| CType | |
| readCallArgMem(int lane, int addr) | |
| { | |
| return *((CType*)(callArgMem->getLaneAddr<CType>(lane, addr))); | |
| } | |
| template<typename CType> | |
| void | |
| writeCallArgMem(int lane, int addr, CType val) | |
| { | |
| callArgMem->setLaneAddr<CType>(lane, addr, val); | |
| } | |
| typedef WavefrontParams Params; | |
| Wavefront(const Params *p); | |
| ~Wavefront(); | |
| virtual void init(); | |
| void | |
| setParent(ComputeUnit *cu) | |
| { | |
| computeUnit = cu; | |
| } | |
| void start(uint64_t _wfDynId, uint64_t _base_ptr); | |
| void exec(); | |
| void updateResources(); | |
| int ready(itype_e type); | |
| bool instructionBufferHasBranch(); | |
| void regStats(); | |
| VectorMask getPred() { return execMask() & initMask; } | |
| bool waitingAtBarrier(int lane); | |
| void pushToReconvergenceStack(uint32_t pc, uint32_t rpc, | |
| const VectorMask& exec_mask); | |
| void popFromReconvergenceStack(); | |
| uint32_t pc() const; | |
| uint32_t rpc() const; | |
| VectorMask execMask() const; | |
| bool execMask(int lane) const; | |
| void pc(uint32_t new_pc); | |
| void discardFetch(); | |
| /** | |
| * Returns the size of the static hardware context of a particular wavefront | |
| * This should be updated everytime the context is changed | |
| */ | |
| uint32_t getStaticContextSize() const; | |
| /** | |
| * Returns the hardware context as a stream of bytes | |
| * This method is designed for HSAIL execution | |
| */ | |
| void getContext(const void *out); | |
| /** | |
| * Sets the hardware context fromt a stream of bytes | |
| * This method is designed for HSAIL execution | |
| */ | |
| void setContext(const void *in); | |
| TheGpuISA::GPUISA& | |
| gpuISA() | |
| { | |
| return _gpuISA; | |
| } | |
| private: | |
| TheGpuISA::GPUISA _gpuISA; | |
| /** | |
| * Stack containing Control Flow Graph nodes (i.e., kernel instructions) | |
| * to be visited by the wavefront, and the associated execution masks. The | |
| * reconvergence stack grows every time the wavefront reaches a divergence | |
| * point (branch instruction), and shrinks every time the wavefront | |
| * reaches a reconvergence point (immediate post-dominator instruction). | |
| */ | |
| std::deque<std::unique_ptr<ReconvergenceStackEntry>> reconvergenceStack; | |
| }; | |
| #endif // __WAVEFRONT_HH__ |