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

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

CLRadeonExtender: ROCmBin: Correct sorting rocmValueTypes table.

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