operand.hh revision 11308:7d8836fd043d
1/*
2 * Copyright (c) 2012-2015 Advanced Micro Devices, Inc.
3 * All rights reserved.
4 *
5 * For use for simulation and test purposes only
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are met:
9 *
10 * 1. Redistributions of source code must retain the above copyright notice,
11 * this list of conditions and the following disclaimer.
12 *
13 * 2. Redistributions in binary form must reproduce the above copyright notice,
14 * this list of conditions and the following disclaimer in the documentation
15 * and/or other materials provided with the distribution.
16 *
17 * 3. Neither the name of the copyright holder nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 * Author: Steve Reinhardt
34 */
35
36#ifndef __ARCH_HSAIL_OPERAND_HH__
37#define __ARCH_HSAIL_OPERAND_HH__
38
39/**
40 *  @file operand.hh
41 *
42 *  Defines classes encapsulating HSAIL instruction operands.
43 */
44
45#include <string>
46
47#include "arch/hsail/Brig.h"
48#include "base/trace.hh"
49#include "base/types.hh"
50#include "debug/GPUReg.hh"
51#include "enums/RegisterType.hh"
52#include "gpu-compute/brig_object.hh"
53#include "gpu-compute/compute_unit.hh"
54#include "gpu-compute/hsail_code.hh"
55#include "gpu-compute/shader.hh"
56#include "gpu-compute/vector_register_file.hh"
57#include "gpu-compute/wavefront.hh"
58
59class Label;
60class StorageElement;
61
62class BaseOperand
63{
64  public:
65    Enums::RegisterType registerType;
66    uint32_t regOperandSize;
67    BaseOperand() { registerType = Enums::RT_NONE; regOperandSize = 0; }
68    bool isVectorRegister() { return registerType == Enums::RT_VECTOR; }
69    bool isScalarRegister() { return registerType == Enums::RT_SCALAR; }
70    bool isCondRegister() { return registerType == Enums::RT_CONDITION; }
71    unsigned int regIndex() { return 0; }
72    uint32_t opSize() { return regOperandSize; }
73    virtual ~BaseOperand() { }
74};
75
76class BrigRegOperandInfo
77{
78  public:
79    Brig::BrigKind16_t kind;
80    Brig::BrigType type;
81    Brig::BrigRegisterKind regKind;
82
83    BrigRegOperandInfo(Brig::BrigKind16_t _kind,
84                       Brig::BrigRegisterKind _regKind)
85        : kind(_kind), regKind(_regKind)
86    {
87    }
88
89    BrigRegOperandInfo(Brig::BrigKind16_t _kind, Brig::BrigType _type)
90        : kind(_kind), type(_type)
91    {
92    }
93
94    BrigRegOperandInfo() : kind(Brig::BRIG_KIND_OPERAND_CONSTANT_BYTES),
95                           type(Brig::BRIG_TYPE_NONE)
96    {
97    }
98};
99
100BrigRegOperandInfo findRegDataType(unsigned opOffset, const BrigObject *obj);
101
102class BaseRegOperand : public BaseOperand
103{
104  public:
105    unsigned regIdx;
106    char regFileChar;
107
108    bool init(unsigned opOffset, const BrigObject *obj,
109              unsigned &maxRegIdx, char _regFileChar);
110
111    bool init_from_vect(unsigned opOffset, const BrigObject *obj, int at,
112                        unsigned &maxRegIdx, char _regFileChar);
113
114    void initWithStrOffset(unsigned strOffset, const BrigObject *obj,
115                           unsigned &maxRegIdx, char _regFileChar);
116    unsigned int regIndex() { return regIdx; }
117};
118
119class SRegOperand : public BaseRegOperand
120{
121  public:
122    static unsigned maxRegIdx;
123
124    bool
125    init(unsigned opOffset, const BrigObject *obj)
126    {
127        regOperandSize = sizeof(uint32_t);
128        registerType = Enums::RT_VECTOR;
129
130        return BaseRegOperand::init(opOffset, obj, maxRegIdx, 's');
131    }
132
133    bool
134    init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
135    {
136        regOperandSize = sizeof(uint32_t);
137        registerType = Enums::RT_VECTOR;
138
139        return BaseRegOperand::init_from_vect(opOffset, obj, at, maxRegIdx,
140                                              's');
141    }
142
143    void
144    initWithStrOffset(unsigned strOffset, const BrigObject *obj)
145    {
146        regOperandSize = sizeof(uint32_t);
147        registerType = Enums::RT_VECTOR;
148
149        return BaseRegOperand::initWithStrOffset(strOffset, obj, maxRegIdx,
150                                                 's');
151    }
152
153    template<typename OperandType>
154    OperandType
155    get(Wavefront *w, int lane)
156    {
157        assert(sizeof(OperandType) <= sizeof(uint32_t));
158        assert(regIdx < w->maxSpVgprs);
159        // if OperandType is smaller than 32-bit, we truncate the value
160        OperandType ret;
161        uint32_t vgprIdx;
162
163        switch (sizeof(OperandType)) {
164          case 1: // 1 byte operand
165              vgprIdx = w->remap(regIdx, 1, 1);
166              ret = (w->computeUnit->vrf[w->simdId]->
167                      read<uint32_t>(vgprIdx, lane)) & 0xff;
168            break;
169          case 2: // 2 byte operand
170              vgprIdx = w->remap(regIdx, 2, 1);
171              ret = (w->computeUnit->vrf[w->simdId]->
172                      read<uint32_t>(vgprIdx, lane)) & 0xffff;
173            break;
174          case 4: // 4 byte operand
175              vgprIdx = w->remap(regIdx,sizeof(OperandType), 1);
176              ret = w->computeUnit->vrf[w->simdId]->
177                  read<OperandType>(vgprIdx, lane);
178            break;
179          default:
180            panic("Bad OperandType\n");
181            break;
182        }
183
184        return (OperandType)ret;
185    }
186
187    // special get method for compatibility with LabelOperand
188    uint32_t
189    getTarget(Wavefront *w, int lane)
190    {
191        return get<uint32_t>(w, lane);
192    }
193
194    template<typename OperandType>
195    void set(Wavefront *w, int lane, OperandType &val);
196    std::string disassemble();
197};
198
199template<typename OperandType>
200void
201SRegOperand::set(Wavefront *w, int lane, OperandType &val)
202{
203    DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $s%d <- %d\n",
204            w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx, val);
205
206    assert(sizeof(OperandType) == sizeof(uint32_t));
207    assert(regIdx < w->maxSpVgprs);
208    uint32_t vgprIdx = w->remap(regIdx, sizeof(OperandType), 1);
209    w->computeUnit->vrf[w->simdId]->write<OperandType>(vgprIdx,val,lane);
210}
211
212template<>
213inline void
214SRegOperand::set(Wavefront *w, int lane, uint64_t &val)
215{
216    DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $s%d <- %d\n",
217            w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx, val);
218
219    assert(regIdx < w->maxSpVgprs);
220    uint32_t vgprIdx = w->remap(regIdx, sizeof(uint32_t), 1);
221    w->computeUnit->vrf[w->simdId]->write<uint32_t>(vgprIdx, val, lane);
222}
223
224class DRegOperand : public BaseRegOperand
225{
226  public:
227    static unsigned maxRegIdx;
228
229    bool
230    init(unsigned opOffset, const BrigObject *obj)
231    {
232        regOperandSize = sizeof(uint64_t);
233        registerType = Enums::RT_VECTOR;
234
235        return BaseRegOperand::init(opOffset, obj, maxRegIdx, 'd');
236    }
237
238    bool
239    init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
240    {
241        regOperandSize = sizeof(uint64_t);
242        registerType = Enums::RT_VECTOR;
243
244        return BaseRegOperand::init_from_vect(opOffset, obj, at, maxRegIdx,
245                                              'd');
246    }
247
248    void
249    initWithStrOffset(unsigned strOffset, const BrigObject *obj)
250    {
251        regOperandSize = sizeof(uint64_t);
252        registerType = Enums::RT_VECTOR;
253
254        return BaseRegOperand::initWithStrOffset(strOffset, obj, maxRegIdx,
255                                                 'd');
256    }
257
258    template<typename OperandType>
259    OperandType
260    get(Wavefront *w, int lane)
261    {
262        assert(sizeof(OperandType) <= sizeof(uint64_t));
263        // TODO: this check is valid only for HSAIL
264        assert(regIdx < w->maxDpVgprs);
265        uint32_t vgprIdx = w->remap(regIdx, sizeof(OperandType), 1);
266
267        return w->computeUnit->vrf[w->simdId]->read<OperandType>(vgprIdx,lane);
268    }
269
270    template<typename OperandType>
271    void
272    set(Wavefront *w, int lane, OperandType &val)
273    {
274        DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $d%d <- %d\n",
275                w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx,
276                val);
277
278        assert(sizeof(OperandType) <= sizeof(uint64_t));
279        // TODO: this check is valid only for HSAIL
280        assert(regIdx < w->maxDpVgprs);
281        uint32_t vgprIdx = w->remap(regIdx, sizeof(OperandType), 1);
282        w->computeUnit->vrf[w->simdId]->write<OperandType>(vgprIdx,val,lane);
283    }
284
285    std::string disassemble();
286};
287
288class CRegOperand : public BaseRegOperand
289{
290  public:
291    static unsigned maxRegIdx;
292
293    bool
294    init(unsigned opOffset, const BrigObject *obj)
295    {
296        regOperandSize = sizeof(uint8_t);
297        registerType = Enums::RT_CONDITION;
298
299        return BaseRegOperand::init(opOffset, obj, maxRegIdx, 'c');
300    }
301
302    bool
303    init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
304    {
305        regOperandSize = sizeof(uint8_t);
306        registerType = Enums::RT_CONDITION;
307
308        return BaseRegOperand::init_from_vect(opOffset, obj, at, maxRegIdx,
309                                              'c');
310    }
311
312    void
313    initWithStrOffset(unsigned strOffset, const BrigObject *obj)
314    {
315        regOperandSize = sizeof(uint8_t);
316        registerType = Enums::RT_CONDITION;
317
318        return BaseRegOperand::initWithStrOffset(strOffset, obj, maxRegIdx,
319                                                 'c');
320    }
321
322    template<typename OperandType>
323    OperandType
324    get(Wavefront *w, int lane)
325    {
326        assert(regIdx < w->condRegState->numRegs());
327
328        return w->condRegState->read<OperandType>((int)regIdx, lane);
329    }
330
331    template<typename OperandType>
332    void
333    set(Wavefront *w, int lane, OperandType &val)
334    {
335        DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: $c%d <- %d\n",
336                w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane, regIdx,
337                val);
338
339        assert(regIdx < w->condRegState->numRegs());
340        w->condRegState->write<OperandType>(regIdx,lane,val);
341    }
342
343    std::string disassemble();
344};
345
346template<typename T>
347class ImmOperand : public BaseOperand
348{
349  public:
350    T bits;
351
352    bool init(unsigned opOffset, const BrigObject *obj);
353    bool init_from_vect(unsigned opOffset, const BrigObject *obj, int at);
354    std::string disassemble();
355
356    template<typename OperandType>
357    OperandType
358    get()
359    {
360        assert(sizeof(OperandType) <= sizeof(T));
361
362        return *(OperandType*)&bits;
363    }
364
365    // This version of get() takes a WF* and a lane id for
366    // compatibility with the register-based get() methods.
367    template<typename OperandType>
368    OperandType
369    get(Wavefront *w, int lane)
370    {
371        return get<OperandType>();
372    }
373};
374
375template<typename T>
376bool
377ImmOperand<T>::init(unsigned opOffset, const BrigObject *obj)
378{
379    const Brig::BrigOperand *brigOp = obj->getOperand(opOffset);
380
381    switch (brigOp->kind) {
382      // this is immediate operand
383      case Brig::BRIG_KIND_OPERAND_CONSTANT_BYTES:
384        {
385            DPRINTF(GPUReg, "sizeof(T): %lu, byteCount: %d\n", sizeof(T),
386                    brigOp->byteCount);
387
388            auto cbptr = (Brig::BrigOperandConstantBytes*)brigOp;
389
390            bits = *((T*)(obj->getData(cbptr->bytes + 4)));
391
392            return true;
393        }
394        break;
395
396      case Brig::BRIG_KIND_OPERAND_WAVESIZE:
397        bits = VSZ;
398        return true;
399
400      default:
401        return false;
402    }
403}
404
405template <typename T>
406bool
407ImmOperand<T>::init_from_vect(unsigned opOffset, const BrigObject *obj, int at)
408{
409    const Brig::BrigOperand *brigOp = obj->getOperand(opOffset);
410
411    if (brigOp->kind != Brig::BRIG_KIND_OPERAND_OPERAND_LIST) {
412        return false;
413    }
414
415
416    const Brig::BrigOperandOperandList *brigVecOp =
417         (const Brig::BrigOperandOperandList *)brigOp;
418
419    unsigned *data_offset =
420        (unsigned *)obj->getData(brigVecOp->elements + 4 * (at + 1));
421
422    const Brig::BrigOperand *p =
423        (const Brig::BrigOperand *)obj->getOperand(*data_offset);
424
425    if (p->kind != Brig::BRIG_KIND_OPERAND_CONSTANT_BYTES) {
426        return false;
427    }
428
429    return init(*data_offset, obj);
430}
431template<typename T>
432std::string
433ImmOperand<T>::disassemble()
434{
435    return csprintf("0x%08x", bits);
436}
437
438template<typename RegOperand, typename T>
439class RegOrImmOperand : public BaseOperand
440{
441  private:
442    bool is_imm;
443
444  public:
445    void setImm(const bool value) { is_imm = value; }
446
447    ImmOperand<T> imm_op;
448    RegOperand reg_op;
449
450    RegOrImmOperand() { is_imm = false; }
451    void init(unsigned opOffset, const BrigObject *obj);
452    void init_from_vect(unsigned opOffset, const BrigObject *obj, int at);
453    std::string disassemble();
454
455    template<typename OperandType>
456    OperandType
457    get(Wavefront *w, int lane)
458    {
459        return is_imm ?  imm_op.template get<OperandType>() :
460                         reg_op.template get<OperandType>(w, lane);
461    }
462
463    uint32_t
464    opSize()
465    {
466        if (!is_imm) {
467            return reg_op.opSize();
468        }
469
470        return 0;
471    }
472
473    bool
474    isVectorRegister()
475    {
476        if (!is_imm) {
477            return reg_op.registerType == Enums::RT_VECTOR;
478        }
479        return false;
480    }
481
482    bool
483    isCondRegister()
484    {
485        if (!is_imm) {
486            return reg_op.registerType == Enums::RT_CONDITION;
487        }
488
489        return false;
490    }
491
492    bool
493    isScalarRegister()
494    {
495        if (!is_imm) {
496            return reg_op.registerType == Enums::RT_SCALAR;
497        }
498
499        return false;
500    }
501
502    unsigned int
503    regIndex()
504    {
505        if (!is_imm) {
506            return reg_op.regIndex();
507        }
508        return 0;
509    }
510};
511
512template<typename RegOperand, typename T>
513void
514RegOrImmOperand<RegOperand, T>::init(unsigned opOffset, const BrigObject *obj)
515{
516    is_imm = false;
517
518    if (reg_op.init(opOffset, obj)) {
519        return;
520    }
521
522    if (imm_op.init(opOffset, obj)) {
523        is_imm = true;
524        return;
525    }
526
527    fatal("RegOrImmOperand::init(): bad operand kind %d\n",
528          obj->getOperand(opOffset)->kind);
529}
530
531template<typename RegOperand, typename T>
532void
533RegOrImmOperand<RegOperand, T>::init_from_vect(unsigned opOffset,
534                                               const BrigObject *obj, int at)
535{
536    if (reg_op.init_from_vect(opOffset, obj, at)) {
537        is_imm = false;
538
539        return;
540    }
541
542    if (imm_op.init_from_vect(opOffset, obj, at)) {
543        is_imm = true;
544
545        return;
546    }
547
548    fatal("RegOrImmOperand::init(): bad operand kind %d\n",
549          obj->getOperand(opOffset)->kind);
550}
551
552template<typename RegOperand, typename T>
553std::string
554RegOrImmOperand<RegOperand, T>::disassemble()
555{
556    return is_imm ? imm_op.disassemble() : reg_op.disassemble();
557}
558
559typedef RegOrImmOperand<SRegOperand, uint32_t> SRegOrImmOperand;
560typedef RegOrImmOperand<DRegOperand, uint64_t> DRegOrImmOperand;
561typedef RegOrImmOperand<CRegOperand, bool> CRegOrImmOperand;
562
563class AddrOperandBase : public BaseOperand
564{
565  protected:
566    // helper function for init()
567    void parseAddr(const Brig::BrigOperandAddress *op, const BrigObject *obj);
568
569    // helper function for disassemble()
570    std::string disassemble(std::string reg_disassembly);
571    uint64_t calcUniformBase();
572
573  public:
574    virtual void calcVector(Wavefront *w, uint64_t *addrVec) = 0;
575    virtual uint64_t calcLane(Wavefront *w, int lane=0) = 0;
576
577    uint64_t offset;
578    const char *name = nullptr;
579    StorageElement *storageElement;
580};
581
582template<typename RegOperandType>
583class RegAddrOperand : public AddrOperandBase
584{
585  public:
586    RegOperandType reg;
587    void init(unsigned opOffset, const BrigObject *obj);
588    uint64_t calcUniform();
589    void calcVector(Wavefront *w, uint64_t *addrVec);
590    uint64_t calcLane(Wavefront *w, int lane=0);
591    uint32_t opSize() { return reg.opSize(); }
592    bool isVectorRegister() { return reg.registerType == Enums::RT_VECTOR; }
593    bool isCondRegister() { return reg.registerType == Enums::RT_CONDITION; }
594    bool isScalarRegister() { return reg.registerType == Enums::RT_SCALAR; }
595    unsigned int regIndex() { return reg.regIndex(); }
596    std::string disassemble();
597};
598
599template<typename RegOperandType>
600void
601RegAddrOperand<RegOperandType>::init(unsigned opOffset, const BrigObject *obj)
602{
603    using namespace Brig;
604
605    const BrigOperand *baseOp = obj->getOperand(opOffset);
606
607    switch (baseOp->kind) {
608      case BRIG_KIND_OPERAND_ADDRESS:
609        {
610            const BrigOperandAddress *op = (BrigOperandAddress*)baseOp;
611            storageElement = nullptr;
612
613            offset = (uint64_t(op->offset.hi) << 32) | uint64_t(op->offset.lo);
614            reg.init(op->reg, obj);
615
616            if (reg.regFileChar == 's') {
617                reg.regOperandSize = sizeof(uint32_t);
618                registerType = Enums::RT_VECTOR;
619            }
620            else if (reg.regFileChar == 'd') {
621                reg.regOperandSize = sizeof(uint64_t);
622                registerType = Enums::RT_VECTOR;
623            }
624        }
625        break;
626
627      default:
628        fatal("RegAddrOperand: bad operand kind %d\n", baseOp->kind);
629        break;
630    }
631}
632
633template<typename RegOperandType>
634uint64_t
635RegAddrOperand<RegOperandType>::calcUniform()
636{
637    fatal("can't do calcUniform() on register-based address\n");
638
639    return 0;
640}
641
642template<typename RegOperandType>
643void
644RegAddrOperand<RegOperandType>::calcVector(Wavefront *w, uint64_t *addrVec)
645{
646    Addr address = calcUniformBase();
647
648    for (int lane = 0; lane < VSZ; ++lane) {
649        if (w->execMask(lane)) {
650            if (reg.regFileChar == 's') {
651                addrVec[lane] = address + reg.template get<uint32_t>(w, lane);
652            } else {
653                addrVec[lane] = address + reg.template get<Addr>(w, lane);
654            }
655        }
656    }
657}
658
659template<typename RegOperandType>
660uint64_t
661RegAddrOperand<RegOperandType>::calcLane(Wavefront *w, int lane)
662{
663    Addr address = calcUniformBase();
664
665    return address + reg.template get<Addr>(w, lane);
666}
667
668template<typename RegOperandType>
669std::string
670RegAddrOperand<RegOperandType>::disassemble()
671{
672    return AddrOperandBase::disassemble(reg.disassemble());
673}
674
675typedef RegAddrOperand<SRegOperand> SRegAddrOperand;
676typedef RegAddrOperand<DRegOperand> DRegAddrOperand;
677
678class NoRegAddrOperand : public AddrOperandBase
679{
680  public:
681    void init(unsigned opOffset, const BrigObject *obj);
682    uint64_t calcUniform();
683    void calcVector(Wavefront *w, uint64_t *addrVec);
684    uint64_t calcLane(Wavefront *w, int lane=0);
685    std::string disassemble();
686};
687
688inline uint64_t
689NoRegAddrOperand::calcUniform()
690{
691    return AddrOperandBase::calcUniformBase();
692}
693
694inline uint64_t
695NoRegAddrOperand::calcLane(Wavefront *w, int lane)
696{
697    return calcUniform();
698}
699
700inline void
701NoRegAddrOperand::calcVector(Wavefront *w, uint64_t *addrVec)
702{
703    uint64_t address = calcUniformBase();
704
705    for (int lane = 0; lane < VSZ; ++lane)
706        addrVec[lane] = address;
707}
708
709class LabelOperand : public BaseOperand
710{
711  public:
712    Label *label;
713
714    void init(unsigned opOffset, const BrigObject *obj);
715    std::string disassemble();
716
717    // special get method for compatibility with SRegOperand
718    uint32_t getTarget(Wavefront *w, int lane);
719
720};
721
722class ListOperand : public BaseOperand
723{
724  public:
725    int elementCount;
726    std::vector<StorageElement*> callArgs;
727
728    int
729    getSrcOperand(int idx)
730    {
731        DPRINTF(GPUReg, "getSrcOperand, idx: %d, sz_args: %d\n", idx,
732                callArgs.size());
733
734        return callArgs.at(idx)->offset;
735    }
736
737    void init(unsigned opOffset, const BrigObject *obj);
738
739    std::string disassemble();
740
741    template<typename OperandType>
742    OperandType
743    get(Wavefront *w, int lane, int arg_idx)
744    {
745        return w->readCallArgMem<OperandType>(lane, getSrcOperand(arg_idx));
746    }
747
748    template<typename OperandType>
749    void
750    set(Wavefront *w, int lane, OperandType val)
751    {
752        w->writeCallArgMem<OperandType>(lane, getSrcOperand(0), val);
753        DPRINTF(GPUReg, "CU%d, WF[%d][%d], lane %d: arg[%d] <- %d\n",
754                w->computeUnit->cu_id, w->simdId, w->wfSlotId, lane,
755                getSrcOperand(0), val);
756    }
757};
758
759class FunctionRefOperand : public BaseOperand
760{
761  public:
762    const char *func_name;
763
764    void init(unsigned opOffset, const BrigObject *obj);
765    std::string disassemble();
766};
767
768#endif // __ARCH_HSAIL_OPERAND_HH__
769