source: CLRX/CLRadeonExtender/trunk/amdbin/GalliumBinaries.cpp @ 3336

Last change on this file since 3336 was 3336, checked in by matszpk, 15 months ago

CLRadeonExtender: GalliumBin?: Fixed indexing of progInfo (include various number of proginfos per kernel).
Remove reinterpret cast from access methods.

File size: 35.9 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2017 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 <climits>
23#include <algorithm>
24#include <cstdint>
25#include <utility>
26#include <memory>
27#include <mutex>
28#include <fstream>
29#include <CLRX/utils/Utilities.h>
30#include <CLRX/utils/MemAccess.h>
31#include <CLRX/utils/InputOutput.h>
32#include <CLRX/utils/Containers.h>
33#include <CLRX/amdbin/GalliumBinaries.h>
34
35using namespace CLRX;
36
37/* Gallium ELF binary */
38
39GalliumElfBinaryBase::GalliumElfBinaryBase() :
40        progInfosNum(0), progInfoEntries(nullptr), disasmSize(0), disasmOffset(0),
41        llvm390(false)
42{ }
43
44template<typename ElfBinary>
45void GalliumElfBinaryBase::loadFromElf(ElfBinary& elfBinary, size_t kernelsNum)
46{
47    uint16_t amdGpuConfigIndex = SHN_UNDEF;
48    try
49    { amdGpuConfigIndex = elfBinary.getSectionIndex(".AMDGPU.config"); }
50    catch(const Exception& ex)
51    { }
52   
53    uint16_t amdGpuDisasmIndex = SHN_UNDEF;
54    try
55    { amdGpuDisasmIndex = elfBinary.getSectionIndex(".AMDGPU.disasm"); }
56    catch(const Exception& ex)
57    { }
58    if (amdGpuDisasmIndex != SHN_UNDEF)
59    {   // set disassembler section
60        const auto& shdr = elfBinary.getSectionHeader(amdGpuDisasmIndex);
61        disasmOffset = ULEV(shdr.sh_offset);
62        disasmSize = ULEV(shdr.sh_size);
63    }
64   
65    uint16_t textIndex = SHN_UNDEF;
66    size_t textSize = 0;
67    try
68    {
69        textIndex = elfBinary.getSectionIndex(".text");
70        textSize = ULEV(elfBinary.getSectionHeader(textIndex).sh_size);
71    }
72    catch(const Exception& ex)
73    { }
74   
75    if (amdGpuConfigIndex == SHN_UNDEF || textIndex == SHN_UNDEF)
76        return;
77    // create amdGPU config systems
78    const auto& shdr = elfBinary.getSectionHeader(amdGpuConfigIndex);
79    size_t shdrSize = ULEV(shdr.sh_size);
80    size_t amdGPUConfigSize = (shdrSize / kernelsNum);
81    if (amdGPUConfigSize != 24 && amdGPUConfigSize != 40 &&
82        shdrSize % amdGPUConfigSize != 0)
83        throw Exception("Wrong size of .AMDGPU.config section!");
84    llvm390 = amdGPUConfigSize==40;
85    const cxuint progInfoEntriesNum = amdGPUConfigSize>>3;
86   
87    const bool hasProgInfoMap = (elfBinary.getCreationFlags() &
88                        GALLIUM_ELF_CREATE_PROGINFOMAP) != 0;
89    /* check symbols */
90    const size_t symbolsNum = elfBinary.getSymbolsNum();
91    progInfosNum = 0;
92    if (hasProgInfoMap)
93        progInfoEntryMap.resize(symbolsNum);
94    for (size_t i = 0; i < symbolsNum; i++)
95    {
96        const auto& sym = elfBinary.getSymbol(i);
97        const char* symName = elfBinary.getSymbolName(i);
98        if (ULEV(sym.st_shndx) == textIndex && ELF32_ST_BIND(sym.st_info) == STB_GLOBAL)
99        {
100            if (ULEV(sym.st_value) >= textSize)
101                throw Exception("kernel symbol offset out of range");
102            if (hasProgInfoMap)
103                progInfoEntryMap[progInfosNum] = std::make_pair(symName,
104                                progInfoEntriesNum*progInfosNum);
105            progInfosNum++;
106        }
107    }
108    if (progInfosNum*amdGPUConfigSize != ULEV(shdr.sh_size))
109        throw Exception("Number of symbol kernels doesn't match progInfos number!");
110    cxbyte* binaryCode = (cxbyte*)elfBinary.getBinaryCode();
111    progInfoEntries = reinterpret_cast<GalliumProgInfoEntry*>(binaryCode +
112                ULEV(shdr.sh_offset));
113   
114    if (hasProgInfoMap)
115    {
116        progInfoEntryMap.resize(progInfosNum);
117        mapSort(progInfoEntryMap.begin(), progInfoEntryMap.end(), CStringLess());
118    }
119}
120
121GalliumElfBinaryBase::~GalliumElfBinaryBase()
122{ }
123
124GalliumElfBinary32::GalliumElfBinary32()
125{ }
126
127GalliumElfBinary32::~GalliumElfBinary32()
128{ }
129
130GalliumElfBinary32::GalliumElfBinary32(size_t binaryCodeSize, cxbyte* binaryCode,
131           Flags creationFlags, size_t kernelsNum) :
132           ElfBinary32(binaryCodeSize, binaryCode, creationFlags)
133       
134{
135    loadFromElf(static_cast<const ElfBinary32&>(*this), kernelsNum);
136}
137
138GalliumElfBinary64::GalliumElfBinary64()
139{ }
140
141GalliumElfBinary64::~GalliumElfBinary64()
142{ }
143
144GalliumElfBinary64::GalliumElfBinary64(size_t binaryCodeSize, cxbyte* binaryCode,
145           Flags creationFlags, size_t kernelsNum) :
146           ElfBinary64(binaryCodeSize, binaryCode, creationFlags)
147       
148{
149    loadFromElf(static_cast<const ElfBinary64&>(*this), kernelsNum);
150}
151
152uint32_t GalliumElfBinaryBase::getProgramInfoEntriesNum(uint32_t index) const
153{ return 3; }
154
155uint32_t GalliumElfBinaryBase::getProgramInfoEntryIndex(const char* name) const
156{
157    ProgInfoEntryIndexMap::const_iterator it = binaryMapFind(progInfoEntryMap.begin(),
158                         progInfoEntryMap.end(), name, CStringLess());
159    if (it == progInfoEntryMap.end())
160        throw Exception("Can't find GalliumElf ProgInfoEntry");
161    return it->second;
162}
163
164const GalliumProgInfoEntry* GalliumElfBinaryBase::getProgramInfo(uint32_t index) const
165{
166    return progInfoEntries + index*(llvm390 ? 5U : 3U);
167}
168
169GalliumProgInfoEntry* GalliumElfBinaryBase::getProgramInfo(uint32_t index)
170{
171    return progInfoEntries + index*(llvm390 ? 5U : 3U);
172}
173
174/* main GalliumBinary */
175
176template<typename GalliumElfBinary>
177static void verifyKernelSymbols(size_t kernelsNum, const GalliumKernel* kernels,
178                const GalliumElfBinary& elfBinary)
179{
180    size_t symIndex = 0;
181    const size_t symsNum = elfBinary.getSymbolsNum();
182    uint16_t textIndex = elfBinary.getSectionIndex(".text");
183    for (uint32_t i = 0; i < kernelsNum; i++)
184    {
185        const GalliumKernel& kernel = kernels[i];
186        for (; symIndex < symsNum; symIndex++)
187        {
188            const auto& sym = elfBinary.getSymbol(symIndex);
189            const char* symName = elfBinary.getSymbolName(symIndex);
190            // kernel symol must be defined as global and must be bound to text section
191            if (ULEV(sym.st_shndx) == textIndex &&
192                ELF32_ST_BIND(sym.st_info) == STB_GLOBAL)
193            {   // names must be stored in order
194                if (kernel.kernelName != symName)
195                    throw Exception("Kernel symbols out of order!");
196                if (ULEV(sym.st_value) != kernel.offset)
197                    throw Exception("Kernel symbol value and Kernel "
198                                "offset doesn't match");
199                break;
200            }
201        }
202        if (symIndex >= symsNum)
203            throw Exception("Number of kernels in ElfBinary and "
204                        "MainBinary doesn't match");
205        symIndex++;
206    }
207}
208
209GalliumBinary::GalliumBinary(size_t _binaryCodeSize, cxbyte* _binaryCode,
210                 Flags _creationFlags) : creationFlags(_creationFlags),
211         binaryCodeSize(_binaryCodeSize), binaryCode(_binaryCode),
212         kernelsNum(0), sectionsNum(0), kernels(nullptr), sections(nullptr),
213         elf64BitBinary(false), mesa170(false)
214{
215    if (binaryCodeSize < 4)
216        throw Exception("GalliumBinary is too small!!!");
217    uint32_t* data32 = reinterpret_cast<uint32_t*>(binaryCode);
218    kernelsNum = ULEV(*data32);
219    if (binaryCodeSize < uint64_t(kernelsNum)*16U)
220        throw Exception("Kernels number is too big!");
221    kernels.reset(new GalliumKernel[kernelsNum]);
222    cxbyte* data = binaryCode + 4;
223    // parse kernels symbol info and their arguments
224    for (cxuint i = 0; i < kernelsNum; i++)
225    {
226        GalliumKernel& kernel = kernels[i];
227        if (usumGt(uint32_t(data-binaryCode), 4U, binaryCodeSize))
228            throw Exception("GalliumBinary is too small!!!");
229       
230        const cxuint symNameLen = ULEV(*reinterpret_cast<const uint32_t*>(data));
231        data+=4;
232        if (usumGt(uint32_t(data-binaryCode), symNameLen, binaryCodeSize))
233            throw Exception("Kernel name length is too long!");
234       
235        kernel.kernelName.assign((const char*)data, symNameLen);
236       
237        /// check kernel name order (sorted order is required by Mesa3D radeon driver)
238        if (i != 0 && kernel.kernelName < kernels[i-1].kernelName)
239            throw Exception("Unsorted kernel table!");
240       
241        data += symNameLen;
242        if (usumGt(uint32_t(data-binaryCode), 12U, binaryCodeSize))
243            throw Exception("GalliumBinary is too small!!!");
244       
245        data32 = reinterpret_cast<uint32_t*>(data);
246        kernel.sectionId = ULEV(data32[0]);
247        kernel.offset = ULEV(data32[1]);
248        const uint32_t argsNum = ULEV(data32[2]);
249        data32 += 3;
250        data = reinterpret_cast<cxbyte*>(data32);
251       
252        if (UINT32_MAX/24U < argsNum)
253            throw Exception("Number of arguments number is too high!");
254        if (usumGt(uint32_t(data-binaryCode), 24U*argsNum, binaryCodeSize))
255            throw Exception("GalliumBinary is too small!!!");
256       
257        kernel.argInfos.resize(argsNum);
258        for (uint32_t j = 0; j < argsNum; j++)
259        {
260            GalliumArgInfo& argInfo = kernel.argInfos[j];
261            const cxuint type = ULEV(data32[0]);
262            // accept not known arg type by this CLRadeonExtender
263            if (type > 255)
264                throw Exception("Type of kernel argument out of handled range");
265            argInfo.type = GalliumArgType(type);
266            argInfo.size = ULEV(data32[1]);
267            argInfo.targetSize = ULEV(data32[2]);
268            argInfo.targetAlign = ULEV(data32[3]);
269            argInfo.signExtended = ULEV(data32[4])!=0;
270            const cxuint semType = ULEV(data32[5]);
271            // accept not known semantic type by this CLRadeonExtender
272            if (semType > 255)
273                throw Exception("Semantic of kernel argument out of handled range");
274            argInfo.semantic = GalliumArgSemantic(semType);
275            data32 += 6;
276        }
277        data = reinterpret_cast<cxbyte*>(data32);
278    }
279   
280    if (usumGt(uint32_t(data-binaryCode), 4U, binaryCodeSize))
281        throw Exception("GalliumBinary is too small!!!");
282   
283    sectionsNum = ULEV(data32[0]);
284    if (binaryCodeSize-(data-binaryCode) < uint64_t(sectionsNum)*20U)
285        throw Exception("Sections number is too big!");
286    sections.reset(new GalliumSection[sectionsNum]);
287    // parse sections and their content
288    data32++;
289    data += 4;
290   
291    uint32_t elfSectionId = 0; // initialize warning
292    for (uint32_t i = 0; i < sectionsNum; i++)
293    {
294        GalliumSection& section = sections[i];
295        if (usumGt(uint32_t(data-binaryCode), 20U, binaryCodeSize))
296            throw Exception("GalliumBinary is too small!!!");
297       
298        section.sectionId = ULEV(data32[0]);
299        const uint32_t secType = ULEV(data32[1]);
300        if (secType > 255)
301            throw Exception("Type of section out of range");
302        section.type = GalliumSectionType(secType);
303        section.size = ULEV(data32[2]);
304        const uint32_t sizeOfData = ULEV(data32[3]);
305        const uint32_t sizeFromHeader = ULEV(data32[4]); // from LLVM binary
306        if (section.size != sizeOfData-4 || section.size != sizeFromHeader)
307            throw Exception("Section size fields doesn't match itself!");
308       
309        data = reinterpret_cast<cxbyte*>(data32+5);
310        if (usumGt(uint32_t(data-binaryCode), section.size, binaryCodeSize))
311            throw Exception("Section size is too big!!!");
312       
313        section.offset = data-binaryCode;
314       
315        if (!elfBinary && (section.type == GalliumSectionType::TEXT ||
316            section.type == GalliumSectionType::TEXT_EXECUTABLE_170))
317        {   // if new Mesa3D 17.0
318            mesa170 = (section.type == GalliumSectionType::TEXT_EXECUTABLE_170);
319            if (section.size < sizeof(Elf32_Ehdr))
320                throw Exception("Wrong GalliumElfBinary size");
321            const Elf32_Ehdr& ehdr = *reinterpret_cast<const Elf32_Ehdr*>(data);
322            if (ehdr.e_ident[EI_CLASS] == ELFCLASS32)
323            {   // 32-bit
324                elfBinary.reset(new GalliumElfBinary32(section.size, data,
325                                 creationFlags>>GALLIUM_INNER_SHIFT, kernelsNum));
326                elf64BitBinary = false;
327            }
328            else if (ehdr.e_ident[EI_CLASS] == ELFCLASS64)
329            {   // 64-bit
330                elfSectionId = section.sectionId;
331                elfBinary.reset(new GalliumElfBinary64(section.size, data,
332                                 creationFlags>>GALLIUM_INNER_SHIFT, kernelsNum));
333                elf64BitBinary = true;
334            }
335            else // wrong class
336                throw Exception("Wrong GalliumElfBinary class");
337        }
338        data += section.size;
339        data32 = reinterpret_cast<uint32_t*>(data);
340    }
341   
342    if (!elfBinary)
343        throw Exception("Gallium Elf binary not found!");
344    for (uint32_t i = 0; i < kernelsNum; i++)
345        if (kernels[i].sectionId != elfSectionId)
346            throw Exception("Kernel not in text section!");
347    // verify kernel offsets
348    if (!elf64BitBinary)
349        verifyKernelSymbols(kernelsNum, kernels.get(), getElfBinary32());
350    else
351        verifyKernelSymbols(kernelsNum, kernels.get(), getElfBinary64());
352}
353
354uint32_t GalliumBinary::getKernelIndex(const char* name) const
355{
356    const GalliumKernel v = { name };
357    const GalliumKernel* it = binaryFind(kernels.get(), kernels.get()+kernelsNum, v,
358       [](const GalliumKernel& k1, const GalliumKernel& k2)
359       { return k1.kernelName < k2.kernelName; });
360    if (it == kernels.get()+kernelsNum || it->kernelName != name)
361        throw Exception("Can't find Gallium Kernel Index");
362    return it-kernels.get();
363}
364
365void GalliumInput::addEmptyKernel(const char* kernelName, cxuint llvmVersion)
366{
367    GalliumKernelInput kinput = { kernelName, {
368        /* default values */
369        { 0x0000b848U, 0x000c0000U },
370        { 0x0000b84cU, 0x00001788U },
371        { 0x0000b860U, 0 } }, false, { }, 0, {} };
372    kinput.config.dimMask = BINGEN_DEFAULT;
373    kinput.config.usedVGPRsNum = BINGEN_DEFAULT;
374    kinput.config.usedSGPRsNum = BINGEN_DEFAULT;
375    kinput.config.floatMode = 0xc0;
376    kinput.config.userDataNum = (llvmVersion >= 40000U) ? BINGEN_DEFAULT : 4;
377    kinput.config.spilledVGPRs = kinput.config.spilledSGPRs = 0;
378    kernels.push_back(std::move(kinput));
379}
380
381/*
382 * GalliumBinGenerator
383 */
384
385GalliumBinGenerator::GalliumBinGenerator() : manageable(false), input(nullptr)
386{ }
387
388GalliumBinGenerator::GalliumBinGenerator(const GalliumInput* galliumInput)
389        : manageable(false), input(galliumInput)
390{ }
391
392GalliumBinGenerator::GalliumBinGenerator(bool _64bitMode, GPUDeviceType deviceType,
393        size_t codeSize, const cxbyte* code,
394        size_t globalDataSize, const cxbyte* globalData,
395        const std::vector<GalliumKernelInput>& kernels)
396        : manageable(true), input(nullptr)
397{
398    input = new GalliumInput{ _64bitMode, false, false, deviceType, globalDataSize,
399            globalData, kernels, codeSize, code, 0, nullptr };
400}
401
402GalliumBinGenerator::GalliumBinGenerator(bool _64bitMode, GPUDeviceType deviceType,
403        size_t codeSize, const cxbyte* code,
404        size_t globalDataSize, const cxbyte* globalData,
405        std::vector<GalliumKernelInput>&& kernels)
406        : manageable(true), input(nullptr)
407{
408    input = new GalliumInput{ _64bitMode, false, false, deviceType, globalDataSize,
409            globalData, std::move(kernels), codeSize, code, 0, nullptr };
410}
411
412
413GalliumBinGenerator::~GalliumBinGenerator()
414{
415    if (manageable)
416        delete input;
417}
418
419void GalliumBinGenerator::setInput(const GalliumInput* input)
420{
421    if (manageable)
422        delete input;
423    manageable = false;
424    this->input = input;
425}
426
427// section index for symbol binding
428static const uint16_t mainBuiltinSectionTable[] =
429{
430    8, // ELFSECTID_SHSTRTAB
431    10, // ELFSECTID_STRTAB
432    9, // ELFSECTID_SYMTAB
433    SHN_UNDEF, // ELFSECTID_DYNSTR
434    SHN_UNDEF, // ELFSECTID_DYNSYM
435    1, // ELFSECTID_TEXT
436    5, // ELFSECTID_RODATA
437    2, // ELFSECTID_DATA
438    3, // ELFSECTID_BSS
439    6, // ELFSECTID_COMMENT
440    4, // GALLIUMSECTID_GPUCONFIG
441    7  // GALLIUMSECTID_NOTEGNUSTACK
442};
443
444// section index for symbol binding
445static const uint16_t mainBuiltinSectionTable2[] =
446{
447    7, // ELFSECTID_SHSTRTAB
448    9, // ELFSECTID_STRTAB
449    8, // ELFSECTID_SYMTAB
450    SHN_UNDEF, // ELFSECTID_DYNSTR
451    SHN_UNDEF, // ELFSECTID_DYNSYM
452    1, // ELFSECTID_TEXT
453    SHN_UNDEF, // ELFSECTID_RODATA
454    2, // ELFSECTID_DATA
455    3, // ELFSECTID_BSS
456    5, // ELFSECTID_COMMENT
457    4, // GALLIUMSECTID_GPUCONFIG
458    6  // GALLIUMSECTID_NOTEGNUSTACK
459};
460
461class CLRX_INTERNAL AmdGpuConfigContent: public ElfRegionContent
462{
463private:
464    const Array<uint32_t>& kernelsOrder;
465    const GalliumInput& input;
466public:
467    AmdGpuConfigContent(const Array<uint32_t>& inKernelsOrder,
468            const GalliumInput& inInput) : kernelsOrder(inKernelsOrder), input(inInput)
469    { }
470   
471    void operator()(FastOutputBuffer& fob) const
472    {
473        const GPUArchitecture arch = getGPUArchitectureFromDeviceType(input.deviceType);
474        const cxuint ldsShift = arch<GPUArchitecture::GCN1_1 ? 8 : 9;
475        const uint32_t ldsMask = (1U<<ldsShift)-1U;
476       
477        const cxuint progInfoEntriesNum = input.isLLVM390 ? 5 : 3;
478       
479        for (uint32_t korder: kernelsOrder)
480        {
481            const GalliumKernelInput& kernel = input.kernels[korder];
482            GalliumProgInfoEntry outEntries[5];
483            if (kernel.useConfig)
484            {
485                const GalliumKernelConfig& config = kernel.config;
486                outEntries[0].address = ULEV(0x0000b848U);
487                outEntries[1].address = ULEV(0x0000b84cU);
488                outEntries[2].address = ULEV(0x0000b860U);
489                outEntries[3].address = ULEV(0x00000004U);
490                outEntries[4].address = ULEV(0x00000008U);
491               
492                uint32_t scratchBlocks = ((config.scratchBufferSize<<6) + 1023)>>10;
493                uint32_t dimValues = 0;
494                if (config.dimMask != BINGEN_DEFAULT)
495                    dimValues = ((config.dimMask&7)<<7) |
496                            (((config.dimMask&4) ? 2 : (config.dimMask&2) ? 1 : 0)<<11);
497                else
498                    dimValues |= (config.pgmRSRC2 & 0x1b80U);
499                cxuint sgprsNum = std::max(config.usedSGPRsNum, 1U);
500                cxuint vgprsNum = std::max(config.usedVGPRsNum, 1U);
501                /// pgmRSRC1
502                outEntries[0].value = (config.pgmRSRC1) | ((vgprsNum-1)>>2) |
503                        (((sgprsNum-1)>>3)<<6) | ((uint32_t(config.floatMode)&0xff)<<12) |
504                        (config.ieeeMode?1U<<23:0) | (uint32_t(config.priority&3)<<10) |
505                        (config.privilegedMode?1U<<20:0) | (config.dx10Clamp?1U<<21:0) |
506                        (config.debugMode?1U<<22:0);
507               
508                outEntries[1].value = (config.pgmRSRC2 & 0xffffe440U) |
509                        (config.userDataNum<<1) | ((config.tgSize) ? 0x400 : 0) |
510                        ((config.scratchBufferSize)?1:0) | dimValues |
511                        (((config.localSize+ldsMask)>>ldsShift)<<15) |
512                        ((uint32_t(config.exceptions)&0x7f)<<24);
513                outEntries[2].value = (scratchBlocks)<<12;
514                if (input.isLLVM390)
515                {
516                    outEntries[3].value = config.spilledSGPRs;
517                    outEntries[4].value = config.spilledVGPRs;
518                }
519                for (cxuint k = 0; k < progInfoEntriesNum; k++)
520                    outEntries[k].value = ULEV(outEntries[k].value);
521            }
522            else
523            {
524                for (cxuint k = 0; k < progInfoEntriesNum; k++)
525                {
526                    outEntries[k].address = ULEV(kernel.progInfo[k].address);
527                    outEntries[k].value = ULEV(kernel.progInfo[k].value);
528                }
529            }
530            fob.writeArray(progInfoEntriesNum, outEntries);
531        }
532    }
533};
534
535template<typename Types>
536static void putSectionsAndSymbols(ElfBinaryGenTemplate<Types>& elfBinGen,
537      const GalliumInput* input, const Array<uint32_t>& kernelsOrder,
538      const AmdGpuConfigContent& amdGpuConfigContent)
539{
540    const char* comment = "CLRX GalliumBinGenerator " CLRX_VERSION;
541    uint32_t commentSize = ::strlen(comment);
542   
543    typedef ElfRegionTemplate<Types> ElfRegion;
544    typedef ElfSymbolTemplate<Types> ElfSymbol;
545    const uint32_t kernelsNum = input->kernels.size();
546    elfBinGen.addRegion(ElfRegion(input->codeSize, input->code, 256, ".text",
547            SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR));
548    elfBinGen.addRegion(ElfRegion(0, (const cxbyte*)nullptr, 4, ".data",
549            SHT_PROGBITS, SHF_ALLOC|SHF_WRITE));
550    elfBinGen.addRegion(ElfRegion(0, (const cxbyte*)nullptr, 4, ".bss",
551            SHT_NOBITS, SHF_ALLOC|SHF_WRITE));
552    // write configuration for kernel execution
553    const cxuint progInfoEntriesNum = input->isLLVM390 ? 5 : 3;
554    elfBinGen.addRegion(ElfRegion(uint64_t(8U*progInfoEntriesNum)*kernelsNum,
555            &amdGpuConfigContent, 1, ".AMDGPU.config", SHT_PROGBITS, 0));
556   
557    if (input->globalData!=nullptr)
558        elfBinGen.addRegion(ElfRegion(input->globalDataSize, input->globalData, 4,
559                ".rodata", SHT_PROGBITS, SHF_ALLOC));
560   
561    if (input->comment!=nullptr)
562    {   // if comment, store comment section
563        comment = input->comment;
564        commentSize = input->commentSize;
565        if (commentSize==0)
566            commentSize = ::strlen(comment);
567    }
568    elfBinGen.addRegion(ElfRegion(commentSize, (const cxbyte*)comment, 1,
569                ".comment", SHT_PROGBITS, SHF_STRINGS|SHF_MERGE, 0, 0, 0, 1));
570    elfBinGen.addRegion(ElfRegion(0, (const cxbyte*)nullptr, 1, ".note.GNU-stack",
571            SHT_PROGBITS, 0));
572    elfBinGen.addRegion(ElfRegion::shstrtabSection());
573    elfBinGen.addRegion(ElfRegion::sectionHeaderTable());
574    elfBinGen.addRegion(ElfRegion::symtabSection());
575    elfBinGen.addRegion(ElfRegion::strtabSection());
576    /* symbols */
577    /// EndOfTextLabel - ?? always is at end of symbol table
578    elfBinGen.addSymbol({"EndOfTextLabel", 1, ELF32_ST_INFO(STB_LOCAL, STT_NOTYPE),
579            0, false, uint32_t(input->codeSize), 0});
580    const cxuint sectSymsNum = 6 + (input->globalData!=nullptr);
581    // local symbols for sections
582    for (cxuint i = 0; i < sectSymsNum; i++)
583        elfBinGen.addSymbol({"", uint16_t(i+1), ELF32_ST_INFO(STB_LOCAL, STT_SECTION),
584                0, false, 0, 0});
585    for (uint32_t korder: kernelsOrder)
586    {
587        const GalliumKernelInput& kernel = input->kernels[korder];
588        elfBinGen.addSymbol({kernel.kernelName.c_str(), 1,
589                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, false, kernel.offset, 0});
590    }
591   
592    /* choose builtin section table and extraSectionStartIndex */
593    const uint16_t* curMainBuiltinSections = (input->globalData!=nullptr) ?
594            mainBuiltinSectionTable : mainBuiltinSectionTable2;
595    cxuint startSectionIndex = (input->globalData!=nullptr) ? 11 : 10;
596   
597    /* extra sections */
598    for (const BinSection& section: input->extraSections)
599        elfBinGen.addRegion(ElfRegion(section, curMainBuiltinSections,
600                         GALLIUMSECTID_MAX, startSectionIndex));
601    /* extra symbols */
602    for (const BinSymbol& symbol: input->extraSymbols)
603        elfBinGen.addSymbol(ElfSymbol(symbol, curMainBuiltinSections,
604                         GALLIUMSECTID_MAX, startSectionIndex));
605}
606
607void GalliumBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
608             Array<cxbyte>* aPtr) const
609{
610    const uint32_t kernelsNum = input->kernels.size();
611    /* compute size of binary */
612    uint64_t elfSize = 0;
613    uint64_t binarySize = uint64_t(8) + size_t(kernelsNum)*16U + 20U /* section */;
614    for (const GalliumKernelInput& kernel: input->kernels)
615        binarySize += uint64_t(kernel.argInfos.size())*24U + kernel.kernelName.size();
616   
617    const GPUArchitecture arch = getGPUArchitectureFromDeviceType(input->deviceType);
618    const cxuint maxSGPRSNum = getGPUMaxRegistersNum(arch, REGTYPE_SGPR, 0);
619    const cxuint maxVGPRSNum = getGPUMaxRegistersNum(arch, REGTYPE_VGPR, 0);
620    for (const GalliumKernelInput& kernel: input->kernels)
621        if (kernel.useConfig)
622        {
623            const GalliumKernelConfig& config = kernel.config;
624            if (config.usedVGPRsNum > maxVGPRSNum)
625                throw Exception("Used VGPRs number out of range");
626            if (config.usedSGPRsNum > maxSGPRSNum)
627                throw Exception("Used SGPRs number out of range");
628            if (config.localSize > 32768)
629                throw Exception("LocalSize out of range");
630            if (config.priority >= 4)
631                throw Exception("Priority out of range");
632            if (config.userDataNum > 16)
633                throw Exception("UserDataNum out of range");
634        }
635   
636    // sort kernels by name (for correct order in binary file) */
637    Array<uint32_t> kernelsOrder(kernelsNum);
638    for (cxuint i = 0; i < kernelsNum; i++)
639        kernelsOrder[i] = i;
640    std::sort(kernelsOrder.begin(), kernelsOrder.end(),
641          [this](const uint32_t& a,const uint32_t& b)
642          { return input->kernels[a].kernelName < input->kernels[b].kernelName; });
643   
644    std::unique_ptr<ElfBinaryGen32> elfBinGen32;
645    std::unique_ptr<ElfBinaryGen64> elfBinGen64;
646   
647    AmdGpuConfigContent amdGpuConfigContent(kernelsOrder, *input);
648    if (!input->is64BitElf)
649    {   /* 32-bit ELF */
650        elfBinGen32.reset(new ElfBinaryGen32({ 0, 0, ELFOSABI_SYSV, 0,  ET_REL, 0,
651                    EV_CURRENT, UINT_MAX, 0, 0 }));
652        putSectionsAndSymbols(*elfBinGen32, input, kernelsOrder, amdGpuConfigContent);
653        elfSize = elfBinGen32->countSize();
654    }
655    else
656    {   /* 64-bit ELF */
657        elfBinGen64.reset(new ElfBinaryGen64({ 0, 0, ELFOSABI_SYSV, 0,  ET_REL, 0,
658                    EV_CURRENT, UINT_MAX, 0, 0 }));
659        putSectionsAndSymbols(*elfBinGen64, input, kernelsOrder, amdGpuConfigContent);
660        elfSize = elfBinGen64->countSize();
661    }
662
663    binarySize += elfSize;
664   
665    if (
666#ifdef HAVE_64BIT
667        !input->is64BitElf &&
668#endif
669        elfSize > UINT32_MAX)
670        throw Exception("Elf binary size is too big!");
671   
672#ifdef HAVE_32BIT
673    if (binarySize > UINT32_MAX)
674        throw Exception("Binary size is too big!");
675#endif
676    /****
677     * prepare for write binary to output
678     ****/
679    std::unique_ptr<std::ostream> outStreamHolder;
680    std::ostream* os = nullptr;
681    if (aPtr != nullptr)
682    {
683        aPtr->resize(binarySize);
684        outStreamHolder.reset(
685                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
686        os = outStreamHolder.get();
687    }
688    else if (vPtr != nullptr)
689    {
690        vPtr->resize(binarySize);
691        outStreamHolder.reset(new VectorOStream(*vPtr));
692        os = outStreamHolder.get();
693    }
694    else // from argument
695        os = osPtr;
696   
697    const std::ios::iostate oldExceptions = os->exceptions();
698    try
699    {
700    os->exceptions(std::ios::failbit | std::ios::badbit);
701    /****
702     * write binary to output
703     ****/
704    FastOutputBuffer bos(256, *os);
705    bos.writeObject<uint32_t>(LEV(kernelsNum));
706    for (uint32_t korder: kernelsOrder)
707    {
708        const GalliumKernelInput& kernel = input->kernels[korder];
709        if (kernel.offset >= input->codeSize)
710            throw Exception("Kernel offset out of range");
711       
712        bos.writeObject<uint32_t>(LEV(uint32_t(kernel.kernelName.size())));
713        bos.writeArray(kernel.kernelName.size(), kernel.kernelName.c_str());
714        const uint32_t other[3] = { 0, LEV(kernel.offset),
715            LEV(cxuint(kernel.argInfos.size())) };
716        bos.writeArray(3, other);
717       
718        for (const GalliumArgInfo arg: kernel.argInfos)
719        {
720            const uint32_t argData[6] = { LEV(cxuint(arg.type)),
721                LEV(arg.size), LEV(arg.targetSize), LEV(arg.targetAlign),
722                LEV(arg.signExtended?1U:0U), LEV(cxuint(arg.semantic)) };
723            bos.writeArray(6, argData);
724        }
725    }
726    /* section */
727    {
728        const uint32_t secType = uint32_t(input->isMesa170 ?
729                GalliumSectionType::TEXT_EXECUTABLE_170 : GalliumSectionType::TEXT);
730        const uint32_t section[6] = { LEV(1U), LEV(0U), LEV(secType),
731            LEV(uint32_t(elfSize)), LEV(uint32_t(elfSize+4)), LEV(uint32_t(elfSize)) };
732        bos.writeArray(6, section);
733    }
734    if (!input->is64BitElf)
735        elfBinGen32->generate(bos);
736    else // 64-bit
737        elfBinGen64->generate(bos);
738    assert(bos.getWritten() == binarySize);
739    }
740    catch(...)
741    {
742        os->exceptions(oldExceptions);
743        throw;
744    }
745    os->exceptions(oldExceptions);
746}
747
748void GalliumBinGenerator::generate(Array<cxbyte>& array) const
749{
750    generateInternal(nullptr, nullptr, &array);
751}
752
753void GalliumBinGenerator::generate(std::ostream& os) const
754{
755    generateInternal(&os, nullptr, nullptr);
756}
757
758void GalliumBinGenerator::generate(std::vector<char>& v) const
759{
760    generateInternal(nullptr, &v, nullptr);
761}
762
763static const char* mesaOclMagicString = "OpenCL 1.1 MESA";
764static const char* mesaOclMagicString2 = "OpenCL 1.1 Mesa";
765static std::mutex detectionMutex;
766static uint64_t detectionFileTimestamp = 0;
767static std::string detectionMesaOclPath;
768static uint64_t detectionLLVMFileTimestamp = 0;
769static std::string detectionLlvmConfigPath;
770static uint32_t detectedDriverVersion = 0;
771static uint32_t detectedLLVMVersion = 0;
772
773static std::string escapePath(const std::string& path)
774{
775    std::string newPath;
776    for (char c: path)
777        if (c == CLRX_NATIVE_DIR_SEP)
778            newPath.append("%#");
779        else if (c == '%')
780            newPath.append("%%");
781        else if (c == ':')
782            newPath.append("%$");
783        else
784            newPath += c;
785    return newPath;
786}
787
788uint32_t CLRX::detectMesaDriverVersion()
789{
790    std::lock_guard<std::mutex> lock(detectionMutex);
791    std::string mesaOclPath = findMesaOCL();
792   
793    bool notThisSameFile = false;
794    if (mesaOclPath != detectionMesaOclPath)
795    {
796        notThisSameFile = true;
797        detectionMesaOclPath = mesaOclPath;
798    }
799   
800    uint64_t timestamp = 0;
801    try
802    { timestamp = getFileTimestamp(mesaOclPath.c_str()); }
803    catch(const Exception& ex)
804    { }
805   
806    if (!notThisSameFile && timestamp == detectionFileTimestamp)
807        return detectedDriverVersion;
808   
809    std::string clrxTimestampPath = getHomeDir();
810    if (!clrxTimestampPath.empty())
811    {   // first, we check from stored version in config files
812        clrxTimestampPath = joinPaths(clrxTimestampPath, ".clrxmesaocltstamp");
813        try
814        { makeDir(clrxTimestampPath.c_str()); }
815        catch(const std::exception& ex)
816        { }
817        // file path
818        clrxTimestampPath = joinPaths(clrxTimestampPath, escapePath(mesaOclPath));
819        try
820        {
821        std::ifstream ifs(clrxTimestampPath.c_str(), std::ios::binary);
822        if (ifs)
823        {   // read driver version from stored config files
824            ifs.exceptions(std::ios::badbit | std::ios::failbit);
825            uint64_t readedTimestamp = 0;
826            uint32_t readedVersion = 0;
827            ifs >> readedTimestamp >> readedVersion;
828            if (readedTimestamp!=0 && readedVersion!=0 && timestamp==readedTimestamp)
829            {   // amdocl has not been changed
830                detectionFileTimestamp = readedTimestamp;
831                detectedDriverVersion = readedVersion;
832                return readedVersion;
833            }
834        }
835        }
836        catch(const std::exception& ex)
837        { }
838    }
839    detectionFileTimestamp = timestamp;
840    detectedDriverVersion = 0;
841    try
842    {
843        std::ifstream fs(mesaOclPath.c_str(), std::ios::binary);
844        if (!fs) return 0;
845        FastInputBuffer fib(256, fs);
846        size_t index = 0;
847        while (mesaOclMagicString[index]!=0)
848        {
849            int c = fib.get();
850            if (c == std::streambuf::traits_type::eof())
851                break;
852            if (mesaOclMagicString[index]==c)
853                index++;
854            else if (mesaOclMagicString2[index]==c)
855                index++;
856            else // reset
857                index=0;
858        }
859        if (mesaOclMagicString[index]==0)
860        { //
861            char buf[30];
862            ::memset(buf, 0, 30);
863            if (fib.get()!=' ')
864                return 0; // skip space
865            // get driver version
866            fib.read(buf, 30);
867           
868            const char* next;
869            detectedDriverVersion = cstrtoui(buf, buf+30, next)*10000;
870            if (next!=buf+30 && *next=='.') // minor version
871                detectedDriverVersion += (cstrtoui(next+1, buf+30, next)%100)*100;
872            if (next!=buf+30 && *next=='.') // micro version
873                detectedDriverVersion += cstrtoui(next+1, buf+30, next)%100;
874        }
875       
876        // write to config
877        if (!clrxTimestampPath.empty()) // if clrxamdocltstamp set
878        {   // write to
879            std::ofstream ofs(clrxTimestampPath.c_str(), std::ios::binary);
880            ofs << detectionFileTimestamp << " " << detectedDriverVersion;
881        }
882    }
883    catch(const std::exception& ex)
884    { }
885    return detectedDriverVersion;
886}
887
888uint32_t CLRX::detectLLVMCompilerVersion()
889{
890    std::lock_guard<std::mutex> lock(detectionMutex);
891    std::string llvmConfigPath = findLLVMConfig();
892   
893    bool notThisSameFile = false;
894    if (llvmConfigPath != detectionLlvmConfigPath)
895    {
896        notThisSameFile = true;
897        detectionLlvmConfigPath = llvmConfigPath;
898    }
899   
900    uint64_t timestamp = 0;
901    try
902    { timestamp = getFileTimestamp(llvmConfigPath.c_str()); }
903    catch(const Exception& ex)
904    { }
905   
906    if (!notThisSameFile && timestamp == detectionLLVMFileTimestamp)
907        return detectedLLVMVersion;
908   
909    std::string clrxTimestampPath = getHomeDir();
910    if (!clrxTimestampPath.empty())
911    {   // first, we check from stored version in config files
912        clrxTimestampPath = joinPaths(clrxTimestampPath, ".clrxllvmcfgtstamp");
913        try
914        { makeDir(clrxTimestampPath.c_str()); }
915        catch(const std::exception& ex)
916        { }
917        // file path
918        clrxTimestampPath = joinPaths(clrxTimestampPath, escapePath(llvmConfigPath));
919        try
920        {
921        std::ifstream ifs(clrxTimestampPath.c_str(), std::ios::binary);
922        if (ifs)
923        {   // read driver version from stored config files
924            ifs.exceptions(std::ios::badbit | std::ios::failbit);
925            uint64_t readedTimestamp = 0;
926            uint32_t readedVersion = 0;
927            ifs >> readedTimestamp >> readedVersion;
928            if (readedTimestamp!=0 && readedVersion!=0 && timestamp==readedTimestamp)
929            {   // amdocl has not been changed
930                detectionLLVMFileTimestamp = readedTimestamp;
931                detectedLLVMVersion = readedVersion;
932                return readedVersion;
933            }
934        }
935        }
936        catch(const std::exception& ex)
937        { }
938    }
939    detectionLLVMFileTimestamp = timestamp;
940    detectedLLVMVersion = 0;
941    try
942    {
943        const char* arguments[3] = { llvmConfigPath.c_str(), "--version", nullptr };
944        Array<cxbyte> out = runExecWithOutput(llvmConfigPath.c_str(), arguments);
945        const char* next;
946        const char* end = ((const char*)out.data()) + out.size();
947        detectedLLVMVersion = cstrtoui(((const char*)out.data()), end, next)*10000;
948        if (next!=end && *next=='.') // minor version
949            detectedLLVMVersion += (cstrtoui(next+1, end, next)%100)*100;
950        if (next!=end && *next=='.') // micro version
951            detectedLLVMVersion += cstrtoui(next+1, end, next)%100;
952       
953        // write to config
954        if (!clrxTimestampPath.empty()) // if clrxamdocltstamp set
955        {   // write to
956            std::ofstream ofs(clrxTimestampPath.c_str(), std::ios::binary);
957            ofs << detectionLLVMFileTimestamp << " " << detectedLLVMVersion;
958        }
959    }
960    catch(const std::exception& ex)
961    { }
962    return detectedLLVMVersion;
963}
Note: See TracBrowser for help on using the repository browser.