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

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

CLRadeonExtender: AsmGallium?: Remove obsolete (stupid) checking spilled GPRs.
AsmROCm: Add first stuff to handling ROCm metadata config. ROCmBin: Checking uniqueness of printf ids.

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