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

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

CLRadeonExtender: ROCmBin: fixed stupid bug: replace newline by space while parsing the string block with newline folding.

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