dispatcher.cc revision 11435
19651SAndreas.Sandberg@ARM.com/*
29651SAndreas.Sandberg@ARM.com * Copyright (c) 2011-2015 Advanced Micro Devices, Inc.
39651SAndreas.Sandberg@ARM.com * All rights reserved.
49651SAndreas.Sandberg@ARM.com *
59651SAndreas.Sandberg@ARM.com * For use for simulation and test purposes only
69651SAndreas.Sandberg@ARM.com *
79651SAndreas.Sandberg@ARM.com * Redistribution and use in source and binary forms, with or without
89651SAndreas.Sandberg@ARM.com * modification, are permitted provided that the following conditions are met:
99651SAndreas.Sandberg@ARM.com *
109651SAndreas.Sandberg@ARM.com * 1. Redistributions of source code must retain the above copyright notice,
119651SAndreas.Sandberg@ARM.com * this list of conditions and the following disclaimer.
129651SAndreas.Sandberg@ARM.com *
139651SAndreas.Sandberg@ARM.com * 2. Redistributions in binary form must reproduce the above copyright notice,
149651SAndreas.Sandberg@ARM.com * this list of conditions and the following disclaimer in the documentation
159651SAndreas.Sandberg@ARM.com * and/or other materials provided with the distribution.
169651SAndreas.Sandberg@ARM.com *
179651SAndreas.Sandberg@ARM.com * 3. Neither the name of the copyright holder nor the names of its contributors
189651SAndreas.Sandberg@ARM.com * may be used to endorse or promote products derived from this software
199651SAndreas.Sandberg@ARM.com * without specific prior written permission.
209651SAndreas.Sandberg@ARM.com *
219651SAndreas.Sandberg@ARM.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
229651SAndreas.Sandberg@ARM.com * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
239651SAndreas.Sandberg@ARM.com * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
249651SAndreas.Sandberg@ARM.com * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
259651SAndreas.Sandberg@ARM.com * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
269651SAndreas.Sandberg@ARM.com * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
279651SAndreas.Sandberg@ARM.com * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
289651SAndreas.Sandberg@ARM.com * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
299651SAndreas.Sandberg@ARM.com * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
309651SAndreas.Sandberg@ARM.com * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
319651SAndreas.Sandberg@ARM.com * POSSIBILITY OF SUCH DAMAGE.
329651SAndreas.Sandberg@ARM.com *
339651SAndreas.Sandberg@ARM.com * Author: Brad Beckmann, Marc Orr
349651SAndreas.Sandberg@ARM.com */
359651SAndreas.Sandberg@ARM.com
369651SAndreas.Sandberg@ARM.com
379651SAndreas.Sandberg@ARM.com#include "gpu-compute/dispatcher.hh"
389651SAndreas.Sandberg@ARM.com
399651SAndreas.Sandberg@ARM.com#include "cpu/base.hh"
4011793Sbrandon.potter@amd.com#include "debug/GPUDisp.hh"
419651SAndreas.Sandberg@ARM.com#include "gpu-compute/cl_driver.hh"
429651SAndreas.Sandberg@ARM.com#include "gpu-compute/cl_event.hh"
439651SAndreas.Sandberg@ARM.com#include "gpu-compute/shader.hh"
449651SAndreas.Sandberg@ARM.com#include "gpu-compute/wavefront.hh"
459651SAndreas.Sandberg@ARM.com#include "mem/packet_access.hh"
469651SAndreas.Sandberg@ARM.com
479651SAndreas.Sandberg@ARM.comGpuDispatcher *GpuDispatcher::instance = nullptr;
489651SAndreas.Sandberg@ARM.com
499651SAndreas.Sandberg@ARM.comGpuDispatcher::GpuDispatcher(const Params *p)
509651SAndreas.Sandberg@ARM.com    : DmaDevice(p), _masterId(p->system->getMasterId(name() + ".disp")),
519651SAndreas.Sandberg@ARM.com      pioAddr(p->pio_addr), pioSize(4096), pioDelay(p->pio_latency),
529651SAndreas.Sandberg@ARM.com      dispatchCount(0), dispatchActive(false), cpu(p->cpu),
5312334Sgabeblack@google.com      shader(p->shader_pointer), driver(p->cl_driver), tickEvent(this)
549651SAndreas.Sandberg@ARM.com{
559651SAndreas.Sandberg@ARM.com    shader->handshake(this);
569651SAndreas.Sandberg@ARM.com    driver->handshake(this);
579651SAndreas.Sandberg@ARM.com
589651SAndreas.Sandberg@ARM.com    ndRange.wg_disp_rem = false;
599651SAndreas.Sandberg@ARM.com    ndRange.globalWgId = 0;
609651SAndreas.Sandberg@ARM.com
619651SAndreas.Sandberg@ARM.com    schedule(&tickEvent, 0);
629651SAndreas.Sandberg@ARM.com
639651SAndreas.Sandberg@ARM.com    // translation port for the dispatcher
649651SAndreas.Sandberg@ARM.com    tlbPort = new TLBPort(csprintf("%s-port%d", name()), this);
659651SAndreas.Sandberg@ARM.com
669651SAndreas.Sandberg@ARM.com    num_kernelLaunched
679651SAndreas.Sandberg@ARM.com    .name(name() + ".num_kernel_launched")
689651SAndreas.Sandberg@ARM.com    .desc("number of kernel launched")
699651SAndreas.Sandberg@ARM.com    ;
709651SAndreas.Sandberg@ARM.com}
719651SAndreas.Sandberg@ARM.com
729651SAndreas.Sandberg@ARM.comGpuDispatcher *GpuDispatcherParams::create()
739651SAndreas.Sandberg@ARM.com{
749651SAndreas.Sandberg@ARM.com    GpuDispatcher *dispatcher = new GpuDispatcher(this);
759651SAndreas.Sandberg@ARM.com    GpuDispatcher::setInstance(dispatcher);
769651SAndreas.Sandberg@ARM.com
779651SAndreas.Sandberg@ARM.com    return GpuDispatcher::getInstance();
789651SAndreas.Sandberg@ARM.com}
799651SAndreas.Sandberg@ARM.com
809651SAndreas.Sandberg@ARM.comvoid
819651SAndreas.Sandberg@ARM.comGpuDispatcher::serialize(CheckpointOut &cp) const
829651SAndreas.Sandberg@ARM.com{
839651SAndreas.Sandberg@ARM.com    Tick event_tick = 0;
849651SAndreas.Sandberg@ARM.com
859651SAndreas.Sandberg@ARM.com    if (ndRange.wg_disp_rem)
869651SAndreas.Sandberg@ARM.com        fatal("Checkpointing not supported during active workgroup execution");
879651SAndreas.Sandberg@ARM.com
889651SAndreas.Sandberg@ARM.com    if (tickEvent.scheduled())
899651SAndreas.Sandberg@ARM.com        event_tick = tickEvent.when();
909651SAndreas.Sandberg@ARM.com
919651SAndreas.Sandberg@ARM.com    SERIALIZE_SCALAR(event_tick);
929651SAndreas.Sandberg@ARM.com
939651SAndreas.Sandberg@ARM.com}
949651SAndreas.Sandberg@ARM.com
959651SAndreas.Sandberg@ARM.comvoid
969651SAndreas.Sandberg@ARM.comGpuDispatcher::unserialize(CheckpointIn &cp)
979651SAndreas.Sandberg@ARM.com{
989651SAndreas.Sandberg@ARM.com    Tick event_tick;
999651SAndreas.Sandberg@ARM.com
1009651SAndreas.Sandberg@ARM.com    if (tickEvent.scheduled())
1019651SAndreas.Sandberg@ARM.com        deschedule(&tickEvent);
1029651SAndreas.Sandberg@ARM.com
1039651SAndreas.Sandberg@ARM.com    UNSERIALIZE_SCALAR(event_tick);
1049651SAndreas.Sandberg@ARM.com
1059651SAndreas.Sandberg@ARM.com    if (event_tick)
1069651SAndreas.Sandberg@ARM.com        schedule(&tickEvent, event_tick);
1079651SAndreas.Sandberg@ARM.com}
1089651SAndreas.Sandberg@ARM.com
1099651SAndreas.Sandberg@ARM.comAddrRangeList
1109651SAndreas.Sandberg@ARM.comGpuDispatcher::getAddrRanges() const
1119651SAndreas.Sandberg@ARM.com{
1129651SAndreas.Sandberg@ARM.com    AddrRangeList ranges;
1139651SAndreas.Sandberg@ARM.com
1149651SAndreas.Sandberg@ARM.com    DPRINTF(GPUDisp, "dispatcher registering addr range at %#x size %#x\n",
1159651SAndreas.Sandberg@ARM.com            pioAddr, pioSize);
1169651SAndreas.Sandberg@ARM.com
1179651SAndreas.Sandberg@ARM.com    ranges.push_back(RangeSize(pioAddr, pioSize));
1189651SAndreas.Sandberg@ARM.com
1199651SAndreas.Sandberg@ARM.com    return ranges;
1209651SAndreas.Sandberg@ARM.com}
1219651SAndreas.Sandberg@ARM.com
1229651SAndreas.Sandberg@ARM.comTick
1239651SAndreas.Sandberg@ARM.comGpuDispatcher::read(PacketPtr pkt)
1249651SAndreas.Sandberg@ARM.com{
1259651SAndreas.Sandberg@ARM.com    assert(pkt->getAddr() >= pioAddr);
1269651SAndreas.Sandberg@ARM.com    assert(pkt->getAddr() < pioAddr + pioSize);
1279651SAndreas.Sandberg@ARM.com
1289651SAndreas.Sandberg@ARM.com    int offset = pkt->getAddr() - pioAddr;
1299651SAndreas.Sandberg@ARM.com    pkt->allocate();
1309651SAndreas.Sandberg@ARM.com
1319651SAndreas.Sandberg@ARM.com    DPRINTF(GPUDisp, " read register %#x size=%d\n", offset, pkt->getSize());
1329651SAndreas.Sandberg@ARM.com
1339651SAndreas.Sandberg@ARM.com    if (offset < 8) {
1349651SAndreas.Sandberg@ARM.com        assert(!offset);
1359651SAndreas.Sandberg@ARM.com        assert(pkt->getSize() == 8);
1369651SAndreas.Sandberg@ARM.com
1379651SAndreas.Sandberg@ARM.com        uint64_t retval = dispatchActive;
1389651SAndreas.Sandberg@ARM.com        pkt->set(retval);
1399651SAndreas.Sandberg@ARM.com    } else {
1409651SAndreas.Sandberg@ARM.com        offset -= 8;
1419651SAndreas.Sandberg@ARM.com        assert(offset + pkt->getSize() < sizeof(HsaQueueEntry));
1429651SAndreas.Sandberg@ARM.com        char *curTaskPtr = (char*)&curTask;
1439651SAndreas.Sandberg@ARM.com
1449651SAndreas.Sandberg@ARM.com        memcpy(pkt->getPtr<const void*>(), curTaskPtr + offset, pkt->getSize());
1459651SAndreas.Sandberg@ARM.com    }
1469651SAndreas.Sandberg@ARM.com
1479651SAndreas.Sandberg@ARM.com    pkt->makeAtomicResponse();
1489651SAndreas.Sandberg@ARM.com
1499651SAndreas.Sandberg@ARM.com    return pioDelay;
1509651SAndreas.Sandberg@ARM.com}
1519651SAndreas.Sandberg@ARM.com
1529651SAndreas.Sandberg@ARM.comTick
1539651SAndreas.Sandberg@ARM.comGpuDispatcher::write(PacketPtr pkt)
1549651SAndreas.Sandberg@ARM.com{
1559651SAndreas.Sandberg@ARM.com    assert(pkt->getAddr() >= pioAddr);
1569651SAndreas.Sandberg@ARM.com    assert(pkt->getAddr() < pioAddr + pioSize);
1579651SAndreas.Sandberg@ARM.com
1589651SAndreas.Sandberg@ARM.com    int offset = pkt->getAddr() - pioAddr;
1599651SAndreas.Sandberg@ARM.com
1609651SAndreas.Sandberg@ARM.com#if TRACING_ON
1619651SAndreas.Sandberg@ARM.com    uint64_t data_val = 0;
1629651SAndreas.Sandberg@ARM.com
1639651SAndreas.Sandberg@ARM.com    switch (pkt->getSize()) {
1649651SAndreas.Sandberg@ARM.com      case 1:
1659651SAndreas.Sandberg@ARM.com        data_val = pkt->get<uint8_t>();
1669651SAndreas.Sandberg@ARM.com        break;
1679651SAndreas.Sandberg@ARM.com      case 2:
1689651SAndreas.Sandberg@ARM.com        data_val = pkt->get<uint16_t>();
1699651SAndreas.Sandberg@ARM.com        break;
1709651SAndreas.Sandberg@ARM.com      case 4:
1719651SAndreas.Sandberg@ARM.com        data_val = pkt->get<uint32_t>();
17213787Sgambordr@oregonstate.edu        break;
17313787Sgambordr@oregonstate.edu      case 8:
17413787Sgambordr@oregonstate.edu        data_val = pkt->get<uint64_t>();
17513787Sgambordr@oregonstate.edu        break;
17613787Sgambordr@oregonstate.edu      default:
17713787Sgambordr@oregonstate.edu        DPRINTF(GPUDisp, "bad size %d\n", pkt->getSize());
17813787Sgambordr@oregonstate.edu    }
17913787Sgambordr@oregonstate.edu
18013787Sgambordr@oregonstate.edu    DPRINTF(GPUDisp, "write register %#x value %#x size=%d\n", offset, data_val,
18113787Sgambordr@oregonstate.edu            pkt->getSize());
18213787Sgambordr@oregonstate.edu#endif
18313787Sgambordr@oregonstate.edu    if (!offset) {
1849651SAndreas.Sandberg@ARM.com        static int nextId = 0;
1859651SAndreas.Sandberg@ARM.com
1869651SAndreas.Sandberg@ARM.com        // The depends field of the qstruct, which was previously unused, is
1879651SAndreas.Sandberg@ARM.com        // used to communicate with simulated application.
1889651SAndreas.Sandberg@ARM.com        if (curTask.depends) {
1899651SAndreas.Sandberg@ARM.com            HostState hs;
1909651SAndreas.Sandberg@ARM.com            shader->ReadMem((uint64_t)(curTask.depends), &hs,
1919651SAndreas.Sandberg@ARM.com                            sizeof(HostState), 0);
1929651SAndreas.Sandberg@ARM.com
1939651SAndreas.Sandberg@ARM.com            // update event start time (in nano-seconds)
1949651SAndreas.Sandberg@ARM.com            uint64_t start = curTick() / 1000;
1959651SAndreas.Sandberg@ARM.com
1969651SAndreas.Sandberg@ARM.com            shader->WriteMem((uint64_t)(&((_cl_event*)hs.event)->start),
1979651SAndreas.Sandberg@ARM.com                             &start, sizeof(uint64_t), 0);
1989651SAndreas.Sandberg@ARM.com        }
1999651SAndreas.Sandberg@ARM.com
2009651SAndreas.Sandberg@ARM.com        // launch kernel
2019651SAndreas.Sandberg@ARM.com        ++num_kernelLaunched;
2029651SAndreas.Sandberg@ARM.com
2039651SAndreas.Sandberg@ARM.com        NDRange *ndr = &(ndRangeMap[nextId]);
2049651SAndreas.Sandberg@ARM.com        // copy dispatch info
2059651SAndreas.Sandberg@ARM.com        ndr->q = curTask;
2069651SAndreas.Sandberg@ARM.com
2079651SAndreas.Sandberg@ARM.com        // update the numDispTask polled by the runtime
2089651SAndreas.Sandberg@ARM.com        accessUserVar(cpu, (uint64_t)(curTask.numDispLeft), 0, 1);
2099651SAndreas.Sandberg@ARM.com
2109651SAndreas.Sandberg@ARM.com        ndr->numWgTotal = 1;
2119651SAndreas.Sandberg@ARM.com
2129651SAndreas.Sandberg@ARM.com        for (int i = 0; i < 3; ++i) {
2139651SAndreas.Sandberg@ARM.com            ndr->wgId[i] = 0;
2149651SAndreas.Sandberg@ARM.com            ndr->numWg[i] = divCeil(curTask.gdSize[i], curTask.wgSize[i]);
2159651SAndreas.Sandberg@ARM.com            ndr->numWgTotal *= ndr->numWg[i];
2169651SAndreas.Sandberg@ARM.com        }
2179651SAndreas.Sandberg@ARM.com
2189651SAndreas.Sandberg@ARM.com        ndr->numWgCompleted = 0;
2199651SAndreas.Sandberg@ARM.com        ndr->globalWgId = 0;
2209651SAndreas.Sandberg@ARM.com        ndr->wg_disp_rem = true;
2219651SAndreas.Sandberg@ARM.com        ndr->execDone = false;
2229651SAndreas.Sandberg@ARM.com        ndr->addrToNotify = (volatile bool*)curTask.addrToNotify;
2239651SAndreas.Sandberg@ARM.com        ndr->numDispLeft = (volatile uint32_t*)curTask.numDispLeft;
2249651SAndreas.Sandberg@ARM.com        ndr->dispatchId = nextId;
2259651SAndreas.Sandberg@ARM.com        ndr->curCid = pkt->req->contextId();
2269651SAndreas.Sandberg@ARM.com        DPRINTF(GPUDisp, "launching kernel %d\n",nextId);
2279651SAndreas.Sandberg@ARM.com        execIds.push(nextId);
2289651SAndreas.Sandberg@ARM.com        ++nextId;
2299651SAndreas.Sandberg@ARM.com
2309651SAndreas.Sandberg@ARM.com        dispatchActive = true;
2319651SAndreas.Sandberg@ARM.com
2329651SAndreas.Sandberg@ARM.com        if (!tickEvent.scheduled()) {
2339651SAndreas.Sandberg@ARM.com            schedule(&tickEvent, curTick() + shader->ticks(1));
2349651SAndreas.Sandberg@ARM.com        }
2359651SAndreas.Sandberg@ARM.com    } else {
2369651SAndreas.Sandberg@ARM.com        // populate current task struct
2379651SAndreas.Sandberg@ARM.com        // first 64 bits are launch reg
2389651SAndreas.Sandberg@ARM.com        offset -= 8;
2399651SAndreas.Sandberg@ARM.com        assert(offset < sizeof(HsaQueueEntry));
2409651SAndreas.Sandberg@ARM.com        char *curTaskPtr = (char*)&curTask;
2419651SAndreas.Sandberg@ARM.com        memcpy(curTaskPtr + offset, pkt->getPtr<const void*>(), pkt->getSize());
2429651SAndreas.Sandberg@ARM.com    }
2439651SAndreas.Sandberg@ARM.com
2449651SAndreas.Sandberg@ARM.com    pkt->makeAtomicResponse();
2459651SAndreas.Sandberg@ARM.com
2469651SAndreas.Sandberg@ARM.com    return pioDelay;
2479651SAndreas.Sandberg@ARM.com}
2489651SAndreas.Sandberg@ARM.com
2499651SAndreas.Sandberg@ARM.com
2509651SAndreas.Sandberg@ARM.comBaseMasterPort&
2519651SAndreas.Sandberg@ARM.comGpuDispatcher::getMasterPort(const std::string &if_name, PortID idx)
2529651SAndreas.Sandberg@ARM.com{
2539651SAndreas.Sandberg@ARM.com    if (if_name == "translation_port") {
2549651SAndreas.Sandberg@ARM.com        return *tlbPort;
2559651SAndreas.Sandberg@ARM.com    }
25611321Ssteve.reinhardt@amd.com
2579651SAndreas.Sandberg@ARM.com    return DmaDevice::getMasterPort(if_name, idx);
258}
259
260void
261GpuDispatcher::exec()
262{
263    int fail_count = 0;
264
265    // There are potentially multiple outstanding kernel launches.
266    // It is possible that the workgroups in a different kernel
267    // can fit on the GPU even if another kernel's workgroups cannot
268    DPRINTF(GPUDisp, "Launching %d Kernels\n", execIds.size());
269
270    while (execIds.size() > fail_count) {
271        int execId = execIds.front();
272
273        while (ndRangeMap[execId].wg_disp_rem) {
274            //update the thread context
275            shader->updateContext(ndRangeMap[execId].curCid);
276
277            // attempt to dispatch_workgroup
278            if (!shader->dispatch_workgroups(&ndRangeMap[execId])) {
279                // if we failed try the next kernel,
280                // it may have smaller workgroups.
281                // put it on the queue to rety latter
282                DPRINTF(GPUDisp, "kernel %d failed to launch\n", execId);
283                execIds.push(execId);
284                ++fail_count;
285                break;
286            }
287        }
288        // let's try the next kernel_id
289        execIds.pop();
290    }
291
292    DPRINTF(GPUDisp, "Returning %d Kernels\n", doneIds.size());
293
294    if (doneIds.size() && cpu) {
295        shader->hostWakeUp(cpu);
296    }
297
298    while (doneIds.size()) {
299        // wakeup the CPU if any Kernels completed this cycle
300        DPRINTF(GPUDisp, "WorkGroup %d completed\n", doneIds.front());
301        doneIds.pop();
302    }
303}
304
305void
306GpuDispatcher::notifyWgCompl(Wavefront *w)
307{
308    int kern_id = w->kern_id;
309    DPRINTF(GPUDisp, "notify WgCompl %d\n",kern_id);
310    assert(ndRangeMap[kern_id].dispatchId == kern_id);
311    ndRangeMap[kern_id].numWgCompleted++;
312
313    if (ndRangeMap[kern_id].numWgCompleted == ndRangeMap[kern_id].numWgTotal) {
314        ndRangeMap[kern_id].execDone = true;
315        doneIds.push(kern_id);
316
317        if (ndRangeMap[kern_id].addrToNotify) {
318            accessUserVar(cpu, (uint64_t)(ndRangeMap[kern_id].addrToNotify), 1,
319                          0);
320        }
321
322        accessUserVar(cpu, (uint64_t)(ndRangeMap[kern_id].numDispLeft), 0, -1);
323
324        // update event end time (in nano-seconds)
325        if (ndRangeMap[kern_id].q.depends) {
326            HostState *host_state = (HostState*)ndRangeMap[kern_id].q.depends;
327            uint64_t event;
328            shader->ReadMem((uint64_t)(&host_state->event), &event,
329                            sizeof(uint64_t), 0);
330
331            uint64_t end = curTick() / 1000;
332
333            shader->WriteMem((uint64_t)(&((_cl_event*)event)->end), &end,
334                             sizeof(uint64_t), 0);
335        }
336    }
337
338    if (!tickEvent.scheduled()) {
339        schedule(&tickEvent, curTick() + shader->ticks(1));
340    }
341}
342
343void
344GpuDispatcher::scheduleDispatch()
345{
346    if (!tickEvent.scheduled())
347        schedule(&tickEvent, curTick() + shader->ticks(1));
348}
349
350void
351GpuDispatcher::accessUserVar(BaseCPU *cpu, uint64_t addr, int val, int off)
352{
353    if (cpu) {
354        if (off) {
355            shader->AccessMem(addr, &val, sizeof(int), 0, MemCmd::ReadReq,
356                              true);
357            val += off;
358        }
359
360        shader->AccessMem(addr, &val, sizeof(int), 0, MemCmd::WriteReq, true);
361    } else {
362        panic("Cannot find host");
363    }
364}
365
366GpuDispatcher::TickEvent::TickEvent(GpuDispatcher *_dispatcher)
367    : Event(CPU_Tick_Pri), dispatcher(_dispatcher)
368{
369}
370
371void
372GpuDispatcher::TickEvent::process()
373{
374    dispatcher->exec();
375}
376
377const char*
378GpuDispatcher::TickEvent::description() const
379{
380    return "GPU Dispatcher tick";
381}
382
383// helper functions for driver to retrieve GPU attributes
384int
385GpuDispatcher::getNumCUs()
386{
387    return shader->cuList.size();
388}
389
390void
391GpuDispatcher::setFuncargsSize(int funcargs_size)
392{
393    shader->funcargs_size = funcargs_size;
394}
395