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

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

CLRadeonExtender: ROCmBin: add parameter singleValue to parsing routines. Skip to newline after parsing value.
Ignore unknown keywords in metadata (by skipping).

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