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, Anthony Gutierrez
34 */
35
36#include "gpu-compute/brig_object.hh"
37
38#include <fcntl.h>
39#include <sys/mman.h>
40#include <sys/types.h>
41#include <unistd.h>
42
43#include <cassert>
44#include <cstddef>
45#include <cstdlib>
46
47#include "arch/hsail/Brig.h"
48#include "base/logging.hh"
49#include "base/trace.hh"
50#include "debug/BRIG.hh"
51#include "debug/HSAILObject.hh"
52#include "debug/HSALoader.hh"
53
54using namespace Brig;
55
56std::vector<std::function<HsaObject*(const std::string&, int, uint8_t*)>>
57    HsaObject::tryFileFuncs = { BrigObject::tryFile };
58
59extern int getBrigDataTypeBytes(BrigType16_t t);
60
61const char *BrigObject::sectionNames[] =
62{
63    "hsa_data",
64    "hsa_code",
65    "hsa_operand",
66    ".shstrtab"
67};
68
69const char *segmentNames[] =
70{
71    "none",
72    "flat",
73    "global",
74    "readonly",
75    "kernarg",
76    "group",
77    "private",
78    "spill",
79    "args"
80};
81
82const uint8_t*
83BrigObject::getSectionOffset(enum SectionIndex sec, int offs) const
84{
85    // allow offs == size for dummy end pointers
86    assert(offs <= sectionInfo[sec].size);
87
88    return sectionInfo[sec].ptr + offs;
89}
90
91const char*
92BrigObject::getString(int offs) const
93{
94    return (const char*)(getSectionOffset(DataSectionIndex, offs) + 4);
95}
96
97const BrigBase*
98BrigObject::getCodeSectionEntry(int offs) const
99{
100    return (const BrigBase*)getSectionOffset(CodeSectionIndex, offs);
101}
102
103const BrigData*
104BrigObject::getBrigBaseData(int offs) const
105{
106    return (Brig::BrigData*)(getSectionOffset(DataSectionIndex, offs));
107}
108
109const uint8_t*
110BrigObject::getData(int offs) const
111{
112    return getSectionOffset(DataSectionIndex, offs);
113}
114
115const BrigOperand*
116BrigObject::getOperand(int offs) const
117{
118    return (const BrigOperand*)getSectionOffset(OperandsSectionIndex, offs);
119}
120
121unsigned
122BrigObject::getOperandPtr(int offs, int index) const
123{
124    unsigned *op_offs = (unsigned*)(getData(offs + 4 * (index + 1)));
125
126    return *op_offs;
127}
128
129const BrigInstBase*
130BrigObject::getInst(int offs) const
131{
132    return (const BrigInstBase*)getSectionOffset(CodeSectionIndex, offs);
133}
134
135HsaCode*
136BrigObject::getKernel(const std::string &name) const
137{
138    return nullptr;
139}
140
141HsaCode*
142BrigObject::getFunction(const std::string &name) const
143{
144    for (int i = 0; i < functions.size(); ++i) {
145        if (functions[i]->name() == name) {
146            return functions[i];
147        }
148    }
149
150    return nullptr;
151}
152
153void
154BrigObject::processDirectives(const BrigBase *dirPtr, const BrigBase *endPtr,
155                              StorageMap *storageMap)
156{
157    while (dirPtr < endPtr) {
158        if (!dirPtr->byteCount) {
159            fatal("Bad directive size 0\n");
160        }
161
162        // calculate next pointer now so we can override it if needed
163        const BrigBase *nextDirPtr = brigNext(dirPtr);
164
165        DPRINTF(HSAILObject, "Code section entry kind: #%x, byte count: %d\n",
166                dirPtr->kind, dirPtr->byteCount);
167
168        switch (dirPtr->kind) {
169          case BRIG_KIND_DIRECTIVE_FUNCTION:
170            {
171                const BrigDirectiveExecutable *p M5_VAR_USED =
172                    reinterpret_cast<const BrigDirectiveExecutable*>(dirPtr);
173
174                DPRINTF(HSAILObject,"DIRECTIVE_FUNCTION: %s offset: "
175                        "%d next: %d\n", getString(p->name),
176                        p->firstCodeBlockEntry, p->nextModuleEntry);
177
178                if (p->firstCodeBlockEntry != p->nextModuleEntry) {
179                    // Function calls are not supported. We allow the BRIG
180                    // object file to create stubs, but the function calls will
181                    // not work properly if the application makes use of them.
182                    warn("HSA function invocations are unsupported.\n");
183
184                    const char *name = getString(p->name);
185
186                    HsailCode *code_obj = nullptr;
187
188                    for (int i = 0; i < functions.size(); ++i) {
189                        if (functions[i]->name() == name) {
190                            code_obj = functions[i];
191                            break;
192                        }
193                    }
194
195                    if (!code_obj) {
196                        // create new local storage map for kernel-local symbols
197                        code_obj = new HsailCode(name, p, this,
198                                                 new StorageMap(storageMap));
199                        functions.push_back(code_obj);
200                    } else {
201                        panic("Multiple definition of Function!!: %s\n",
202                              getString(p->name));
203                    }
204                }
205
206                nextDirPtr = getCodeSectionEntry(p->nextModuleEntry);
207            }
208            break;
209
210          case BRIG_KIND_DIRECTIVE_KERNEL:
211            {
212                const BrigDirectiveExecutable *p =
213                    reinterpret_cast<const BrigDirectiveExecutable*>(dirPtr);
214
215                DPRINTF(HSAILObject,"DIRECTIVE_KERNEL: %s offset: %d count: "
216                        "next: %d\n", getString(p->name),
217                        p->firstCodeBlockEntry, p->nextModuleEntry);
218
219                const char *name = getString(p->name);
220
221                if (name[0] == '&')
222                    name++;
223
224                std::string str = name;
225                char *temp;
226                int len = str.length();
227
228                if (str[len - 1] >= 'a' && str[len - 1] <= 'z') {
229                    temp = new char[str.size() + 1];
230                    std::copy(str.begin(), str.end() , temp);
231                    temp[str.size()] = '\0';
232                } else {
233                    temp = new char[str.size()];
234                    std::copy(str.begin(), str.end() - 1 , temp);
235                    temp[str.size() - 1 ] = '\0';
236                }
237
238                std::string kernel_name = temp;
239                delete[] temp;
240
241                HsailCode *code_obj = nullptr;
242
243                for (const auto &kernel : kernels) {
244                    if (kernel->name() == kernel_name) {
245                        code_obj = kernel;
246                        break;
247                    }
248                }
249
250                if (!code_obj) {
251                    // create new local storage map for kernel-local symbols
252                    code_obj = new HsailCode(kernel_name, p, this,
253                                             new StorageMap(storageMap));
254
255                    kernels.push_back(code_obj);
256                }
257
258                nextDirPtr = getCodeSectionEntry(p->nextModuleEntry);
259            }
260            break;
261
262          case BRIG_KIND_DIRECTIVE_VARIABLE:
263            {
264                const BrigDirectiveVariable *p =
265                    reinterpret_cast<const BrigDirectiveVariable*>(dirPtr);
266
267                uint64_t readonlySize_old =
268                    storageMap->getSize(BRIG_SEGMENT_READONLY);
269
270                StorageElement* se = storageMap->addSymbol(p, this);
271
272                DPRINTF(HSAILObject, "DIRECTIVE_VARIABLE, symbol %s\n",
273                        getString(p->name));
274
275                if (p->segment == BRIG_SEGMENT_READONLY) {
276                    // readonly memory has initialization data
277                    uint8_t* readonlyData_old = readonlyData;
278
279                    readonlyData =
280                        new uint8_t[storageMap->getSize(BRIG_SEGMENT_READONLY)];
281
282                    if (p->init) {
283                        if ((p->type == BRIG_TYPE_ROIMG) ||
284                            (p->type == BRIG_TYPE_WOIMG) ||
285                            (p->type == BRIG_TYPE_SAMP) ||
286                            (p->type == BRIG_TYPE_SIG32) ||
287                            (p->type == BRIG_TYPE_SIG64)) {
288                            panic("Read only data type not supported: %s\n",
289                                  getString(p->name));
290                        }
291
292                        const BrigOperand *brigOp = getOperand(p->init);
293                        assert(brigOp->kind ==
294                               BRIG_KIND_OPERAND_CONSTANT_BYTES);
295
296                        const Brig::BrigData *operand_data M5_VAR_USED =
297                            getBrigBaseData(((BrigOperandConstantBytes*)
298                                            brigOp)->bytes);
299
300                        assert((operand_data->byteCount / 4) > 0);
301
302                        uint8_t *symbol_data =
303                            (uint8_t*)getData(((BrigOperandConstantBytes*)
304                                              brigOp)->bytes + 4);
305
306                        // copy the old data and add the new data
307                        if (readonlySize_old > 0) {
308                            memcpy(readonlyData, readonlyData_old,
309                                   readonlySize_old);
310                        }
311
312                        memcpy(readonlyData + se->offset, symbol_data,
313                               se->size);
314
315                        delete[] readonlyData_old;
316                   }
317                }
318            }
319            break;
320
321          case BRIG_KIND_DIRECTIVE_LABEL:
322            {
323              const BrigDirectiveLabel M5_VAR_USED *p =
324                    reinterpret_cast<const BrigDirectiveLabel*>(dirPtr);
325
326              panic("Label directives cannot be at the module level: %s\n",
327                    getString(p->name));
328
329            }
330            break;
331
332          case BRIG_KIND_DIRECTIVE_COMMENT:
333            {
334              const BrigDirectiveComment M5_VAR_USED *p =
335                  reinterpret_cast<const BrigDirectiveComment*>(dirPtr);
336
337              DPRINTF(HSAILObject, "DIRECTIVE_COMMENT: %s\n",
338                      getString(p->name));
339            }
340            break;
341
342          case BRIG_KIND_DIRECTIVE_LOC:
343            {
344                DPRINTF(HSAILObject, "BRIG_DIRECTIVE_LOC\n");
345            }
346            break;
347
348          case BRIG_KIND_DIRECTIVE_MODULE:
349            {
350                const BrigDirectiveModule M5_VAR_USED *p =
351                    reinterpret_cast<const BrigDirectiveModule*>(dirPtr);
352
353                DPRINTF(HSAILObject, "BRIG_DIRECTIVE_MODULE: %s\n",
354                        getString(p->name));
355            }
356            break;
357
358          case BRIG_KIND_DIRECTIVE_CONTROL:
359            {
360                DPRINTF(HSAILObject, "DIRECTIVE_CONTROL\n");
361            }
362            break;
363
364          case BRIG_KIND_DIRECTIVE_PRAGMA:
365            {
366                DPRINTF(HSAILObject, "DIRECTIVE_PRAGMA\n");
367            }
368            break;
369
370          case BRIG_KIND_DIRECTIVE_EXTENSION:
371            {
372                DPRINTF(HSAILObject, "DIRECTIVE_EXTENSION\n");
373            }
374            break;
375
376          case BRIG_KIND_DIRECTIVE_ARG_BLOCK_START:
377            {
378                DPRINTF(HSAILObject, "DIRECTIVE_ARG_BLOCK_START\n");
379            }
380            break;
381
382          case BRIG_KIND_DIRECTIVE_ARG_BLOCK_END:
383            {
384                DPRINTF(HSAILObject, "DIRECTIVE_ARG_BLOCK_END\n");
385            }
386            break;
387          default:
388            if (dirPtr->kind >= BRIG_KIND_INST_BEGIN &&
389                dirPtr->kind <= BRIG_KIND_INST_END)
390                break;
391
392            if (dirPtr->kind >= BRIG_KIND_OPERAND_BEGIN &&
393                dirPtr->kind <= BRIG_KIND_OPERAND_END)
394                break;
395
396            warn("Unknown Brig directive kind: %d\n", dirPtr->kind);
397            break;
398        }
399
400        dirPtr = nextDirPtr;
401    }
402}
403
404HsaObject*
405BrigObject::tryFile(const std::string &fname, int len, uint8_t *fileData)
406{
407    const char *brig_ident = "HSA BRIG";
408
409    if (memcmp(brig_ident, fileData, MODULE_IDENTIFICATION_LENGTH))
410        return nullptr;
411
412    return new BrigObject(fname, len, fileData);
413}
414
415BrigObject::BrigObject(const std::string &fname, int len, uint8_t *fileData)
416    : HsaObject(fname), storageMap(new StorageMap())
417{
418    const char *brig_ident = "HSA BRIG";
419    BrigModuleHeader *mod_hdr = (BrigModuleHeader*)fileData;
420
421    fatal_if(memcmp(brig_ident, mod_hdr, MODULE_IDENTIFICATION_LENGTH),
422             "%s is not a BRIG file\n", fname);
423
424    if (mod_hdr->brigMajor != BRIG_VERSION_BRIG_MAJOR ||
425        mod_hdr->brigMinor != BRIG_VERSION_BRIG_MINOR) {
426        fatal("%s: BRIG version mismatch, %d.%d != %d.%d\n",
427              fname, mod_hdr->brigMajor, mod_hdr->brigMinor,
428              BRIG_VERSION_BRIG_MAJOR, BRIG_VERSION_BRIG_MINOR);
429    }
430
431    fatal_if(mod_hdr->sectionCount != NumSectionIndices, "%s: BRIG section "
432             "count (%d) != expected value (%d)\n", fname,
433             mod_hdr->sectionCount, NumSectionIndices);
434
435    for (int i = 0; i < NumSectionIndices; ++i) {
436        sectionInfo[i].ptr = nullptr;
437    }
438
439    uint64_t *sec_idx_table = (uint64_t*)(fileData + mod_hdr->sectionIndex);
440    for (int sec_idx = 0; sec_idx < mod_hdr->sectionCount; ++sec_idx) {
441        uint8_t *sec_hdr_byte_ptr = fileData + sec_idx_table[sec_idx];
442        BrigSectionHeader *sec_hdr = (BrigSectionHeader*)sec_hdr_byte_ptr;
443
444        // It doesn't look like cprintf supports string precision values,
445        // but if this breaks, the right answer is to fix that
446        DPRINTF(HSAILObject, "found section %.*s\n", sec_hdr->nameLength,
447                sec_hdr->name);
448
449        sectionInfo[sec_idx].ptr = new uint8_t[sec_hdr->byteCount];
450        memcpy(sectionInfo[sec_idx].ptr, sec_hdr_byte_ptr, sec_hdr->byteCount);
451        sectionInfo[sec_idx].size = sec_hdr->byteCount;
452    }
453
454    BrigSectionHeader *code_hdr =
455        (BrigSectionHeader*)sectionInfo[CodeSectionIndex].ptr;
456
457    DPRINTF(HSAILObject, "Code section hdr, count: %d, hdr count: %d, "
458            "name len: %d\n", code_hdr->byteCount, code_hdr->headerByteCount,
459            code_hdr->nameLength);
460
461    // start at offset 4 to skip initial null entry (see Brig spec)
462    processDirectives(getCodeSectionEntry(code_hdr->headerByteCount),
463                      getCodeSectionEntry(sectionInfo[CodeSectionIndex].size),
464                      storageMap);
465
466    delete[] fileData;
467
468    DPRINTF(HSALoader, "BRIG object %s loaded.\n", fname);
469}
470
471BrigObject::~BrigObject()
472{
473    for (int i = 0; i < NumSectionIndices; ++i)
474        if (sectionInfo[i].ptr)
475            delete[] sectionInfo[i].ptr;
476}
477