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

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

CLRadeonExtender: ROCmBin: ROCm YAML Metadata parser stuff (untested).

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