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

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

CLRadeonExtender: ROCmBin: Detect end of metadata YAML document. Integrate ROCm metadata parser with ROCmBinary class.
Add ROCmBinary::getKernelInfo.

File size: 60.6 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 (ptr+3 <= end && *ptr=='.' && ptr[1]=='.' && ptr[2]=='.' &&
800                (ptr+3==end || (ptr+3 < end && ptr[3]=='\n')))
801                break; // end of the document
802           
803            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
804                        mainMetadataKeywordsNum, mainMetadataKeywords);
805           
806            switch(keyIndex)
807            {
808                case ROCMMT_MAIN_KERNELS:
809                    inKernels = true;
810                    canToNextLevel = true;
811                    break;
812                case ROCMMT_MAIN_PRINTF:
813                {
814                    YAMLPrintfVectorConsumer consumer(metadataInfo.printfInfos);
815                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer, true);
816                    break;
817                }
818                case ROCMMT_MAIN_VERSION:
819                {
820                    YAMLIntArrayConsumer<uint32_t> consumer(2, metadataInfo.version);
821                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer, true);
822                    break;
823                }
824                default:
825                    skipYAMLValue(ptr, end, lineNo, level);
826                    break;
827            }
828        }
829       
830        if (curLevel==1 && inKernels)
831        {
832            // enter to kernel level
833            if (ptr == end || *ptr != '-')
834                throw ParseException(lineNo, "No '-' before kernel object");
835            ptr++;
836            const char* afterMinus = ptr;
837            skipSpacesToLineEnd(ptr, end);
838            levels[++curLevel] = level + 1 + ptr-afterMinus;
839            level = levels[curLevel];
840            inKernel = true;
841           
842            kernels.push_back(ROCmKernelMetadata());
843        }
844       
845        if (curLevel==2 && inKernel)
846        {
847            // in kernel
848            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
849                        kernelMetadataKeywordsNum, kernelMetadataKeywords);
850           
851            ROCmKernelMetadata& kernel = kernels.back();
852            switch(keyIndex)
853            {
854                case ROCMMT_KERNEL_ARGS:
855                    inKernelArgs = true;
856                    canToNextLevel = true;
857                    break;
858                case ROCMMT_KERNEL_ATTRS:
859                    inKernelAttrs = true;
860                    canToNextLevel = true;
861                    break;
862                case ROCMMT_KERNEL_CODEPROPS:
863                    kernel.kernargSegmentSize = BINGEN64_DEFAULT;
864                    kernel.groupSegmentFixedSize = BINGEN64_DEFAULT;
865                    kernel.privateSegmentFixedSize = BINGEN64_DEFAULT;
866                    kernel.kernargSegmentAlign = BINGEN64_DEFAULT;
867                    kernel.wavefrontSize = BINGEN_DEFAULT;
868                    kernel.sgprsNum = BINGEN_DEFAULT;
869                    kernel.vgprsNum = BINGEN_DEFAULT;
870                    kernel.maxFlatWorkGroupSize = BINGEN64_DEFAULT;
871                    inKernelCodeProps = true;
872                    canToNextLevel = true;
873                    break;
874                case ROCMMT_KERNEL_LANGUAGE:
875                    kernel.language = parseYAMLStringValue(ptr, end, lineNo, level, true);
876                    break;
877                case ROCMMT_KERNEL_LANGUAGE_VERSION:
878                {
879                    YAMLIntArrayConsumer<uint32_t> consumer(2, kernel.langVersion);
880                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer);
881                    break;
882                }
883                case ROCMMT_KERNEL_NAME:
884                    kernel.name = parseYAMLStringValue(ptr, end, lineNo, level, true);
885                    break;
886                case ROCMMT_KERNEL_SYMBOLNAME:
887                    kernel.symbolName = parseYAMLStringValue(ptr, end, lineNo, level, true);
888                    break;
889                default:
890                    skipYAMLValue(ptr, end, lineNo, level);
891                    break;
892            }
893        }
894       
895        if (curLevel==3 && inKernelAttrs)
896        {
897            // in kernel attributes
898            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
899                        kernelAttrMetadataKeywordsNum, kernelAttrMetadataKeywords);
900           
901            ROCmKernelMetadata& kernel = kernels.back();
902            switch(keyIndex)
903            {
904                case ROCMMT_ATTRS_REQD_WORK_GROUP_SIZE:
905                {
906                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.reqdWorkGroupSize);
907                    parseYAMLValArray(ptr, end, lineNo, level, &consumer);
908                    break;
909                }
910                case ROCMMT_ATTRS_RUNTIME_HANDLE:
911                    kernel.runtimeHandle = parseYAMLStringValue(
912                                ptr, end, lineNo, level, true);
913                    break;
914                case ROCMMT_ATTRS_VECTYPEHINT:
915                    kernel.vecTypeHint = parseYAMLStringValue(
916                                ptr, end, lineNo, level, true);
917                    break;
918                case ROCMMT_ATTRS_WORK_GROUP_SIZE_HINT:
919                {
920                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.workGroupSizeHint);
921                    parseYAMLValArray(ptr, end, lineNo, level, &consumer, true);
922                    break;
923                }
924                default:
925                    skipYAMLValue(ptr, end, lineNo, level);
926                    break;
927            }
928        }
929       
930        if (curLevel==3 && inKernelCodeProps)
931        {
932            // in kernel codeProps
933            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
934                        kernelCodePropsKeywordsNum, kernelCodePropsKeywords);
935           
936            ROCmKernelMetadata& kernel = kernels.back();
937            switch(keyIndex)
938            {
939                case ROCMMT_CODEPROPS_FIXED_WORK_GROUP_SIZE:
940                {
941                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.fixedWorkGroupSize);
942                    parseYAMLValArray(ptr, end, lineNo, level, &consumer);
943                    break;
944                }
945                case ROCMMT_CODEPROPS_GROUP_SEGMENT_FIXED_SIZE:
946                    kernel.groupSegmentFixedSize =
947                                parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
948                    break;
949                case ROCMMT_CODEPROPS_KERNARG_SEGMENT_ALIGN:
950                    kernel.kernargSegmentAlign =
951                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
952                    break;
953                case ROCMMT_CODEPROPS_KERNARG_SEGMENT_SIZE:
954                    kernel.kernargSegmentSize =
955                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
956                    break;
957                case ROCMMT_CODEPROPS_MAX_FLAT_WORK_GROUP_SIZE:
958                    kernel.maxFlatWorkGroupSize =
959                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
960                    break;
961                case ROCMMT_CODEPROPS_NUM_SGPRS:
962                    kernel.sgprsNum = parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
963                    break;
964                case ROCMMT_CODEPROPS_NUM_SPILLED_SGPRS:
965                    kernel.spilledSgprs =
966                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
967                    break;
968                case ROCMMT_CODEPROPS_NUM_SPILLED_VGPRS:
969                    kernel.spilledVgprs =
970                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
971                    break;
972                case ROCMMT_CODEPROPS_NUM_VGPRS:
973                    kernel.vgprsNum = parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
974                    break;
975                case ROCMMT_CODEPROPS_PRIVATE_SEGMENT_FIXED_SIZE:
976                    kernel.privateSegmentFixedSize =
977                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
978                    break;
979                case ROCMMT_CODEPROPS_WAVEFRONT_SIZE:
980                    kernel.wavefrontSize =
981                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
982                    break;
983                default:
984                    skipYAMLValue(ptr, end, lineNo, level);
985                    break;
986            }
987        }
988       
989        if (curLevel==3 && inKernelArgs)
990        {
991            // enter to kernel argument level
992            if (ptr == end || *ptr != '-')
993                throw ParseException(lineNo, "No '-' before argument object");
994            ptr++;
995            const char* afterMinus = ptr;
996            skipSpacesToLineEnd(ptr, end);
997            levels[++curLevel] = level + 1 + ptr-afterMinus;
998            level = levels[curLevel];
999            inKernelArg = true;
1000           
1001            kernels.back().argInfos.push_back(ROCmKernelArgInfo{});
1002        }
1003       
1004        if (curLevel==4 && inKernelArg)
1005        {
1006            // in kernel argument
1007            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
1008                        kernelArgInfosKeywordsNum, kernelArgInfosKeywords);
1009           
1010            ROCmKernelArgInfo& kernelArg = kernels.back().argInfos.back();
1011            switch(keyIndex)
1012            {
1013                case ROCMMT_ARGS_ACCQUAL:
1014                case ROCMMT_ARGS_ACTUALACCQUAL:
1015                {
1016                    const std::string acc = trimStrSpaces(parseYAMLStringValue(
1017                                    ptr, end, lineNo, level, true));
1018                    size_t accIndex = 0;
1019                    for (; accIndex < 6; accIndex++)
1020                        if (::strcmp(rocmAccessQualifierTbl[accIndex], acc.c_str())==0)
1021                            break;
1022                    if (accIndex == 4)
1023                        throw ParseException(lineNo, "Wrong access qualifier");
1024                    if (keyIndex == ROCMMT_ARGS_ACCQUAL)
1025                        kernelArg.accessQual = ROCmAccessQual(accIndex);
1026                    else
1027                        kernelArg.actualAccessQual = ROCmAccessQual(accIndex);
1028                    break;
1029                }
1030                case ROCMMT_ARGS_ADDRSPACEQUAL:
1031                {
1032                    const std::string aspace = trimStrSpaces(parseYAMLStringValue(
1033                                    ptr, end, lineNo, level, true));
1034                    size_t aspaceIndex = 0;
1035                    for (; aspaceIndex < 6; aspaceIndex++)
1036                        if (::strcmp(rocmAddrSpaceTypesTbl[aspaceIndex],
1037                                    aspace.c_str())==0)
1038                            break;
1039                    if (aspaceIndex == 6)
1040                        throw ParseException(lineNo, "Wrong address space");
1041                    kernelArg.addressSpace = ROCmAddressSpace(aspaceIndex);
1042                    break;
1043                }
1044                case ROCMMT_ARGS_ALIGN:
1045                    kernelArg.align = parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1046                    break;
1047                case ROCMMT_ARGS_ISCONST:
1048                    kernelArg.isConst = parseYAMLBoolValue(ptr, end, lineNo, true);
1049                    break;
1050                case ROCMMT_ARGS_ISPIPE:
1051                    kernelArg.isPipe = parseYAMLBoolValue(ptr, end, lineNo, true);
1052                    break;
1053                case ROCMMT_ARGS_ISRESTRICT:
1054                    kernelArg.isRestrict = parseYAMLBoolValue(ptr, end, lineNo, true);
1055                    break;
1056                case ROCMMT_ARGS_ISVOLATILE:
1057                    kernelArg.isVolatile = parseYAMLBoolValue(ptr, end, lineNo, true);
1058                    break;
1059                case ROCMMT_ARGS_NAME:
1060                    kernelArg.name = parseYAMLStringValue(ptr, end, lineNo, level, true);
1061                    break;
1062                case ROCMMT_ARGS_POINTEE_ALIGN:
1063                    kernelArg.pointeeAlign =
1064                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1065                    break;
1066                case ROCMMT_ARGS_SIZE:
1067                    kernelArg.size = parseYAMLIntValue<uint64_t>(ptr, end, lineNo);
1068                    break;
1069                case ROCMMT_ARGS_TYPENAME:
1070                    kernelArg.typeName =
1071                                parseYAMLStringValue(ptr, end, lineNo, level, true);
1072                    break;
1073                case ROCMMT_ARGS_VALUEKIND:
1074                {
1075                    const std::string vkind = trimStrSpaces(parseYAMLStringValue(
1076                                ptr, end, lineNo, level, true));
1077                    const size_t vkindIndex = binaryMapFind(rocmValueKindNames,
1078                            rocmValueKindNames + rocmValueKindNamesNum, vkind.c_str(),
1079                            CStringLess()) - rocmValueKindNames;
1080                    // if unknown kind
1081                    if (vkindIndex == rocmValueKindNamesNum)
1082                        throw ParseException(lineNo, "Wrong argument value kind");
1083                    kernelArg.valueKind = rocmValueKindNames[vkindIndex].second;
1084                    break;
1085                }
1086                case ROCMMT_ARGS_VALUETYPE:
1087                {
1088                    const std::string vtype = trimStrSpaces(parseYAMLStringValue(
1089                                    ptr, end, lineNo, level, true));
1090                    const size_t vtypeIndex = binaryMapFind(rocmValueTypeNames,
1091                            rocmValueTypeNames + rocmValueTypeNamesNum, vtype.c_str(),
1092                            CStringLess()) - rocmValueTypeNames;
1093                    // if unknown type
1094                    if (vtypeIndex == rocmValueTypeNamesNum)
1095                        throw ParseException(lineNo, "Wrong argument value type");
1096                    kernelArg.valueKind = rocmValueKindNames[vtypeIndex].second;
1097                    break;
1098                }
1099                default:
1100                    skipYAMLValue(ptr, end, lineNo, level);
1101                    break;
1102            }
1103        }
1104    }
1105}
1106
1107/*
1108 * ROCm binary reader and generator
1109 */
1110
1111/* TODO: add support for various kernel code offset (now only 256 is supported) */
1112
1113ROCmBinary::ROCmBinary(size_t binaryCodeSize, cxbyte* binaryCode, Flags creationFlags)
1114        : ElfBinary64(binaryCodeSize, binaryCode, creationFlags),
1115          regionsNum(0), codeSize(0), code(nullptr),
1116          globalDataSize(0), globalData(nullptr), metadataSize(0), metadata(nullptr),
1117          newBinFormat(false)
1118{
1119    cxuint textIndex = SHN_UNDEF;
1120    try
1121    { textIndex = getSectionIndex(".text"); }
1122    catch(const Exception& ex)
1123    { } // ignore failed
1124    uint64_t codeOffset = 0;
1125    // find '.text' section
1126    if (textIndex!=SHN_UNDEF)
1127    {
1128        code = getSectionContent(textIndex);
1129        const Elf64_Shdr& textShdr = getSectionHeader(textIndex);
1130        codeSize = ULEV(textShdr.sh_size);
1131        codeOffset = ULEV(textShdr.sh_offset);
1132    }
1133   
1134    cxuint rodataIndex = SHN_UNDEF;
1135    try
1136    { rodataIndex = getSectionIndex(".rodata"); }
1137    catch(const Exception& ex)
1138    { } // ignore failed
1139    // find '.text' section
1140    if (rodataIndex!=SHN_UNDEF)
1141    {
1142        globalData = getSectionContent(rodataIndex);
1143        const Elf64_Shdr& rodataShdr = getSectionHeader(rodataIndex);
1144        globalDataSize = ULEV(rodataShdr.sh_size);
1145    }
1146   
1147    cxuint gpuConfigIndex = SHN_UNDEF;
1148    try
1149    { gpuConfigIndex = getSectionIndex(".AMDGPU.config"); }
1150    catch(const Exception& ex)
1151    { } // ignore failed
1152    newBinFormat = (gpuConfigIndex == SHN_UNDEF);
1153   
1154    // counts regions (symbol or kernel)
1155    regionsNum = 0;
1156    const size_t symbolsNum = getSymbolsNum();
1157    for (size_t i = 0; i < symbolsNum; i++)
1158    {
1159        // count regions number
1160        const Elf64_Sym& sym = getSymbol(i);
1161        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
1162        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
1163        if (ULEV(sym.st_shndx)==textIndex &&
1164            (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
1165                (bind==STB_GLOBAL && symType==STT_OBJECT)))
1166            regionsNum++;
1167    }
1168    if (code==nullptr && regionsNum!=0)
1169        throw BinException("No code if regions number is not zero");
1170    regions.reset(new ROCmRegion[regionsNum]);
1171    size_t j = 0;
1172    typedef std::pair<uint64_t, size_t> RegionOffsetEntry;
1173    std::unique_ptr<RegionOffsetEntry[]> symOffsets(new RegionOffsetEntry[regionsNum]);
1174   
1175    // get regions info
1176    for (size_t i = 0; i < symbolsNum; i++)
1177    {
1178        const Elf64_Sym& sym = getSymbol(i);
1179        if (ULEV(sym.st_shndx)!=textIndex)
1180            continue;   // if not in '.text' section
1181        const size_t value = ULEV(sym.st_value);
1182        if (value < codeOffset)
1183            throw BinException("Region offset is too small!");
1184        const size_t size = ULEV(sym.st_size);
1185       
1186        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
1187        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
1188        if (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
1189                (bind==STB_GLOBAL && symType==STT_OBJECT))
1190        {
1191            ROCmRegionType type = ROCmRegionType::DATA;
1192            // if kernel
1193            if (symType==STT_GNU_IFUNC) 
1194                type = ROCmRegionType::KERNEL;
1195            // if function kernel
1196            else if (symType==STT_FUNC)
1197                type = ROCmRegionType::FKERNEL;
1198            symOffsets[j] = std::make_pair(value, j);
1199            if (type!=ROCmRegionType::DATA && value+0x100 > codeOffset+codeSize)
1200                throw BinException("Kernel or code offset is too big!");
1201            regions[j++] = { getSymbolName(i), size, value, type };
1202        }
1203    }
1204    // sort regions by offset
1205    std::sort(symOffsets.get(), symOffsets.get()+regionsNum,
1206            [](const RegionOffsetEntry& a, const RegionOffsetEntry& b)
1207            { return a.first < b.first; });
1208    // checking distance between regions
1209    for (size_t i = 1; i <= regionsNum; i++)
1210    {
1211        size_t end = (i<regionsNum) ? symOffsets[i].first : codeOffset+codeSize;
1212        ROCmRegion& region = regions[symOffsets[i-1].second];
1213        if (region.type==ROCmRegionType::KERNEL && symOffsets[i-1].first+0x100 > end)
1214            throw BinException("Kernel size is too small!");
1215       
1216        const size_t regSize = end - symOffsets[i-1].first;
1217        if (region.size==0)
1218            region.size = regSize;
1219        else
1220            region.size = std::min(regSize, region.size);
1221    }
1222   
1223    // get metadata
1224    const size_t notesSize = getNotesSize();
1225    const cxbyte* noteContent = (const cxbyte*)getNotes();
1226   
1227    for (size_t offset = 0; offset < notesSize; )
1228    {
1229        const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
1230        size_t namesz = ULEV(nhdr->n_namesz);
1231        size_t descsz = ULEV(nhdr->n_descsz);
1232        if (usumGt(offset, namesz+descsz, notesSize))
1233            throw BinException("Note offset+size out of range");
1234       
1235        if (namesz==4 &&
1236            ::strcmp((const char*)noteContent+offset+ sizeof(Elf64_Nhdr), "AMD")==0)
1237        {
1238            const uint32_t noteType = ULEV(nhdr->n_type);
1239            if (noteType == 0xa)
1240            {
1241                metadata = (char*)(noteContent+offset+sizeof(Elf64_Nhdr) + 4);
1242                metadataSize = descsz;
1243            }
1244            else if (noteType == 0xb)
1245                target.assign((char*)(noteContent+offset+sizeof(Elf64_Nhdr) + 4), descsz);
1246        }
1247        size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
1248        offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
1249    }
1250   
1251    if (hasRegionMap())
1252    {
1253        // create region map
1254        regionsMap.resize(regionsNum);
1255        for (size_t i = 0; i < regionsNum; i++)
1256            regionsMap[i] = std::make_pair(regions[i].regionName, i);
1257        // sort region map
1258        mapSort(regionsMap.begin(), regionsMap.end());
1259    }
1260   
1261    if (hasMetadataInfo() && metadata != nullptr && metadataSize != 0)
1262    {
1263        metadataInfo.reset(new ROCmMetadata());
1264        parseROCmMetadata(metadataSize, metadata, *metadataInfo);
1265       
1266        if (hasKernelInfoMap())
1267        {
1268            const std::vector<ROCmKernelMetadata>& kernels = metadataInfo->kernels;
1269            kernelInfosMap.resize(kernels.size());
1270            for (size_t i = 0; i < kernelInfosMap.size(); i++)
1271                kernelInfosMap[i] = std::make_pair(kernels[i].name, i);
1272            // sort region map
1273            mapSort(kernelInfosMap.begin(), kernelInfosMap.end());
1274        }
1275    }
1276}
1277
1278/// determint GPU device from ROCm notes
1279GPUDeviceType ROCmBinary::determineGPUDeviceType(uint32_t& outArchMinor,
1280                     uint32_t& outArchStepping) const
1281{
1282    uint32_t archMajor = 0;
1283    uint32_t archMinor = 0;
1284    uint32_t archStepping = 0;
1285   
1286    {
1287        const cxbyte* noteContent = (const cxbyte*)getNotes();
1288        if (noteContent==nullptr)
1289            throw BinException("Missing notes in inner binary!");
1290        size_t notesSize = getNotesSize();
1291        // find note about AMDGPU
1292        for (size_t offset = 0; offset < notesSize; )
1293        {
1294            const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
1295            size_t namesz = ULEV(nhdr->n_namesz);
1296            size_t descsz = ULEV(nhdr->n_descsz);
1297            if (usumGt(offset, namesz+descsz, notesSize))
1298                throw BinException("Note offset+size out of range");
1299            if (ULEV(nhdr->n_type) == 0x3 && namesz==4 && descsz>=0x1a &&
1300                ::strcmp((const char*)noteContent+offset+sizeof(Elf64_Nhdr), "AMD")==0)
1301            {    // AMDGPU type
1302                const uint32_t* content = (const uint32_t*)
1303                        (noteContent+offset+sizeof(Elf64_Nhdr) + 4);
1304                archMajor = ULEV(content[1]);
1305                archMinor = ULEV(content[2]);
1306                archStepping = ULEV(content[3]);
1307            }
1308            size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
1309            offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
1310        }
1311    }
1312    // determine device type
1313    GPUDeviceType deviceType = getGPUDeviceTypeFromArchVersion(archMajor, archMinor,
1314                                    archStepping);
1315    outArchMinor = archMinor;
1316    outArchStepping = archStepping;
1317    return deviceType;
1318}
1319
1320const ROCmRegion& ROCmBinary::getRegion(const char* name) const
1321{
1322    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
1323                             regionsMap.end(), name);
1324    if (it == regionsMap.end())
1325        throw BinException("Can't find region name");
1326    return regions[it->second];
1327}
1328
1329const ROCmKernelMetadata& ROCmBinary::getKernelInfo(const char* name) const
1330{
1331    if (!hasMetadataInfo())
1332        throw BinException("Can't find kernel info name");
1333    RegionMap::const_iterator it = binaryMapFind(kernelInfosMap.begin(),
1334                             kernelInfosMap.end(), name);
1335    if (it == kernelInfosMap.end())
1336        throw BinException("Can't find kernel info name");
1337    return metadataInfo->kernels[it->second];
1338}
1339
1340// if ROCm binary
1341bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
1342{
1343    if (!isElfBinary(binarySize, binary))
1344        return false;
1345    if (binary[EI_CLASS] != ELFCLASS64)
1346        return false;
1347    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
1348    if (ULEV(ehdr->e_machine) != 0xe0)
1349        return false;
1350    return true;
1351}
1352
1353
1354void ROCmInput::addEmptyKernel(const char* kernelName)
1355{
1356    symbols.push_back({ kernelName, 0, 0, ROCmRegionType::KERNEL });
1357}
1358
1359/*
1360 * ROCm Binary Generator
1361 */
1362
1363ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr)
1364{ }
1365
1366ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
1367        : manageable(false), input(rocmInput)
1368{ }
1369
1370ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
1371        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
1372        size_t globalDataSize, const cxbyte* globalData,
1373        const std::vector<ROCmSymbolInput>& symbols)
1374{
1375    input = new ROCmInput{ deviceType, archMinor, archStepping, 0, false,
1376            globalDataSize, globalData, symbols, codeSize, code };
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        std::vector<ROCmSymbolInput>&& symbols)
1383{
1384    input = new ROCmInput{ deviceType, archMinor, archStepping, 0, false,
1385            globalDataSize, globalData, std::move(symbols), codeSize, code };
1386}
1387
1388ROCmBinGenerator::~ROCmBinGenerator()
1389{
1390    if (manageable)
1391        delete input;
1392}
1393
1394void ROCmBinGenerator::setInput(const ROCmInput* input)
1395{
1396    if (manageable)
1397        delete input;
1398    manageable = false;
1399    this->input = input;
1400}
1401
1402// ELF notes contents
1403static const cxbyte noteDescType1[8] =
1404{ 2, 0, 0, 0, 1, 0, 0, 0 };
1405
1406static const cxbyte noteDescType3[27] =
1407{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1408  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
1409
1410static inline void addMainSectionToTable(cxuint& sectionsNum, uint16_t* builtinTable,
1411                cxuint elfSectId)
1412{ builtinTable[elfSectId - ELFSECTID_START] = sectionsNum++; }
1413
1414void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
1415             Array<cxbyte>* aPtr) const
1416{
1417    AMDGPUArchVersion amdGpuArchValues = getGPUArchVersion(input->deviceType,
1418                GPUArchVersionTable::ROCM);
1419    if (input->archMinor!=UINT32_MAX)
1420        amdGpuArchValues.minor = input->archMinor;
1421    if (input->archStepping!=UINT32_MAX)
1422        amdGpuArchValues.stepping = input->archStepping;
1423   
1424    const char* comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
1425    uint32_t commentSize = ::strlen(comment);
1426    if (input->comment!=nullptr)
1427    {
1428        // if comment, store comment section
1429        comment = input->comment;
1430        commentSize = input->commentSize;
1431        if (commentSize==0)
1432            commentSize = ::strlen(comment);
1433    }
1434   
1435    uint32_t eflags = input->newBinFormat ? 2 : 0;
1436    if (input->eflags != BINGEN_DEFAULT)
1437        eflags = input->eflags;
1438   
1439    ElfBinaryGen64 elfBinGen64({ 0U, 0U, 0x40, 0, ET_DYN,
1440            0xe0, EV_CURRENT, UINT_MAX, 0, eflags },
1441            true, true, true, PHREGION_FILESTART);
1442   
1443    uint16_t mainBuiltinSectTable[ROCMSECTID_MAX-ELFSECTID_START+1];
1444    std::fill(mainBuiltinSectTable,
1445              mainBuiltinSectTable + ROCMSECTID_MAX-ELFSECTID_START+1, SHN_UNDEF);
1446    cxuint mainSectionsNum = 1;
1447   
1448    // generate main builtin section table (for section id translation)
1449    if (input->newBinFormat)
1450        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
1451    if (input->globalData != nullptr)
1452        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_RODATA);
1453    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSYM);
1454    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_HASH);
1455    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSTR);
1456    const cxuint execProgHeaderRegionIndex = mainSectionsNum;
1457    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_TEXT);
1458    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_DYNAMIC);
1459    if (!input->newBinFormat)
1460    {
1461        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
1462        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_GPUCONFIG);
1463    }
1464    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_COMMENT);
1465    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SYMTAB);
1466    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SHSTRTAB);
1467    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_STRTAB);
1468   
1469    // add symbols (kernels, function kernels and data symbols)
1470    elfBinGen64.addSymbol(ElfSymbol64("_DYNAMIC",
1471                  mainBuiltinSectTable[ROCMSECTID_DYNAMIC-ELFSECTID_START],
1472                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
1473    const uint16_t textSectIndex = mainBuiltinSectTable[ELFSECTID_TEXT-ELFSECTID_START];
1474    for (const ROCmSymbolInput& symbol: input->symbols)
1475    {
1476        ElfSymbol64 elfsym;
1477        switch (symbol.type)
1478        {
1479            case ROCmRegionType::KERNEL:
1480                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1481                      ELF64_ST_INFO(STB_GLOBAL, STT_GNU_IFUNC), 0, true,
1482                      symbol.offset, symbol.size);
1483                break;
1484            case ROCmRegionType::FKERNEL:
1485                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1486                      ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 0, true,
1487                      symbol.offset, symbol.size);
1488                break;
1489            case ROCmRegionType::DATA:
1490                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1491                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
1492                      symbol.offset, symbol.size);
1493                break;
1494            default:
1495                break;
1496        }
1497        // add to symbols and dynamic symbols table
1498        elfBinGen64.addSymbol(elfsym);
1499        elfBinGen64.addDynSymbol(elfsym);
1500    }
1501   
1502    static const int32_t dynTags[] = {
1503        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
1504    elfBinGen64.addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
1505   
1506    // elf program headers
1507    elfBinGen64.addProgramHeader({ PT_PHDR, PF_R, 0, 1,
1508                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1509    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART,
1510                    execProgHeaderRegionIndex,
1511                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
1512    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_X, execProgHeaderRegionIndex, 1,
1513                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1514    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
1515                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1516    elfBinGen64.addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
1517                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
1518    elfBinGen64.addProgramHeader({ PT_GNU_RELRO, PF_R, execProgHeaderRegionIndex+1, 1,
1519                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
1520    elfBinGen64.addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
1521                    true, 0, 0, 0 });
1522   
1523    if (input->newBinFormat)
1524        // program header for note (new binary format)
1525        elfBinGen64.addProgramHeader({ PT_NOTE, PF_R, 1, 1, true,
1526                    Elf64Types::nobase, Elf64Types::nobase, 0, 4 });
1527   
1528    std::string target = input->target.c_str();
1529    if (target.empty() && !input->targetTripple.empty())
1530    {
1531        target = input->targetTripple.c_str();
1532        char dbuf[20];
1533        snprintf(dbuf, 20, "-gfx%u%u%u", amdGpuArchValues.major, amdGpuArchValues.minor,
1534                 amdGpuArchValues.stepping);
1535        target += dbuf;
1536    }
1537    // elf notes
1538    elfBinGen64.addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
1539    std::unique_ptr<cxbyte[]> noteBuf(new cxbyte[0x1b]);
1540    ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
1541    SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
1542    SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
1543    SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
1544    elfBinGen64.addNote({"AMD", 0x1b, noteBuf.get(), 3U});
1545    if (!target.empty())
1546        elfBinGen64.addNote({"AMD", target.size(), (const cxbyte*)target.c_str(), 0xbU});
1547    if (input->metadataSize != 0)
1548        elfBinGen64.addNote({"AMD", input->metadataSize,
1549                (const cxbyte*)input->metadata, 0xaU});
1550   
1551    /// region and sections
1552    elfBinGen64.addRegion(ElfRegion64::programHeaderTable());
1553    if (input->newBinFormat)
1554        elfBinGen64.addRegion(ElfRegion64::noteSection());
1555    if (input->globalData != nullptr)
1556        elfBinGen64.addRegion(ElfRegion64(input->globalDataSize, input->globalData, 4,
1557                ".rodata", SHT_PROGBITS, SHF_ALLOC, 0, 0, Elf64Types::nobase));
1558   
1559    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
1560                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, 1, Elf64Types::nobase));
1561    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
1562                ".hash", SHT_HASH, SHF_ALLOC,
1563                mainBuiltinSectTable[ELFSECTID_DYNSYM-ELFSECTID_START], 0,
1564                Elf64Types::nobase));
1565    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
1566                SHF_ALLOC, 0, 0, Elf64Types::nobase));
1567    // '.text' with alignment=4096
1568    elfBinGen64.addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
1569              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
1570              Elf64Types::nobase, 0, false, 256));
1571    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
1572                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE,
1573                mainBuiltinSectTable[ELFSECTID_DYNSTR-ELFSECTID_START], 0,
1574                Elf64Types::nobase, 0, false, 8));
1575    if (!input->newBinFormat)
1576    {
1577        elfBinGen64.addRegion(ElfRegion64::noteSection());
1578        elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1,
1579                    ".AMDGPU.config", SHT_PROGBITS, 0));
1580    }
1581    elfBinGen64.addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
1582              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
1583    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
1584                ".symtab", SHT_SYMTAB, 0, 0, 2));
1585    elfBinGen64.addRegion(ElfRegion64::shstrtabSection());
1586    elfBinGen64.addRegion(ElfRegion64::strtabSection());
1587    elfBinGen64.addRegion(ElfRegion64::sectionHeaderTable());
1588   
1589    /* extra sections */
1590    for (const BinSection& section: input->extraSections)
1591        elfBinGen64.addRegion(ElfRegion64(section, mainBuiltinSectTable,
1592                         ROCMSECTID_MAX, mainSectionsNum));
1593    /* extra symbols */
1594    for (const BinSymbol& symbol: input->extraSymbols)
1595        elfBinGen64.addSymbol(ElfSymbol64(symbol, mainBuiltinSectTable,
1596                         ROCMSECTID_MAX, mainSectionsNum));
1597   
1598    size_t binarySize = elfBinGen64.countSize();
1599    /****
1600     * prepare for write binary to output
1601     ****/
1602    std::unique_ptr<std::ostream> outStreamHolder;
1603    std::ostream* os = nullptr;
1604    if (aPtr != nullptr)
1605    {
1606        aPtr->resize(binarySize);
1607        outStreamHolder.reset(
1608                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
1609        os = outStreamHolder.get();
1610    }
1611    else if (vPtr != nullptr)
1612    {
1613        vPtr->resize(binarySize);
1614        outStreamHolder.reset(new VectorOStream(*vPtr));
1615        os = outStreamHolder.get();
1616    }
1617    else // from argument
1618        os = osPtr;
1619   
1620    const std::ios::iostate oldExceptions = os->exceptions();
1621    try
1622    {
1623    os->exceptions(std::ios::failbit | std::ios::badbit);
1624    /****
1625     * write binary to output
1626     ****/
1627    FastOutputBuffer bos(256, *os);
1628    elfBinGen64.generate(bos);
1629    assert(bos.getWritten() == binarySize);
1630    }
1631    catch(...)
1632    {
1633        os->exceptions(oldExceptions);
1634        throw;
1635    }
1636    os->exceptions(oldExceptions);
1637}
1638
1639void ROCmBinGenerator::generate(Array<cxbyte>& array) const
1640{
1641    generateInternal(nullptr, nullptr, &array);
1642}
1643
1644void ROCmBinGenerator::generate(std::ostream& os) const
1645{
1646    generateInternal(&os, nullptr, nullptr);
1647}
1648
1649void ROCmBinGenerator::generate(std::vector<char>& v) const
1650{
1651    generateInternal(nullptr, &v, nullptr);
1652}
Note: See TracBrowser for help on using the repository browser.