source: CLRX/CLRadeonExtender/trunk/amdbin/ROCmBinaries.cpp @ 3684

Last change on this file since 3684 was 3684, checked in by matszpk, 23 months ago

CLRadeonExtender: ROCmBin: Detect start of the document in ROCm YAML metadata.

File size: 60.9 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2018 Mateusz Szpakowski
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <CLRX/Config.h>
21#include <cassert>
22#include <cstdio>
23#include <cstring>
24#include <cstdint>
25#include <string>
26#include <vector>
27#include <algorithm>
28#include <utility>
29#include <CLRX/amdbin/ElfBinaries.h>
30#include <CLRX/utils/Utilities.h>
31#include <CLRX/utils/MemAccess.h>
32#include <CLRX/utils/InputOutput.h>
33#include <CLRX/utils/Containers.h>
34#include <CLRX/amdbin/ROCmBinaries.h>
35
36using namespace CLRX;
37
38/*
39 * ROCm metadata YAML parser
40 */
41
42ROCmKernelMetadata::ROCmKernelMetadata() :
43    langVersion{ BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED },
44    reqdWorkGroupSize{ BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED },
45    workGroupSizeHint{ BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED },
46    kernargSegmentSize(BINGEN64_NOTSUPPLIED),
47    groupSegmentFixedSize(BINGEN64_NOTSUPPLIED),
48    privateSegmentFixedSize(BINGEN64_NOTSUPPLIED),
49    kernargSegmentAlign(BINGEN64_NOTSUPPLIED),
50    wavefrontSize(BINGEN_NOTSUPPLIED),
51    sgprsNum(BINGEN_NOTSUPPLIED), vgprsNum(BINGEN_NOTSUPPLIED),
52    maxFlatWorkGroupSize(BINGEN64_NOTSUPPLIED),
53    fixedWorkGroupSize{ BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED, BINGEN_NOTSUPPLIED },
54    spilledSgprs(BINGEN_NOTSUPPLIED),
55    spilledVgprs(BINGEN_NOTSUPPLIED)
56{ }
57
58ROCmMetadata::ROCmMetadata() : version{ 0, 0 }
59{ }
60
61// return trailing spaces
62static size_t skipSpacesAndComments(const char*& ptr, const char* end, size_t& lineNo)
63{
64    const char* lineStart = ptr;
65    while (ptr != end)
66    {
67        lineStart = ptr;
68        while (ptr != end && *ptr!='\n' && isSpace(*ptr)) ptr++;
69        if (ptr == end)
70            break; // end of stream
71        if (*ptr=='#')
72        {
73            // skip comment
74            while (ptr != end && *ptr!='\n') ptr++;
75            if (ptr == end)
76                return 0; // no trailing spaces and end
77        }
78        else if (*ptr!='\n')
79            break; // no comment and no end of line
80        else
81            lineNo++; // next line
82    }
83    return lineStart - ptr;
84}
85
86static inline void skipSpacesToLineEnd(const char*& ptr, const char* end)
87{
88    while (ptr != end && *ptr!='\n' && isSpace(*ptr)) ptr++;
89}
90
91static void skipSpacesToNextLine(const char*& ptr, const char* end, size_t& lineNo)
92{
93    skipSpacesToLineEnd(ptr, end);
94    if (ptr != end && *ptr != '\n' && *ptr!='#')
95        throw ParseException(lineNo, "Garbages at line");
96    if (ptr != end && *ptr == '#')
97        // skip comment at end of line
98        while (ptr!=end && *ptr!='\n') ptr++;
99    if (ptr!=end)
100    {   // newline
101        ptr++;
102        lineNo++;
103    }
104}
105
106static size_t parseYAMLKey(const char*& ptr, const char* end, size_t lineNo,
107            size_t keywordsNum, const char** keywords)
108{
109    const char* keyPtr = ptr;
110    while (ptr != end && (isAlnum(*ptr) || *ptr=='_')) ptr++;
111    if (keyPtr == end)
112        throw ParseException(lineNo, "Expected key name");
113    const char* keyEnd = ptr;
114    while (ptr != end && *ptr!='\n' && isSpace(*ptr)) ptr++;
115    if (ptr == end || *ptr==':')
116        throw ParseException(lineNo, "Expected colon");
117    ptr++;
118    const char* afterColon = ptr;
119    skipSpacesToLineEnd(ptr, end);
120    if (afterColon == ptr)
121        throw ParseException("After key and colon must be space");
122    CString keyword(keyPtr, keyEnd);
123    const size_t index = binaryFind(keywords, keywords+keywordsNum,
124                        keyword.c_str(), CStringLess()) - keywords;
125    return index;
126}
127
128template<typename T>
129static T parseYAMLIntValue(const char*& ptr, const char* end, size_t& lineNo,
130                bool singleValue = false)
131{
132    skipSpacesToLineEnd(ptr, end);
133    if (ptr == end || *ptr=='\n')
134        throw ParseException(lineNo, "Expected integer value");
135    T value = 0;
136    try
137    { value = cstrtovCStyle<T>(ptr, end, ptr); }
138    catch(const ParseException& ex)
139    { throw ParseException(lineNo, ex.what()); }
140   
141    if (singleValue)
142        skipSpacesToNextLine(ptr, end, lineNo);
143    return value;
144}
145
146static bool parseYAMLBoolValue(const char*& ptr, const char* end, size_t& lineNo,
147        bool singleValue = false)
148{
149    skipSpacesToLineEnd(ptr, end);
150    if (ptr == end || *ptr=='\n')
151        throw ParseException(lineNo, "Expected boolean value");
152   
153    const char* wordPtr = ptr;
154    while(ptr == end && isAlnum(*ptr)) ptr++;
155    CString word(wordPtr, ptr);
156   
157    bool value = false;
158    bool isSet = false;
159    for (const char* v: { "1", "true", "t", "on", "yes", "y"})
160        if (::strcasecmp(word.c_str(), v) == 0)
161        {
162            isSet = true;
163            value = true;
164            break;
165        }
166    for (const char* v: { "0", "false", "f", "off", "no", "n"})
167        if (::strcasecmp(word.c_str(), v) == 0)
168        {
169            isSet = true;
170            value = false;
171            break;
172        }
173    if (!isSet)
174        throw ParseException(lineNo, "Is not boolean value");
175   
176    if (singleValue)
177        skipSpacesToNextLine(ptr, end, lineNo);
178    return value;
179}
180
181static std::string trimStrSpaces(const std::string& str)
182{
183    size_t i = 0;
184    const size_t sz = str.size();
185    while (i!=sz && isSpace(str[i])) i++;
186    if (i == sz) return "";
187    size_t j = sz-1;
188    while (j>i && isSpace(str[j])) j--;
189    return str.substr(i, j-i+1);
190}
191
192static std::string parseYAMLString(const char*& linePtr, const char* end,
193            size_t& lineNo)
194{
195    std::string strarray;
196    if (linePtr == end || (*linePtr != '"' && *linePtr != '\''))
197    {
198        while (linePtr != end && !isSpace(*linePtr) && *linePtr != ',') linePtr++;
199        throw ParseException(lineNo, "Expected string");
200    }
201    const char termChar = *linePtr;
202    linePtr++;
203   
204    // main loop, where is character parsing
205    while (linePtr != end && *linePtr != termChar)
206    {
207        if (*linePtr == '\\')
208        {
209            // escape
210            linePtr++;
211            uint16_t value;
212            if (linePtr == end)
213                throw ParseException(lineNo, "Unterminated character of string");
214            if (*linePtr == 'x')
215            {
216                // hex literal
217                linePtr++;
218                if (linePtr == end)
219                    throw ParseException(lineNo, "Unterminated character of string");
220                value = 0;
221                if (isXDigit(*linePtr))
222                    for (; linePtr != end; linePtr++)
223                    {
224                        cxuint digit;
225                        if (*linePtr >= '0' && *linePtr <= '9')
226                            digit = *linePtr-'0';
227                        else if (*linePtr >= 'a' && *linePtr <= 'f')
228                            digit = *linePtr-'a'+10;
229                        else if (*linePtr >= 'A' && *linePtr <= 'F')
230                            digit = *linePtr-'A'+10;
231                        else
232                            break;
233                        value = (value<<4) + digit;
234                    }
235                else
236                    throw ParseException(lineNo, "Expected hexadecimal character code");
237                value &= 0xff;
238            }
239            else if (isODigit(*linePtr))
240            {
241                // octal literal
242                value = 0;
243                for (cxuint i = 0; linePtr != end && i < 3; i++, linePtr++)
244                {
245                    if (!isODigit(*linePtr))
246                        break;
247                    value = (value<<3) + uint64_t(*linePtr-'0');
248                    // checking range
249                    if (value > 255)
250                        throw ParseException(lineNo, "Octal code out of range");
251                }
252            }
253            else
254            {
255                // normal escapes
256                const char c = *linePtr++;
257                switch (c)
258                {
259                    case 'a':
260                        value = '\a';
261                        break;
262                    case 'b':
263                        value = '\b';
264                        break;
265                    case 'r':
266                        value = '\r';
267                        break;
268                    case 'n':
269                        value = '\n';
270                        break;
271                    case 'f':
272                        value = '\f';
273                        break;
274                    case 'v':
275                        value = '\v';
276                        break;
277                    case 't':
278                        value = '\t';
279                        break;
280                    case '\\':
281                        value = '\\';
282                        break;
283                    case '\'':
284                        value = '\'';
285                        break;
286                    case '\"':
287                        value = '\"';
288                        break;
289                    default:
290                        value = c;
291                }
292            }
293            strarray.push_back(value);
294        }
295        else // regular character
296        {
297            if (*linePtr=='\n')
298                lineNo++;
299            strarray.push_back(*linePtr++);
300        }
301    }
302    if (linePtr == end)
303        throw ParseException(lineNo, "Unterminated string");
304    linePtr++;
305    return strarray;
306}
307
308static std::string parseYAMLStringValue(const char*& ptr, const char* end, size_t& lineNo,
309                    cxuint prevIndent, bool singleValue = false)
310{
311    skipSpacesToLineEnd(ptr, end);
312    if (ptr == end)
313        return "";
314    if (*ptr=='"' || *ptr== '\'')
315        std::string buf = parseYAMLString(ptr, end, lineNo);
316    // otherwise parse stream
317    else if (*ptr == '|' || *ptr == '>')
318    {
319        // multiline
320        bool newLineFold = *ptr=='>';
321        while (ptr != end && *ptr!='\n') ptr++;
322        if (ptr == end)
323            return ""; // end
324        lineNo++;
325        ptr++; // skip newline
326        const char* lineStart = ptr;
327        skipSpacesToLineEnd(ptr, end);
328        size_t indent = ptr - lineStart;
329        if (indent <= prevIndent)
330            throw ParseException(lineNo, "Unindented string block");
331       
332        std::string buf;
333        while(ptr != end)
334        {
335            const char* strStart = ptr;
336            while (ptr != end && *ptr!='\n') ptr++;
337            buf.append(strStart, end);
338           
339            lineNo++;
340            const char* lineStart = ptr;
341            skipSpacesToLineEnd(ptr, end);
342            if (size_t(ptr - lineStart) < indent)
343            {
344                if (ptr == end || *ptr=='\n')
345                {
346                    if (!newLineFold)
347                        buf.append("\n");
348                    continue;
349                }
350                // if smaller indent
351                ptr = lineStart;
352                return buf;
353            }
354            ptr = lineStart + indent;
355            if (!newLineFold)
356                buf.append("\n");
357        }
358        return buf;
359    }
360    // single line string (unquoted)
361    const char* strStart = ptr;
362    while (ptr != end && *ptr!='\n' && *ptr!='#') ptr++;
363    std::string buf(strStart, ptr);
364   
365    if (singleValue)
366        skipSpacesToNextLine(ptr, end, lineNo);
367    return buf;
368}
369
370class CLRX_INTERNAL YAMLElemConsumer
371{
372public:
373    virtual void consume(const char*& ptr, const char* end, size_t& lineNo,
374                cxuint prevIndent, bool singleValue) = 0;
375};
376
377static void parseYAMLValArray(const char*& ptr, const char* end, size_t& lineNo,
378            size_t prevIndent, YAMLElemConsumer* elemConsumer, bool singleValue = false)
379{
380    skipSpacesToLineEnd(ptr, end);
381    if (ptr == end)
382        return;
383   
384    if (*ptr == '[')
385    {
386        ptr++;
387        skipSpacesAndComments(ptr, end, lineNo);
388        while (ptr != end)
389        {
390            // parse in line
391            elemConsumer->consume(ptr, end, lineNo, 0, false);
392            if (ptr!=end && *ptr==',')
393                throw ParseException(lineNo, "Expected ','");
394            else if (ptr!=end && *ptr==']')
395                // just end
396                break;
397            ptr++;
398            skipSpacesAndComments(ptr, end, lineNo);
399        }
400        if (ptr == end)
401            throw ParseException(lineNo, "Unterminated array");
402        ptr++;
403       
404        if (singleValue)
405            skipSpacesToNextLine(ptr, end, lineNo);
406        return;
407    }
408    // sequence
409    size_t oldLineNo = lineNo;
410    size_t indent0 = skipSpacesAndComments(ptr, end, lineNo);
411    if (ptr == end || lineNo == oldLineNo)
412        throw ParseException(lineNo, "Expected sequence of values");
413   
414    if (indent0 < prevIndent)
415        throw ParseException(lineNo, "Unindented sequence of objects");
416   
417    while (ptr != end)
418    {
419        if (*ptr != '-')
420            throw ParseException(lineNo, "No '-' before element value");
421        ptr++;
422        const char* afterMinus = ptr;
423        skipSpacesToLineEnd(ptr, end);
424        if (afterMinus == ptr)
425            throw ParseException(lineNo, "No spaces after '-'");
426        elemConsumer->consume(ptr, end, lineNo, indent0+1 + afterMinus-ptr, true);
427       
428        size_t indent = skipSpacesAndComments(ptr, end, lineNo);
429        if (indent < indent0)
430        {
431            // if parent level
432            ptr -= indent;
433            break;
434        }
435        if (indent != indent0)
436            throw ParseException(lineNo, "Wrong indentation of element");
437    }
438}
439
440template<typename T>
441class CLRX_INTERNAL YAMLIntArrayConsumer: public YAMLElemConsumer
442{
443private:
444    size_t elemsNum;
445    size_t requiredElemsNum;
446public:
447    T* array;
448   
449    YAMLIntArrayConsumer(size_t reqElemsNum, T* _array)
450            : elemsNum(0), requiredElemsNum(reqElemsNum), array(_array)
451    { }
452   
453    virtual void consume(const char*& ptr, const char* end, size_t& lineNo,
454                cxuint prevIndent, bool singleValue)
455    {
456        if (elemsNum == requiredElemsNum)
457            throw ParseException(lineNo, "Too many elements");
458        try
459        { array[elemsNum] = cstrtovCStyle<T>(ptr, end, ptr); }
460        catch(const ParseException& ex)
461        { throw ParseException(lineNo, ex.what()); }
462        elemsNum++;
463        if (singleValue)
464            skipSpacesToNextLine(ptr, end, lineNo);
465    }
466};
467
468// printf info string consumer
469
470class CLRX_INTERNAL YAMLPrintfVectorConsumer: public YAMLElemConsumer
471{
472public:
473    std::vector<ROCmPrintfInfo>& printfInfos;
474   
475    YAMLPrintfVectorConsumer(std::vector<ROCmPrintfInfo>& _printInfos)
476        : printfInfos(_printInfos)
477    { }
478   
479    virtual void consume(const char*& ptr, const char* end, size_t& lineNo,
480                cxuint prevIndent, bool singleValue)
481    {
482        std::string str = parseYAMLStringValue(ptr, end, lineNo, prevIndent, singleValue);
483        // parse printf string
484        ROCmPrintfInfo printfInfo{};
485       
486        const char* ptr2 = str.c_str();
487        const char* end2 = str.c_str() + str.size();
488        skipSpacesToLineEnd(ptr2, end2);
489        printfInfo.id = cstrtovCStyle<uint32_t>(ptr2, end2, ptr2);
490        skipSpacesToLineEnd(ptr2, end2);
491        if (ptr2==end || *ptr2!=':')
492            throw ParseException(lineNo, "No colon after printf callId");
493        ptr2++;
494        skipSpacesToLineEnd(ptr2, end2);
495        uint32_t argsNum = cstrtovCStyle<uint32_t>(ptr2, end2, ptr2);
496        skipSpacesToLineEnd(ptr2, end2);
497        if (ptr2==end || *ptr2!=':')
498            throw ParseException(lineNo, "No colon after printf argsNum");
499        ptr2++;
500       
501        printfInfo.argSizes.resize(argsNum);
502       
503        // parse arg sizes
504        for (size_t i = 0; i < argsNum; i++)
505        {
506            skipSpacesToLineEnd(ptr2, end2);
507            printfInfo.argSizes[i] = cstrtovCStyle<uint32_t>(ptr2, end2, ptr2);
508            skipSpacesToLineEnd(ptr2, end2);
509            if (ptr2==end || *ptr2!=':')
510                throw ParseException(lineNo, "No colon after printf argsNum");
511            ptr2++;
512        }
513        // format
514        printfInfo.format.assign(ptr2, end2);
515    }
516};
517
518static void skipYAMLValue(const char* ptr, const char* end, size_t& lineNo,
519                cxuint prevIndent)
520{
521    skipSpacesToLineEnd(ptr, end);
522    if (ptr == end || *ptr=='\n')
523        return;
524    if (ptr==end || (*ptr!='\'' && *ptr!='"' && *ptr!='|' && *ptr!='>' && *ptr !='['))
525    {
526        skipSpacesToLineEnd(ptr, end);
527        if (ptr!=end) ptr++;
528        return;
529    }
530    // string
531    if (*ptr=='\'' || *ptr=='"')
532    {
533        const char delim = *ptr++;
534        bool escape = false;
535        while(ptr!=end && (escape || *ptr!=delim))
536        {
537            if (!escape && *ptr=='\\')
538                escape = true;
539            else if (escape)
540                escape = false;
541            if (*ptr=='\n') lineNo++;
542            ptr++;
543        }
544        if (ptr==end)
545            throw ParseException(lineNo, "Unterminated string");
546        ptr++;
547        skipSpacesToNextLine(ptr, end, lineNo);
548    }
549    else if (*ptr=='[')
550    {   // otherwise [array]
551        ptr++;
552        skipSpacesAndComments(ptr, end, lineNo);
553        while (ptr != end)
554        {
555            // parse in line
556            skipYAMLValue(ptr, end, lineNo, 0);
557            if (ptr!=end && *ptr==',')
558                throw ParseException(lineNo, "Expected ','");
559            else if (ptr!=end && *ptr==']')
560                // just end
561                break;
562            ptr++;
563            skipSpacesAndComments(ptr, end, lineNo);
564        }
565        if (ptr == end)
566            throw ParseException(lineNo, "Unterminated array");
567        ptr++;
568        skipSpacesToNextLine(ptr, end, lineNo);
569    }
570    else
571    {   // block value
572        if (ptr!=end && (*ptr=='|' || *ptr=='>'))
573            ptr++; // skip '|' or '>'
574        skipSpacesToLineEnd(ptr, end);
575        if (ptr!=end && *ptr!='\n')
576            throw ParseException(lineNo, "Garbages before block or children");
577        ptr++;
578        lineNo++;
579        // skip all lines indented beyound previous level
580        while (ptr != end)
581        {
582            const char* lineStart = ptr;
583            skipSpacesToLineEnd(ptr, end);
584            if (ptr == end)
585            {
586                ptr++;
587                lineNo++;
588                continue;
589            }
590            if (ptr-lineStart < prevIndent)
591            {
592                ptr = lineStart;
593                break;
594            }
595        }
596    }
597}
598
599enum {
600    ROCMMT_MAIN_KERNELS = 0, ROCMMT_MAIN_PRINTF,  ROCMMT_MAIN_VERSION
601};
602
603static const char* mainMetadataKeywords[] =
604{
605    "Kernels", "Printf", "Version"
606};
607
608static const size_t mainMetadataKeywordsNum =
609        sizeof(mainMetadataKeywords) / sizeof(const char*);
610
611enum {
612    ROCMMT_KERNEL_ARGS = 0, ROCMMT_KERNEL_ATTRS, ROCMMT_KERNEL_CODEPROPS,
613    ROCMMT_KERNEL_LANGUAGE, ROCMMT_KERNEL_LANGUAGE_VERSION,
614    ROCMMT_KERNEL_NAME, ROCMMT_KERNEL_SYMBOLNAME
615};
616
617static const char* kernelMetadataKeywords[] =
618{
619    "Args", "Attrs", "CodeProps", "Language", "LanguageVersion", "Name", "SymbolName"
620};
621
622static const size_t kernelMetadataKeywordsNum =
623        sizeof(kernelMetadataKeywords) / sizeof(const char*);
624
625enum {
626    ROCMMT_ATTRS_REQD_WORK_GROUP_SIZE = 0, ROCMMT_ATTRS_RUNTIME_HANDLE,
627    ROCMMT_ATTRS_VECTYPEHINT, ROCMMT_ATTRS_WORK_GROUP_SIZE_HINT
628};
629
630static const char* kernelAttrMetadataKeywords[] =
631{
632    "ReqdWorkGroupSize", "RuntimeHandle", "VecTypeHint", "WorkGroupSizeHint"
633};
634
635static const size_t kernelAttrMetadataKeywordsNum =
636        sizeof(kernelAttrMetadataKeywords) / sizeof(const char*);
637
638enum {
639    ROCMMT_CODEPROPS_FIXED_WORK_GROUP_SIZE = 0, ROCMMT_CODEPROPS_GROUP_SEGMENT_FIXED_SIZE,
640    ROCMMT_CODEPROPS_KERNARG_SEGMENT_ALIGN, ROCMMT_CODEPROPS_KERNARG_SEGMENT_SIZE,
641    ROCMMT_CODEPROPS_MAX_FLAT_WORK_GROUP_SIZE, ROCMMT_CODEPROPS_NUM_SGPRS,
642    ROCMMT_CODEPROPS_NUM_SPILLED_SGPRS, ROCMMT_CODEPROPS_NUM_SPILLED_VGPRS,
643    ROCMMT_CODEPROPS_NUM_VGPRS, ROCMMT_CODEPROPS_PRIVATE_SEGMENT_FIXED_SIZE,
644    ROCMMT_CODEPROPS_WAVEFRONT_SIZE
645};
646
647static const char* kernelCodePropsKeywords[] =
648{
649    "FixedWorkGroupSize", "GroupSegmentFixedSize", "KernargSegmentAlign",
650    "KernargSegmentSize", "MaxFlatWorkGroupSize", "NumSGPRs",
651    "NumSpilledSGPRs", "NumSpilledVGPRs", "NumVGPRs", "PrivateSegmentFixedSize",
652    "WavefrontSize"
653};
654
655static const size_t kernelCodePropsKeywordsNum =
656        sizeof(kernelCodePropsKeywords) / sizeof(const char*);
657
658enum {
659    ROCMMT_ARGS_ACCQUAL = 0, ROCMMT_ARGS_ACTUALACCQUAL, ROCMMT_ARGS_ADDRSPACEQUAL,
660    ROCMMT_ARGS_ALIGN, ROCMMT_ARGS_ISCONST, ROCMMT_ARGS_ISPIPE, ROCMMT_ARGS_ISRESTRICT,
661    ROCMMT_ARGS_ISVOLATILE, ROCMMT_ARGS_NAME, ROCMMT_ARGS_POINTEE_ALIGN,
662    ROCMMT_ARGS_SIZE, ROCMMT_ARGS_TYPENAME, ROCMMT_ARGS_VALUEKIND,
663    ROCMMT_ARGS_VALUETYPE
664};
665
666static const char* kernelArgInfosKeywords[] =
667{
668    "AccQual", "ActualAccQual", "AddrSpaceQual", "Align", "IsConst", "IsPipe",
669    "IsRestrict", "IsVolatile", "Name", "PointeeAlign", "Size", "TypeName",
670    "ValueKind", "ValueType"
671};
672
673static const size_t kernelArgInfosKeywordsNum =
674        sizeof(kernelArgInfosKeywords) / sizeof(const char*);
675
676static const std::pair<const char*, ROCmValueKind> rocmValueKindNames[] =
677{
678    { "ByValue", ROCmValueKind::BY_VALUE },
679    { "DynamicSharedPointer", ROCmValueKind::DYN_SHARED_PTR },
680    { "GlobalBuffer", ROCmValueKind::GLOBAL_BUFFER },
681    { "HiddenCompletionAction", ROCmValueKind::HIDDEN_COMPLETION_ACTION },
682    { "HiddenDefaultQueue", ROCmValueKind::HIDDEN_DEFAULT_QUEUE },
683    { "HiddenGlobalOffsetX", ROCmValueKind::HIDDEN_GLOBAL_OFFSET_X },
684    { "HiddenGlobalOffsetY", ROCmValueKind::HIDDEN_GLOBAL_OFFSET_Y },
685    { "HiddenglobalOffsetZ", ROCmValueKind::HIDDEN_GLOBAL_OFFSET_Z },
686    { "HiddenNone", ROCmValueKind::HIDDEN_NONE },
687    { "HiddenPrintfBuffer", ROCmValueKind::HIDDEN_PRINTF_BUFFER },
688    { "Image", ROCmValueKind::IMAGE },
689    { "Pipe", ROCmValueKind::PIPE },
690    { "Queue", ROCmValueKind::QUEUE },
691    { "Sampler", ROCmValueKind::SAMPLER }
692};
693
694static const size_t rocmValueKindNamesNum =
695        sizeof(rocmValueKindNames) / sizeof(std::pair<const char*, ROCmValueKind>);
696
697static const std::pair<const char*, ROCmValueType> rocmValueTypeNames[] =
698{
699    { "F16", ROCmValueType::FLOAT16 },
700    { "F32", ROCmValueType::FLOAT32 },
701    { "F64", ROCmValueType::FLOAT64 },
702    { "I16", ROCmValueType::INT16 },
703    { "I8", ROCmValueType::INT8 },
704    { "I32", ROCmValueType::INT32 },
705    { "I64", ROCmValueType::INT64 },
706    { "U8", ROCmValueType::UINT8 },
707    { "U16", ROCmValueType::UINT16 },
708    { "U32", ROCmValueType::UINT32 },
709    { "U64", ROCmValueType::UINT64 },
710    { "Struct", ROCmValueType::STRUCTURE }
711};
712
713static const size_t rocmValueTypeNamesNum =
714        sizeof(rocmValueTypeNames) / sizeof(std::pair<const char*, ROCmValueType>);
715
716static const char* rocmAddrSpaceTypesTbl[] =
717{ "Private", "Global", "Constant", "Local", "Generic", "Region" };
718
719static const char* rocmAccessQualifierTbl[] =
720{ "Default", "ReadOnly", "WriteOnly", "ReadWrite" };
721
722static void parseROCmMetadata(size_t metadataSize, const char* metadata,
723                ROCmMetadata& metadataInfo)
724{
725    const char* ptr = metadata;
726    const char* end = metadata + metadataSize;
727    size_t lineNo = 1;
728    // init metadata info object
729    metadataInfo.kernels.clear();
730    metadataInfo.printfInfos.clear();
731    metadataInfo.version[0] = metadataInfo.version[1] = 0;
732   
733    std::vector<ROCmKernelMetadata>& kernels = metadataInfo.kernels;
734   
735    cxuint levels[6] = { UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX };
736    cxuint curLevel = 0;
737    bool inKernels = false;
738    bool inKernel = false;
739    bool inKernelArgs = false;
740    bool inKernelArg = false;
741    bool inKernelCodeProps = false;
742    bool inKernelAttrs = false;
743    bool canToNextLevel = false;
744   
745    size_t oldLineNo = 0;
746    while (ptr != end)
747    {
748        cxuint level = skipSpacesAndComments(ptr, end, lineNo);
749        if (ptr == end || lineNo == oldLineNo)
750            throw ParseException(lineNo, "Expected new line");
751       
752        if (levels[curLevel] == UINT_MAX)
753            levels[curLevel] = level;
754        else if (levels[curLevel] < level)
755        {
756            if (canToNextLevel)
757                // go to next nesting level
758                levels[++curLevel] = level;
759            else
760                throw ParseException(lineNo, "Unexpected nesting level");
761            canToNextLevel = false;
762        }
763        else if (levels[curLevel] > level)
764        {
765            while (curLevel != UINT_MAX && levels[curLevel] > level)
766                curLevel--;
767            if (curLevel == UINT_MAX)
768                throw ParseException(lineNo, "Indentation smaller than in main level");
769           
770            // pop from previous level
771            if (curLevel < 3)
772            {
773                if (inKernelArgs)
774                {
775                    // leave from kernel args
776                    inKernelArgs = false;
777                    inKernelArg = false;
778                }
779           
780                inKernelCodeProps = false;
781                inKernelAttrs = false;
782            }
783            if (curLevel < 1 && inKernels)
784            {
785                // leave from kernels
786                metadataInfo.kernels.assign(kernels.begin(), kernels.end());
787                kernels.clear();
788                inKernels = false;
789                inKernel = false;
790            }
791           
792            if (levels[curLevel] != level)
793                throw ParseException(lineNo, "Unexpected nesting level");
794        }
795       
796        oldLineNo = lineNo;
797        if (curLevel == 0)
798        {
799            if (lineNo==1 && ptr+3 <= end && *ptr=='-' && ptr[1]=='-' && ptr[2]=='-' &&
800                (ptr+3==end || (ptr+3 < end && ptr[3]=='\n')))
801            {
802                ptr += 3;
803                if (ptr!=end)
804                    ptr++; // to newline
805                continue; // skip document start
806            }
807           
808            if (ptr+3 <= end && *ptr=='.' && ptr[1]=='.' && ptr[2]=='.' &&
809                (ptr+3==end || (ptr+3 < end && ptr[3]=='\n')))
810                break; // end of the document
811           
812            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
813                        mainMetadataKeywordsNum, mainMetadataKeywords);
814           
815            switch(keyIndex)
816            {
817                case ROCMMT_MAIN_KERNELS:
818                    inKernels = true;
819                    canToNextLevel = true;
820                    break;
821                case ROCMMT_MAIN_PRINTF:
822                {
823                    YAMLPrintfVectorConsumer consumer(metadataInfo.printfInfos);
824                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer, true);
825                    break;
826                }
827                case ROCMMT_MAIN_VERSION:
828                {
829                    YAMLIntArrayConsumer<uint32_t> consumer(2, metadataInfo.version);
830                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer, true);
831                    break;
832                }
833                default:
834                    skipYAMLValue(ptr, end, lineNo, level);
835                    break;
836            }
837        }
838       
839        if (curLevel==1 && inKernels)
840        {
841            // enter to kernel level
842            if (ptr == end || *ptr != '-')
843                throw ParseException(lineNo, "No '-' before kernel object");
844            ptr++;
845            const char* afterMinus = ptr;
846            skipSpacesToLineEnd(ptr, end);
847            levels[++curLevel] = level + 1 + ptr-afterMinus;
848            level = levels[curLevel];
849            inKernel = true;
850           
851            kernels.push_back(ROCmKernelMetadata());
852        }
853       
854        if (curLevel==2 && inKernel)
855        {
856            // in kernel
857            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
858                        kernelMetadataKeywordsNum, kernelMetadataKeywords);
859           
860            ROCmKernelMetadata& kernel = kernels.back();
861            switch(keyIndex)
862            {
863                case ROCMMT_KERNEL_ARGS:
864                    inKernelArgs = true;
865                    canToNextLevel = true;
866                    break;
867                case ROCMMT_KERNEL_ATTRS:
868                    inKernelAttrs = true;
869                    canToNextLevel = true;
870                    break;
871                case ROCMMT_KERNEL_CODEPROPS:
872                    kernel.kernargSegmentSize = BINGEN64_DEFAULT;
873                    kernel.groupSegmentFixedSize = BINGEN64_DEFAULT;
874                    kernel.privateSegmentFixedSize = BINGEN64_DEFAULT;
875                    kernel.kernargSegmentAlign = BINGEN64_DEFAULT;
876                    kernel.wavefrontSize = BINGEN_DEFAULT;
877                    kernel.sgprsNum = BINGEN_DEFAULT;
878                    kernel.vgprsNum = BINGEN_DEFAULT;
879                    kernel.maxFlatWorkGroupSize = BINGEN64_DEFAULT;
880                    inKernelCodeProps = true;
881                    canToNextLevel = true;
882                    break;
883                case ROCMMT_KERNEL_LANGUAGE:
884                    kernel.language = parseYAMLStringValue(ptr, end, lineNo, level, true);
885                    break;
886                case ROCMMT_KERNEL_LANGUAGE_VERSION:
887                {
888                    YAMLIntArrayConsumer<uint32_t> consumer(2, kernel.langVersion);
889                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer);
890                    break;
891                }
892                case ROCMMT_KERNEL_NAME:
893                    kernel.name = parseYAMLStringValue(ptr, end, lineNo, level, true);
894                    break;
895                case ROCMMT_KERNEL_SYMBOLNAME:
896                    kernel.symbolName = parseYAMLStringValue(ptr, end, lineNo, level, true);
897                    break;
898                default:
899                    skipYAMLValue(ptr, end, lineNo, level);
900                    break;
901            }
902        }
903       
904        if (curLevel==3 && inKernelAttrs)
905        {
906            // in kernel attributes
907            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
908                        kernelAttrMetadataKeywordsNum, kernelAttrMetadataKeywords);
909           
910            ROCmKernelMetadata& kernel = kernels.back();
911            switch(keyIndex)
912            {
913                case ROCMMT_ATTRS_REQD_WORK_GROUP_SIZE:
914                {
915                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.reqdWorkGroupSize);
916                    parseYAMLValArray(ptr, end, lineNo, level, &consumer);
917                    break;
918                }
919                case ROCMMT_ATTRS_RUNTIME_HANDLE:
920                    kernel.runtimeHandle = parseYAMLStringValue(
921                                ptr, end, lineNo, level, true);
922                    break;
923                case ROCMMT_ATTRS_VECTYPEHINT:
924                    kernel.vecTypeHint = parseYAMLStringValue(
925                                ptr, end, lineNo, level, true);
926                    break;
927                case ROCMMT_ATTRS_WORK_GROUP_SIZE_HINT:
928                {
929                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.workGroupSizeHint);
930                    parseYAMLValArray(ptr, end, lineNo, level, &consumer, true);
931                    break;
932                }
933                default:
934                    skipYAMLValue(ptr, end, lineNo, level);
935                    break;
936            }
937        }
938       
939        if (curLevel==3 && inKernelCodeProps)
940        {
941            // in kernel codeProps
942            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
943                        kernelCodePropsKeywordsNum, kernelCodePropsKeywords);
944           
945            ROCmKernelMetadata& kernel = kernels.back();
946            switch(keyIndex)
947            {
948                case ROCMMT_CODEPROPS_FIXED_WORK_GROUP_SIZE:
949                {
950                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.fixedWorkGroupSize);
951                    parseYAMLValArray(ptr, end, lineNo, level, &consumer);
952                    break;
953                }
954                case ROCMMT_CODEPROPS_GROUP_SEGMENT_FIXED_SIZE:
955                    kernel.groupSegmentFixedSize =
956                                parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
957                    break;
958                case ROCMMT_CODEPROPS_KERNARG_SEGMENT_ALIGN:
959                    kernel.kernargSegmentAlign =
960                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
961                    break;
962                case ROCMMT_CODEPROPS_KERNARG_SEGMENT_SIZE:
963                    kernel.kernargSegmentSize =
964                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
965                    break;
966                case ROCMMT_CODEPROPS_MAX_FLAT_WORK_GROUP_SIZE:
967                    kernel.maxFlatWorkGroupSize =
968                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
969                    break;
970                case ROCMMT_CODEPROPS_NUM_SGPRS:
971                    kernel.sgprsNum = parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
972                    break;
973                case ROCMMT_CODEPROPS_NUM_SPILLED_SGPRS:
974                    kernel.spilledSgprs =
975                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
976                    break;
977                case ROCMMT_CODEPROPS_NUM_SPILLED_VGPRS:
978                    kernel.spilledVgprs =
979                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
980                    break;
981                case ROCMMT_CODEPROPS_NUM_VGPRS:
982                    kernel.vgprsNum = parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
983                    break;
984                case ROCMMT_CODEPROPS_PRIVATE_SEGMENT_FIXED_SIZE:
985                    kernel.privateSegmentFixedSize =
986                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
987                    break;
988                case ROCMMT_CODEPROPS_WAVEFRONT_SIZE:
989                    kernel.wavefrontSize =
990                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
991                    break;
992                default:
993                    skipYAMLValue(ptr, end, lineNo, level);
994                    break;
995            }
996        }
997       
998        if (curLevel==3 && inKernelArgs)
999        {
1000            // enter to kernel argument level
1001            if (ptr == end || *ptr != '-')
1002                throw ParseException(lineNo, "No '-' before argument object");
1003            ptr++;
1004            const char* afterMinus = ptr;
1005            skipSpacesToLineEnd(ptr, end);
1006            levels[++curLevel] = level + 1 + ptr-afterMinus;
1007            level = levels[curLevel];
1008            inKernelArg = true;
1009           
1010            kernels.back().argInfos.push_back(ROCmKernelArgInfo{});
1011        }
1012       
1013        if (curLevel==4 && inKernelArg)
1014        {
1015            // in kernel argument
1016            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
1017                        kernelArgInfosKeywordsNum, kernelArgInfosKeywords);
1018           
1019            ROCmKernelArgInfo& kernelArg = kernels.back().argInfos.back();
1020            switch(keyIndex)
1021            {
1022                case ROCMMT_ARGS_ACCQUAL:
1023                case ROCMMT_ARGS_ACTUALACCQUAL:
1024                {
1025                    const std::string acc = trimStrSpaces(parseYAMLStringValue(
1026                                    ptr, end, lineNo, level, true));
1027                    size_t accIndex = 0;
1028                    for (; accIndex < 6; accIndex++)
1029                        if (::strcmp(rocmAccessQualifierTbl[accIndex], acc.c_str())==0)
1030                            break;
1031                    if (accIndex == 4)
1032                        throw ParseException(lineNo, "Wrong access qualifier");
1033                    if (keyIndex == ROCMMT_ARGS_ACCQUAL)
1034                        kernelArg.accessQual = ROCmAccessQual(accIndex);
1035                    else
1036                        kernelArg.actualAccessQual = ROCmAccessQual(accIndex);
1037                    break;
1038                }
1039                case ROCMMT_ARGS_ADDRSPACEQUAL:
1040                {
1041                    const std::string aspace = trimStrSpaces(parseYAMLStringValue(
1042                                    ptr, end, lineNo, level, true));
1043                    size_t aspaceIndex = 0;
1044                    for (; aspaceIndex < 6; aspaceIndex++)
1045                        if (::strcmp(rocmAddrSpaceTypesTbl[aspaceIndex],
1046                                    aspace.c_str())==0)
1047                            break;
1048                    if (aspaceIndex == 6)
1049                        throw ParseException(lineNo, "Wrong address space");
1050                    kernelArg.addressSpace = ROCmAddressSpace(aspaceIndex);
1051                    break;
1052                }
1053                case ROCMMT_ARGS_ALIGN:
1054                    kernelArg.align = parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1055                    break;
1056                case ROCMMT_ARGS_ISCONST:
1057                    kernelArg.isConst = parseYAMLBoolValue(ptr, end, lineNo, true);
1058                    break;
1059                case ROCMMT_ARGS_ISPIPE:
1060                    kernelArg.isPipe = parseYAMLBoolValue(ptr, end, lineNo, true);
1061                    break;
1062                case ROCMMT_ARGS_ISRESTRICT:
1063                    kernelArg.isRestrict = parseYAMLBoolValue(ptr, end, lineNo, true);
1064                    break;
1065                case ROCMMT_ARGS_ISVOLATILE:
1066                    kernelArg.isVolatile = parseYAMLBoolValue(ptr, end, lineNo, true);
1067                    break;
1068                case ROCMMT_ARGS_NAME:
1069                    kernelArg.name = parseYAMLStringValue(ptr, end, lineNo, level, true);
1070                    break;
1071                case ROCMMT_ARGS_POINTEE_ALIGN:
1072                    kernelArg.pointeeAlign =
1073                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1074                    break;
1075                case ROCMMT_ARGS_SIZE:
1076                    kernelArg.size = parseYAMLIntValue<uint64_t>(ptr, end, lineNo);
1077                    break;
1078                case ROCMMT_ARGS_TYPENAME:
1079                    kernelArg.typeName =
1080                                parseYAMLStringValue(ptr, end, lineNo, level, true);
1081                    break;
1082                case ROCMMT_ARGS_VALUEKIND:
1083                {
1084                    const std::string vkind = trimStrSpaces(parseYAMLStringValue(
1085                                ptr, end, lineNo, level, true));
1086                    const size_t vkindIndex = binaryMapFind(rocmValueKindNames,
1087                            rocmValueKindNames + rocmValueKindNamesNum, vkind.c_str(),
1088                            CStringLess()) - rocmValueKindNames;
1089                    // if unknown kind
1090                    if (vkindIndex == rocmValueKindNamesNum)
1091                        throw ParseException(lineNo, "Wrong argument value kind");
1092                    kernelArg.valueKind = rocmValueKindNames[vkindIndex].second;
1093                    break;
1094                }
1095                case ROCMMT_ARGS_VALUETYPE:
1096                {
1097                    const std::string vtype = trimStrSpaces(parseYAMLStringValue(
1098                                    ptr, end, lineNo, level, true));
1099                    const size_t vtypeIndex = binaryMapFind(rocmValueTypeNames,
1100                            rocmValueTypeNames + rocmValueTypeNamesNum, vtype.c_str(),
1101                            CStringLess()) - rocmValueTypeNames;
1102                    // if unknown type
1103                    if (vtypeIndex == rocmValueTypeNamesNum)
1104                        throw ParseException(lineNo, "Wrong argument value type");
1105                    kernelArg.valueKind = rocmValueKindNames[vtypeIndex].second;
1106                    break;
1107                }
1108                default:
1109                    skipYAMLValue(ptr, end, lineNo, level);
1110                    break;
1111            }
1112        }
1113    }
1114}
1115
1116/*
1117 * ROCm binary reader and generator
1118 */
1119
1120/* TODO: add support for various kernel code offset (now only 256 is supported) */
1121
1122ROCmBinary::ROCmBinary(size_t binaryCodeSize, cxbyte* binaryCode, Flags creationFlags)
1123        : ElfBinary64(binaryCodeSize, binaryCode, creationFlags),
1124          regionsNum(0), codeSize(0), code(nullptr),
1125          globalDataSize(0), globalData(nullptr), metadataSize(0), metadata(nullptr),
1126          newBinFormat(false)
1127{
1128    cxuint textIndex = SHN_UNDEF;
1129    try
1130    { textIndex = getSectionIndex(".text"); }
1131    catch(const Exception& ex)
1132    { } // ignore failed
1133    uint64_t codeOffset = 0;
1134    // find '.text' section
1135    if (textIndex!=SHN_UNDEF)
1136    {
1137        code = getSectionContent(textIndex);
1138        const Elf64_Shdr& textShdr = getSectionHeader(textIndex);
1139        codeSize = ULEV(textShdr.sh_size);
1140        codeOffset = ULEV(textShdr.sh_offset);
1141    }
1142   
1143    cxuint rodataIndex = SHN_UNDEF;
1144    try
1145    { rodataIndex = getSectionIndex(".rodata"); }
1146    catch(const Exception& ex)
1147    { } // ignore failed
1148    // find '.text' section
1149    if (rodataIndex!=SHN_UNDEF)
1150    {
1151        globalData = getSectionContent(rodataIndex);
1152        const Elf64_Shdr& rodataShdr = getSectionHeader(rodataIndex);
1153        globalDataSize = ULEV(rodataShdr.sh_size);
1154    }
1155   
1156    cxuint gpuConfigIndex = SHN_UNDEF;
1157    try
1158    { gpuConfigIndex = getSectionIndex(".AMDGPU.config"); }
1159    catch(const Exception& ex)
1160    { } // ignore failed
1161    newBinFormat = (gpuConfigIndex == SHN_UNDEF);
1162   
1163    // counts regions (symbol or kernel)
1164    regionsNum = 0;
1165    const size_t symbolsNum = getSymbolsNum();
1166    for (size_t i = 0; i < symbolsNum; i++)
1167    {
1168        // count regions number
1169        const Elf64_Sym& sym = getSymbol(i);
1170        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
1171        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
1172        if (ULEV(sym.st_shndx)==textIndex &&
1173            (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
1174                (bind==STB_GLOBAL && symType==STT_OBJECT)))
1175            regionsNum++;
1176    }
1177    if (code==nullptr && regionsNum!=0)
1178        throw BinException("No code if regions number is not zero");
1179    regions.reset(new ROCmRegion[regionsNum]);
1180    size_t j = 0;
1181    typedef std::pair<uint64_t, size_t> RegionOffsetEntry;
1182    std::unique_ptr<RegionOffsetEntry[]> symOffsets(new RegionOffsetEntry[regionsNum]);
1183   
1184    // get regions info
1185    for (size_t i = 0; i < symbolsNum; i++)
1186    {
1187        const Elf64_Sym& sym = getSymbol(i);
1188        if (ULEV(sym.st_shndx)!=textIndex)
1189            continue;   // if not in '.text' section
1190        const size_t value = ULEV(sym.st_value);
1191        if (value < codeOffset)
1192            throw BinException("Region offset is too small!");
1193        const size_t size = ULEV(sym.st_size);
1194       
1195        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
1196        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
1197        if (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
1198                (bind==STB_GLOBAL && symType==STT_OBJECT))
1199        {
1200            ROCmRegionType type = ROCmRegionType::DATA;
1201            // if kernel
1202            if (symType==STT_GNU_IFUNC) 
1203                type = ROCmRegionType::KERNEL;
1204            // if function kernel
1205            else if (symType==STT_FUNC)
1206                type = ROCmRegionType::FKERNEL;
1207            symOffsets[j] = std::make_pair(value, j);
1208            if (type!=ROCmRegionType::DATA && value+0x100 > codeOffset+codeSize)
1209                throw BinException("Kernel or code offset is too big!");
1210            regions[j++] = { getSymbolName(i), size, value, type };
1211        }
1212    }
1213    // sort regions by offset
1214    std::sort(symOffsets.get(), symOffsets.get()+regionsNum,
1215            [](const RegionOffsetEntry& a, const RegionOffsetEntry& b)
1216            { return a.first < b.first; });
1217    // checking distance between regions
1218    for (size_t i = 1; i <= regionsNum; i++)
1219    {
1220        size_t end = (i<regionsNum) ? symOffsets[i].first : codeOffset+codeSize;
1221        ROCmRegion& region = regions[symOffsets[i-1].second];
1222        if (region.type==ROCmRegionType::KERNEL && symOffsets[i-1].first+0x100 > end)
1223            throw BinException("Kernel size is too small!");
1224       
1225        const size_t regSize = end - symOffsets[i-1].first;
1226        if (region.size==0)
1227            region.size = regSize;
1228        else
1229            region.size = std::min(regSize, region.size);
1230    }
1231   
1232    // get metadata
1233    const size_t notesSize = getNotesSize();
1234    const cxbyte* noteContent = (const cxbyte*)getNotes();
1235   
1236    for (size_t offset = 0; offset < notesSize; )
1237    {
1238        const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
1239        size_t namesz = ULEV(nhdr->n_namesz);
1240        size_t descsz = ULEV(nhdr->n_descsz);
1241        if (usumGt(offset, namesz+descsz, notesSize))
1242            throw BinException("Note offset+size out of range");
1243       
1244        if (namesz==4 &&
1245            ::strcmp((const char*)noteContent+offset+ sizeof(Elf64_Nhdr), "AMD")==0)
1246        {
1247            const uint32_t noteType = ULEV(nhdr->n_type);
1248            if (noteType == 0xa)
1249            {
1250                metadata = (char*)(noteContent+offset+sizeof(Elf64_Nhdr) + 4);
1251                metadataSize = descsz;
1252            }
1253            else if (noteType == 0xb)
1254                target.assign((char*)(noteContent+offset+sizeof(Elf64_Nhdr) + 4), descsz);
1255        }
1256        size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
1257        offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
1258    }
1259   
1260    if (hasRegionMap())
1261    {
1262        // create region map
1263        regionsMap.resize(regionsNum);
1264        for (size_t i = 0; i < regionsNum; i++)
1265            regionsMap[i] = std::make_pair(regions[i].regionName, i);
1266        // sort region map
1267        mapSort(regionsMap.begin(), regionsMap.end());
1268    }
1269   
1270    if (hasMetadataInfo() && metadata != nullptr && metadataSize != 0)
1271    {
1272        metadataInfo.reset(new ROCmMetadata());
1273        parseROCmMetadata(metadataSize, metadata, *metadataInfo);
1274       
1275        if (hasKernelInfoMap())
1276        {
1277            const std::vector<ROCmKernelMetadata>& kernels = metadataInfo->kernels;
1278            kernelInfosMap.resize(kernels.size());
1279            for (size_t i = 0; i < kernelInfosMap.size(); i++)
1280                kernelInfosMap[i] = std::make_pair(kernels[i].name, i);
1281            // sort region map
1282            mapSort(kernelInfosMap.begin(), kernelInfosMap.end());
1283        }
1284    }
1285}
1286
1287/// determint GPU device from ROCm notes
1288GPUDeviceType ROCmBinary::determineGPUDeviceType(uint32_t& outArchMinor,
1289                     uint32_t& outArchStepping) const
1290{
1291    uint32_t archMajor = 0;
1292    uint32_t archMinor = 0;
1293    uint32_t archStepping = 0;
1294   
1295    {
1296        const cxbyte* noteContent = (const cxbyte*)getNotes();
1297        if (noteContent==nullptr)
1298            throw BinException("Missing notes in inner binary!");
1299        size_t notesSize = getNotesSize();
1300        // find note about AMDGPU
1301        for (size_t offset = 0; offset < notesSize; )
1302        {
1303            const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
1304            size_t namesz = ULEV(nhdr->n_namesz);
1305            size_t descsz = ULEV(nhdr->n_descsz);
1306            if (usumGt(offset, namesz+descsz, notesSize))
1307                throw BinException("Note offset+size out of range");
1308            if (ULEV(nhdr->n_type) == 0x3 && namesz==4 && descsz>=0x1a &&
1309                ::strcmp((const char*)noteContent+offset+sizeof(Elf64_Nhdr), "AMD")==0)
1310            {    // AMDGPU type
1311                const uint32_t* content = (const uint32_t*)
1312                        (noteContent+offset+sizeof(Elf64_Nhdr) + 4);
1313                archMajor = ULEV(content[1]);
1314                archMinor = ULEV(content[2]);
1315                archStepping = ULEV(content[3]);
1316            }
1317            size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
1318            offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
1319        }
1320    }
1321    // determine device type
1322    GPUDeviceType deviceType = getGPUDeviceTypeFromArchVersion(archMajor, archMinor,
1323                                    archStepping);
1324    outArchMinor = archMinor;
1325    outArchStepping = archStepping;
1326    return deviceType;
1327}
1328
1329const ROCmRegion& ROCmBinary::getRegion(const char* name) const
1330{
1331    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
1332                             regionsMap.end(), name);
1333    if (it == regionsMap.end())
1334        throw BinException("Can't find region name");
1335    return regions[it->second];
1336}
1337
1338const ROCmKernelMetadata& ROCmBinary::getKernelInfo(const char* name) const
1339{
1340    if (!hasMetadataInfo())
1341        throw BinException("Can't find kernel info name");
1342    RegionMap::const_iterator it = binaryMapFind(kernelInfosMap.begin(),
1343                             kernelInfosMap.end(), name);
1344    if (it == kernelInfosMap.end())
1345        throw BinException("Can't find kernel info name");
1346    return metadataInfo->kernels[it->second];
1347}
1348
1349// if ROCm binary
1350bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
1351{
1352    if (!isElfBinary(binarySize, binary))
1353        return false;
1354    if (binary[EI_CLASS] != ELFCLASS64)
1355        return false;
1356    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
1357    if (ULEV(ehdr->e_machine) != 0xe0)
1358        return false;
1359    return true;
1360}
1361
1362
1363void ROCmInput::addEmptyKernel(const char* kernelName)
1364{
1365    symbols.push_back({ kernelName, 0, 0, ROCmRegionType::KERNEL });
1366}
1367
1368/*
1369 * ROCm Binary Generator
1370 */
1371
1372ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr)
1373{ }
1374
1375ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
1376        : manageable(false), input(rocmInput)
1377{ }
1378
1379ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
1380        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
1381        size_t globalDataSize, const cxbyte* globalData,
1382        const std::vector<ROCmSymbolInput>& symbols)
1383{
1384    input = new ROCmInput{ deviceType, archMinor, archStepping, 0, false,
1385            globalDataSize, globalData, symbols, codeSize, code };
1386}
1387
1388ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
1389        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
1390        size_t globalDataSize, const cxbyte* globalData,
1391        std::vector<ROCmSymbolInput>&& symbols)
1392{
1393    input = new ROCmInput{ deviceType, archMinor, archStepping, 0, false,
1394            globalDataSize, globalData, std::move(symbols), codeSize, code };
1395}
1396
1397ROCmBinGenerator::~ROCmBinGenerator()
1398{
1399    if (manageable)
1400        delete input;
1401}
1402
1403void ROCmBinGenerator::setInput(const ROCmInput* input)
1404{
1405    if (manageable)
1406        delete input;
1407    manageable = false;
1408    this->input = input;
1409}
1410
1411// ELF notes contents
1412static const cxbyte noteDescType1[8] =
1413{ 2, 0, 0, 0, 1, 0, 0, 0 };
1414
1415static const cxbyte noteDescType3[27] =
1416{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1417  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
1418
1419static inline void addMainSectionToTable(cxuint& sectionsNum, uint16_t* builtinTable,
1420                cxuint elfSectId)
1421{ builtinTable[elfSectId - ELFSECTID_START] = sectionsNum++; }
1422
1423void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
1424             Array<cxbyte>* aPtr) const
1425{
1426    AMDGPUArchVersion amdGpuArchValues = getGPUArchVersion(input->deviceType,
1427                GPUArchVersionTable::ROCM);
1428    if (input->archMinor!=UINT32_MAX)
1429        amdGpuArchValues.minor = input->archMinor;
1430    if (input->archStepping!=UINT32_MAX)
1431        amdGpuArchValues.stepping = input->archStepping;
1432   
1433    const char* comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
1434    uint32_t commentSize = ::strlen(comment);
1435    if (input->comment!=nullptr)
1436    {
1437        // if comment, store comment section
1438        comment = input->comment;
1439        commentSize = input->commentSize;
1440        if (commentSize==0)
1441            commentSize = ::strlen(comment);
1442    }
1443   
1444    uint32_t eflags = input->newBinFormat ? 2 : 0;
1445    if (input->eflags != BINGEN_DEFAULT)
1446        eflags = input->eflags;
1447   
1448    ElfBinaryGen64 elfBinGen64({ 0U, 0U, 0x40, 0, ET_DYN,
1449            0xe0, EV_CURRENT, UINT_MAX, 0, eflags },
1450            true, true, true, PHREGION_FILESTART);
1451   
1452    uint16_t mainBuiltinSectTable[ROCMSECTID_MAX-ELFSECTID_START+1];
1453    std::fill(mainBuiltinSectTable,
1454              mainBuiltinSectTable + ROCMSECTID_MAX-ELFSECTID_START+1, SHN_UNDEF);
1455    cxuint mainSectionsNum = 1;
1456   
1457    // generate main builtin section table (for section id translation)
1458    if (input->newBinFormat)
1459        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
1460    if (input->globalData != nullptr)
1461        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_RODATA);
1462    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSYM);
1463    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_HASH);
1464    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSTR);
1465    const cxuint execProgHeaderRegionIndex = mainSectionsNum;
1466    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_TEXT);
1467    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_DYNAMIC);
1468    if (!input->newBinFormat)
1469    {
1470        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
1471        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_GPUCONFIG);
1472    }
1473    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_COMMENT);
1474    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SYMTAB);
1475    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SHSTRTAB);
1476    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_STRTAB);
1477   
1478    // add symbols (kernels, function kernels and data symbols)
1479    elfBinGen64.addSymbol(ElfSymbol64("_DYNAMIC",
1480                  mainBuiltinSectTable[ROCMSECTID_DYNAMIC-ELFSECTID_START],
1481                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
1482    const uint16_t textSectIndex = mainBuiltinSectTable[ELFSECTID_TEXT-ELFSECTID_START];
1483    for (const ROCmSymbolInput& symbol: input->symbols)
1484    {
1485        ElfSymbol64 elfsym;
1486        switch (symbol.type)
1487        {
1488            case ROCmRegionType::KERNEL:
1489                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1490                      ELF64_ST_INFO(STB_GLOBAL, STT_GNU_IFUNC), 0, true,
1491                      symbol.offset, symbol.size);
1492                break;
1493            case ROCmRegionType::FKERNEL:
1494                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1495                      ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 0, true,
1496                      symbol.offset, symbol.size);
1497                break;
1498            case ROCmRegionType::DATA:
1499                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1500                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
1501                      symbol.offset, symbol.size);
1502                break;
1503            default:
1504                break;
1505        }
1506        // add to symbols and dynamic symbols table
1507        elfBinGen64.addSymbol(elfsym);
1508        elfBinGen64.addDynSymbol(elfsym);
1509    }
1510   
1511    static const int32_t dynTags[] = {
1512        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
1513    elfBinGen64.addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
1514   
1515    // elf program headers
1516    elfBinGen64.addProgramHeader({ PT_PHDR, PF_R, 0, 1,
1517                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1518    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART,
1519                    execProgHeaderRegionIndex,
1520                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
1521    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_X, execProgHeaderRegionIndex, 1,
1522                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1523    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
1524                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1525    elfBinGen64.addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
1526                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
1527    elfBinGen64.addProgramHeader({ PT_GNU_RELRO, PF_R, execProgHeaderRegionIndex+1, 1,
1528                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
1529    elfBinGen64.addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
1530                    true, 0, 0, 0 });
1531   
1532    if (input->newBinFormat)
1533        // program header for note (new binary format)
1534        elfBinGen64.addProgramHeader({ PT_NOTE, PF_R, 1, 1, true,
1535                    Elf64Types::nobase, Elf64Types::nobase, 0, 4 });
1536   
1537    std::string target = input->target.c_str();
1538    if (target.empty() && !input->targetTripple.empty())
1539    {
1540        target = input->targetTripple.c_str();
1541        char dbuf[20];
1542        snprintf(dbuf, 20, "-gfx%u%u%u", amdGpuArchValues.major, amdGpuArchValues.minor,
1543                 amdGpuArchValues.stepping);
1544        target += dbuf;
1545    }
1546    // elf notes
1547    elfBinGen64.addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
1548    std::unique_ptr<cxbyte[]> noteBuf(new cxbyte[0x1b]);
1549    ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
1550    SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
1551    SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
1552    SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
1553    elfBinGen64.addNote({"AMD", 0x1b, noteBuf.get(), 3U});
1554    if (!target.empty())
1555        elfBinGen64.addNote({"AMD", target.size(), (const cxbyte*)target.c_str(), 0xbU});
1556    if (input->metadataSize != 0)
1557        elfBinGen64.addNote({"AMD", input->metadataSize,
1558                (const cxbyte*)input->metadata, 0xaU});
1559   
1560    /// region and sections
1561    elfBinGen64.addRegion(ElfRegion64::programHeaderTable());
1562    if (input->newBinFormat)
1563        elfBinGen64.addRegion(ElfRegion64::noteSection());
1564    if (input->globalData != nullptr)
1565        elfBinGen64.addRegion(ElfRegion64(input->globalDataSize, input->globalData, 4,
1566                ".rodata", SHT_PROGBITS, SHF_ALLOC, 0, 0, Elf64Types::nobase));
1567   
1568    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
1569                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, 1, Elf64Types::nobase));
1570    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
1571                ".hash", SHT_HASH, SHF_ALLOC,
1572                mainBuiltinSectTable[ELFSECTID_DYNSYM-ELFSECTID_START], 0,
1573                Elf64Types::nobase));
1574    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
1575                SHF_ALLOC, 0, 0, Elf64Types::nobase));
1576    // '.text' with alignment=4096
1577    elfBinGen64.addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
1578              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
1579              Elf64Types::nobase, 0, false, 256));
1580    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
1581                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE,
1582                mainBuiltinSectTable[ELFSECTID_DYNSTR-ELFSECTID_START], 0,
1583                Elf64Types::nobase, 0, false, 8));
1584    if (!input->newBinFormat)
1585    {
1586        elfBinGen64.addRegion(ElfRegion64::noteSection());
1587        elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1,
1588                    ".AMDGPU.config", SHT_PROGBITS, 0));
1589    }
1590    elfBinGen64.addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
1591              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
1592    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
1593                ".symtab", SHT_SYMTAB, 0, 0, 2));
1594    elfBinGen64.addRegion(ElfRegion64::shstrtabSection());
1595    elfBinGen64.addRegion(ElfRegion64::strtabSection());
1596    elfBinGen64.addRegion(ElfRegion64::sectionHeaderTable());
1597   
1598    /* extra sections */
1599    for (const BinSection& section: input->extraSections)
1600        elfBinGen64.addRegion(ElfRegion64(section, mainBuiltinSectTable,
1601                         ROCMSECTID_MAX, mainSectionsNum));
1602    /* extra symbols */
1603    for (const BinSymbol& symbol: input->extraSymbols)
1604        elfBinGen64.addSymbol(ElfSymbol64(symbol, mainBuiltinSectTable,
1605                         ROCMSECTID_MAX, mainSectionsNum));
1606   
1607    size_t binarySize = elfBinGen64.countSize();
1608    /****
1609     * prepare for write binary to output
1610     ****/
1611    std::unique_ptr<std::ostream> outStreamHolder;
1612    std::ostream* os = nullptr;
1613    if (aPtr != nullptr)
1614    {
1615        aPtr->resize(binarySize);
1616        outStreamHolder.reset(
1617                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
1618        os = outStreamHolder.get();
1619    }
1620    else if (vPtr != nullptr)
1621    {
1622        vPtr->resize(binarySize);
1623        outStreamHolder.reset(new VectorOStream(*vPtr));
1624        os = outStreamHolder.get();
1625    }
1626    else // from argument
1627        os = osPtr;
1628   
1629    const std::ios::iostate oldExceptions = os->exceptions();
1630    try
1631    {
1632    os->exceptions(std::ios::failbit | std::ios::badbit);
1633    /****
1634     * write binary to output
1635     ****/
1636    FastOutputBuffer bos(256, *os);
1637    elfBinGen64.generate(bos);
1638    assert(bos.getWritten() == binarySize);
1639    }
1640    catch(...)
1641    {
1642        os->exceptions(oldExceptions);
1643        throw;
1644    }
1645    os->exceptions(oldExceptions);
1646}
1647
1648void ROCmBinGenerator::generate(Array<cxbyte>& array) const
1649{
1650    generateInternal(nullptr, nullptr, &array);
1651}
1652
1653void ROCmBinGenerator::generate(std::ostream& os) const
1654{
1655    generateInternal(&os, nullptr, nullptr);
1656}
1657
1658void ROCmBinGenerator::generate(std::vector<char>& v) const
1659{
1660    generateInternal(nullptr, &v, nullptr);
1661}
Note: See TracBrowser for help on using the repository browser.