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#include "arch/hsail/operand.hh"
37
38using namespace Brig;
39
40bool
41BaseRegOperand::init(unsigned opOffset, const BrigObject *obj,
42                     unsigned &maxRegIdx, char _regFileChar)
43{
44    regFileChar = _regFileChar;
45    const BrigOperand *brigOp = obj->getOperand(opOffset);
46
47    if (brigOp->kind != BRIG_KIND_OPERAND_REGISTER)
48        return false;
49
50    const BrigOperandRegister *brigRegOp = (const BrigOperandRegister*)brigOp;
51
52    regIdx = brigRegOp->regNum;
53
54    DPRINTF(GPUReg, "Operand: regNum: %d, kind: %d\n", regIdx,
55            brigRegOp->regKind);
56
57    maxRegIdx = std::max(maxRegIdx, regIdx);
58
59    return true;
60}
61
62void
63ListOperand::init(unsigned opOffset, const BrigObject *obj)
64{
65    const BrigOperand *brigOp = (const BrigOperand*)obj->getOperand(opOffset);
66
67    switch (brigOp->kind) {
68      case BRIG_KIND_OPERAND_CODE_LIST:
69        {
70            const BrigOperandCodeList *opList =
71                (const BrigOperandCodeList*)brigOp;
72
73            const Brig::BrigData *oprnd_data =
74                obj->getBrigBaseData(opList->elements);
75
76            // Note: for calls Dest list of operands could be size of 0.
77            elementCount = oprnd_data->byteCount / 4;
78
79            DPRINTF(GPUReg, "Operand Code List: # elements: %d\n",
80                    elementCount);
81
82            for (int i = 0; i < elementCount; ++i) {
83                unsigned *data_offset =
84                    (unsigned*)obj->getData(opList->elements + 4 * (i + 1));
85
86                const BrigDirectiveVariable *p =
87                    (const BrigDirectiveVariable*)obj->
88                    getCodeSectionEntry(*data_offset);
89
90                StorageElement *se = obj->currentCode->storageMap->
91                    findSymbol(BRIG_SEGMENT_ARG, p);
92
93                assert(se);
94                callArgs.push_back(se);
95            }
96        }
97        break;
98      default:
99        fatal("ListOperand: bad operand kind %d\n", brigOp->kind);
100    }
101}
102
103std::string
104ListOperand::disassemble()
105{
106    std::string res_str("");
107
108    for (auto it : callArgs) {
109        res_str += csprintf("%s ", it->name.c_str());
110    }
111
112    return res_str;
113}
114
115void
116FunctionRefOperand::init(unsigned opOffset, const BrigObject *obj)
117{
118    const BrigOperand *baseOp = obj->getOperand(opOffset);
119
120    if (baseOp->kind != BRIG_KIND_OPERAND_CODE_REF) {
121        fatal("FunctionRefOperand: bad operand kind %d\n", baseOp->kind);
122    }
123
124    const BrigOperandCodeRef *brigOp = (const BrigOperandCodeRef*)baseOp;
125
126    const BrigDirectiveExecutable *p =
127        (const BrigDirectiveExecutable*)obj->getCodeSectionEntry(brigOp->ref);
128
129    func_name = obj->getString(p->name);
130}
131
132std::string
133FunctionRefOperand::disassemble()
134{
135    DPRINTF(GPUReg, "Operand Func-ref name: %s\n", func_name);
136
137    return csprintf("%s", func_name);
138}
139
140bool
141BaseRegOperand::init_from_vect(unsigned opOffset, const BrigObject *obj,
142                               int at, unsigned &maxRegIdx, char _regFileChar)
143{
144    regFileChar = _regFileChar;
145    const BrigOperand *brigOp = obj->getOperand(opOffset);
146
147    if (brigOp->kind != BRIG_KIND_OPERAND_OPERAND_LIST)
148        return false;
149
150
151    const Brig::BrigOperandOperandList *brigRegVecOp =
152         (const Brig::BrigOperandOperandList*)brigOp;
153
154    unsigned *data_offset =
155        (unsigned*)obj->getData(brigRegVecOp->elements + 4 * (at + 1));
156
157    const BrigOperand *p =
158        (const BrigOperand*)obj->getOperand(*data_offset);
159    if (p->kind != BRIG_KIND_OPERAND_REGISTER) {
160        return false;
161    }
162
163    const BrigOperandRegister *brigRegOp =(const BrigOperandRegister*)p;
164
165    regIdx = brigRegOp->regNum;
166
167    DPRINTF(GPUReg, "Operand: regNum: %d, kind: %d \n", regIdx,
168            brigRegOp->regKind);
169
170    maxRegIdx = std::max(maxRegIdx, regIdx);
171
172    return true;
173}
174
175void
176BaseRegOperand::initWithStrOffset(unsigned strOffset, const BrigObject *obj,
177                     unsigned &maxRegIdx, char _regFileChar)
178{
179    const char *name = obj->getString(strOffset);
180    char *endptr;
181    regIdx = strtoul(name + 2, &endptr, 10);
182
183    if (name[0] != '$' || name[1] != _regFileChar) {
184        fatal("register operand parse error on \"%s\"\n", name);
185    }
186
187    maxRegIdx = std::max(maxRegIdx, regIdx);
188}
189
190unsigned SRegOperand::maxRegIdx;
191unsigned DRegOperand::maxRegIdx;
192unsigned CRegOperand::maxRegIdx;
193
194std::string
195SRegOperand::disassemble()
196{
197    return csprintf("$s%d", regIdx);
198}
199
200std::string
201DRegOperand::disassemble()
202{
203    return csprintf("$d%d", regIdx);
204}
205
206std::string
207CRegOperand::disassemble()
208{
209    return csprintf("$c%d", regIdx);
210}
211
212BrigRegOperandInfo
213findRegDataType(unsigned opOffset, const BrigObject *obj)
214{
215    const BrigOperand *baseOp = obj->getOperand(opOffset);
216
217    switch (baseOp->kind) {
218      case BRIG_KIND_OPERAND_REGISTER:
219        {
220            const BrigOperandRegister *op = (BrigOperandRegister*)baseOp;
221
222            return BrigRegOperandInfo((BrigKind16_t)baseOp->kind,
223                                      (BrigRegisterKind)op->regKind);
224        }
225        break;
226
227      case BRIG_KIND_OPERAND_WAVESIZE:
228        {
229            BrigRegisterKind reg_kind = BRIG_REGISTER_KIND_DOUBLE;
230            return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, reg_kind);
231        }
232
233      case BRIG_KIND_OPERAND_OPERAND_LIST:
234        {
235            const BrigOperandOperandList *op =
236               (BrigOperandOperandList*)baseOp;
237            const BrigData *data_p = (BrigData*)obj->getData(op->elements);
238
239
240            int num_operands = 0;
241            BrigRegisterKind reg_kind = (BrigRegisterKind)0;
242            for (int offset = 0; offset < data_p->byteCount; offset += 4) {
243                const BrigOperand *op_p = (const BrigOperand *)
244                   obj->getOperand(((int *)data_p->bytes)[offset/4]);
245
246                if (op_p->kind == BRIG_KIND_OPERAND_REGISTER) {
247                    const BrigOperandRegister *brigRegOp =
248                       (const BrigOperandRegister*)op_p;
249                    reg_kind = (BrigRegisterKind)brigRegOp->regKind;
250                } else if (op_p->kind == BRIG_KIND_OPERAND_CONSTANT_BYTES) {
251                    uint16_t num_bytes =
252                       ((Brig::BrigOperandConstantBytes*)op_p)->base.byteCount
253                           - sizeof(BrigBase);
254                    if (num_bytes == sizeof(uint32_t)) {
255                        reg_kind = BRIG_REGISTER_KIND_SINGLE;
256                    } else if (num_bytes == sizeof(uint64_t)) {
257                        reg_kind = BRIG_REGISTER_KIND_DOUBLE;
258                    } else {
259                        fatal("OperandList: bad operand size %d\n", num_bytes);
260                    }
261                } else if (op_p->kind == BRIG_KIND_OPERAND_WAVESIZE) {
262                    reg_kind = BRIG_REGISTER_KIND_DOUBLE;
263                } else {
264                    fatal("OperandList: bad operand kind %d\n", op_p->kind);
265                }
266
267                num_operands++;
268            }
269            assert(baseOp->kind == BRIG_KIND_OPERAND_OPERAND_LIST);
270
271            return BrigRegOperandInfo((BrigKind16_t)baseOp->kind, reg_kind);
272        }
273        break;
274
275      case BRIG_KIND_OPERAND_ADDRESS:
276        {
277            const BrigOperandAddress *op = (BrigOperandAddress*)baseOp;
278
279            if (!op->reg) {
280                BrigType type = BRIG_TYPE_NONE;
281
282                if (op->symbol) {
283                    const BrigDirective *dir = (BrigDirective*)
284                        obj->getCodeSectionEntry(op->symbol);
285
286                    assert(dir->kind == BRIG_KIND_DIRECTIVE_VARIABLE);
287
288                    const BrigDirectiveVariable *sym =
289                       (const BrigDirectiveVariable*)dir;
290
291                    type = (BrigType)sym->type;
292                }
293                return BrigRegOperandInfo(BRIG_KIND_OPERAND_ADDRESS,
294                                          (BrigType)type);
295            } else {
296                const BrigOperandAddress *b = (const BrigOperandAddress*)baseOp;
297                const BrigOperand *reg = obj->getOperand(b->reg);
298                const BrigOperandRegister *rop = (BrigOperandRegister*)reg;
299
300                return BrigRegOperandInfo(BRIG_KIND_OPERAND_REGISTER,
301                                          (BrigRegisterKind)rop->regKind);
302            }
303        }
304        break;
305
306     default:
307       fatal("AddrOperand: bad operand kind %d\n", baseOp->kind);
308       break;
309   }
310}
311
312void
313AddrOperandBase::parseAddr(const BrigOperandAddress *op, const BrigObject *obj)
314{
315    assert(op->base.kind == BRIG_KIND_OPERAND_ADDRESS);
316
317    const BrigDirective *d =
318        (BrigDirective*)obj->getCodeSectionEntry(op->symbol);
319
320    /**
321     * HSAIL does not properly handle immediate offsets for instruction types
322     * that utilize them. It currently only supports instructions that use
323     * variables instead. Again, these pop up in code that is never executed
324     * (i.e. the HCC AMP codes) so we just hack it here to let us pass through
325     * the HSAIL object initialization. If such code is ever called, we would
326     * have to implement this properly.
327     */
328    if (d->kind != BRIG_KIND_DIRECTIVE_VARIABLE) {
329        warn("HSAIL implementation does not support instructions with "
330             "address calculations where the operand is not a variable\n");
331    }
332
333    const BrigDirectiveVariable *sym = (BrigDirectiveVariable*)d;
334    name = obj->getString(sym->name);
335
336    if (sym->segment != BRIG_SEGMENT_ARG) {
337        storageElement =
338            obj->currentCode->storageMap->findSymbol(sym->segment, name);
339        offset = 0;
340    } else {
341        // sym->name does not work for BRIG_SEGMENT_ARG for the following case:
342        //
343        //     void foo(int a);
344        //     void bar(double a);
345        //
346        //     foo(...) --> arg_u32 %param_p0;
347        //                  st_arg_u32 $s0, [%param_p0];
348        //                  call &foo (%param_p0);
349        //     bar(...) --> arg_f64 %param_p0;
350        //                  st_arg_u64 $d0, [%param_p0];
351        //                  call &foo (%param_p0);
352        //
353        //  Both functions use the same variable name (param_p0)!!!
354        //
355        //  Maybe this is a bug in the compiler (I don't know).
356        //
357        // Solution:
358        // Use directive pointer (BrigDirectiveVariable) to differentiate 2
359        // versions of param_p0.
360        //
361        // Note this solution is kind of stupid, because we are pulling stuff
362        // out of the brig binary via the directive pointer and putting it into
363        // the symbol table, but now we are indexing the symbol table by the
364        // brig directive pointer! It makes the symbol table sort of pointless.
365        // But I don't want to mess with the rest of the infrastructure, so
366        // let's go with this for now.
367        //
368        // When we update the compiler again, we should see if this problem goes
369        // away. If so, we can fold some of this functionality into the code for
370        // kernel arguments. If not, maybe we can index the symbol name on a
371        // hash of the variable AND function name
372        storageElement = obj->currentCode->
373                 storageMap->findSymbol((Brig::BrigSegment)sym->segment, sym);
374
375        assert(storageElement);
376    }
377}
378
379uint64_t
380AddrOperandBase::calcUniformBase()
381{
382    // start with offset, will be 0 if not specified
383    uint64_t address = offset;
384
385    // add in symbol value if specified
386    if (storageElement) {
387        address += storageElement->offset;
388    }
389
390    return address;
391}
392
393std::string
394AddrOperandBase::disassemble(std::string reg_disassembly)
395{
396    std::string disasm;
397
398    if (offset || reg_disassembly != "") {
399        disasm += "[";
400
401        if (reg_disassembly != "") {
402            disasm += reg_disassembly;
403
404            if (offset > 0) {
405                disasm += "+";
406            }
407        }
408
409        if (offset) {
410            disasm += csprintf("%d", offset);
411        }
412
413        disasm += "]";
414    } else if (name) {
415        disasm += csprintf("[%s]", name);
416    }
417
418    return disasm;
419}
420
421void
422NoRegAddrOperand::init(unsigned opOffset, const BrigObject *obj)
423{
424    const BrigOperand *baseOp = obj->getOperand(opOffset);
425
426    if (baseOp->kind == BRIG_KIND_OPERAND_ADDRESS) {
427        BrigOperandAddress *addrOp = (BrigOperandAddress*)baseOp;
428        parseAddr(addrOp, obj);
429        offset = (uint64_t(addrOp->offset.hi) << 32) |
430                  uint64_t(addrOp->offset.lo);
431    } else {
432        fatal("NoRegAddrOperand: bad operand kind %d\n", baseOp->kind);
433    }
434
435}
436
437std::string
438NoRegAddrOperand::disassemble()
439{
440    return AddrOperandBase::disassemble(std::string(""));
441}
442
443void
444LabelOperand::init(unsigned opOffset, const BrigObject *obj)
445{
446    const BrigOperandCodeRef *op =
447        (const BrigOperandCodeRef*)obj->getOperand(opOffset);
448
449    assert(op->base.kind == BRIG_KIND_OPERAND_CODE_REF);
450
451    const BrigDirective *dir =
452        (const BrigDirective*)obj->getCodeSectionEntry(op->ref);
453
454    assert(dir->kind == BRIG_KIND_DIRECTIVE_LABEL);
455    label = obj->currentCode->refLabel((BrigDirectiveLabel*)dir, obj);
456}
457
458uint32_t
459LabelOperand::getTarget(Wavefront *w, int lane)
460{
461    return label->get();
462}
463
464std::string
465LabelOperand::disassemble()
466{
467    return label->name;
468}
469