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

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

CLRadeonExtender: ROCmMetadata: Fixed String block parsing (treating empty lines and etc).

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