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

Last change on this file since 3348 was 3348, checked in by matszpk, 10 months ago

CLRadeonExtender: Move routines to calculate PGMRSRC1 and PGMRSRC2 to GPUId code. Apply these function in code.

File size: 35.3 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 progInfoEntriesNum = input.isLLVM390 ? 5 : 3;
475       
476        for (uint32_t korder: kernelsOrder)
477        {
478            const GalliumKernelInput& kernel = input.kernels[korder];
479            GalliumProgInfoEntry outEntries[5];
480            if (kernel.useConfig)
481            {
482                const GalliumKernelConfig& config = kernel.config;
483                outEntries[0].address = ULEV(0x0000b848U);
484                outEntries[1].address = ULEV(0x0000b84cU);
485                outEntries[2].address = ULEV(0x0000b860U);
486                outEntries[3].address = ULEV(0x00000004U);
487                outEntries[4].address = ULEV(0x00000008U);
488               
489                uint32_t scratchBlocks = ((config.scratchBufferSize<<6) + 1023)>>10;
490                cxuint sgprsNum = std::max(config.usedSGPRsNum, 1U);
491                cxuint vgprsNum = std::max(config.usedVGPRsNum, 1U);
492                /// pgmRSRC1 and pgmRSRC2
493                outEntries[0].value = (config.pgmRSRC1) |
494                    calculatePgmRSrc1(arch, vgprsNum, sgprsNum, config.priority,
495                            config.floatMode, config.privilegedMode, config.dx10Clamp,
496                            config.debugMode, config.ieeeMode);
497                outEntries[1].value = (config.pgmRSRC2 & 0xffffe440U) |
498                    calculatePgmRSrc2(arch, (config.scratchBufferSize != 0),
499                            config.userDataNum, false, config.dimMask,
500                            (config.pgmRSRC2 & 0x1b80U), config.tgSize,
501                            config.localSize, config.exceptions);
502               
503                outEntries[2].value = (scratchBlocks)<<12;
504                if (input.isLLVM390)
505                {
506                    outEntries[3].value = config.spilledSGPRs;
507                    outEntries[4].value = config.spilledVGPRs;
508                }
509                for (cxuint k = 0; k < progInfoEntriesNum; k++)
510                    outEntries[k].value = ULEV(outEntries[k].value);
511            }
512            else
513            {
514                for (cxuint k = 0; k < progInfoEntriesNum; k++)
515                {
516                    outEntries[k].address = ULEV(kernel.progInfo[k].address);
517                    outEntries[k].value = ULEV(kernel.progInfo[k].value);
518                }
519            }
520            fob.writeArray(progInfoEntriesNum, outEntries);
521        }
522    }
523};
524
525template<typename Types>
526static void putSectionsAndSymbols(ElfBinaryGenTemplate<Types>& elfBinGen,
527      const GalliumInput* input, const Array<uint32_t>& kernelsOrder,
528      const AmdGpuConfigContent& amdGpuConfigContent)
529{
530    const char* comment = "CLRX GalliumBinGenerator " CLRX_VERSION;
531    uint32_t commentSize = ::strlen(comment);
532   
533    typedef ElfRegionTemplate<Types> ElfRegion;
534    typedef ElfSymbolTemplate<Types> ElfSymbol;
535    const uint32_t kernelsNum = input->kernels.size();
536    elfBinGen.addRegion(ElfRegion(input->codeSize, input->code, 256, ".text",
537            SHT_PROGBITS, SHF_ALLOC|SHF_EXECINSTR));
538    elfBinGen.addRegion(ElfRegion(0, (const cxbyte*)nullptr, 4, ".data",
539            SHT_PROGBITS, SHF_ALLOC|SHF_WRITE));
540    elfBinGen.addRegion(ElfRegion(0, (const cxbyte*)nullptr, 4, ".bss",
541            SHT_NOBITS, SHF_ALLOC|SHF_WRITE));
542    // write configuration for kernel execution
543    const cxuint progInfoEntriesNum = input->isLLVM390 ? 5 : 3;
544    elfBinGen.addRegion(ElfRegion(uint64_t(8U*progInfoEntriesNum)*kernelsNum,
545            &amdGpuConfigContent, 1, ".AMDGPU.config", SHT_PROGBITS, 0));
546   
547    if (input->globalData!=nullptr)
548        elfBinGen.addRegion(ElfRegion(input->globalDataSize, input->globalData, 4,
549                ".rodata", SHT_PROGBITS, SHF_ALLOC));
550   
551    if (input->comment!=nullptr)
552    {   // if comment, store comment section
553        comment = input->comment;
554        commentSize = input->commentSize;
555        if (commentSize==0)
556            commentSize = ::strlen(comment);
557    }
558    elfBinGen.addRegion(ElfRegion(commentSize, (const cxbyte*)comment, 1,
559                ".comment", SHT_PROGBITS, SHF_STRINGS|SHF_MERGE, 0, 0, 0, 1));
560    elfBinGen.addRegion(ElfRegion(0, (const cxbyte*)nullptr, 1, ".note.GNU-stack",
561            SHT_PROGBITS, 0));
562    elfBinGen.addRegion(ElfRegion::shstrtabSection());
563    elfBinGen.addRegion(ElfRegion::sectionHeaderTable());
564    elfBinGen.addRegion(ElfRegion::symtabSection());
565    elfBinGen.addRegion(ElfRegion::strtabSection());
566    /* symbols */
567    /// EndOfTextLabel - ?? always is at end of symbol table
568    elfBinGen.addSymbol({"EndOfTextLabel", 1, ELF32_ST_INFO(STB_LOCAL, STT_NOTYPE),
569            0, false, uint32_t(input->codeSize), 0});
570    const cxuint sectSymsNum = 6 + (input->globalData!=nullptr);
571    // local symbols for sections
572    for (cxuint i = 0; i < sectSymsNum; i++)
573        elfBinGen.addSymbol({"", uint16_t(i+1), ELF32_ST_INFO(STB_LOCAL, STT_SECTION),
574                0, false, 0, 0});
575    for (uint32_t korder: kernelsOrder)
576    {
577        const GalliumKernelInput& kernel = input->kernels[korder];
578        elfBinGen.addSymbol({kernel.kernelName.c_str(), 1,
579                ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE), 0, false, kernel.offset, 0});
580    }
581   
582    /* choose builtin section table and extraSectionStartIndex */
583    const uint16_t* curMainBuiltinSections = (input->globalData!=nullptr) ?
584            mainBuiltinSectionTable : mainBuiltinSectionTable2;
585    cxuint startSectionIndex = (input->globalData!=nullptr) ? 11 : 10;
586   
587    /* extra sections */
588    for (const BinSection& section: input->extraSections)
589        elfBinGen.addRegion(ElfRegion(section, curMainBuiltinSections,
590                         GALLIUMSECTID_MAX, startSectionIndex));
591    /* extra symbols */
592    for (const BinSymbol& symbol: input->extraSymbols)
593        elfBinGen.addSymbol(ElfSymbol(symbol, curMainBuiltinSections,
594                         GALLIUMSECTID_MAX, startSectionIndex));
595}
596
597void GalliumBinGenerator::generateInternal(std::ostream* osPtr, std::vector<char>* vPtr,
598             Array<cxbyte>* aPtr) const
599{
600    const uint32_t kernelsNum = input->kernels.size();
601    /* compute size of binary */
602    uint64_t elfSize = 0;
603    uint64_t binarySize = uint64_t(8) + size_t(kernelsNum)*16U + 20U /* section */;
604    for (const GalliumKernelInput& kernel: input->kernels)
605        binarySize += uint64_t(kernel.argInfos.size())*24U + kernel.kernelName.size();
606   
607    const GPUArchitecture arch = getGPUArchitectureFromDeviceType(input->deviceType);
608    const cxuint maxSGPRSNum = getGPUMaxRegistersNum(arch, REGTYPE_SGPR, 0);
609    const cxuint maxVGPRSNum = getGPUMaxRegistersNum(arch, REGTYPE_VGPR, 0);
610    for (const GalliumKernelInput& kernel: input->kernels)
611        if (kernel.useConfig)
612        {
613            const GalliumKernelConfig& config = kernel.config;
614            if (config.usedVGPRsNum > maxVGPRSNum)
615                throw Exception("Used VGPRs number out of range");
616            if (config.usedSGPRsNum > maxSGPRSNum)
617                throw Exception("Used SGPRs number out of range");
618            if (config.localSize > 32768)
619                throw Exception("LocalSize out of range");
620            if (config.priority >= 4)
621                throw Exception("Priority out of range");
622            if (config.userDataNum > 16)
623                throw Exception("UserDataNum out of range");
624        }
625   
626    // sort kernels by name (for correct order in binary file) */
627    Array<uint32_t> kernelsOrder(kernelsNum);
628    for (cxuint i = 0; i < kernelsNum; i++)
629        kernelsOrder[i] = i;
630    std::sort(kernelsOrder.begin(), kernelsOrder.end(),
631          [this](const uint32_t& a,const uint32_t& b)
632          { return input->kernels[a].kernelName < input->kernels[b].kernelName; });
633   
634    std::unique_ptr<ElfBinaryGen32> elfBinGen32;
635    std::unique_ptr<ElfBinaryGen64> elfBinGen64;
636   
637    AmdGpuConfigContent amdGpuConfigContent(kernelsOrder, *input);
638    if (!input->is64BitElf)
639    {   /* 32-bit ELF */
640        elfBinGen32.reset(new ElfBinaryGen32({ 0, 0, ELFOSABI_SYSV, 0,  ET_REL, 0,
641                    EV_CURRENT, UINT_MAX, 0, 0 }));
642        putSectionsAndSymbols(*elfBinGen32, input, kernelsOrder, amdGpuConfigContent);
643        elfSize = elfBinGen32->countSize();
644    }
645    else
646    {   /* 64-bit ELF */
647        elfBinGen64.reset(new ElfBinaryGen64({ 0, 0, ELFOSABI_SYSV, 0,  ET_REL, 0,
648                    EV_CURRENT, UINT_MAX, 0, 0 }));
649        putSectionsAndSymbols(*elfBinGen64, input, kernelsOrder, amdGpuConfigContent);
650        elfSize = elfBinGen64->countSize();
651    }
652
653    binarySize += elfSize;
654   
655    if (
656#ifdef HAVE_64BIT
657        !input->is64BitElf &&
658#endif
659        elfSize > UINT32_MAX)
660        throw Exception("Elf binary size is too big!");
661   
662#ifdef HAVE_32BIT
663    if (binarySize > UINT32_MAX)
664        throw Exception("Binary size is too big!");
665#endif
666    /****
667     * prepare for write binary to output
668     ****/
669    std::unique_ptr<std::ostream> outStreamHolder;
670    std::ostream* os = nullptr;
671    if (aPtr != nullptr)
672    {
673        aPtr->resize(binarySize);
674        outStreamHolder.reset(
675                new ArrayOStream(binarySize, reinterpret_cast<char*>(aPtr->data())));
676        os = outStreamHolder.get();
677    }
678    else if (vPtr != nullptr)
679    {
680        vPtr->resize(binarySize);
681        outStreamHolder.reset(new VectorOStream(*vPtr));
682        os = outStreamHolder.get();
683    }
684    else // from argument
685        os = osPtr;
686   
687    const std::ios::iostate oldExceptions = os->exceptions();
688    try
689    {
690    os->exceptions(std::ios::failbit | std::ios::badbit);
691    /****
692     * write binary to output
693     ****/
694    FastOutputBuffer bos(256, *os);
695    bos.writeObject<uint32_t>(LEV(kernelsNum));
696    for (uint32_t korder: kernelsOrder)
697    {
698        const GalliumKernelInput& kernel = input->kernels[korder];
699        if (kernel.offset >= input->codeSize)
700            throw Exception("Kernel offset out of range");
701       
702        bos.writeObject<uint32_t>(LEV(uint32_t(kernel.kernelName.size())));
703        bos.writeArray(kernel.kernelName.size(), kernel.kernelName.c_str());
704        const uint32_t other[3] = { 0, LEV(kernel.offset),
705            LEV(cxuint(kernel.argInfos.size())) };
706        bos.writeArray(3, other);
707       
708        for (const GalliumArgInfo arg: kernel.argInfos)
709        {
710            const uint32_t argData[6] = { LEV(cxuint(arg.type)),
711                LEV(arg.size), LEV(arg.targetSize), LEV(arg.targetAlign),
712                LEV(arg.signExtended?1U:0U), LEV(cxuint(arg.semantic)) };
713            bos.writeArray(6, argData);
714        }
715    }
716    /* section */
717    {
718        const uint32_t secType = uint32_t(input->isMesa170 ?
719                GalliumSectionType::TEXT_EXECUTABLE_170 : GalliumSectionType::TEXT);
720        const uint32_t section[6] = { LEV(1U), LEV(0U), LEV(secType),
721            LEV(uint32_t(elfSize)), LEV(uint32_t(elfSize+4)), LEV(uint32_t(elfSize)) };
722        bos.writeArray(6, section);
723    }
724    if (!input->is64BitElf)
725        elfBinGen32->generate(bos);
726    else // 64-bit
727        elfBinGen64->generate(bos);
728    assert(bos.getWritten() == binarySize);
729    }
730    catch(...)
731    {
732        os->exceptions(oldExceptions);
733        throw;
734    }
735    os->exceptions(oldExceptions);
736}
737
738void GalliumBinGenerator::generate(Array<cxbyte>& array) const
739{
740    generateInternal(nullptr, nullptr, &array);
741}
742
743void GalliumBinGenerator::generate(std::ostream& os) const
744{
745    generateInternal(&os, nullptr, nullptr);
746}
747
748void GalliumBinGenerator::generate(std::vector<char>& v) const
749{
750    generateInternal(nullptr, &v, nullptr);
751}
752
753static const char* mesaOclMagicString = "OpenCL 1.1 MESA";
754static const char* mesaOclMagicString2 = "OpenCL 1.1 Mesa";
755static std::mutex detectionMutex;
756static uint64_t detectionFileTimestamp = 0;
757static std::string detectionMesaOclPath;
758static uint64_t detectionLLVMFileTimestamp = 0;
759static std::string detectionLlvmConfigPath;
760static uint32_t detectedDriverVersion = 0;
761static uint32_t detectedLLVMVersion = 0;
762
763static std::string escapePath(const std::string& path)
764{
765    std::string newPath;
766    for (char c: path)
767        if (c == CLRX_NATIVE_DIR_SEP)
768            newPath.append("%#");
769        else if (c == '%')
770            newPath.append("%%");
771        else if (c == ':')
772            newPath.append("%$");
773        else
774            newPath += c;
775    return newPath;
776}
777
778uint32_t CLRX::detectMesaDriverVersion()
779{
780    std::lock_guard<std::mutex> lock(detectionMutex);
781    std::string mesaOclPath = findMesaOCL();
782   
783    bool notThisSameFile = false;
784    if (mesaOclPath != detectionMesaOclPath)
785    {
786        notThisSameFile = true;
787        detectionMesaOclPath = mesaOclPath;
788    }
789   
790    uint64_t timestamp = 0;
791    try
792    { timestamp = getFileTimestamp(mesaOclPath.c_str()); }
793    catch(const Exception& ex)
794    { }
795   
796    if (!notThisSameFile && timestamp == detectionFileTimestamp)
797        return detectedDriverVersion;
798   
799    std::string clrxTimestampPath = getHomeDir();
800    if (!clrxTimestampPath.empty())
801    {   // first, we check from stored version in config files
802        clrxTimestampPath = joinPaths(clrxTimestampPath, ".clrxmesaocltstamp");
803        try
804        { makeDir(clrxTimestampPath.c_str()); }
805        catch(const std::exception& ex)
806        { }
807        // file path
808        clrxTimestampPath = joinPaths(clrxTimestampPath, escapePath(mesaOclPath));
809        try
810        {
811        std::ifstream ifs(clrxTimestampPath.c_str(), std::ios::binary);
812        if (ifs)
813        {   // read driver version from stored config files
814            ifs.exceptions(std::ios::badbit | std::ios::failbit);
815            uint64_t readedTimestamp = 0;
816            uint32_t readedVersion = 0;
817            ifs >> readedTimestamp >> readedVersion;
818            if (readedTimestamp!=0 && readedVersion!=0 && timestamp==readedTimestamp)
819            {   // amdocl has not been changed
820                detectionFileTimestamp = readedTimestamp;
821                detectedDriverVersion = readedVersion;
822                return readedVersion;
823            }
824        }
825        }
826        catch(const std::exception& ex)
827        { }
828    }
829    detectionFileTimestamp = timestamp;
830    detectedDriverVersion = 0;
831    try
832    {
833        std::ifstream fs(mesaOclPath.c_str(), std::ios::binary);
834        if (!fs) return 0;
835        FastInputBuffer fib(256, fs);
836        size_t index = 0;
837        while (mesaOclMagicString[index]!=0)
838        {
839            int c = fib.get();
840            if (c == std::streambuf::traits_type::eof())
841                break;
842            if (mesaOclMagicString[index]==c)
843                index++;
844            else if (mesaOclMagicString2[index]==c)
845                index++;
846            else // reset
847                index=0;
848        }
849        if (mesaOclMagicString[index]==0)
850        { //
851            char buf[30];
852            ::memset(buf, 0, 30);
853            if (fib.get()!=' ')
854                return 0; // skip space
855            // get driver version
856            fib.read(buf, 30);
857           
858            const char* next;
859            detectedDriverVersion = cstrtoui(buf, buf+30, next)*10000;
860            if (next!=buf+30 && *next=='.') // minor version
861                detectedDriverVersion += (cstrtoui(next+1, buf+30, next)%100)*100;
862            if (next!=buf+30 && *next=='.') // micro version
863                detectedDriverVersion += cstrtoui(next+1, buf+30, next)%100;
864        }
865       
866        // write to config
867        if (!clrxTimestampPath.empty()) // if clrxamdocltstamp set
868        {   // write to
869            std::ofstream ofs(clrxTimestampPath.c_str(), std::ios::binary);
870            ofs << detectionFileTimestamp << " " << detectedDriverVersion;
871        }
872    }
873    catch(const std::exception& ex)
874    { }
875    return detectedDriverVersion;
876}
877
878uint32_t CLRX::detectLLVMCompilerVersion()
879{
880    std::lock_guard<std::mutex> lock(detectionMutex);
881    std::string llvmConfigPath = findLLVMConfig();
882   
883    bool notThisSameFile = false;
884    if (llvmConfigPath != detectionLlvmConfigPath)
885    {
886        notThisSameFile = true;
887        detectionLlvmConfigPath = llvmConfigPath;
888    }
889   
890    uint64_t timestamp = 0;
891    try
892    { timestamp = getFileTimestamp(llvmConfigPath.c_str()); }
893    catch(const Exception& ex)
894    { }
895   
896    if (!notThisSameFile && timestamp == detectionLLVMFileTimestamp)
897        return detectedLLVMVersion;
898   
899    std::string clrxTimestampPath = getHomeDir();
900    if (!clrxTimestampPath.empty())
901    {   // first, we check from stored version in config files
902        clrxTimestampPath = joinPaths(clrxTimestampPath, ".clrxllvmcfgtstamp");
903        try
904        { makeDir(clrxTimestampPath.c_str()); }
905        catch(const std::exception& ex)
906        { }
907        // file path
908        clrxTimestampPath = joinPaths(clrxTimestampPath, escapePath(llvmConfigPath));
909        try
910        {
911        std::ifstream ifs(clrxTimestampPath.c_str(), std::ios::binary);
912        if (ifs)
913        {   // read driver version from stored config files
914            ifs.exceptions(std::ios::badbit | std::ios::failbit);
915            uint64_t readedTimestamp = 0;
916            uint32_t readedVersion = 0;
917            ifs >> readedTimestamp >> readedVersion;
918            if (readedTimestamp!=0 && readedVersion!=0 && timestamp==readedTimestamp)
919            {   // amdocl has not been changed
920                detectionLLVMFileTimestamp = readedTimestamp;
921                detectedLLVMVersion = readedVersion;
922                return readedVersion;
923            }
924        }
925        }
926        catch(const std::exception& ex)
927        { }
928    }
929    detectionLLVMFileTimestamp = timestamp;
930    detectedLLVMVersion = 0;
931    try
932    {
933        const char* arguments[3] = { llvmConfigPath.c_str(), "--version", nullptr };
934        Array<cxbyte> out = runExecWithOutput(llvmConfigPath.c_str(), arguments);
935        const char* next;
936        const char* end = ((const char*)out.data()) + out.size();
937        detectedLLVMVersion = cstrtoui(((const char*)out.data()), end, next)*10000;
938        if (next!=end && *next=='.') // minor version
939            detectedLLVMVersion += (cstrtoui(next+1, end, next)%100)*100;
940        if (next!=end && *next=='.') // micro version
941            detectedLLVMVersion += cstrtoui(next+1, end, next)%100;
942       
943        // write to config
944        if (!clrxTimestampPath.empty()) // if clrxamdocltstamp set
945        {   // write to
946            std::ofstream ofs(clrxTimestampPath.c_str(), std::ios::binary);
947            ofs << detectionLLVMFileTimestamp << " " << detectedLLVMVersion;
948        }
949    }
950    catch(const std::exception& ex)
951    { }
952    return detectedLLVMVersion;
953}
Note: See TracBrowser for help on using the repository browser.