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

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

CLRadeonExtender: ROcmMetadata: Fixed handling string block at end of stream.

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