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

Last change on this file since 3730 was 3730, checked in by matszpk, 22 months ago

CLRadeonExtender: ROCm: change syntax of the pseudo-op '.md_symname'. initialize version of metadata to 1.0. fixed parsing of the '.arg' pseudo-op.
fixed string escaping in ROCm metadata. Escape ':' printf format in ROCm metadata. Use default symname if not supplied.

File size: 80.1 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 <unordered_set>
30#include <CLRX/amdbin/ElfBinaries.h>
31#include <CLRX/utils/Utilities.h>
32#include <CLRX/utils/MemAccess.h>
33#include <CLRX/utils/InputOutput.h>
34#include <CLRX/utils/Containers.h>
35#include <CLRX/amdbin/ROCmBinaries.h>
36
37using namespace CLRX;
38
39/*
40 * ROCm metadata YAML parser
41 */
42
43void ROCmKernelMetadata::initialize()
44{
45    langVersion[0] = langVersion[1] = BINGEN_NOTSUPPLIED;
46    reqdWorkGroupSize[0] = reqdWorkGroupSize[1] = reqdWorkGroupSize[2] = 0;
47    workGroupSizeHint[0] = workGroupSizeHint[1] = workGroupSizeHint[2] = 0;
48    kernargSegmentSize = BINGEN64_NOTSUPPLIED;
49    groupSegmentFixedSize = BINGEN64_NOTSUPPLIED;
50    privateSegmentFixedSize = BINGEN64_NOTSUPPLIED;
51    kernargSegmentAlign = BINGEN64_NOTSUPPLIED;
52    wavefrontSize = BINGEN_NOTSUPPLIED;
53    sgprsNum = BINGEN_NOTSUPPLIED;
54    vgprsNum = BINGEN_NOTSUPPLIED;
55    maxFlatWorkGroupSize = BINGEN64_NOTSUPPLIED;
56    fixedWorkGroupSize[0] = fixedWorkGroupSize[1] = fixedWorkGroupSize[2] = 0;
57    spilledSgprs = BINGEN_NOTSUPPLIED;
58    spilledVgprs = BINGEN_NOTSUPPLIED;
59}
60
61void ROCmMetadata::initialize()
62{
63    version[0] = 1;
64    version[1] = 0;
65}
66
67// return trailing spaces
68static size_t skipSpacesAndComments(const char*& ptr, const char* end, size_t& lineNo)
69{
70    const char* lineStart = ptr;
71    while (ptr != end)
72    {
73        lineStart = ptr;
74        while (ptr != end && *ptr!='\n' && isSpace(*ptr)) ptr++;
75        if (ptr == end)
76            break; // end of stream
77        if (*ptr=='#')
78        {
79            // skip comment
80            while (ptr != end && *ptr!='\n') ptr++;
81            if (ptr == end)
82                return 0; // no trailing spaces and end
83        }
84        else if (*ptr!='\n')
85            break; // no comment and no end of line
86        else
87        {
88            ptr++;
89            lineNo++; // next line
90        }
91    }
92    return ptr - lineStart;
93}
94
95static inline void skipSpacesToLineEnd(const char*& ptr, const char* end)
96{
97    while (ptr != end && *ptr!='\n' && isSpace(*ptr)) ptr++;
98}
99
100static void skipSpacesToNextLine(const char*& ptr, const char* end, size_t& lineNo)
101{
102    skipSpacesToLineEnd(ptr, end);
103    if (ptr != end && *ptr != '\n' && *ptr!='#')
104        throw ParseException(lineNo, "Garbages at line");
105    if (ptr != end && *ptr == '#')
106        // skip comment at end of line
107        while (ptr!=end && *ptr!='\n') ptr++;
108    if (ptr!=end)
109    {   // newline
110        ptr++;
111        lineNo++;
112    }
113}
114
115// parse YAML key (keywords - recognized keys)
116static size_t parseYAMLKey(const char*& ptr, const char* end, size_t lineNo,
117            size_t keywordsNum, const char** keywords)
118{
119    const char* keyPtr = ptr;
120    while (ptr != end && (isAlnum(*ptr) || *ptr=='_')) ptr++;
121    if (keyPtr == end)
122        throw ParseException(lineNo, "Expected key name");
123    const char* keyEnd = ptr;
124    skipSpacesToLineEnd(ptr, end);
125    if (ptr == end || *ptr!=':')
126        throw ParseException(lineNo, "Expected colon");
127    ptr++;
128    const char* afterColon = ptr;
129    skipSpacesToLineEnd(ptr, end);
130    if (afterColon == ptr && ptr != end && *ptr!='\n')
131        // only if not immediate newline
132        throw ParseException(lineNo, "After key and colon must be space");
133    CString keyword(keyPtr, keyEnd);
134    const size_t index = binaryFind(keywords, keywords+keywordsNum,
135                        keyword.c_str(), CStringLess()) - keywords;
136    return index;
137}
138
139// parse YAML integer value
140template<typename T>
141static T parseYAMLIntValue(const char*& ptr, const char* end, size_t& lineNo,
142                bool singleValue = false)
143{
144    skipSpacesToLineEnd(ptr, end);
145    if (ptr == end || *ptr=='\n')
146        throw ParseException(lineNo, "Expected integer value");
147    T value = 0;
148    try
149    { value = cstrtovCStyle<T>(ptr, end, ptr); }
150    catch(const ParseException& ex)
151    { throw ParseException(lineNo, ex.what()); }
152   
153    if (singleValue)
154        skipSpacesToNextLine(ptr, end, lineNo);
155    return value;
156}
157
158// parse YAML boolean value
159static bool parseYAMLBoolValue(const char*& ptr, const char* end, size_t& lineNo,
160        bool singleValue = false)
161{
162    skipSpacesToLineEnd(ptr, end);
163    if (ptr == end || *ptr=='\n')
164        throw ParseException(lineNo, "Expected boolean value");
165   
166    const char* wordPtr = ptr;
167    while(ptr != end && isAlnum(*ptr)) ptr++;
168    CString word(wordPtr, ptr);
169   
170    bool value = false;
171    bool isSet = false;
172    for (const char* v: { "1", "true", "t", "on", "yes", "y"})
173        if (::strcasecmp(word.c_str(), v) == 0)
174        {
175            isSet = true;
176            value = true;
177            break;
178        }
179    if (!isSet)
180        for (const char* v: { "0", "false", "f", "off", "no", "n"})
181            if (::strcasecmp(word.c_str(), v) == 0)
182            {
183                isSet = true;
184                value = false;
185                break;
186            }
187    if (!isSet)
188        throw ParseException(lineNo, "This is not boolean value");
189   
190    if (singleValue)
191        skipSpacesToNextLine(ptr, end, lineNo);
192    return value;
193}
194
195// trim spaces (remove spaces from start and end)
196static std::string trimStrSpaces(const std::string& str)
197{
198    size_t i = 0;
199    const size_t sz = str.size();
200    while (i!=sz && isSpace(str[i])) i++;
201    if (i == sz) return "";
202    size_t j = sz-1;
203    while (j>i && isSpace(str[j])) j--;
204    return str.substr(i, j-i+1);
205}
206
207static std::string parseYAMLString(const char*& linePtr, const char* end,
208            size_t& lineNo)
209{
210    std::string strarray;
211    if (linePtr == end || (*linePtr != '"' && *linePtr != '\''))
212    {
213        while (linePtr != end && !isSpace(*linePtr) && *linePtr != ',') linePtr++;
214        throw ParseException(lineNo, "Expected string");
215    }
216    const char termChar = *linePtr;
217    linePtr++;
218   
219    // main loop, where is character parsing
220    while (linePtr != end && *linePtr != termChar)
221    {
222        if (*linePtr == '\\')
223        {
224            // escape
225            linePtr++;
226            uint16_t value;
227            if (linePtr == end)
228                throw ParseException(lineNo, "Unterminated character of string");
229            if (*linePtr == 'x')
230            {
231                // hex literal
232                linePtr++;
233                if (linePtr == end)
234                    throw ParseException(lineNo, "Unterminated character of string");
235                value = 0;
236                if (isXDigit(*linePtr))
237                    for (; linePtr != end; linePtr++)
238                    {
239                        cxuint digit;
240                        if (*linePtr >= '0' && *linePtr <= '9')
241                            digit = *linePtr-'0';
242                        else if (*linePtr >= 'a' && *linePtr <= 'f')
243                            digit = *linePtr-'a'+10;
244                        else if (*linePtr >= 'A' && *linePtr <= 'F')
245                            digit = *linePtr-'A'+10;
246                        else
247                            break;
248                        value = (value<<4) + digit;
249                    }
250                else
251                    throw ParseException(lineNo, "Expected hexadecimal character code");
252                value &= 0xff;
253            }
254            else if (isODigit(*linePtr))
255            {
256                // octal literal
257                value = 0;
258                for (cxuint i = 0; linePtr != end && i < 3; i++, linePtr++)
259                {
260                    if (!isODigit(*linePtr))
261                        break;
262                    value = (value<<3) + uint64_t(*linePtr-'0');
263                    // checking range
264                    if (value > 255)
265                        throw ParseException(lineNo, "Octal code out of range");
266                }
267            }
268            else
269            {
270                // normal escapes
271                const char c = *linePtr++;
272                switch (c)
273                {
274                    case 'a':
275                        value = '\a';
276                        break;
277                    case 'b':
278                        value = '\b';
279                        break;
280                    case 'r':
281                        value = '\r';
282                        break;
283                    case 'n':
284                        value = '\n';
285                        break;
286                    case 'f':
287                        value = '\f';
288                        break;
289                    case 'v':
290                        value = '\v';
291                        break;
292                    case 't':
293                        value = '\t';
294                        break;
295                    case '\\':
296                        value = '\\';
297                        break;
298                    case '\'':
299                        value = '\'';
300                        break;
301                    case '\"':
302                        value = '\"';
303                        break;
304                    default:
305                        value = c;
306                }
307            }
308            strarray.push_back(value);
309        }
310        else // regular character
311        {
312            if (*linePtr=='\n')
313                lineNo++;
314            strarray.push_back(*linePtr++);
315        }
316    }
317    if (linePtr == end)
318        throw ParseException(lineNo, "Unterminated string");
319    linePtr++;
320    return strarray;
321}
322
323static std::string parseYAMLStringValue(const char*& ptr, const char* end, size_t& lineNo,
324                    cxuint prevIndent, bool singleValue = false, bool blockAccept = true)
325{
326    skipSpacesToLineEnd(ptr, end);
327    if (ptr == end)
328        return "";
329    std::string buf;
330    if (*ptr=='"' || *ptr== '\'')
331        buf = parseYAMLString(ptr, end, lineNo);
332    // otherwise parse stream
333    else if (*ptr == '|' || *ptr == '>')
334    {
335        if (!blockAccept)
336            throw ParseException(lineNo, "Illegal block string start");
337        // multiline
338        bool newLineFold = *ptr=='>';
339        ptr++;
340        skipSpacesToLineEnd(ptr, end);
341        if (ptr!=end && *ptr!='\n')
342            throw ParseException(lineNo, "Garbages at string block");
343        if (ptr == end)
344            return ""; // end
345        lineNo++;
346        ptr++; // skip newline
347        const char* lineStart = ptr;
348        skipSpacesToLineEnd(ptr, end);
349        size_t indent = ptr - lineStart;
350        if (indent <= prevIndent)
351            throw ParseException(lineNo, "Unindented string block");
352       
353        std::string buf;
354        while(ptr != end)
355        {
356            const char* strStart = ptr;
357            while (ptr != end && *ptr!='\n') ptr++;
358            buf.append(strStart, ptr);
359           
360            if (ptr != end) // if new line
361            {
362                lineNo++;
363                ptr++;
364            }
365            else // end of stream
366                break;
367           
368            const char* lineStart = ptr;
369            skipSpacesToLineEnd(ptr, end);
370            bool emptyLines = false;
371            while (size_t(ptr - lineStart) <= indent)
372            {
373                if (ptr != end && *ptr=='\n')
374                {
375                    // empty line
376                    buf.append("\n");
377                    ptr++;
378                    lineNo++;
379                    lineStart = ptr;
380                    skipSpacesToLineEnd(ptr, end);
381                    emptyLines = true;
382                    continue;
383                }
384                // if smaller indent
385                if (size_t(ptr - lineStart) < indent)
386                {
387                    buf.append("\n"); // always add newline at last line
388                    if (ptr != end)
389                        ptr = lineStart;
390                    return buf;
391                }
392                else // if this same and not end of line
393                    break;
394            }
395           
396            if (!emptyLines || !newLineFold)
397                // add missing newline after line with text
398                // only if no emptyLines or no newLineFold
399                buf.append(newLineFold ? " " : "\n");
400            // to indent
401            ptr = lineStart + indent;
402        }
403        return buf;
404    }
405    else
406    {
407        // single line string (unquoted)
408        const char* strStart = ptr;
409        // automatically trim spaces at ends
410        const char* strEnd = ptr;
411        while (ptr != end && *ptr!='\n' && *ptr!='#')
412        {
413            if (!isSpace(*ptr))
414                strEnd = ptr; // to trim at end
415            ptr++;
416        }
417        if (strEnd != end && !isSpace(*strEnd))
418            strEnd++;
419       
420        buf.assign(strStart, strEnd);
421    }
422   
423    if (singleValue)
424        skipSpacesToNextLine(ptr, end, lineNo);
425    return buf;
426}
427
428/// element consumer class
429class CLRX_INTERNAL YAMLElemConsumer
430{
431public:
432    virtual void consume(const char*& ptr, const char* end, size_t& lineNo,
433                cxuint prevIndent, bool singleValue, bool blockAccept) = 0;
434};
435
436static void parseYAMLValArray(const char*& ptr, const char* end, size_t& lineNo,
437            size_t prevIndent, YAMLElemConsumer* elemConsumer, bool singleValue = false)
438{
439    skipSpacesToLineEnd(ptr, end);
440    if (ptr == end)
441        return;
442   
443    if (*ptr == '[')
444    {
445        // parse array []
446        ptr++;
447        skipSpacesAndComments(ptr, end, lineNo);
448        while (ptr != end)
449        {
450            // parse in line
451            elemConsumer->consume(ptr, end, lineNo, 0, false, false);
452            skipSpacesAndComments(ptr, end, lineNo);
453            if (ptr!=end && *ptr==']')
454                // just end
455                break;
456            else if (ptr==end || *ptr!=',')
457                throw ParseException(lineNo, "Expected ','");
458            ptr++;
459            skipSpacesAndComments(ptr, end, lineNo);
460        }
461        if (ptr == end)
462            throw ParseException(lineNo, "Unterminated array");
463        ptr++;
464       
465        if (singleValue)
466            skipSpacesToNextLine(ptr, end, lineNo);
467        return;
468    }
469    // parse sequence
470    size_t oldLineNo = lineNo;
471    size_t indent0 = skipSpacesAndComments(ptr, end, lineNo);
472    if (ptr == end || lineNo == oldLineNo)
473        throw ParseException(lineNo, "Expected sequence of values");
474   
475    if (indent0 < prevIndent)
476        throw ParseException(lineNo, "Unindented sequence of objects");
477   
478    // main loop to parse sequence
479    while (ptr != end)
480    {
481        if (*ptr != '-')
482            throw ParseException(lineNo, "No '-' before element value");
483        ptr++;
484        const char* afterMinus = ptr;
485        skipSpacesToLineEnd(ptr, end);
486        if (afterMinus == ptr)
487            throw ParseException(lineNo, "No spaces after '-'");
488        elemConsumer->consume(ptr, end, lineNo, indent0, true, true);
489       
490        size_t indent = skipSpacesAndComments(ptr, end, lineNo);
491        if (indent < indent0)
492        {
493            // if parent level
494            ptr -= indent;
495            break;
496        }
497        if (indent != indent0)
498            throw ParseException(lineNo, "Wrong indentation of element");
499    }
500}
501
502// integer element consumer
503template<typename T>
504class CLRX_INTERNAL YAMLIntArrayConsumer: public YAMLElemConsumer
505{
506private:
507    size_t elemsNum;
508    size_t requiredElemsNum;
509public:
510    T* array;
511   
512    YAMLIntArrayConsumer(size_t reqElemsNum, T* _array)
513            : elemsNum(0), requiredElemsNum(reqElemsNum), array(_array)
514    { }
515   
516    virtual void consume(const char*& ptr, const char* end, size_t& lineNo,
517                cxuint prevIndent, bool singleValue, bool blockAccept)
518    {
519        if (elemsNum == requiredElemsNum)
520            throw ParseException(lineNo, "Too many elements");
521        try
522        { array[elemsNum] = cstrtovCStyle<T>(ptr, end, ptr); }
523        catch(const ParseException& ex)
524        { throw ParseException(lineNo, ex.what()); }
525        elemsNum++;
526        if (singleValue)
527            skipSpacesToNextLine(ptr, end, lineNo);
528    }
529};
530
531// printf info string consumer
532class CLRX_INTERNAL YAMLPrintfVectorConsumer: public YAMLElemConsumer
533{
534private:
535    std::unordered_set<cxuint> printfIds;
536public:
537    std::vector<ROCmPrintfInfo>& printfInfos;
538   
539    YAMLPrintfVectorConsumer(std::vector<ROCmPrintfInfo>& _printInfos)
540        : printfInfos(_printInfos)
541    { }
542   
543    virtual void consume(const char*& ptr, const char* end, size_t& lineNo,
544                cxuint prevIndent, bool singleValue, bool blockAccept)
545    {
546        const size_t oldLineNo = lineNo;
547        std::string str = parseYAMLStringValue(ptr, end, lineNo, prevIndent,
548                                singleValue, blockAccept);
549        // parse printf string
550        ROCmPrintfInfo printfInfo{};
551       
552        const char* ptr2 = str.c_str();
553        const char* end2 = str.c_str() + str.size();
554        skipSpacesToLineEnd(ptr2, end2);
555        try
556        { printfInfo.id = cstrtovCStyle<uint32_t>(ptr2, end2, ptr2); }
557        catch(const ParseException& ex)
558        { throw ParseException(oldLineNo, ex.what()); }
559       
560        // check printf id uniqueness
561        if (!printfIds.insert(printfInfo.id).second)
562            throw ParseException(oldLineNo, "Duplicate of printf id");
563       
564        skipSpacesToLineEnd(ptr2, end2);
565        if (ptr2==end || *ptr2!=':')
566            throw ParseException(oldLineNo, "No colon after printf callId");
567        ptr2++;
568        skipSpacesToLineEnd(ptr2, end2);
569        uint32_t argsNum = cstrtovCStyle<uint32_t>(ptr2, end2, ptr2);
570        skipSpacesToLineEnd(ptr2, end2);
571        if (ptr2==end || *ptr2!=':')
572            throw ParseException(oldLineNo, "No colon after printf argsNum");
573        ptr2++;
574       
575        printfInfo.argSizes.resize(argsNum);
576       
577        // parse arg sizes
578        for (size_t i = 0; i < argsNum; i++)
579        {
580            skipSpacesToLineEnd(ptr2, end2);
581            printfInfo.argSizes[i] = cstrtovCStyle<uint32_t>(ptr2, end2, ptr2);
582            skipSpacesToLineEnd(ptr2, end2);
583            if (ptr2==end || *ptr2!=':')
584                throw ParseException(lineNo, "No colon after printf argsNum");
585            ptr2++;
586        }
587        // format
588        printfInfo.format.assign(ptr2, end2);
589       
590        printfInfos.push_back(printfInfo);
591    }
592};
593
594// skip YAML value after key
595static void skipYAMLValue(const char*& ptr, const char* end, size_t& lineNo,
596                cxuint prevIndent, bool singleValue = true)
597{
598    skipSpacesToLineEnd(ptr, end);
599    if (ptr==end || (*ptr!='\'' && *ptr!='"' && *ptr!='|' && *ptr!='>' && *ptr !='[' &&
600                *ptr!='#' && *ptr!='\n'))
601    {
602        while (ptr!=end && *ptr!='\n') ptr++;
603        skipSpacesToNextLine(ptr, end, lineNo);
604        return;
605    }
606    // string
607    if (*ptr=='\'' || *ptr=='"')
608    {
609        const char delim = *ptr++;
610        bool escape = false;
611        while(ptr!=end && (escape || *ptr!=delim))
612        {
613            if (!escape && *ptr=='\\')
614                escape = true;
615            else if (escape)
616                escape = false;
617            if (*ptr=='\n') lineNo++;
618            ptr++;
619        }
620        if (ptr==end)
621            throw ParseException(lineNo, "Unterminated string");
622        ptr++;
623        if (singleValue)
624            skipSpacesToNextLine(ptr, end, lineNo);
625    }
626    else if (*ptr=='[')
627    {   // otherwise [array]
628        ptr++;
629        skipSpacesAndComments(ptr, end, lineNo);
630        while (ptr != end)
631        {
632            // parse in line
633            if (ptr!=end && (*ptr=='\'' || *ptr=='"'))
634                // skip YAML string
635                skipYAMLValue(ptr, end, lineNo, 0, false);
636            else
637                while (ptr!=end && *ptr!='\n' &&
638                            *ptr!='#' && *ptr!=',' && *ptr!=']') ptr++;
639            skipSpacesAndComments(ptr, end, lineNo);
640           
641            if (ptr!=end && *ptr==']')
642                // just end
643                break;
644            else if (ptr!=end && *ptr!=',')
645                throw ParseException(lineNo, "Expected ','");
646            ptr++;
647            skipSpacesAndComments(ptr, end, lineNo);
648        }
649        if (ptr == end)
650            throw ParseException(lineNo, "Unterminated array");
651        ptr++;
652        skipSpacesToNextLine(ptr, end, lineNo);
653    }
654    else
655    {   // block value
656        bool blockValue = false;
657        if (ptr!=end && (*ptr=='|' || *ptr=='>'))
658        {
659            ptr++; // skip '|' or '>'
660            blockValue = true;
661        }
662        if (ptr!=end && *ptr=='#')
663            while (ptr!=end && *ptr!='\n') ptr++;
664        else
665            skipSpacesToLineEnd(ptr, end);
666        if (ptr!=end && *ptr!='\n')
667            throw ParseException(lineNo, "Garbages before block or children");
668        ptr++;
669        lineNo++;
670        // skip all lines indented beyound previous level
671        while (ptr != end)
672        {
673            const char* lineStart = ptr;
674            skipSpacesToLineEnd(ptr, end);
675            if (ptr == end)
676            {
677                ptr++;
678                lineNo++;
679                continue;
680            }
681            if (size_t(ptr-lineStart) <= prevIndent && *ptr!='\n' &&
682                (blockValue || *ptr!='#'))
683                // if indent is short and not empty line (same spaces) or
684                // or with only comment and not blockValue
685            {
686                ptr = lineStart;
687                break;
688            }
689           
690            while (ptr!=end && *ptr!='\n') ptr++;
691            if (ptr!=end)
692            {
693                lineNo++;
694                ptr++;
695            }
696        }
697    }
698}
699
700enum {
701    ROCMMT_MAIN_KERNELS = 0, ROCMMT_MAIN_PRINTF,  ROCMMT_MAIN_VERSION
702};
703
704static const char* mainMetadataKeywords[] =
705{
706    "Kernels", "Printf", "Version"
707};
708
709static const size_t mainMetadataKeywordsNum =
710        sizeof(mainMetadataKeywords) / sizeof(const char*);
711
712enum {
713    ROCMMT_KERNEL_ARGS = 0, ROCMMT_KERNEL_ATTRS, ROCMMT_KERNEL_CODEPROPS,
714    ROCMMT_KERNEL_LANGUAGE, ROCMMT_KERNEL_LANGUAGE_VERSION,
715    ROCMMT_KERNEL_NAME, ROCMMT_KERNEL_SYMBOLNAME
716};
717
718static const char* kernelMetadataKeywords[] =
719{
720    "Args", "Attrs", "CodeProps", "Language", "LanguageVersion", "Name", "SymbolName"
721};
722
723static const size_t kernelMetadataKeywordsNum =
724        sizeof(kernelMetadataKeywords) / sizeof(const char*);
725
726enum {
727    ROCMMT_ATTRS_REQD_WORK_GROUP_SIZE = 0, ROCMMT_ATTRS_RUNTIME_HANDLE,
728    ROCMMT_ATTRS_VECTYPEHINT, ROCMMT_ATTRS_WORK_GROUP_SIZE_HINT
729};
730
731static const char* kernelAttrMetadataKeywords[] =
732{
733    "ReqdWorkGroupSize", "RuntimeHandle", "VecTypeHint", "WorkGroupSizeHint"
734};
735
736static const size_t kernelAttrMetadataKeywordsNum =
737        sizeof(kernelAttrMetadataKeywords) / sizeof(const char*);
738
739enum {
740    ROCMMT_CODEPROPS_FIXED_WORK_GROUP_SIZE = 0, ROCMMT_CODEPROPS_GROUP_SEGMENT_FIXED_SIZE,
741    ROCMMT_CODEPROPS_KERNARG_SEGMENT_ALIGN, ROCMMT_CODEPROPS_KERNARG_SEGMENT_SIZE,
742    ROCMMT_CODEPROPS_MAX_FLAT_WORK_GROUP_SIZE, ROCMMT_CODEPROPS_NUM_SGPRS,
743    ROCMMT_CODEPROPS_NUM_SPILLED_SGPRS, ROCMMT_CODEPROPS_NUM_SPILLED_VGPRS,
744    ROCMMT_CODEPROPS_NUM_VGPRS, ROCMMT_CODEPROPS_PRIVATE_SEGMENT_FIXED_SIZE,
745    ROCMMT_CODEPROPS_WAVEFRONT_SIZE
746};
747
748static const char* kernelCodePropsKeywords[] =
749{
750    "FixedWorkGroupSize", "GroupSegmentFixedSize", "KernargSegmentAlign",
751    "KernargSegmentSize", "MaxFlatWorkGroupSize", "NumSGPRs",
752    "NumSpilledSGPRs", "NumSpilledVGPRs", "NumVGPRs", "PrivateSegmentFixedSize",
753    "WavefrontSize"
754};
755
756static const size_t kernelCodePropsKeywordsNum =
757        sizeof(kernelCodePropsKeywords) / sizeof(const char*);
758
759enum {
760    ROCMMT_ARGS_ACCQUAL = 0, ROCMMT_ARGS_ACTUALACCQUAL, ROCMMT_ARGS_ADDRSPACEQUAL,
761    ROCMMT_ARGS_ALIGN, ROCMMT_ARGS_ISCONST, ROCMMT_ARGS_ISPIPE, ROCMMT_ARGS_ISRESTRICT,
762    ROCMMT_ARGS_ISVOLATILE, ROCMMT_ARGS_NAME, ROCMMT_ARGS_POINTEE_ALIGN,
763    ROCMMT_ARGS_SIZE, ROCMMT_ARGS_TYPENAME, ROCMMT_ARGS_VALUEKIND,
764    ROCMMT_ARGS_VALUETYPE
765};
766
767static const char* kernelArgInfosKeywords[] =
768{
769    "AccQual", "ActualAccQual", "AddrSpaceQual", "Align", "IsConst", "IsPipe",
770    "IsRestrict", "IsVolatile", "Name", "PointeeAlign", "Size", "TypeName",
771    "ValueKind", "ValueType"
772};
773
774static const size_t kernelArgInfosKeywordsNum =
775        sizeof(kernelArgInfosKeywords) / sizeof(const char*);
776
777static const std::pair<const char*, ROCmValueKind> rocmValueKindNamesMap[] =
778{
779    { "ByValue", ROCmValueKind::BY_VALUE },
780    { "DynamicSharedPointer", ROCmValueKind::DYN_SHARED_PTR },
781    { "GlobalBuffer", ROCmValueKind::GLOBAL_BUFFER },
782    { "HiddenCompletionAction", ROCmValueKind::HIDDEN_COMPLETION_ACTION },
783    { "HiddenDefaultQueue", ROCmValueKind::HIDDEN_DEFAULT_QUEUE },
784    { "HiddenGlobalOffsetX", ROCmValueKind::HIDDEN_GLOBAL_OFFSET_X },
785    { "HiddenGlobalOffsetY", ROCmValueKind::HIDDEN_GLOBAL_OFFSET_Y },
786    { "HiddenGlobalOffsetZ", ROCmValueKind::HIDDEN_GLOBAL_OFFSET_Z },
787    { "HiddenNone", ROCmValueKind::HIDDEN_NONE },
788    { "HiddenPrintfBuffer", ROCmValueKind::HIDDEN_PRINTF_BUFFER },
789    { "Image", ROCmValueKind::IMAGE },
790    { "Pipe", ROCmValueKind::PIPE },
791    { "Queue", ROCmValueKind::QUEUE },
792    { "Sampler", ROCmValueKind::SAMPLER }
793};
794
795static const size_t rocmValueKindNamesNum =
796        sizeof(rocmValueKindNamesMap) / sizeof(std::pair<const char*, ROCmValueKind>);
797
798static const std::pair<const char*, ROCmValueType> rocmValueTypeNamesMap[] =
799{
800    { "F16", ROCmValueType::FLOAT16 },
801    { "F32", ROCmValueType::FLOAT32 },
802    { "F64", ROCmValueType::FLOAT64 },
803    { "I16", ROCmValueType::INT16 },
804    { "I32", ROCmValueType::INT32 },
805    { "I64", ROCmValueType::INT64 },
806    { "I8", ROCmValueType::INT8 },
807    { "Struct", ROCmValueType::STRUCTURE },
808    { "U16", ROCmValueType::UINT16 },
809    { "U32", ROCmValueType::UINT32 },
810    { "U64", ROCmValueType::UINT64 },
811    { "U8", ROCmValueType::UINT8 }
812};
813
814static const size_t rocmValueTypeNamesNum =
815        sizeof(rocmValueTypeNamesMap) / sizeof(std::pair<const char*, ROCmValueType>);
816
817static const char* rocmAddrSpaceTypesTbl[] =
818{ "Private", "Global", "Constant", "Local", "Generic", "Region" };
819
820static const char* rocmAccessQualifierTbl[] =
821{ "Default", "ReadOnly", "WriteOnly", "ReadWrite" };
822
823static void parseROCmMetadata(size_t metadataSize, const char* metadata,
824                ROCmMetadata& metadataInfo)
825{
826    const char* ptr = metadata;
827    const char* end = metadata + metadataSize;
828    size_t lineNo = 1;
829    // init metadata info object
830    metadataInfo.kernels.clear();
831    metadataInfo.printfInfos.clear();
832    metadataInfo.version[0] = metadataInfo.version[1] = 0;
833   
834    std::vector<ROCmKernelMetadata>& kernels = metadataInfo.kernels;
835   
836    cxuint levels[6] = { UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX, UINT_MAX };
837    cxuint curLevel = 0;
838    bool inKernels = false;
839    bool inKernel = false;
840    bool inKernelArgs = false;
841    bool inKernelArg = false;
842    bool inKernelCodeProps = false;
843    bool inKernelAttrs = false;
844    bool canToNextLevel = false;
845   
846    size_t oldLineNo = 0;
847    while (ptr != end)
848    {
849        cxuint level = skipSpacesAndComments(ptr, end, lineNo);
850        if (ptr == end || lineNo == oldLineNo)
851            throw ParseException(lineNo, "Expected new line");
852       
853        if (levels[curLevel] == UINT_MAX)
854            levels[curLevel] = level;
855        else if (levels[curLevel] < level)
856        {
857            if (canToNextLevel)
858                // go to next nesting level
859                levels[++curLevel] = level;
860            else
861                throw ParseException(lineNo, "Unexpected nesting level");
862            canToNextLevel = false;
863        }
864        else if (levels[curLevel] > level)
865        {
866            while (curLevel != UINT_MAX && levels[curLevel] > level)
867                curLevel--;
868            if (curLevel == UINT_MAX)
869                throw ParseException(lineNo, "Indentation smaller than in main level");
870           
871            // pop from previous level
872            if (curLevel < 3)
873            {
874                if (inKernelArgs)
875                {
876                    // leave from kernel args
877                    inKernelArgs = false;
878                    inKernelArg = false;
879                }
880           
881                inKernelCodeProps = false;
882                inKernelAttrs = false;
883            }
884            if (curLevel < 1 && inKernels)
885            {
886                // leave from kernels
887                inKernels = false;
888                inKernel = false;
889            }
890           
891            if (levels[curLevel] != level)
892                throw ParseException(lineNo, "Unexpected nesting level");
893        }
894       
895        oldLineNo = lineNo;
896        if (curLevel == 0)
897        {
898            if (lineNo==1 && ptr+3 <= end && *ptr=='-' && ptr[1]=='-' && ptr[2]=='-' &&
899                (ptr+3==end || (ptr+3 < end && ptr[3]=='\n')))
900            {
901                ptr += 3;
902                if (ptr!=end)
903                {
904                    lineNo++;
905                    ptr++; // to newline
906                }
907                continue; // skip document start
908            }
909           
910            if (ptr+3 <= end && *ptr=='.' && ptr[1]=='.' && ptr[2]=='.' &&
911                (ptr+3==end || (ptr+3 < end && ptr[3]=='\n')))
912                break; // end of the document
913           
914            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
915                        mainMetadataKeywordsNum, mainMetadataKeywords);
916           
917            switch(keyIndex)
918            {
919                case ROCMMT_MAIN_KERNELS:
920                    inKernels = true;
921                    canToNextLevel = true;
922                    break;
923                case ROCMMT_MAIN_PRINTF:
924                {
925                    YAMLPrintfVectorConsumer consumer(metadataInfo.printfInfos);
926                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer, true);
927                    break;
928                }
929                case ROCMMT_MAIN_VERSION:
930                {
931                    YAMLIntArrayConsumer<uint32_t> consumer(2, metadataInfo.version);
932                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer, true);
933                    break;
934                }
935                default:
936                    skipYAMLValue(ptr, end, lineNo, level);
937                    break;
938            }
939        }
940       
941        if (curLevel==1 && inKernels)
942        {
943            // enter to kernel level
944            if (ptr == end || *ptr != '-')
945                throw ParseException(lineNo, "No '-' before kernel object");
946            ptr++;
947            const char* afterMinus = ptr;
948            skipSpacesToLineEnd(ptr, end);
949            levels[++curLevel] = level + 1 + ptr-afterMinus;
950            level = levels[curLevel];
951            inKernel = true;
952           
953            kernels.push_back(ROCmKernelMetadata());
954            kernels.back().initialize();
955        }
956       
957        if (curLevel==2 && inKernel)
958        {
959            // in kernel
960            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
961                        kernelMetadataKeywordsNum, kernelMetadataKeywords);
962           
963            ROCmKernelMetadata& kernel = kernels.back();
964            switch(keyIndex)
965            {
966                case ROCMMT_KERNEL_ARGS:
967                    inKernelArgs = true;
968                    canToNextLevel = true;
969                    kernel.argInfos.clear();
970                    break;
971                case ROCMMT_KERNEL_ATTRS:
972                    inKernelAttrs = true;
973                    canToNextLevel = true;
974                    // initialize kernel attributes values
975                    kernel.reqdWorkGroupSize[0] = 0;
976                    kernel.reqdWorkGroupSize[1] = 0;
977                    kernel.reqdWorkGroupSize[2] = 0;
978                    kernel.workGroupSizeHint[0] = 0;
979                    kernel.workGroupSizeHint[1] = 0;
980                    kernel.workGroupSizeHint[2] = 0;
981                    kernel.runtimeHandle.clear();
982                    kernel.vecTypeHint.clear();
983                    break;
984                case ROCMMT_KERNEL_CODEPROPS:
985                    // initialize CodeProps values
986                    kernel.kernargSegmentSize = BINGEN64_DEFAULT;
987                    kernel.groupSegmentFixedSize = BINGEN64_DEFAULT;
988                    kernel.privateSegmentFixedSize = BINGEN64_DEFAULT;
989                    kernel.kernargSegmentAlign = BINGEN64_DEFAULT;
990                    kernel.wavefrontSize = BINGEN_DEFAULT;
991                    kernel.sgprsNum = BINGEN_DEFAULT;
992                    kernel.vgprsNum = BINGEN_DEFAULT;
993                    kernel.spilledSgprs = BINGEN_NOTSUPPLIED;
994                    kernel.spilledVgprs = BINGEN_NOTSUPPLIED;
995                    kernel.maxFlatWorkGroupSize = BINGEN64_DEFAULT;
996                    kernel.fixedWorkGroupSize[0] = 0;
997                    kernel.fixedWorkGroupSize[1] = 0;
998                    kernel.fixedWorkGroupSize[2] = 0;
999                    inKernelCodeProps = true;
1000                    canToNextLevel = true;
1001                    break;
1002                case ROCMMT_KERNEL_LANGUAGE:
1003                    kernel.language = parseYAMLStringValue(ptr, end, lineNo, level, true);
1004                    break;
1005                case ROCMMT_KERNEL_LANGUAGE_VERSION:
1006                {
1007                    YAMLIntArrayConsumer<uint32_t> consumer(2, kernel.langVersion);
1008                    parseYAMLValArray(ptr, end, lineNo, levels[curLevel], &consumer);
1009                    break;
1010                }
1011                case ROCMMT_KERNEL_NAME:
1012                    kernel.name = parseYAMLStringValue(ptr, end, lineNo, level, true);
1013                    break;
1014                case ROCMMT_KERNEL_SYMBOLNAME:
1015                    kernel.symbolName = parseYAMLStringValue(ptr, end, lineNo, level, true);
1016                    break;
1017                default:
1018                    skipYAMLValue(ptr, end, lineNo, level);
1019                    break;
1020            }
1021        }
1022       
1023        if (curLevel==3 && inKernelAttrs)
1024        {
1025            // in kernel attributes
1026            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
1027                        kernelAttrMetadataKeywordsNum, kernelAttrMetadataKeywords);
1028           
1029            ROCmKernelMetadata& kernel = kernels.back();
1030            switch(keyIndex)
1031            {
1032                case ROCMMT_ATTRS_REQD_WORK_GROUP_SIZE:
1033                {
1034                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.reqdWorkGroupSize);
1035                    parseYAMLValArray(ptr, end, lineNo, level, &consumer);
1036                    break;
1037                }
1038                case ROCMMT_ATTRS_RUNTIME_HANDLE:
1039                    kernel.runtimeHandle = parseYAMLStringValue(
1040                                ptr, end, lineNo, level, true);
1041                    break;
1042                case ROCMMT_ATTRS_VECTYPEHINT:
1043                    kernel.vecTypeHint = parseYAMLStringValue(
1044                                ptr, end, lineNo, level, true);
1045                    break;
1046                case ROCMMT_ATTRS_WORK_GROUP_SIZE_HINT:
1047                {
1048                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.workGroupSizeHint);
1049                    parseYAMLValArray(ptr, end, lineNo, level, &consumer, true);
1050                    break;
1051                }
1052                default:
1053                    skipYAMLValue(ptr, end, lineNo, level);
1054                    break;
1055            }
1056        }
1057       
1058        if (curLevel==3 && inKernelCodeProps)
1059        {
1060            // in kernel codeProps
1061            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
1062                        kernelCodePropsKeywordsNum, kernelCodePropsKeywords);
1063           
1064            ROCmKernelMetadata& kernel = kernels.back();
1065            switch(keyIndex)
1066            {
1067                case ROCMMT_CODEPROPS_FIXED_WORK_GROUP_SIZE:
1068                {
1069                    YAMLIntArrayConsumer<cxuint> consumer(3, kernel.fixedWorkGroupSize);
1070                    parseYAMLValArray(ptr, end, lineNo, level, &consumer);
1071                    break;
1072                }
1073                case ROCMMT_CODEPROPS_GROUP_SEGMENT_FIXED_SIZE:
1074                    kernel.groupSegmentFixedSize =
1075                                parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
1076                    break;
1077                case ROCMMT_CODEPROPS_KERNARG_SEGMENT_ALIGN:
1078                    kernel.kernargSegmentAlign =
1079                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1080                    break;
1081                case ROCMMT_CODEPROPS_KERNARG_SEGMENT_SIZE:
1082                    kernel.kernargSegmentSize =
1083                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1084                    break;
1085                case ROCMMT_CODEPROPS_MAX_FLAT_WORK_GROUP_SIZE:
1086                    kernel.maxFlatWorkGroupSize =
1087                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1088                    break;
1089                case ROCMMT_CODEPROPS_NUM_SGPRS:
1090                    kernel.sgprsNum = parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
1091                    break;
1092                case ROCMMT_CODEPROPS_NUM_SPILLED_SGPRS:
1093                    kernel.spilledSgprs =
1094                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
1095                    break;
1096                case ROCMMT_CODEPROPS_NUM_SPILLED_VGPRS:
1097                    kernel.spilledVgprs =
1098                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
1099                    break;
1100                case ROCMMT_CODEPROPS_NUM_VGPRS:
1101                    kernel.vgprsNum = parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
1102                    break;
1103                case ROCMMT_CODEPROPS_PRIVATE_SEGMENT_FIXED_SIZE:
1104                    kernel.privateSegmentFixedSize =
1105                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1106                    break;
1107                case ROCMMT_CODEPROPS_WAVEFRONT_SIZE:
1108                    kernel.wavefrontSize =
1109                            parseYAMLIntValue<cxuint>(ptr, end, lineNo, true);
1110                    break;
1111                default:
1112                    skipYAMLValue(ptr, end, lineNo, level);
1113                    break;
1114            }
1115        }
1116       
1117        if (curLevel==3 && inKernelArgs)
1118        {
1119            // enter to kernel argument level
1120            if (ptr == end || *ptr != '-')
1121                throw ParseException(lineNo, "No '-' before argument object");
1122            ptr++;
1123            const char* afterMinus = ptr;
1124            skipSpacesToLineEnd(ptr, end);
1125            levels[++curLevel] = level + 1 + ptr-afterMinus;
1126            level = levels[curLevel];
1127            inKernelArg = true;
1128           
1129            kernels.back().argInfos.push_back(ROCmKernelArgInfo{});
1130        }
1131       
1132        if (curLevel==4 && inKernelArg)
1133        {
1134            // in kernel argument
1135            const size_t keyIndex = parseYAMLKey(ptr, end, lineNo,
1136                        kernelArgInfosKeywordsNum, kernelArgInfosKeywords);
1137           
1138            ROCmKernelArgInfo& kernelArg = kernels.back().argInfos.back();
1139           
1140            size_t valLineNo = lineNo;
1141            switch(keyIndex)
1142            {
1143                case ROCMMT_ARGS_ACCQUAL:
1144                case ROCMMT_ARGS_ACTUALACCQUAL:
1145                {
1146                    const std::string acc = trimStrSpaces(parseYAMLStringValue(
1147                                    ptr, end, lineNo, level, true));
1148                    size_t accIndex = 0;
1149                    for (; accIndex < 6; accIndex++)
1150                        if (::strcmp(rocmAccessQualifierTbl[accIndex], acc.c_str())==0)
1151                            break;
1152                    if (accIndex == 4)
1153                        throw ParseException(lineNo, "Wrong access qualifier");
1154                    if (keyIndex == ROCMMT_ARGS_ACCQUAL)
1155                        kernelArg.accessQual = ROCmAccessQual(accIndex);
1156                    else
1157                        kernelArg.actualAccessQual = ROCmAccessQual(accIndex);
1158                    break;
1159                }
1160                case ROCMMT_ARGS_ADDRSPACEQUAL:
1161                {
1162                    const std::string aspace = trimStrSpaces(parseYAMLStringValue(
1163                                    ptr, end, lineNo, level, true));
1164                    size_t aspaceIndex = 0;
1165                    for (; aspaceIndex < 6; aspaceIndex++)
1166                        if (::strcmp(rocmAddrSpaceTypesTbl[aspaceIndex],
1167                                    aspace.c_str())==0)
1168                            break;
1169                    if (aspaceIndex == 6)
1170                        throw ParseException(valLineNo, "Wrong address space");
1171                    kernelArg.addressSpace = ROCmAddressSpace(aspaceIndex+1);
1172                    break;
1173                }
1174                case ROCMMT_ARGS_ALIGN:
1175                    kernelArg.align = parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1176                    break;
1177                case ROCMMT_ARGS_ISCONST:
1178                    kernelArg.isConst = parseYAMLBoolValue(ptr, end, lineNo, true);
1179                    break;
1180                case ROCMMT_ARGS_ISPIPE:
1181                    kernelArg.isPipe = parseYAMLBoolValue(ptr, end, lineNo, true);
1182                    break;
1183                case ROCMMT_ARGS_ISRESTRICT:
1184                    kernelArg.isRestrict = parseYAMLBoolValue(ptr, end, lineNo, true);
1185                    break;
1186                case ROCMMT_ARGS_ISVOLATILE:
1187                    kernelArg.isVolatile = parseYAMLBoolValue(ptr, end, lineNo, true);
1188                    break;
1189                case ROCMMT_ARGS_NAME:
1190                    kernelArg.name = parseYAMLStringValue(ptr, end, lineNo, level, true);
1191                    break;
1192                case ROCMMT_ARGS_POINTEE_ALIGN:
1193                    kernelArg.pointeeAlign =
1194                                parseYAMLIntValue<uint64_t>(ptr, end, lineNo, true);
1195                    break;
1196                case ROCMMT_ARGS_SIZE:
1197                    kernelArg.size = parseYAMLIntValue<uint64_t>(ptr, end, lineNo);
1198                    break;
1199                case ROCMMT_ARGS_TYPENAME:
1200                    kernelArg.typeName =
1201                                parseYAMLStringValue(ptr, end, lineNo, level, true);
1202                    break;
1203                case ROCMMT_ARGS_VALUEKIND:
1204                {
1205                    const std::string vkind = trimStrSpaces(parseYAMLStringValue(
1206                                ptr, end, lineNo, level, true));
1207                    const size_t vkindIndex = binaryMapFind(rocmValueKindNamesMap,
1208                            rocmValueKindNamesMap + rocmValueKindNamesNum, vkind.c_str(),
1209                            CStringLess()) - rocmValueKindNamesMap;
1210                    // if unknown kind
1211                    if (vkindIndex == rocmValueKindNamesNum)
1212                        throw ParseException(valLineNo, "Wrong argument value kind");
1213                    kernelArg.valueKind = rocmValueKindNamesMap[vkindIndex].second;
1214                    break;
1215                }
1216                case ROCMMT_ARGS_VALUETYPE:
1217                {
1218                    const std::string vtype = trimStrSpaces(parseYAMLStringValue(
1219                                    ptr, end, lineNo, level, true));
1220                    const size_t vtypeIndex = binaryMapFind(rocmValueTypeNamesMap,
1221                            rocmValueTypeNamesMap + rocmValueTypeNamesNum, vtype.c_str(),
1222                            CStringLess()) - rocmValueTypeNamesMap;
1223                    // if unknown type
1224                    if (vtypeIndex == rocmValueTypeNamesNum)
1225                        throw ParseException(valLineNo, "Wrong argument value type");
1226                    kernelArg.valueType = rocmValueTypeNamesMap[vtypeIndex].second;
1227                    break;
1228                }
1229                default:
1230                    skipYAMLValue(ptr, end, lineNo, level);
1231                    break;
1232            }
1233        }
1234    }
1235}
1236
1237void ROCmMetadata::parse(size_t metadataSize, const char* metadata)
1238{
1239    parseROCmMetadata(metadataSize, metadata, *this);
1240}
1241
1242/*
1243 * ROCm binary reader and generator
1244 */
1245
1246/* TODO: add support for various kernel code offset (now only 256 is supported) */
1247
1248ROCmBinary::ROCmBinary(size_t binaryCodeSize, cxbyte* binaryCode, Flags creationFlags)
1249        : ElfBinary64(binaryCodeSize, binaryCode, creationFlags),
1250          regionsNum(0), codeSize(0), code(nullptr),
1251          globalDataSize(0), globalData(nullptr), metadataSize(0), metadata(nullptr),
1252          newBinFormat(false)
1253{
1254    cxuint textIndex = SHN_UNDEF;
1255    try
1256    { textIndex = getSectionIndex(".text"); }
1257    catch(const Exception& ex)
1258    { } // ignore failed
1259    uint64_t codeOffset = 0;
1260    // find '.text' section
1261    if (textIndex!=SHN_UNDEF)
1262    {
1263        code = getSectionContent(textIndex);
1264        const Elf64_Shdr& textShdr = getSectionHeader(textIndex);
1265        codeSize = ULEV(textShdr.sh_size);
1266        codeOffset = ULEV(textShdr.sh_offset);
1267    }
1268   
1269    cxuint rodataIndex = SHN_UNDEF;
1270    try
1271    { rodataIndex = getSectionIndex(".rodata"); }
1272    catch(const Exception& ex)
1273    { } // ignore failed
1274    // find '.text' section
1275    if (rodataIndex!=SHN_UNDEF)
1276    {
1277        globalData = getSectionContent(rodataIndex);
1278        const Elf64_Shdr& rodataShdr = getSectionHeader(rodataIndex);
1279        globalDataSize = ULEV(rodataShdr.sh_size);
1280    }
1281   
1282    cxuint gpuConfigIndex = SHN_UNDEF;
1283    try
1284    { gpuConfigIndex = getSectionIndex(".AMDGPU.config"); }
1285    catch(const Exception& ex)
1286    { } // ignore failed
1287    newBinFormat = (gpuConfigIndex == SHN_UNDEF);
1288   
1289    // counts regions (symbol or kernel)
1290    regionsNum = 0;
1291    const size_t symbolsNum = getSymbolsNum();
1292    for (size_t i = 0; i < symbolsNum; i++)
1293    {
1294        // count regions number
1295        const Elf64_Sym& sym = getSymbol(i);
1296        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
1297        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
1298        if (ULEV(sym.st_shndx)==textIndex &&
1299            (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
1300                (bind==STB_GLOBAL && symType==STT_OBJECT)))
1301            regionsNum++;
1302    }
1303    if (code==nullptr && regionsNum!=0)
1304        throw BinException("No code if regions number is not zero");
1305    regions.reset(new ROCmRegion[regionsNum]);
1306    size_t j = 0;
1307    typedef std::pair<uint64_t, size_t> RegionOffsetEntry;
1308    std::unique_ptr<RegionOffsetEntry[]> symOffsets(new RegionOffsetEntry[regionsNum]);
1309   
1310    // get regions info
1311    for (size_t i = 0; i < symbolsNum; i++)
1312    {
1313        const Elf64_Sym& sym = getSymbol(i);
1314        if (ULEV(sym.st_shndx)!=textIndex)
1315            continue;   // if not in '.text' section
1316        const size_t value = ULEV(sym.st_value);
1317        if (value < codeOffset)
1318            throw BinException("Region offset is too small!");
1319        const size_t size = ULEV(sym.st_size);
1320       
1321        const cxbyte symType = ELF64_ST_TYPE(sym.st_info);
1322        const cxbyte bind = ELF64_ST_BIND(sym.st_info);
1323        if (symType==STT_GNU_IFUNC || symType==STT_FUNC ||
1324                (bind==STB_GLOBAL && symType==STT_OBJECT))
1325        {
1326            ROCmRegionType type = ROCmRegionType::DATA;
1327            // if kernel
1328            if (symType==STT_GNU_IFUNC) 
1329                type = ROCmRegionType::KERNEL;
1330            // if function kernel
1331            else if (symType==STT_FUNC)
1332                type = ROCmRegionType::FKERNEL;
1333            symOffsets[j] = std::make_pair(value, j);
1334            if (type!=ROCmRegionType::DATA && value+0x100 > codeOffset+codeSize)
1335                throw BinException("Kernel or code offset is too big!");
1336            regions[j++] = { getSymbolName(i), size, value, type };
1337        }
1338    }
1339    // sort regions by offset
1340    std::sort(symOffsets.get(), symOffsets.get()+regionsNum,
1341            [](const RegionOffsetEntry& a, const RegionOffsetEntry& b)
1342            { return a.first < b.first; });
1343    // checking distance between regions
1344    for (size_t i = 1; i <= regionsNum; i++)
1345    {
1346        size_t end = (i<regionsNum) ? symOffsets[i].first : codeOffset+codeSize;
1347        ROCmRegion& region = regions[symOffsets[i-1].second];
1348        if (region.type==ROCmRegionType::KERNEL && symOffsets[i-1].first+0x100 > end)
1349            throw BinException("Kernel size is too small!");
1350       
1351        const size_t regSize = end - symOffsets[i-1].first;
1352        if (region.size==0)
1353            region.size = regSize;
1354        else
1355            region.size = std::min(regSize, region.size);
1356    }
1357   
1358    // get metadata
1359    const size_t notesSize = getNotesSize();
1360    const cxbyte* noteContent = (const cxbyte*)getNotes();
1361   
1362    for (size_t offset = 0; offset < notesSize; )
1363    {
1364        const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
1365        size_t namesz = ULEV(nhdr->n_namesz);
1366        size_t descsz = ULEV(nhdr->n_descsz);
1367        if (usumGt(offset, namesz+descsz, notesSize))
1368            throw BinException("Note offset+size out of range");
1369       
1370        if (namesz==4 &&
1371            ::strcmp((const char*)noteContent+offset+ sizeof(Elf64_Nhdr), "AMD")==0)
1372        {
1373            const uint32_t noteType = ULEV(nhdr->n_type);
1374            if (noteType == 0xa)
1375            {
1376                metadata = (char*)(noteContent+offset+sizeof(Elf64_Nhdr) + 4);
1377                metadataSize = descsz;
1378            }
1379            else if (noteType == 0xb)
1380                target.assign((char*)(noteContent+offset+sizeof(Elf64_Nhdr) + 4), descsz);
1381        }
1382        size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
1383        offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
1384    }
1385   
1386    if (hasRegionMap())
1387    {
1388        // create region map
1389        regionsMap.resize(regionsNum);
1390        for (size_t i = 0; i < regionsNum; i++)
1391            regionsMap[i] = std::make_pair(regions[i].regionName, i);
1392        // sort region map
1393        mapSort(regionsMap.begin(), regionsMap.end());
1394    }
1395   
1396    if ((creationFlags & ROCMBIN_CREATE_METADATAINFO) != 0 &&
1397        metadata != nullptr && metadataSize != 0)
1398    {
1399        metadataInfo.reset(new ROCmMetadata());
1400        parseROCmMetadata(metadataSize, metadata, *metadataInfo);
1401       
1402        if (hasKernelInfoMap())
1403        {
1404            const std::vector<ROCmKernelMetadata>& kernels = metadataInfo->kernels;
1405            kernelInfosMap.resize(kernels.size());
1406            for (size_t i = 0; i < kernelInfosMap.size(); i++)
1407                kernelInfosMap[i] = std::make_pair(kernels[i].name, i);
1408            // sort region map
1409            mapSort(kernelInfosMap.begin(), kernelInfosMap.end());
1410        }
1411    }
1412}
1413
1414/// determint GPU device from ROCm notes
1415GPUDeviceType ROCmBinary::determineGPUDeviceType(uint32_t& outArchMinor,
1416                     uint32_t& outArchStepping) const
1417{
1418    uint32_t archMajor = 0;
1419    uint32_t archMinor = 0;
1420    uint32_t archStepping = 0;
1421   
1422    {
1423        const cxbyte* noteContent = (const cxbyte*)getNotes();
1424        if (noteContent==nullptr)
1425            throw BinException("Missing notes in inner binary!");
1426        size_t notesSize = getNotesSize();
1427        // find note about AMDGPU
1428        for (size_t offset = 0; offset < notesSize; )
1429        {
1430            const Elf64_Nhdr* nhdr = (const Elf64_Nhdr*)(noteContent + offset);
1431            size_t namesz = ULEV(nhdr->n_namesz);
1432            size_t descsz = ULEV(nhdr->n_descsz);
1433            if (usumGt(offset, namesz+descsz, notesSize))
1434                throw BinException("Note offset+size out of range");
1435            if (ULEV(nhdr->n_type) == 0x3 && namesz==4 && descsz>=0x1a &&
1436                ::strcmp((const char*)noteContent+offset+sizeof(Elf64_Nhdr), "AMD")==0)
1437            {    // AMDGPU type
1438                const uint32_t* content = (const uint32_t*)
1439                        (noteContent+offset+sizeof(Elf64_Nhdr) + 4);
1440                archMajor = ULEV(content[1]);
1441                archMinor = ULEV(content[2]);
1442                archStepping = ULEV(content[3]);
1443            }
1444            size_t align = (((namesz+descsz)&3)!=0) ? 4-((namesz+descsz)&3) : 0;
1445            offset += sizeof(Elf64_Nhdr) + namesz + descsz + align;
1446        }
1447    }
1448    // determine device type
1449    GPUDeviceType deviceType = getGPUDeviceTypeFromArchVersion(archMajor, archMinor,
1450                                    archStepping);
1451    outArchMinor = archMinor;
1452    outArchStepping = archStepping;
1453    return deviceType;
1454}
1455
1456const ROCmRegion& ROCmBinary::getRegion(const char* name) const
1457{
1458    RegionMap::const_iterator it = binaryMapFind(regionsMap.begin(),
1459                             regionsMap.end(), name);
1460    if (it == regionsMap.end())
1461        throw BinException("Can't find region name");
1462    return regions[it->second];
1463}
1464
1465const ROCmKernelMetadata& ROCmBinary::getKernelInfo(const char* name) const
1466{
1467    if (!hasMetadataInfo())
1468        throw BinException("Can't find kernel info name");
1469    RegionMap::const_iterator it = binaryMapFind(kernelInfosMap.begin(),
1470                             kernelInfosMap.end(), name);
1471    if (it == kernelInfosMap.end())
1472        throw BinException("Can't find kernel info name");
1473    return metadataInfo->kernels[it->second];
1474}
1475
1476// if ROCm binary
1477bool CLRX::isROCmBinary(size_t binarySize, const cxbyte* binary)
1478{
1479    if (!isElfBinary(binarySize, binary))
1480        return false;
1481    if (binary[EI_CLASS] != ELFCLASS64)
1482        return false;
1483    const Elf64_Ehdr* ehdr = reinterpret_cast<const Elf64_Ehdr*>(binary);
1484    if (ULEV(ehdr->e_machine) != 0xe0)
1485        return false;
1486    return true;
1487}
1488
1489
1490void ROCmInput::addEmptyKernel(const char* kernelName)
1491{
1492    symbols.push_back({ kernelName, 0, 0, ROCmRegionType::KERNEL });
1493}
1494
1495/*
1496 * ROCm YAML metadata generator
1497 */
1498
1499static const char* rocmValueKindNames[] =
1500{
1501    "ByValue", "GlobalBuffer", "DynamicSharedPointer", "Sampler", "Image", "Pipe", "Queue",
1502    "HiddenGlobalOffsetX", "HiddenGlobalOffsetY", "HiddenGlobalOffsetZ", "HiddenNone",
1503    "HiddenPrintfBuffer", "HiddenDefaultQueue", "HiddenCompletionAction"
1504};
1505
1506static const char* rocmValueTypeNames[] =
1507{
1508    "Struct", "I8", "U8", "I16", "U16", "F16", "I32", "U32", "F32", "I64", "U64", "F64"
1509};
1510
1511static void genArrayValue(cxuint n, const cxuint* values, std::string& output)
1512{
1513    char numBuf[24];
1514    output += "[ ";
1515    for (cxuint i = 0; i < n; i++)
1516    {
1517        itocstrCStyle(values[i], numBuf, 24);
1518        output += numBuf;
1519        output += (i+1<n) ? ", " : " ]\n";
1520    }
1521}
1522
1523// helper for checking whether value is supplied
1524static inline bool hasValue(cxuint value)
1525{ return value!=BINGEN_NOTSUPPLIED && value!=BINGEN_DEFAULT; }
1526
1527static inline bool hasValue(uint64_t value)
1528{ return value!=BINGEN64_NOTSUPPLIED && value!=BINGEN64_DEFAULT; }
1529
1530// get escaped YAML string if needed, otherwise get this same string
1531static std::string escapeYAMLString(const CString& input)
1532{
1533    bool toEscape = false;
1534    const char* s;
1535    for (s = input.c_str(); *s!=0; s++)
1536    {
1537        cxbyte c = *s;
1538        if (c < 0x20 || c >= 0x80 || c=='*' || c=='&' || c=='!' || c=='@' ||
1539            c=='\'' || c=='\"')
1540            toEscape = true;
1541    }
1542    // if spaces in begin and end
1543    if (isSpace(input[0]) || isDigit(input[0]) ||
1544        (!input.empty() && isSpace(s[-1])))
1545        toEscape = true;
1546   
1547    if (toEscape)
1548    {
1549        std::string out = "'";
1550        out += escapeStringCStyle(s-input.c_str(), input.c_str());
1551        out += "'";
1552        return out;
1553    }
1554    return input.c_str();
1555}
1556
1557static std::string escapePrintfFormat(const std::string& fmt)
1558{
1559    std::string out;
1560    out.reserve(fmt.size());
1561    for (char c: fmt)
1562        if (c!=':')
1563            out.push_back(c);
1564        else
1565            out += "\\72";
1566    return out;
1567}
1568
1569static void generateROCmMetadata(const ROCmMetadata& mdInfo,
1570                    const ROCmKernelConfig** kconfigs, std::string& output)
1571{
1572    output.clear();
1573    char numBuf[24];
1574    output += "---\n";
1575    // version
1576    output += "Version:         ";
1577    if (hasValue(mdInfo.version[0]))
1578        genArrayValue(2, mdInfo.version, output);
1579    else // default
1580        output += "[ 1, 0 ]\n";
1581    if (!mdInfo.printfInfos.empty())
1582        output += "Printf:          \n";
1583    // check print ids uniquness
1584    {
1585        std::unordered_set<cxuint> printfIds;
1586        for (const ROCmPrintfInfo& printfInfo: mdInfo.printfInfos)
1587            if (!printfIds.insert(printfInfo.id).second)
1588                throw BinGenException("Duplicate of printf id");
1589    }
1590    // printfs
1591    for (const ROCmPrintfInfo& printfInfo: mdInfo.printfInfos)
1592    {
1593        output += "  - '";
1594        itocstrCStyle(printfInfo.id, numBuf, 24);
1595        output += numBuf;
1596        output += ':';
1597        itocstrCStyle(printfInfo.argSizes.size(), numBuf, 24);
1598        output += numBuf;
1599        output += ':';
1600        for (size_t argSize: printfInfo.argSizes)
1601        {
1602            itocstrCStyle(argSize, numBuf, 24);
1603            output += numBuf;
1604            output += ':';
1605        }
1606        // printf format
1607        std::string escapedFmt = escapeStringCStyle(printfInfo.format);
1608        escapedFmt = escapePrintfFormat(escapedFmt);
1609        output += escapedFmt;
1610        output += "'\n";
1611    }
1612   
1613    if (!mdInfo.printfInfos.empty())
1614        output += "Kernels:         \n";
1615    // kernels
1616    for (size_t i = 0; i < mdInfo.kernels.size(); i++)
1617    {
1618        const ROCmKernelMetadata& kernel = mdInfo.kernels[i];
1619        output += "  - Name:            ";
1620        output.append(kernel.name.c_str(), kernel.name.size());
1621        output += "\n    SymbolName:      ";
1622        if (!kernel.symbolName.empty())
1623            output += escapeYAMLString(kernel.symbolName);
1624        else
1625        {
1626            // default is kernel name + '@kd'
1627            std::string symName = kernel.name.c_str();
1628            symName += "@kd";
1629            output += escapeYAMLString(symName);
1630        }
1631        output += "\n";
1632        if (!kernel.language.empty())
1633        {
1634            output += "    Language:        ";
1635            output += escapeYAMLString(kernel.language);
1636            output += "\n";
1637        }
1638        if (kernel.langVersion[0] != BINGEN_NOTSUPPLIED)
1639        {
1640            output += "    LanguageVersion: ";
1641            genArrayValue(2, kernel.langVersion, output);
1642        }
1643        // kernel attributes
1644        if (kernel.reqdWorkGroupSize[0] != 0 || kernel.reqdWorkGroupSize[1] != 0 ||
1645            kernel.reqdWorkGroupSize[2] != 0 ||
1646            kernel.workGroupSizeHint[0] != 0 || kernel.workGroupSizeHint[1] != 0 ||
1647            kernel.workGroupSizeHint[2] != 0 ||
1648            !kernel.vecTypeHint.empty() || !kernel.runtimeHandle.empty())
1649        {
1650            output += "    Attrs:           \n";
1651            if (kernel.workGroupSizeHint[0] != 0 || kernel.workGroupSizeHint[1] != 0 ||
1652                kernel.workGroupSizeHint[2] != 0)
1653            {
1654                output += "      WorkGroupSizeHint: ";
1655                genArrayValue(3, kernel.workGroupSizeHint, output);
1656            }
1657            if (kernel.reqdWorkGroupSize[0] != 0 || kernel.reqdWorkGroupSize[1] != 0 ||
1658                kernel.reqdWorkGroupSize[2] != 0)
1659            {
1660                output += "      ReqdWorkGroupSize: ";
1661                genArrayValue(3, kernel.reqdWorkGroupSize, output);
1662            }
1663            if (!kernel.vecTypeHint.empty())
1664            {
1665                output += "      VecTypeHint:     ";
1666                output += escapeYAMLString(kernel.vecTypeHint);
1667                output += "\n";
1668            }
1669            if (!kernel.runtimeHandle.empty())
1670            {
1671                output += "      RuntimeHandle:   ";
1672                output += escapeYAMLString(kernel.runtimeHandle);
1673                output += "\n";
1674            }
1675        }
1676        // kernel arguments
1677        if (!kernel.argInfos.empty())
1678            output += "    Args:            \n";
1679        for (const ROCmKernelArgInfo& argInfo: kernel.argInfos)
1680        {
1681            output += "      - ";
1682            if (!argInfo.name.empty())
1683            {
1684                output += "Name:            ";
1685                output += escapeYAMLString(argInfo.name);
1686                output += "\n        ";
1687            }
1688            if (!argInfo.typeName.empty())
1689            {
1690                output += "TypeName:        ";
1691                output += escapeYAMLString(argInfo.typeName);
1692                output += "\n        ";
1693            }
1694            output += "Size:            ";
1695            itocstrCStyle(argInfo.size, numBuf, 24);
1696            output += numBuf;
1697            output += "\n        Align:           ";
1698            itocstrCStyle(argInfo.align, numBuf, 24);
1699            output += numBuf;
1700            output += "\n        ValueKind:       ";
1701           
1702            if (argInfo.valueKind > ROCmValueKind::MAX_VALUE)
1703                throw BinGenException("Unknown ValueKind");
1704            output += rocmValueKindNames[cxuint(argInfo.valueKind)];
1705           
1706            if (argInfo.valueType > ROCmValueType::MAX_VALUE)
1707                throw BinGenException("Unknown ValueType");
1708            output += "\n        ValueType:       ";
1709            output += rocmValueTypeNames[cxuint(argInfo.valueType)];
1710            output += "\n";
1711           
1712            if (argInfo.valueKind == ROCmValueKind::DYN_SHARED_PTR)
1713            {
1714                output += "        PointeeAlign:    ";
1715                itocstrCStyle(argInfo.pointeeAlign, numBuf, 24);
1716                output += numBuf;
1717                output += "\n";
1718            }
1719            if (argInfo.valueKind == ROCmValueKind::DYN_SHARED_PTR ||
1720                argInfo.valueKind == ROCmValueKind::GLOBAL_BUFFER)
1721            {
1722                if (argInfo.addressSpace > ROCmAddressSpace::MAX_VALUE)
1723                    throw BinGenException("Unknown AddressSpace");
1724                output += "        AddrSpaceQual:   ";
1725                output += rocmAddrSpaceTypesTbl[cxuint(argInfo.addressSpace)];
1726                output += "\n";
1727            }
1728            if (argInfo.valueKind == ROCmValueKind::IMAGE ||
1729                argInfo.valueKind == ROCmValueKind::PIPE)
1730            {
1731                if (argInfo.accessQual> ROCmAccessQual::MAX_VALUE)
1732                    throw BinGenException("Unknown AccessQualifier");
1733                output += "        AccQual:         ";
1734                output += rocmAccessQualifierTbl[cxuint(argInfo.accessQual)];
1735                output += "\n";
1736            }
1737            if (argInfo.valueKind == ROCmValueKind::GLOBAL_BUFFER ||
1738                argInfo.valueKind == ROCmValueKind::IMAGE ||
1739                argInfo.valueKind == ROCmValueKind::PIPE)
1740            {
1741                if (argInfo.actualAccessQual> ROCmAccessQual::MAX_VALUE)
1742                    throw BinGenException("Unknown ActualAccessQualifier");
1743                output += "        ActualAccQual:   ";
1744                output += rocmAccessQualifierTbl[cxuint(argInfo.actualAccessQual)];
1745                output += "\n";
1746            }
1747            if (argInfo.isConst)
1748                output += "        IsConst:         true\n";
1749            if (argInfo.isRestrict)
1750                output += "        IsRestrict:      true\n";
1751            if (argInfo.isVolatile)
1752                output += "        IsVolatile:      true\n";
1753            if (argInfo.isPipe)
1754                output += "        IsPipe:          true\n";
1755        }
1756       
1757        // kernel code properties
1758        if (kernel.kernargSegmentSize != BINGEN64_NOTSUPPLIED ||
1759            kernel.kernargSegmentAlign != BINGEN64_NOTSUPPLIED ||
1760            kernel.privateSegmentFixedSize != BINGEN64_NOTSUPPLIED ||
1761            kernel.groupSegmentFixedSize != BINGEN64_NOTSUPPLIED ||
1762            kernel.wavefrontSize != BINGEN_NOTSUPPLIED ||
1763            kernel.sgprsNum != BINGEN_NOTSUPPLIED ||
1764            kernel.vgprsNum != BINGEN_NOTSUPPLIED ||
1765            kernel.maxFlatWorkGroupSize != BINGEN64_NOTSUPPLIED ||
1766            hasValue(kernel.spilledSgprs) || hasValue(kernel.spilledVgprs) ||
1767            kernel.fixedWorkGroupSize[0] != 0 || kernel.fixedWorkGroupSize[1] != 0 ||
1768            kernel.fixedWorkGroupSize[2] != 0)
1769        {
1770            const ROCmKernelConfig& kconfig = *kconfigs[i];
1771           
1772            output += "    CodeProps:       \n";
1773            output += "      KernargSegmentSize: ";
1774            itocstrCStyle(hasValue(kernel.kernargSegmentSize) ?
1775                    kernel.kernargSegmentSize : ULEV(kconfig.kernargSegmentSize),
1776                    numBuf, 24);
1777            output += numBuf;
1778            output += "\n      GroupSegmentFixedSize: ";
1779            itocstrCStyle(hasValue(kernel.groupSegmentFixedSize) ?
1780                    kernel.groupSegmentFixedSize :
1781                    uint64_t(ULEV(kconfig.workgroupGroupSegmentSize)),
1782                    numBuf, 24);
1783            output += numBuf;
1784            output += "\n      PrivateSegmentFixedSize: ";
1785            itocstrCStyle(hasValue(kernel.privateSegmentFixedSize) ?
1786                    kernel.privateSegmentFixedSize :
1787                    uint64_t(ULEV(kconfig.workitemPrivateSegmentSize)),
1788                    numBuf, 24);
1789            output += numBuf;
1790            output += "\n      KernargSegmentAlign: ";
1791            itocstrCStyle(hasValue(kernel.kernargSegmentAlign) ?
1792                    kernel.kernargSegmentAlign :
1793                    uint64_t(1ULL<<kconfig.kernargSegmentAlignment),
1794                    numBuf, 24);
1795            output += numBuf;
1796            output += "\n      WavefrontSize:   ";
1797            itocstrCStyle(hasValue(kernel.wavefrontSize) ? kernel.wavefrontSize :
1798                    cxuint(1U<<kconfig.wavefrontSize), numBuf, 24);
1799            output += numBuf;
1800            output += "\n      NumSGPRs:        ";
1801            itocstrCStyle(hasValue(kernel.sgprsNum) ? kernel.sgprsNum :
1802                    cxuint(ULEV(kconfig.wavefrontSgprCount)), numBuf, 24);
1803            output += numBuf;
1804            output += "\n      NumVGPRs:        ";
1805            itocstrCStyle(hasValue(kernel.vgprsNum) ? kernel.vgprsNum :
1806                    cxuint(ULEV(kconfig.workitemVgprCount)), numBuf, 24);
1807            output += numBuf;
1808            // spilled registers
1809            if (hasValue(kernel.spilledSgprs))
1810            {
1811                output += "\n      NumSpilledSGPRs: ";
1812                itocstrCStyle(kernel.spilledSgprs, numBuf, 24);
1813                output += numBuf;
1814            }
1815            if (hasValue(kernel.spilledVgprs))
1816            {
1817                output += "\n      NumSpilledVGPRs: ";
1818                itocstrCStyle(kernel.spilledVgprs, numBuf, 24);
1819                output += numBuf;
1820            }
1821            output += "\n      MaxFlatWorkGroupSize: ";
1822            itocstrCStyle(hasValue(kernel.maxFlatWorkGroupSize) ?
1823                        kernel.maxFlatWorkGroupSize : uint64_t(256), numBuf, 24);
1824            output += numBuf;
1825            output += "\n";
1826            if (kernel.fixedWorkGroupSize[0] != 0 || kernel.fixedWorkGroupSize[1] != 0 ||
1827                kernel.fixedWorkGroupSize[2] != 0)
1828            {
1829                output += "      FixedWorkGroupSize:   ";
1830                genArrayValue(3, kernel.fixedWorkGroupSize, output);
1831            }
1832        }
1833    }
1834    output += "...\n";
1835}
1836
1837/*
1838 * ROCm Binary Generator
1839 */
1840
1841ROCmBinGenerator::ROCmBinGenerator() : manageable(false), input(nullptr)
1842{ }
1843
1844ROCmBinGenerator::ROCmBinGenerator(const ROCmInput* rocmInput)
1845        : manageable(false), input(rocmInput)
1846{ }
1847
1848ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
1849        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
1850        size_t globalDataSize, const cxbyte* globalData,
1851        const std::vector<ROCmSymbolInput>& symbols)
1852{
1853    input = new ROCmInput{ deviceType, archMinor, archStepping, 0, false,
1854            globalDataSize, globalData, symbols, codeSize, code };
1855}
1856
1857ROCmBinGenerator::ROCmBinGenerator(GPUDeviceType deviceType,
1858        uint32_t archMinor, uint32_t archStepping, size_t codeSize, const cxbyte* code,
1859        size_t globalDataSize, const cxbyte* globalData,
1860        std::vector<ROCmSymbolInput>&& symbols)
1861{
1862    input = new ROCmInput{ deviceType, archMinor, archStepping, 0, false,
1863            globalDataSize, globalData, std::move(symbols), codeSize, code };
1864}
1865
1866ROCmBinGenerator::~ROCmBinGenerator()
1867{
1868    if (manageable)
1869        delete input;
1870}
1871
1872void ROCmBinGenerator::setInput(const ROCmInput* input)
1873{
1874    if (manageable)
1875        delete input;
1876    manageable = false;
1877    this->input = input;
1878}
1879
1880// ELF notes contents
1881static const cxbyte noteDescType1[8] =
1882{ 2, 0, 0, 0, 1, 0, 0, 0 };
1883
1884static const cxbyte noteDescType3[27] =
1885{ 4, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1886  'A', 'M', 'D', 0, 'A', 'M', 'D', 'G', 'P', 'U', 0 };
1887
1888static inline void addMainSectionToTable(cxuint& sectionsNum, uint16_t* builtinTable,
1889                cxuint elfSectId)
1890{ builtinTable[elfSectId - ELFSECTID_START] = sectionsNum++; }
1891
1892void ROCmBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
1893             Array<cxbyte>* aPtr) const
1894{
1895    AMDGPUArchVersion amdGpuArchValues = getGPUArchVersion(input->deviceType,
1896                GPUArchVersionTable::ROCM);
1897    if (input->archMinor!=UINT32_MAX)
1898        amdGpuArchValues.minor = input->archMinor;
1899    if (input->archStepping!=UINT32_MAX)
1900        amdGpuArchValues.stepping = input->archStepping;
1901   
1902    const char* comment = "CLRX ROCmBinGenerator " CLRX_VERSION;
1903    uint32_t commentSize = ::strlen(comment);
1904    if (input->comment!=nullptr)
1905    {
1906        // if comment, store comment section
1907        comment = input->comment;
1908        commentSize = input->commentSize;
1909        if (commentSize==0)
1910            commentSize = ::strlen(comment);
1911    }
1912   
1913    uint32_t eflags = input->newBinFormat ? 2 : 0;
1914    if (input->eflags != BINGEN_DEFAULT)
1915        eflags = input->eflags;
1916   
1917    ElfBinaryGen64 elfBinGen64({ 0U, 0U, 0x40, 0, ET_DYN,
1918            0xe0, EV_CURRENT, UINT_MAX, 0, eflags },
1919            true, true, true, PHREGION_FILESTART);
1920   
1921    uint16_t mainBuiltinSectTable[ROCMSECTID_MAX-ELFSECTID_START+1];
1922    std::fill(mainBuiltinSectTable,
1923              mainBuiltinSectTable + ROCMSECTID_MAX-ELFSECTID_START+1, SHN_UNDEF);
1924    cxuint mainSectionsNum = 1;
1925   
1926    // generate main builtin section table (for section id translation)
1927    if (input->newBinFormat)
1928        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
1929    if (input->globalData != nullptr)
1930        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_RODATA);
1931    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSYM);
1932    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_HASH);
1933    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_DYNSTR);
1934    const cxuint execProgHeaderRegionIndex = mainSectionsNum;
1935    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_TEXT);
1936    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_DYNAMIC);
1937    if (!input->newBinFormat)
1938    {
1939        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_NOTE);
1940        addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ROCMSECTID_GPUCONFIG);
1941    }
1942    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_COMMENT);
1943    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SYMTAB);
1944    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_SHSTRTAB);
1945    addMainSectionToTable(mainSectionsNum, mainBuiltinSectTable, ELFSECTID_STRTAB);
1946   
1947    // add symbols (kernels, function kernels and data symbols)
1948    elfBinGen64.addSymbol(ElfSymbol64("_DYNAMIC",
1949                  mainBuiltinSectTable[ROCMSECTID_DYNAMIC-ELFSECTID_START],
1950                  ELF64_ST_INFO(STB_LOCAL, STT_NOTYPE), STV_HIDDEN, true, 0, 0));
1951    const uint16_t textSectIndex = mainBuiltinSectTable[ELFSECTID_TEXT-ELFSECTID_START];
1952    for (const ROCmSymbolInput& symbol: input->symbols)
1953    {
1954        ElfSymbol64 elfsym;
1955        switch (symbol.type)
1956        {
1957            case ROCmRegionType::KERNEL:
1958                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1959                      ELF64_ST_INFO(STB_GLOBAL, STT_GNU_IFUNC), 0, true,
1960                      symbol.offset, symbol.size);
1961                break;
1962            case ROCmRegionType::FKERNEL:
1963                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1964                      ELF64_ST_INFO(STB_GLOBAL, STT_FUNC), 0, true,
1965                      symbol.offset, symbol.size);
1966                break;
1967            case ROCmRegionType::DATA:
1968                elfsym = ElfSymbol64(symbol.symbolName.c_str(), textSectIndex,
1969                      ELF64_ST_INFO(STB_GLOBAL, STT_OBJECT), 0, true,
1970                      symbol.offset, symbol.size);
1971                break;
1972            default:
1973                break;
1974        }
1975        // add to symbols and dynamic symbols table
1976        elfBinGen64.addSymbol(elfsym);
1977        elfBinGen64.addDynSymbol(elfsym);
1978    }
1979   
1980    static const int32_t dynTags[] = {
1981        DT_SYMTAB, DT_SYMENT, DT_STRTAB, DT_STRSZ, DT_HASH };
1982    elfBinGen64.addDynamics(sizeof(dynTags)/sizeof(int32_t), dynTags);
1983   
1984    // elf program headers
1985    elfBinGen64.addProgramHeader({ PT_PHDR, PF_R, 0, 1,
1986                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1987    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R, PHREGION_FILESTART,
1988                    execProgHeaderRegionIndex,
1989                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 0x1000 });
1990    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_X, execProgHeaderRegionIndex, 1,
1991                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1992    elfBinGen64.addProgramHeader({ PT_LOAD, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
1993                    true, Elf64Types::nobase, Elf64Types::nobase, 0 });
1994    elfBinGen64.addProgramHeader({ PT_DYNAMIC, PF_R|PF_W, execProgHeaderRegionIndex+1, 1,
1995                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 8 });
1996    elfBinGen64.addProgramHeader({ PT_GNU_RELRO, PF_R, execProgHeaderRegionIndex+1, 1,
1997                    true, Elf64Types::nobase, Elf64Types::nobase, 0, 1 });
1998    elfBinGen64.addProgramHeader({ PT_GNU_STACK, PF_R|PF_W, PHREGION_FILESTART, 0,
1999                    true, 0, 0, 0 });
2000   
2001    if (input->newBinFormat)
2002        // program header for note (new binary format)
2003        elfBinGen64.addProgramHeader({ PT_NOTE, PF_R, 1, 1, true,
2004                    Elf64Types::nobase, Elf64Types::nobase, 0, 4 });
2005   
2006    std::string target = input->target.c_str();
2007    if (target.empty() && !input->targetTripple.empty())
2008    {
2009        target = input->targetTripple.c_str();
2010        char dbuf[20];
2011        snprintf(dbuf, 20, "-gfx%u%u%u", amdGpuArchValues.major, amdGpuArchValues.minor,
2012                 amdGpuArchValues.stepping);
2013        target += dbuf;
2014    }
2015    // elf notes
2016    elfBinGen64.addNote({"AMD", sizeof noteDescType1, noteDescType1, 1U});
2017    std::unique_ptr<cxbyte[]> noteBuf(new cxbyte[0x1b]);
2018    ::memcpy(noteBuf.get(), noteDescType3, 0x1b);
2019    SULEV(*(uint32_t*)(noteBuf.get()+4), amdGpuArchValues.major);
2020    SULEV(*(uint32_t*)(noteBuf.get()+8), amdGpuArchValues.minor);
2021    SULEV(*(uint32_t*)(noteBuf.get()+12), amdGpuArchValues.stepping);
2022    elfBinGen64.addNote({"AMD", 0x1b, noteBuf.get(), 3U});
2023    if (!target.empty())
2024        elfBinGen64.addNote({"AMD", target.size(), (const cxbyte*)target.c_str(), 0xbU});
2025   
2026    std::string metadataStr;
2027    size_t metadataSize = input->metadataSize;
2028    const char* metadata = input->metadata;
2029    if (input->useMetadataInfo)
2030    {
2031        // generate ROCm metadata
2032        std::vector<std::pair<CString, size_t> > symbolIndices(input->symbols.size());
2033        // create sorted indices of symbols by its name
2034        for (size_t k = 0; k < input->symbols.size(); k++)
2035            symbolIndices[k] = std::make_pair(input->symbols[k].symbolName, k);
2036        mapSort(symbolIndices.begin(), symbolIndices.end());
2037       
2038        const size_t mdKernelsNum = input->metadataInfo.kernels.size();
2039        std::unique_ptr<const ROCmKernelConfig*[]> kernelConfigPtrs(
2040                new const ROCmKernelConfig*[mdKernelsNum]);
2041        // generate ROCm kernel config pointers
2042        for (size_t k = 0; k < mdKernelsNum; k++)
2043        {
2044            auto it = binaryMapFind(symbolIndices.begin(), symbolIndices.end(), 
2045                        input->metadataInfo.kernels[k].name);
2046            if (it == symbolIndices.end() ||
2047                (input->symbols[it->second].type != ROCmRegionType::FKERNEL &&
2048                 input->symbols[it->second].type != ROCmRegionType::KERNEL))
2049                throw BinGenException("Kernel in metadata doesn't exists in code");
2050            kernelConfigPtrs[k] = reinterpret_cast<const ROCmKernelConfig*>(
2051                        input->code + input->symbols[it->second].offset);
2052        }
2053        // just generate ROCm metadata from info
2054        generateROCmMetadata(input->metadataInfo, kernelConfigPtrs.get(), metadataStr);
2055        metadataSize = metadataStr.size();
2056        metadata = metadataStr.c_str();
2057    }
2058   
2059    if (metadataSize != 0)
2060        elfBinGen64.addNote({"AMD", metadataSize, (const cxbyte*)metadata, 0xaU});
2061   
2062    /// region and sections
2063    elfBinGen64.addRegion(ElfRegion64::programHeaderTable());
2064    if (input->newBinFormat)
2065        elfBinGen64.addRegion(ElfRegion64::noteSection());
2066    if (input->globalData != nullptr)
2067        elfBinGen64.addRegion(ElfRegion64(input->globalDataSize, input->globalData, 4,
2068                ".rodata", SHT_PROGBITS, SHF_ALLOC, 0, 0, Elf64Types::nobase));
2069   
2070    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
2071                ".dynsym", SHT_DYNSYM, SHF_ALLOC, 0, 1, Elf64Types::nobase));
2072    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 4,
2073                ".hash", SHT_HASH, SHF_ALLOC,
2074                mainBuiltinSectTable[ELFSECTID_DYNSYM-ELFSECTID_START], 0,
2075                Elf64Types::nobase));
2076    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1, ".dynstr", SHT_STRTAB,
2077                SHF_ALLOC, 0, 0, Elf64Types::nobase));
2078    // '.text' with alignment=4096
2079    elfBinGen64.addRegion(ElfRegion64(input->codeSize, (const cxbyte*)input->code, 
2080              0x1000, ".text", SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR, 0, 0,
2081              Elf64Types::nobase, 0, false, 256));
2082    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 0x1000,
2083                ".dynamic", SHT_DYNAMIC, SHF_ALLOC|SHF_WRITE,
2084                mainBuiltinSectTable[ELFSECTID_DYNSTR-ELFSECTID_START], 0,
2085                Elf64Types::nobase, 0, false, 8));
2086    if (!input->newBinFormat)
2087    {
2088        elfBinGen64.addRegion(ElfRegion64::noteSection());
2089        elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 1,
2090                    ".AMDGPU.config", SHT_PROGBITS, 0));
2091    }
2092    elfBinGen64.addRegion(ElfRegion64(commentSize, (const cxbyte*)comment, 1, ".comment",
2093              SHT_PROGBITS, SHF_MERGE|SHF_STRINGS, 0, 0, 0, 1));
2094    elfBinGen64.addRegion(ElfRegion64(0, (const cxbyte*)nullptr, 8,
2095                ".symtab", SHT_SYMTAB, 0, 0, 2));
2096    elfBinGen64.addRegion(ElfRegion64::shstrtabSection());
2097    elfBinGen64.addRegion(ElfRegion64::strtabSection());
2098    elfBinGen64.addRegion(ElfRegion64::sectionHeaderTable());
2099   
2100    /* extra sections */
2101    for (const BinSection& section: input->extraSections)
2102        elfBinGen64.addRegion(ElfRegion64(section, mainBuiltinSectTable,
2103                         ROCMSECTID_MAX, mainSectionsNum));
2104    /* extra symbols */
2105    for (const BinSymbol& symbol: input->extraSymbols)
2106        elfBinGen64.addSymbol(ElfSymbol64(symbol, mainBuiltinSectTable,
2107                         ROCMSECTID_MAX, mainSectionsNum));
2108   
2109    size_t binarySize = elfBinGen64.countSize();
2110    /****
2111     * prepare for write binary to output
2112     ****/
2113    std::unique_ptr<std::ostream> outStreamHolder;
2114    std::ostream* os = nullptr;
2115    if (aPtr != nullptr)
2116    {
2117        aPtr->resize(binarySize);
2118        outStreamHolder.reset(
2119                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
2120        os = outStreamHolder.get();
2121    }
2122    else if (vPtr != nullptr)
2123    {
2124        vPtr->resize(binarySize);
2125        outStreamHolder.reset(new VectorOStream(*vPtr));
2126        os = outStreamHolder.get();
2127    }
2128    else // from argument
2129        os = osPtr;
2130   
2131    const std::ios::iostate oldExceptions = os->exceptions();
2132    try
2133    {
2134    os->exceptions(std::ios::failbit | std::ios::badbit);
2135    /****
2136     * write binary to output
2137     ****/
2138    FastOutputBuffer bos(256, *os);
2139    elfBinGen64.generate(bos);
2140    assert(bos.getWritten() == binarySize);
2141    }
2142    catch(...)
2143    {
2144        os->exceptions(oldExceptions);
2145        throw;
2146    }
2147    os->exceptions(oldExceptions);
2148}
2149
2150void ROCmBinGenerator::generate(Array<cxbyte>& array) const
2151{
2152    generateInternal(nullptr, nullptr, &array);
2153}
2154
2155void ROCmBinGenerator::generate(std::ostream& os) const
2156{
2157    generateInternal(&os, nullptr, nullptr);
2158}
2159
2160void ROCmBinGenerator::generate(std::vector<char>& v) const
2161{
2162    generateInternal(nullptr, &v, nullptr);
2163}
Note: See TracBrowser for help on using the repository browser.