source: CLRX/CLRadeonExtender/trunk/amdasm/GCNAsmHelpers.cpp @ 4998

Last change on this file since 4998 was 4998, checked in by matszpk, 11 months ago

CLRadeonExtender: GCNASM: Preliminary support for SMRD 32-bit immediate literal if arch==GCN1.1.

File size: 118.1 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2018 Mateusz Szpakowski
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <CLRX/Config.h>
21#include <cstdio>
22#include <cstring>
23#include <string>
24#include <memory>
25#include <utility>
26#include <algorithm>
27#include <CLRX/utils/GPUId.h>
28#include "GCNAsmInternals.h"
29
30using namespace CLRX;
31
32// simple routine to parse byte value (0-255)
33cxbyte CLRX::cstrtobyte(const char*& str, const char* end)
34{
35    uint16_t value = 0;
36    if (str==end || !isDigit(*str))
37        throw ParseException("Missing number");
38    for (;str!=end && isDigit(*str); str++)
39    {
40        value = value*10 + *str-'0';
41        if (value >= 256)
42            throw ParseException("Number is too big");
43    }
44    return value;
45}
46
47namespace CLRX
48{
49
50// print error 'range expected', return true if printed, false if not
51bool GCNAsmUtils::printRegisterRangeExpected(Assembler& asmr, const char* linePtr,
52               const char* regPoolName, cxuint regsNum, bool required)
53{
54    if (!required)
55        return false;
56    char buf[60];
57    if (regsNum!=0)
58        snprintf(buf, 50, "Expected %u %s register%s", regsNum, regPoolName,
59                 (regsNum==1)?"":"s");
60    else
61        snprintf(buf, 50, "Expected %s registers", regPoolName);
62    asmr.printError(linePtr, buf);
63    return true;
64}
65
66void GCNAsmUtils::printXRegistersRequired(Assembler& asmr, const char* linePtr,
67              const char* regPoolName, cxuint regsNum)
68{
69    char buf[60];
70    snprintf(buf, 60, "Required %u %s register%s", regsNum, regPoolName,
71             (regsNum==1)?"":"s");
72    asmr.printError(linePtr, buf);
73}
74
75static inline cxbyte getRegVarAlign(const AsmRegVar* regVar, cxuint regsNum, Flags flags)
76{
77    if ((flags & INSTROP_UNALIGNED) == 0 && regVar->type==REGTYPE_SGPR)
78        return regsNum==2 ? 2 : regsNum>=3 ? 4 : 1;
79    return 1;
80}
81
82bool GCNAsmUtils::parseRegVarRange(Assembler& asmr, const char*& linePtr,
83                 RegRange& regPair, GPUArchMask arch, cxuint regsNum, AsmRegField regField,
84                 Flags flags, bool required)
85{
86    const char* oldLinePtr = linePtr;
87    const char* end = asmr.line+asmr.lineSize;
88    skipSpacesToEnd(linePtr, end);
89    const char* regVarPlace = linePtr;
90    const char *regTypeName = (flags&INSTROP_VREGS) ? "vector" : "scalar";
91   
92    const CString name = extractScopedSymName(linePtr, end, false);
93    bool regVarFound = false;
94    //AsmSection& section = asmr.sections[asmr.currentSection];
95    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
96    const AsmRegVar* regVar;
97    if (!name.empty())
98        regVarFound = asmr.getRegVar(name, regVar);
99    if (regVarFound)
100    {
101        // if regvar found
102        cxuint rstart = 0;
103        cxuint rend = regVar->size;
104        if (((flags & INSTROP_VREGS)!=0 && regVar->type==REGTYPE_VGPR) ||
105            ((flags & INSTROP_SREGS)!=0 && regVar->type==REGTYPE_SGPR))
106        {
107            skipSpacesToEnd(linePtr, end);
108            if (linePtr != end && *linePtr == '[')
109            {
110                // if we have range '[first:last]'
111                uint64_t value1, value2;
112                skipCharAndSpacesToEnd(linePtr, end);
113                // parse first register index
114                if (!getAbsoluteValueArg(asmr, value1, linePtr, true))
115                    return false;
116                skipSpacesToEnd(linePtr, end);
117                if (linePtr == end || (*linePtr!=':' && *linePtr!=']'))
118                    // error
119                    ASM_FAIL_BY_ERROR(regVarPlace, "Unterminated register range")
120                if (linePtr!=end && *linePtr==':')
121                {
122                    skipCharAndSpacesToEnd(linePtr, end);
123                    // parse second register index
124                    if (!getAbsoluteValueArg(asmr, value2, linePtr, true))
125                        return false;
126                }
127                else // we assume [first] -> [first:first]
128                    value2 = value1;
129                skipSpacesToEnd(linePtr, end);
130                if (linePtr == end || *linePtr != ']')
131                    // error
132                    ASM_FAIL_BY_ERROR(regVarPlace, "Unterminated register range")
133                ++linePtr;
134                if (value2 < value1)
135                    // error (illegal register range)
136                    ASM_FAIL_BY_ERROR(regVarPlace, "Illegal register range")
137                if (value2 >= rend || value1 >= rend)
138                    ASM_FAIL_BY_ERROR(regVarPlace, "Regvar range out of range")
139                rend = value2+1;
140                rstart = value1;
141            }
142           
143            if (regsNum!=0 && regsNum != rend-rstart)
144            {
145                printXRegistersRequired(asmr, regVarPlace, regTypeName, regsNum);
146                return false;
147            }
148           
149            if (regField!=ASMFIELD_NONE)
150            {
151                cxbyte align = getRegVarAlign(regVar, regsNum, flags);
152                // set reg var usage for current position and instruction field
153                gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), regVar,
154                    uint16_t(rstart), uint16_t(rend), regField,
155                    cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
156                    ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), align });
157            }
158            regPair = { rstart, rend, regVar };
159            return true;
160        }
161    }
162    if (printRegisterRangeExpected(asmr, regVarPlace, regTypeName, regsNum, required))
163        return false;
164    regPair = { 0, 0 };
165    linePtr = oldLinePtr; // revert current line pointer
166    return true;
167}
168
169static inline bool isGCNConstLiteral(uint16_t rstart, GPUArchMask arch)
170{
171    if ((rstart >= 128 && rstart <= 208) || (rstart >= 240 && rstart <= 247))
172        return true;
173    return ((arch & ARCH_GCN_1_2_4_5) != 0 && rstart == 248);
174}
175
176static inline bool isGCNVReg(uint16_t rstart, uint16_t rend, const AsmRegVar* regVar)
177{ return (regVar==nullptr && rstart >= 256 && rend >= 256) ||
178            (regVar!=nullptr && regVar->type == REGTYPE_VGPR); }
179
180static inline bool isGCNSReg(uint16_t rstart, uint16_t rend, const AsmRegVar* regVar)
181{ return (regVar==nullptr && rstart < 128 && rend < 128) ||
182        (regVar!=nullptr && regVar->type == REGTYPE_SGPR); }
183
184static inline bool isGCNSSource(uint16_t rstart, uint16_t rend, const AsmRegVar* regVar)
185{ return (regVar==nullptr && rstart >= 128 && rstart<255); }
186
187bool GCNAsmUtils::parseSymRegRange(Assembler& asmr, const char*& linePtr,
188            RegRange& regPair, GPUArchMask arch, cxuint regsNum, AsmRegField regField,
189            Flags flags, bool required)
190{
191    const char* oldLinePtr = linePtr;
192    const char* end = asmr.line+asmr.lineSize;
193    skipSpacesToEnd(linePtr, end);
194    const char* regRangePlace = linePtr;
195    const bool isGCN15 = ((arch & ARCH_GCN_1_5)!=0);
196   
197    AsmSymbolEntry* symEntry = nullptr;
198    if (linePtr!=end && *linePtr=='@')
199        skipCharAndSpacesToEnd(linePtr, end);
200   
201    const char *regTypeName = (flags&INSTROP_VREGS) ? "vector" : "scalar";
202    const cxuint maxSGPRsNum = getGPUMaxAddrRegsNumByArchMask(arch, REGTYPE_SGPR);
203    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
204   
205    if (asmr.parseSymbol(linePtr, symEntry, false, true)==
206        Assembler::ParseState::PARSED && symEntry!=nullptr &&
207        symEntry->second.regRange)
208    {
209        // set up regrange
210        const AsmRegVar* regVar = symEntry->second.regVar;
211        cxuint rstart = symEntry->second.value&UINT_MAX;
212        cxuint rend = symEntry->second.value>>32;
213        /* parse register range if:
214         * vector/scalar register enabled and is vector/scalar register or
215         * other scalar register, (ignore VCCZ, EXECZ, SCC if no SSOURCE enabled) */
216        if (((flags & INSTROP_VREGS)!=0 && isGCNVReg(rstart, rend, regVar)) ||
217            ((flags & INSTROP_SREGS)!=0 && isGCNSReg(rstart, rend, regVar)) ||
218            (((flags&INSTROP_SSOURCE)!=0) && isGCNSSource(rstart, rend, regVar)))
219        {
220            skipSpacesToEnd(linePtr, end);
221            const bool isConstLit = (regVar==nullptr && isGCNConstLiteral(rstart, arch));
222           
223            if (linePtr != end && *linePtr == '[')
224            {
225                uint64_t value1, value2;
226                // parse first register index
227                skipCharAndSpacesToEnd(linePtr, end);
228                if (!getAbsoluteValueArg(asmr, value1, linePtr, true))
229                    return false;
230                skipSpacesToEnd(linePtr, end);
231                if (linePtr == end || (*linePtr!=':' && *linePtr!=']'))
232                    // error
233                    ASM_FAIL_BY_ERROR(regRangePlace, "Unterminated register range")
234                if (linePtr!=end && *linePtr==':')
235                {
236                    // parse last register index
237                    skipCharAndSpacesToEnd(linePtr, end);
238                    if (!getAbsoluteValueArg(asmr, value2, linePtr, true))
239                        return false;
240                }
241                else  // we assume [first] -> [first:first]
242                    value2 = value1;
243                skipSpacesToEnd(linePtr, end);
244                if (linePtr == end || *linePtr != ']')
245                    // error
246                    ASM_FAIL_BY_ERROR(regRangePlace, "Unterminated register range")
247                ++linePtr;
248                if (value2 < value1)
249                    // error (illegal register range)
250                    ASM_FAIL_BY_ERROR(regRangePlace, "Illegal register range")
251                if (value2 >= rend-rstart || value1 >= rend-rstart)
252                    ASM_FAIL_BY_ERROR(regRangePlace, "Register range out of range")
253                   
254                if (isConstLit && (value1!=0 || (value2!=0 && value2+1!=regsNum)))
255                {
256                    ASM_FAIL_BY_ERROR(linePtr, "Register range for const literals must be"
257                            "[0] or [0:regsNum-1]");
258                    return false;
259                }
260               
261                rend = rstart + value2+1;
262                rstart += value1;
263            }
264           
265            if (!isConstLit)
266            {
267                if (regsNum!=0 && regsNum != rend-rstart)
268                {
269                    printXRegistersRequired(asmr, regRangePlace, regTypeName, regsNum);
270                    return false;
271                }
272                /// check aligned for scalar registers but not regvars
273                if (regVar==nullptr && rstart<maxSGPRsNum)
274                {
275                    if ((flags & INSTROP_UNALIGNED) == 0)
276                    {
277                        if ((rend-rstart==2 && (rstart&1)!=0) ||
278                            (rend-rstart>2 && (rstart&3)!=0))
279                            ASM_FAIL_BY_ERROR(regRangePlace,
280                                        "Unaligned scalar register range")
281                    }
282                    else if (!isGCN15 &&
283                        (flags & INSTROP_UNALIGNED) == INSTROP_SGPR_UNALIGNED)
284                        if ((rstart & 0xfc) != ((rend-1) & 0xfc))
285                            // unaligned, but some restrictions:
286                            // two regs can be in single 4-dword register line
287                            ASM_FAIL_BY_ERROR(regRangePlace,
288                                    "Scalar register range cross two register lines")
289                }
290               
291                // set reg var usage for current position and instruction field
292                if (regField != ASMFIELD_NONE)
293                {
294                    cxbyte align = 0;
295                    if (regVar != nullptr)
296                        align = getRegVarAlign(regVar, regsNum, flags);
297                   
298                    gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), regVar,
299                        uint16_t(rstart), uint16_t(rend), regField,
300                        cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
301                        ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), align });
302                }
303                regPair = { rstart, rend, regVar };
304            }
305            else
306                regPair = { rstart, 0, nullptr };
307           
308            return true;
309        }
310    }
311    if (printRegisterRangeExpected(asmr, regRangePlace, regTypeName, regsNum, required))
312        return false;
313    regPair = { 0, 0 }; // no range
314    linePtr = oldLinePtr; // revert current line pointer
315    return true;
316}
317
318bool GCNAsmUtils::parseVRegRange(Assembler& asmr, const char*& linePtr, RegRange& regPair,
319                    cxuint regsNum, AsmRegField regField, bool required, Flags flags)
320{
321    const char* oldLinePtr = linePtr;
322    const char* end = asmr.line+asmr.lineSize;
323    skipSpacesToEnd(linePtr, end);
324    const char* vgprRangePlace = linePtr;
325   
326    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
327   
328    bool isRange = false;
329    try /* for handling parse exception */
330    {
331        if (linePtr!=end && toLower(*linePtr) == 'v' && linePtr+1 != end)
332        {
333            if (isDigit(linePtr[1]))
334            {
335                const char* oldPlace = linePtr;
336                linePtr++;
337                // if single register
338                cxuint value = cstrtobyte(linePtr, end);
339                // check whether is register name: same v0, v1, but not v43xxx
340                if (linePtr==end || (!isAlpha(*linePtr) && *linePtr!='_' &&
341                            *linePtr!='$' && *linePtr!='.'))
342                {
343                    if (regsNum!=0 && regsNum != 1)
344                    {
345                        printXRegistersRequired(asmr, vgprRangePlace, "vector", regsNum);
346                        return false;
347                    }
348                    regPair = { 256+value, 256+value+1 };
349                   
350                    // set reg var usage for current position and instruction field
351                    if (regField != ASMFIELD_NONE)
352                        gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
353                            regPair.start, regPair.end, regField,
354                            cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
355                            ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
356                   
357                    return true;
358                }
359                else // if not register name
360                    linePtr = oldPlace;
361            }
362            else if (linePtr[1]=='[')
363                isRange = true;
364        }
365    } catch(const ParseException& ex)
366    {
367        asmr.printError(linePtr, ex.what());
368        return false;
369    }
370    // if is not range: try to parse regvar or symbol with register range
371    if (!isRange)
372    {
373        linePtr = oldLinePtr;
374        if (!parseRegVarRange(asmr, linePtr, regPair, 0, regsNum, regField,
375                    INSTROP_VREGS | (flags&INSTROP_ACCESS_MASK), false))
376            return false;
377        if (!regPair && (flags&INSTROP_SYMREGRANGE) != 0)
378        {
379            // try to parse regrange symbol
380            linePtr = oldLinePtr;
381            return parseSymRegRange(asmr, linePtr, regPair, 0, regsNum,
382                        regField, INSTROP_VREGS|(flags&INSTROP_ACCESS_MASK), required);
383        }
384        else if (regPair)
385            return true;
386        if (printRegisterRangeExpected(asmr, vgprRangePlace, "vector", regsNum, required))
387            return false;
388        regPair = { 0, 0 }; // no range
389        linePtr = oldLinePtr; // revert current line pointer
390        return true;
391    }
392    linePtr++;
393   
394    try /* for handling parse exception */
395    {
396        // many registers
397        uint64_t value1, value2;
398        skipCharAndSpacesToEnd(linePtr, end);
399        // parse first register index
400        if (!getAbsoluteValueArg(asmr, value1, linePtr, true))
401            return false;
402        skipSpacesToEnd(linePtr, end);
403        if (linePtr == end || (*linePtr!=':' && *linePtr!=']'))
404            // error
405            ASM_FAIL_BY_ERROR(vgprRangePlace, "Unterminated vector register range")
406        if (linePtr!=end && *linePtr==':')
407        {
408            skipCharAndSpacesToEnd(linePtr, end);
409            // parse last register index
410            if (!getAbsoluteValueArg(asmr, value2, linePtr, true))
411                return false;
412        }
413        else
414            value2 = value1;
415       
416        skipSpacesToEnd(linePtr, end);
417        if (linePtr == end || *linePtr != ']')
418            // error
419            ASM_FAIL_BY_ERROR(vgprRangePlace, "Unterminated vector register range")
420        ++linePtr;
421       
422        if (value2 < value1)
423            // error (illegal register range)
424            ASM_FAIL_BY_ERROR(vgprRangePlace, "Illegal vector register range")
425        if (value1 >= 256 || value2 >= 256)
426            ASM_FAIL_BY_ERROR(vgprRangePlace, "Some vector register number out of range")
427       
428        if (regsNum!=0 && regsNum != value2-value1+1)
429        {
430            printXRegistersRequired(asmr, vgprRangePlace, "vector", regsNum);
431            return false;
432        }
433        regPair = { 256+value1, 256+value2+1 };
434       
435        // set reg var usage for current position and instruction field
436        if (regField != ASMFIELD_NONE)
437            gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
438                regPair.start, regPair.end, regField,
439                cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
440                ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
441        return true;
442    } catch(const ParseException& ex)
443    {
444        asmr.printError(linePtr, ex.what());
445        return false;
446    }
447}
448
449bool GCNAsmUtils::parseVRegRangesLimited(Assembler& asmr, const char*& linePtr,
450                   cxuint vgprsLimit, std::vector<RegRange>& regPairs,
451                   AsmRegField regField, Flags flags)
452{
453    const char* oldLinePtr = linePtr;
454    const char* end = asmr.line+asmr.lineSize;
455    regPairs.clear();
456    skipSpacesToEnd(linePtr, end);
457    if (linePtr==end && *linePtr!='[')
458    {
459        linePtr = oldLinePtr;
460        return false;
461    }
462   
463    skipCharAndSpacesToEnd(linePtr, end);
464    // real parsing
465    cxuint curRegField = regField;
466    for (cxuint parsedVgprs = 0; parsedVgprs < vgprsLimit;)
467    {
468        const char *curRangePlace = linePtr;
469        regPairs.push_back({ 0, 0 });
470        if (!parseVRegRange(asmr, linePtr, regPairs.back(), 0, curRegField, true, flags))
471            return false;
472        const RegRange& rpair = regPairs.back();
473        if (cxuint(rpair.end-rpair.start) > vgprsLimit-parsedVgprs)
474            ASM_FAIL_BY_ERROR(curRangePlace,
475                              "Register range have more registers than left")
476        parsedVgprs += rpair.end-rpair.start;
477        skipSpacesToEnd(linePtr, end);
478        if (linePtr!=end && *linePtr==']')
479        {
480            skipCharAndSpacesToEnd(linePtr, end);
481            break; // end of register list
482        }
483       
484        else if (linePtr!=end && *linePtr==',')
485        {
486            if (parsedVgprs==vgprsLimit)
487            {
488                char buf[60];
489                snprintf(buf, 60, "VGPR register list need no more than %u registers",
490                                    vgprsLimit);
491                ASM_FAIL_BY_ERROR(curRangePlace, buf)
492            }
493            skipCharAndSpacesToEnd(linePtr, end);
494        }
495        else
496            ASM_FAIL_BY_ERROR(curRangePlace, "Expected ',' in  VGPR register list")
497        curRegField++;
498    }
499    return true;
500}
501
502bool GCNAsmUtils::parseSRegRange(Assembler& asmr, const char*& linePtr, RegRange& regPair,
503                    GPUArchMask arch, cxuint regsNum, AsmRegField regField,
504                    bool required, Flags flags)
505{
506    const char* oldLinePtr = linePtr;
507    const char* end = asmr.line+asmr.lineSize;
508    skipSpacesToEnd(linePtr, end);
509    const char* sgprRangePlace = linePtr;
510    if (linePtr == end)
511    {
512        if (printRegisterRangeExpected(asmr, sgprRangePlace, "scalar", regsNum, required))
513            return false;
514        regPair = { 0, 0 };
515        linePtr = oldLinePtr; // revert current line pointer
516        return true;
517    }
518   
519    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
520    bool isRange = false;
521    bool ttmpReg = false;
522    bool singleSorTtmp = false;
523    try
524    {
525    const char* oldPlace = linePtr;
526    if (linePtr+4 < end && toLower(linePtr[0]) == 't' &&
527        toLower(linePtr[1]) == 't' && toLower(linePtr[2]) == 'm' &&
528        toLower(linePtr[3]) == 'p')
529    {
530        // we have ttmp registers
531        singleSorTtmp = ttmpReg = true;
532        linePtr += 4;
533    }
534    else if (linePtr!=end && toLower(linePtr[0]) == 's' && linePtr+1 != end)
535    {
536        singleSorTtmp = true;
537        linePtr++;
538    }
539   
540    /* parse single SGPR */
541    const bool isGCN11No5 = (arch & ARCH_GCN_1_1_2_4) != 0;
542    const bool isGCN14 = (arch & ARCH_GCN_1_4_5) != 0;
543    const bool isGCN15 = (arch & ARCH_GCN_1_5) != 0;
544    const cxuint ttmpSize = isGCN14 ? 16 : 12;
545    const cxuint ttmpStart = isGCN14 ? 108 : 112;
546       
547    const cxuint maxSGPRsNum = getGPUMaxAddrRegsNumByArchMask(arch, REGTYPE_SGPR);
548    if (singleSorTtmp)
549    {
550        if (isDigit(*linePtr))
551        {
552            // if single register
553            cxuint value = cstrtobyte(linePtr, end);
554            // check wether is register name: s0, s4, but not s63v, s0.XX
555            if (linePtr==end || (!isAlpha(*linePtr) && *linePtr!='_' &&
556                    *linePtr!='$' && *linePtr!='.'))
557            {
558                if (!ttmpReg)
559                {
560                    // if scalar register
561                    if (value >= maxSGPRsNum)
562                        ASM_FAIL_BY_ERROR(sgprRangePlace,
563                                        "Scalar register number out of range")
564                }
565                else
566                {
567                    // ttmp register
568                    if (value >= ttmpSize)
569                        ASM_FAIL_BY_ERROR(sgprRangePlace,
570                            isGCN14 ? "TTMPRegister number out of range (0-15)" :
571                            "TTMPRegister number out of range (0-11)")
572                }
573                if (regsNum!=0 && regsNum!=1)
574                {
575                    printXRegistersRequired(asmr, linePtr, "scalar", regsNum);
576                    return false;
577                }
578                if (!ttmpReg)
579                {
580                    regPair = { value, value+1 };
581                    // set reg var usage for current position and instruction field
582                    if (regField != ASMFIELD_NONE)
583                        gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
584                            regPair.start, regPair.end, regField,
585                            cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
586                            ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
587                }
588                else
589                    regPair = { ttmpStart+value, ttmpStart+value+1 };
590                return true;
591            }
592            else // not register name
593                linePtr = oldPlace;
594        }
595        else if (*linePtr=='[')
596            isRange = true;
597        else
598            linePtr = oldPlace;
599    }
600   
601    bool doubleReg = false; // register that have two 32-bit regs
602    bool specialSGPRReg = false;
603    if (!isRange) // if not sgprs
604    {
605        const char* oldLinePtr = linePtr;
606        char regName[20];
607        if (linePtr==end || *linePtr != '@')
608        {
609            // if not '@'
610            if (!getNameArg(asmr, 20, regName, linePtr, "register name", required, true))
611                return false;
612        }
613        else // otherwise reset regName
614            regName[0] = 0;
615        toLowerString(regName);
616       
617        if (isGCN15 && ::strcmp(regName, "null")==0)
618        {
619            regPair = { 125, 126 };
620            return true;
621        }
622       
623        size_t loHiRegSuffix = 0;
624        cxuint loHiReg = 0;
625        if (regName[0] == 'v' && regName[1] == 'c' && regName[2] == 'c')
626        {
627            /// vcc
628            loHiRegSuffix = 3;
629            loHiReg = 106;
630            specialSGPRReg = true;
631        }
632        else if (::strncmp(regName, "exec", 4)==0)
633        {
634            /* exec* */
635            loHiRegSuffix = 4;
636            loHiReg = 126;
637        }
638        else if (!isGCN14 && regName[0]=='t')
639        {
640            /* tma,tba */
641            if (regName[1] == 'b' && regName[2] == 'a')
642            {
643                loHiRegSuffix = 3;
644                loHiReg = 108;
645            }
646            else if (regName[1] == 'm' && regName[2] == 'a')
647            {
648                loHiRegSuffix = 3;
649                loHiReg = 110;
650            }
651        }
652        else if (regName[0] == 'm' && regName[1] == '0' && regName[2] == 0)
653        {
654            /* M0 */
655            if (regsNum!=0 && regsNum!=1 && regsNum!=2)
656            {
657                printXRegistersRequired(asmr, sgprRangePlace, "scalar", regsNum);
658                return false;
659            }
660            regPair = { 124, 125 };
661            return true;
662        }
663        else if (isGCN11No5)
664        {
665            if (::strncmp(regName, "flat_scratch", 12)==0)
666            {
667                // flat
668                loHiRegSuffix = 12;
669                loHiReg = (arch&ARCH_GCN_1_2_4)?102:104;
670                specialSGPRReg = true;
671            }
672            else if ((arch&ARCH_GCN_1_2_4)!=0 && ::strncmp(regName, "xnack_mask", 10)==0)
673            {
674                // xnack
675                loHiRegSuffix = 10;
676                loHiReg = 104;
677                specialSGPRReg = true;
678            }
679        }
680       
681        bool trySymReg = false;
682        if (loHiRegSuffix != 0) // handle 64-bit registers
683        {
684            if (regName[loHiRegSuffix] == '_')
685            {
686                bool isLoHiName = false;
687                // if suffix _lo
688                if (regName[loHiRegSuffix+1] == 'l' && regName[loHiRegSuffix+2] == 'o' &&
689                    regName[loHiRegSuffix+3] == 0)
690                {
691                    regPair = { loHiReg, loHiReg+1 };
692                    isLoHiName = true;
693                }
694                // if suffxi _hi
695                else if (regName[loHiRegSuffix+1] == 'h' &&
696                    regName[loHiRegSuffix+2] == 'i' && regName[loHiRegSuffix+3] == 0)
697                {
698                    regPair = { loHiReg+1, loHiReg+2 };
699                    isLoHiName = true;
700                }
701                if (isLoHiName)
702                {
703                    if (regsNum!=0 && regsNum != 1)
704                    {
705                        printXRegistersRequired(asmr, sgprRangePlace, "scalar", regsNum);
706                        return false;
707                    }
708                   
709                    // set reg var usage for current position and instruction field
710                    if (regField != ASMFIELD_NONE && specialSGPRReg)
711                        gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
712                            regPair.start, regPair.end, regField,
713                            cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
714                            ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
715                    return true;
716                }
717                else
718                    trySymReg = true;
719            }
720            else if (regName[loHiRegSuffix] == 0)
721            {
722                // full 64-bit register
723                regPair = { loHiReg, loHiReg+2 };
724               
725                if (linePtr == end || *linePtr!='[')
726                {
727                    if (regsNum!=0 && regsNum != 2)
728                    {
729                        printXRegistersRequired(asmr, sgprRangePlace, "scalar", regsNum);
730                        return false;
731                    }
732                    // set reg var usage for current position and instruction field
733                    if (regField != ASMFIELD_NONE && specialSGPRReg)
734                        gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
735                            regPair.start, regPair.end, regField,
736                            cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
737                            ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
738                    return true;
739                }
740                else
741                {
742                    // if regrange []
743                    isRange = true;
744                    doubleReg = true;
745                }
746            }
747            else // this is not this register
748                trySymReg = true;
749        }
750        else
751            trySymReg = true;
752       
753        if (trySymReg)
754        {
755            // otherwise, we try to parse regvar or symreg
756            linePtr = oldLinePtr;
757            if (!parseRegVarRange(asmr, linePtr, regPair, 0, regsNum, regField,
758                    INSTROP_SREGS|(flags&(INSTROP_ACCESS_MASK|INSTROP_UNALIGNED)), false))
759                return false;
760            if (!regPair && (flags&INSTROP_SYMREGRANGE) != 0)
761            {
762                linePtr = oldLinePtr;
763                return parseSymRegRange(asmr, linePtr, regPair, arch, regsNum,
764                        regField, INSTROP_SREGS |
765                        (flags & (INSTROP_ACCESS_MASK|INSTROP_UNALIGNED)), required);
766            }
767            else if (regPair)
768                return true;
769            if (printRegisterRangeExpected(asmr, sgprRangePlace, "scalar",
770                            regsNum, required))
771                return false;
772            regPair = { 0, 0 };
773            linePtr = oldLinePtr; // revert current line pointer
774            return true;
775        }
776    }
777   
778    {
779        // many registers
780        uint64_t value1, value2;
781        skipCharAndSpacesToEnd(linePtr, end);
782        // parse first register index
783        if (!getAbsoluteValueArg(asmr, value1, linePtr, true))
784            return false;
785        skipSpacesToEnd(linePtr, end);
786        if (linePtr == end || (*linePtr!=':' && *linePtr!=']'))
787            // error
788            ASM_FAIL_BY_ERROR(sgprRangePlace, (!ttmpReg) ?
789                        "Unterminated scalar register range" :
790                        "Unterminated TTMPRegister range")
791        if (linePtr!=end && *linePtr==':')
792        {
793            skipCharAndSpacesToEnd(linePtr, end);
794            // parse last register index
795            if (!getAbsoluteValueArg(asmr, value2, linePtr, true))
796                return false;
797        }
798        else
799            value2 = value1;
800       
801        skipSpacesToEnd(linePtr, end);
802        if (linePtr == end || *linePtr != ']')
803        {
804            // error
805            const char* errMessage = (doubleReg ? "Unterminated double register range" :
806                    (ttmpReg ? "Unterminated TTMPRegister range" :
807                            "Unterminated scalar register range"));
808            ASM_FAIL_BY_ERROR(sgprRangePlace, errMessage)
809        }
810        ++linePtr;
811       
812        if (!ttmpReg && !doubleReg)
813        {
814            // is scalar register
815            if (value2 < value1)
816                // error (illegal register range)
817                ASM_FAIL_BY_ERROR(sgprRangePlace, "Illegal scalar register range")
818            if (value1 >= maxSGPRsNum || value2 >= maxSGPRsNum)
819                ASM_FAIL_BY_ERROR(sgprRangePlace,
820                            "Some scalar register number out of range")
821        }
822        else if (!doubleReg)
823        {
824            // is TTMP register
825            if (value2 < value1)
826                // error (illegal register range)
827                ASM_FAIL_BY_ERROR(sgprRangePlace, "Illegal TTMPRegister range")
828            if (value1 >= ttmpSize || value2 >= ttmpSize)
829                ASM_FAIL_BY_ERROR(sgprRangePlace,
830                            isGCN14 ? "Some TTMPRegister number out of range (0-15)" :
831                            "Some TTMPRegister number out of range (0-11)")
832        }
833        else
834        {
835            // double reg - VCC, flat_scratch, xnack_mask
836            if (value2 < value1)
837                // error (illegal register range)
838                ASM_FAIL_BY_ERROR(sgprRangePlace, "Illegal doublereg range")
839            if (value1 >= 2 || value2 >= 2)
840                ASM_FAIL_BY_ERROR(sgprRangePlace,
841                            "Some doublereg number out of range (0-1)")
842        }
843       
844        if (regsNum!=0 && regsNum != value2-value1+1)
845        {
846            printXRegistersRequired(asmr, sgprRangePlace, "scalar", regsNum);
847            return false;
848        }
849        /// check alignment
850        if (!ttmpReg && !doubleReg)
851        {
852            if ((flags & INSTROP_UNALIGNED)==0)
853            {
854                if ((value2-value1==1 && (value1&1)!=0) ||
855                    (value2-value1>1 && (value1&3)!=0))
856                    ASM_FAIL_BY_ERROR(sgprRangePlace, "Unaligned scalar register range")
857            }
858            else  if (!isGCN15 &&
859                (flags & INSTROP_UNALIGNED)==INSTROP_SGPR_UNALIGNED)
860                if ((value1 & 0xfc) != ((value2) & 0xfc))
861                   // unaligned, but some restrictions
862                    // two regs can be in single 4-dword register line
863                    ASM_FAIL_BY_ERROR(sgprRangePlace,
864                            "Scalar register range cross two register lines")
865            regPair = { value1, uint16_t(value2)+1 };
866           
867            // set reg var usage for current position and instruction field
868            if (regField != ASMFIELD_NONE)
869                gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
870                    regPair.start, regPair.end, regField,
871                    cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
872                    ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
873        }
874        else if (!doubleReg)
875            regPair = { ttmpStart+value1, ttmpStart+uint16_t(value2)+1 };
876        else
877        {
878            regPair = { regPair.start+value1, regPair.start+uint16_t(value2)+1 };
879            // set reg var usage for current position and instruction field
880            if (regField != ASMFIELD_NONE && specialSGPRReg)
881                gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
882                    regPair.start, regPair.end, regField,
883                    cxbyte(((flags & INSTROP_READ)!=0 ? ASMRVU_READ: 0) |
884                    ((flags & INSTROP_WRITE)!=0 ? ASMRVU_WRITE : 0)), 0 });
885        }
886        return true;
887    }
888    } catch(const ParseException& ex)
889    {
890        asmr.printError(linePtr, ex.what());
891        return false;
892    }
893}
894
895// internal routine to parse immediate (with specified number of bits and signess)
896// if expression is not resolved, then returns expresion to outTargetExpr
897bool GCNAsmUtils::parseImmInt(Assembler& asmr, const char*& linePtr, uint32_t& outValue,
898            std::unique_ptr<AsmExpression>* outTargetExpr, cxuint bits, cxbyte signess)
899{
900    const char* end = asmr.line+asmr.lineSize;
901    if (outTargetExpr!=nullptr)
902        outTargetExpr->reset();
903    skipSpacesToEnd(linePtr, end);
904   
905    uint64_t value;
906    const char* exprPlace = linePtr;
907    // if fast expression
908    if (AsmExpression::fastExprEvaluate(asmr, linePtr, value))
909    {
910        if (bits != UINT_MAX && bits < 64)
911        {
912            asmr.printWarningForRange(bits, value,
913                            asmr.getSourcePos(exprPlace), signess);
914            outValue = value & ((1ULL<<bits)-1ULL);
915        }
916        else // just copy
917            outValue = value;
918        return true;
919    }
920   
921    std::unique_ptr<AsmExpression> expr(AsmExpression::parse(asmr, linePtr));
922    if (expr==nullptr) // error
923        return false;
924    if (expr->isEmpty())
925        ASM_FAIL_BY_ERROR(exprPlace, "Expected expression")
926    if (expr->getSymOccursNum()==0)
927    {
928        // resolved now
929        cxuint sectionId; // for getting
930        if (!expr->evaluate(asmr, value, sectionId)) // failed evaluation!
931            return false;
932        else if (sectionId != ASMSECT_ABS)
933            // if not absolute value
934            ASM_FAIL_BY_ERROR(exprPlace, "Expression must be absolute!")
935        if (bits != UINT_MAX && bits < 64)
936        {
937            asmr.printWarningForRange(bits, value,
938                            asmr.getSourcePos(exprPlace), signess);
939            outValue = value & ((1ULL<<bits)-1ULL);
940        }
941        else // just copy
942            outValue = value;
943        return true;
944    }
945    else
946    {
947        // return output expression with symbols to resolve
948        if (outTargetExpr!=nullptr)
949            *outTargetExpr = std::move(expr);
950        else
951            ASM_FAIL_BY_ERROR(exprPlace, "Unresolved expression is illegal in this place")
952        return true;
953    }
954}
955
956bool GCNAsmUtils::parseSMRDImm(Assembler& asmr, const char*& linePtr, uint32_t& outValue,
957            std::unique_ptr<AsmExpression>* outTargetExpr, bool &litimm)
958{
959    const char* end = asmr.line+asmr.lineSize;
960    if (outTargetExpr!=nullptr)
961        outTargetExpr->reset();
962    skipSpacesToEnd(linePtr, end);
963   
964    bool haveLit = false;
965    const char* oldLinePtr = linePtr;
966    if (linePtr+4<end && toLower(linePtr[0])=='l' && toLower(linePtr[1])=='i' &&
967            toLower(linePtr[2])=='t' && (isSpace(linePtr[3]) || linePtr[3]=='('))
968    {
969        // lit() - force encoding as literal
970        linePtr+=3;
971        skipSpacesToEnd(linePtr, end);
972       
973        // space between lit and (
974        if (linePtr!=end && *linePtr=='(')
975        {
976            linePtr++;
977            skipSpacesToEnd(linePtr, end);
978            haveLit = true;
979        }
980        else // back to expression start
981            linePtr = oldLinePtr;
982    }
983   
984    uint64_t value;
985    const char* exprPlace = linePtr;
986    // if fast expression
987    if (AsmExpression::fastExprEvaluate(asmr, linePtr, value))
988    {
989        asmr.printWarningForRange(32, value,
990                        asmr.getSourcePos(exprPlace), WS_BOTH);
991        outValue = value & ((1ULL<<32)-1ULL);
992        if (haveLit)
993        {
994            if (linePtr==end || *linePtr!=')')
995                ASM_FAIL_BY_ERROR(linePtr, "Expected ')' after expression at 'lit'")
996            else // skip end of lit
997                linePtr++;
998        }
999        litimm = haveLit || (outValue >= 0x100);
1000        return true;
1001    }
1002   
1003    std::unique_ptr<AsmExpression> expr(AsmExpression::parse(asmr, linePtr));
1004    if (expr==nullptr) // error
1005        return false;
1006    if (expr->isEmpty())
1007        ASM_FAIL_BY_ERROR(exprPlace, "Expected expression")
1008    if (expr->getSymOccursNum()==0)
1009    {
1010        // resolved now
1011        cxuint sectionId; // for getting
1012        if (!expr->evaluate(asmr, value, sectionId)) // failed evaluation!
1013            return false;
1014        else if (sectionId != ASMSECT_ABS)
1015            // if not absolute value
1016            ASM_FAIL_BY_ERROR(exprPlace, "Expression must be absolute!")
1017       
1018        asmr.printWarningForRange(32, value,
1019                        asmr.getSourcePos(exprPlace), WS_BOTH);
1020        outValue = value & ((1ULL<<32)-1ULL);
1021    }
1022    else
1023    {
1024        // return output expression with symbols to resolve
1025        if (outTargetExpr!=nullptr)
1026            *outTargetExpr = std::move(expr);
1027        else
1028            ASM_FAIL_BY_ERROR(exprPlace, "Unresolved expression is illegal in this place")
1029    }
1030    litimm = haveLit || (outValue >= 0x100);
1031    // end of 'lit('
1032    if (haveLit)
1033    {
1034        skipSpacesToEnd(linePtr, end);
1035        if (linePtr==end || *linePtr!=')')
1036            ASM_FAIL_BY_ERROR(linePtr, "Expected ')' after expression at 'lit'")
1037        else // skip end of lit
1038            linePtr++;
1039    }
1040    return true;
1041}
1042
1043enum FloatLitType
1044{
1045    FLTT_F16,
1046    FLTT_F32,
1047    FLTT_F64
1048};
1049
1050// determinr float literal type from suffix
1051static FloatLitType getFloatLitType(const char* str, const char* end,
1052                        FloatLitType defaultFPType)
1053{
1054    if (str==end)
1055        return defaultFPType; // end of string and no replacing suffix
1056    else if (toLower(*str)=='l')
1057        return FLTT_F64;
1058    else if (toLower(*str)=='s')
1059        return FLTT_F32;
1060    else if (toLower(*str)=='h')
1061        return FLTT_F16;
1062    else
1063        return defaultFPType;
1064}
1065
1066/* check whether string is exclusively floating point value
1067 * (only floating point, and neither integer and nor symbol) */
1068static bool isOnlyFloat(const char* str, const char* end, FloatLitType defaultFPType,
1069                        FloatLitType& outFPType)
1070{
1071    if (str == end)
1072        return false;
1073    if (*str=='-' || *str=='+')
1074        str++; // skip '-' or '+'
1075    if (str+2 < end && *str=='0' && (str[1]=='X' || str[1]=='x'))
1076    {
1077        // hexadecimal
1078        str += 2;
1079        const char* beforeComma = str;
1080        while (str!=end && isXDigit(*str)) str++;
1081        const char* point = str;
1082        if (str == end || *str!='.')
1083        {
1084            if (beforeComma-point!=0 && str!=end && (*str=='p' || *str=='P'))
1085            {
1086                // handle part P[+|-]exp
1087                str++;
1088                if (str!=end && (*str=='-' || *str=='+'))
1089                    str++;
1090                const char* expPlace = str;
1091                while (str!=end && isDigit(*str)) str++;
1092                if (str-expPlace!=0)
1093                {
1094                    outFPType = getFloatLitType(str, end, defaultFPType);
1095                    return true; // if 'XXXp[+|-]XXX'
1096                }
1097            }
1098            return false; // no '.'
1099        }
1100        // if XXX.XXX
1101        str++;
1102        while (str!=end && isXDigit(*str)) str++;
1103        const char* afterComma = str;
1104       
1105        if (point-beforeComma!=0 || afterComma-(point+1)!=0)
1106        {
1107            if (beforeComma-point!=0 && str!=end && (*str=='p' || *str=='P'))
1108            {
1109                // handle part P[+|-]exp
1110                str++;
1111                if (str!=end && (*str=='-' || *str=='+'))
1112                    str++;
1113                while (str!=end && isDigit(*str)) str++;
1114            }
1115            outFPType = getFloatLitType(str, end, defaultFPType);
1116            return true;
1117        }
1118    }
1119    else
1120    {
1121        // decimal
1122        const char* beforeComma = str;
1123        while (str!=end && isDigit(*str)) str++;
1124        const char* point = str;
1125        if (str == end || *str!='.')
1126        {
1127            if (beforeComma-point!=0 && str!=end && (*str=='e' || *str=='E'))
1128            {
1129                // handle part E[+|-]exp
1130                str++;
1131                if (str!=end && (*str=='-' || *str=='+'))
1132                    str++;
1133                const char* expPlace = str;
1134                while (str!=end && isDigit(*str)) str++;
1135                if (str-expPlace!=0)
1136                {
1137                    outFPType = getFloatLitType(str, end, defaultFPType);
1138                    return true; // if 'XXXe[+|-]XXX'
1139                }
1140            }
1141            return false; // no '.'
1142        }
1143        // if XXX.XXX
1144        str++;
1145        while (str!=end && isDigit(*str)) str++;
1146        const char* afterComma = str;
1147       
1148        if (point-beforeComma!=0 || afterComma-(point+1)!=0)
1149        {
1150            if (beforeComma-point!=0 && str!=end && (*str=='e' || *str=='E'))
1151            {
1152                // handle part E[+|-]exp
1153                str++;
1154                if (str!=end && (*str=='-' || *str=='+'))
1155                    str++;
1156                while (str!=end && isDigit(*str)) str++;
1157            }
1158            outFPType = getFloatLitType(str, end, defaultFPType);
1159            return true;
1160        }
1161    }
1162    return false;
1163}
1164
1165bool GCNAsmUtils::parseLiteralImm(Assembler& asmr, const char*& linePtr, uint32_t& value,
1166            std::unique_ptr<AsmExpression>* outTargetExpr, Flags instropMask)
1167{
1168    if (outTargetExpr!=nullptr)
1169        outTargetExpr->reset();
1170    const char* end = asmr.line+asmr.lineSize;
1171    skipSpacesToEnd(linePtr, end);
1172    FloatLitType fpType;
1173    FloatLitType defaultFpType = (instropMask&INSTROP_TYPE_MASK)!=INSTROP_F16 ?
1174            ((instropMask&INSTROP_TYPE_MASK)==INSTROP_V64BIT ?
1175            FLTT_F64 : FLTT_F32) : FLTT_F16;
1176   
1177    // before parsing, we check that is floating point and integer value
1178    if (isOnlyFloat(linePtr, end, defaultFpType, fpType))
1179    {
1180        // try to parse floating point FP16, FP32 or high 32-bit part of FP64
1181        try
1182        {
1183        if (fpType==FLTT_F16)
1184        {
1185            value = cstrtohCStyle(linePtr, end, linePtr);
1186            if (linePtr!=end && toLower(*linePtr)=='h')
1187                linePtr++;
1188        }
1189        else if (fpType==FLTT_F32)
1190        {
1191            union FloatUnion { uint32_t i; float f; };
1192            FloatUnion v;
1193            v.f = cstrtovCStyle<float>(linePtr, end, linePtr);
1194            if (linePtr!=end && toLower(*linePtr)=='s')
1195                linePtr++;
1196            value = v.i;
1197        }
1198        else
1199        {
1200            /* 64-bit (high 32-bits) */
1201            uint32_t v = cstrtofXCStyle(linePtr, end, linePtr, 11, 20);
1202            if (linePtr!=end && toLower(*linePtr)=='l')
1203                linePtr++;
1204            value = v;
1205        }
1206        }
1207        catch(const ParseException& ex)
1208        {
1209            // error
1210            asmr.printError(linePtr, ex.what());
1211            return false;
1212        }
1213        return true;
1214    }
1215    return parseImm(asmr, linePtr, value, outTargetExpr);
1216}
1217
1218// source register names (EXECZ,SCC,VCCZ, ...) for GCN1.0/1.1/1.2
1219static const std::pair<const char*, uint16_t> ssourceNamesTbl[] =
1220{
1221    { "execz", 252 },
1222    { "scc", 253 },
1223    { "src_execz", 252 },
1224    { "src_scc", 253 },
1225    { "src_vccz", 251 },
1226    { "vccz", 251 }
1227};
1228
1229static const size_t ssourceNamesTblSize = sizeof(ssourceNamesTbl) /
1230        sizeof(std::pair<const char*, uint16_t>);
1231
1232// source register names (EXECZ,SCC,VCCZ, ...) for GCN1.4
1233static const std::pair<const char*, uint16_t> ssourceNamesGCN14Tbl[] =
1234{
1235    { "execz", 252 },
1236    { "pops_exiting_wave_id", 0xef },
1237    { "private_base", 0xed },
1238    { "private_limit", 0xee },
1239    { "scc", 253 },
1240    { "shared_base", 0xeb },
1241    { "shared_limit", 0xec },
1242    { "src_execz", 252 },
1243    { "src_pops_exiting_wave_id", 0xef },
1244    { "src_private_base", 0xed },
1245    { "src_private_limit", 0xee },
1246    { "src_scc", 253 },
1247    { "src_shared_base", 0xeb },
1248    { "src_shared_limit", 0xec },
1249    { "src_vccz", 251 },
1250    { "vccz", 251 }
1251};
1252
1253static const size_t ssourceNamesGCN14TblSize = sizeof(ssourceNamesGCN14Tbl) /
1254        sizeof(std::pair<const char*, uint16_t>);
1255
1256// main routine to parse operand
1257bool GCNAsmUtils::parseOperand(Assembler& asmr, const char*& linePtr, GCNOperand& operand,
1258             std::unique_ptr<AsmExpression>* outTargetExpr, GPUArchMask arch,
1259             cxuint regsNum, Flags instrOpMask, AsmRegField regField)
1260{
1261    const bool isGCN12 = (arch & ARCH_GCN_1_2_4_5)!=0;
1262    const bool isGCN14 = (arch & ARCH_GCN_1_4_5)!=0;
1263   
1264    if (outTargetExpr!=nullptr)
1265        outTargetExpr->reset();
1266   
1267    if (asmr.buggyFPLit && (instrOpMask&INSTROP_TYPE_MASK)==INSTROP_V64BIT)
1268        // buggy fplit does not accept 64-bit values (high 32-bits)
1269        instrOpMask = (instrOpMask&~INSTROP_TYPE_MASK) | INSTROP_INT;
1270   
1271    const Flags optionFlags = (instrOpMask & (INSTROP_UNALIGNED|INSTROP_ACCESS_MASK));
1272    // fast path to parse only VGPR or SGPR
1273    if ((instrOpMask&~INSTROP_UNALIGNED) == INSTROP_SREGS)
1274        return parseSRegRange(asmr, linePtr, operand.range, arch, regsNum, regField, true,
1275                              INSTROP_SYMREGRANGE | optionFlags);
1276    else if ((instrOpMask&~INSTROP_UNALIGNED) == INSTROP_VREGS)
1277        return parseVRegRange(asmr, linePtr, operand.range, regsNum, regField, true,
1278                              INSTROP_SYMREGRANGE | optionFlags);
1279   
1280    // otherwise we must include modifiers and other registers or literals/constants
1281    const char* end = asmr.line+asmr.lineSize;
1282    if (instrOpMask & INSTROP_VOP3MODS)
1283    {
1284        operand.vopMods = 0;
1285        skipSpacesToEnd(linePtr, end);
1286        if (linePtr!=end && *linePtr=='@') // treat this operand as expression
1287            return parseOperand(asmr, linePtr, operand, outTargetExpr, arch, regsNum,
1288                             instrOpMask & ~INSTROP_VOP3MODS, regField);
1289       
1290        if (isGCN12 && (instrOpMask & (INSTROP_NOSEXT|INSTROP_VOP3P))==0 &&
1291            linePtr+4 <= end && strncasecmp(linePtr, "sext", 4)==0)
1292        {
1293            /* sext */
1294            linePtr += 4;
1295            skipSpacesToEnd(linePtr, end);
1296            if (linePtr!=end && *linePtr=='(')
1297            {
1298                operand.vopMods |= VOPOP_SEXT;
1299                ++linePtr;
1300            }
1301            else
1302                ASM_FAIL_BY_ERROR(linePtr, "Expected '(' after sext")
1303        }
1304       
1305        const char* negPlace = linePtr;
1306        // parse negation
1307        if (linePtr!=end && *linePtr=='-')
1308        {
1309            operand.vopMods |= VOPOP_NEG;
1310            skipCharAndSpacesToEnd(linePtr, end);
1311        }
1312        // parse abs modifier
1313        bool llvmAbs = false;
1314        if (linePtr+3 <= end && (instrOpMask & INSTROP_VOP3P)==0 &&
1315            toLower(linePtr[0])=='a' &&
1316            toLower(linePtr[1])=='b' && toLower(linePtr[2])=='s')
1317        {
1318            // if 'abs' operand modifier
1319            linePtr += 3;
1320            skipSpacesToEnd(linePtr, end);
1321            if (linePtr!=end && *linePtr=='(')
1322            {
1323                operand.vopMods |= VOPOP_ABS;
1324                ++linePtr;
1325            }
1326            else
1327                ASM_FAIL_BY_ERROR(linePtr, "Expected '(' after abs")
1328        }
1329        else if (linePtr<=end && linePtr[0]=='|')
1330        {
1331            // LLVM like syntax for abs modifier
1332            linePtr++;
1333            skipSpacesToEnd(linePtr, end);
1334            operand.vopMods |= VOPOP_ABS;
1335            llvmAbs = true;
1336        }
1337       
1338        bool good;
1339        // now we parse operand except VOP modifiers
1340        if ((operand.vopMods&(VOPOP_NEG|VOPOP_ABS)) != VOPOP_NEG)
1341            good = parseOperand(asmr, linePtr, operand, outTargetExpr, arch, regsNum,
1342                                     instrOpMask & ~INSTROP_VOP3MODS, regField);
1343        else //
1344        {
1345            // parse with negation if neg (it can be literal with negation)
1346            linePtr = negPlace;
1347            good = parseOperand(asmr, linePtr, operand, outTargetExpr, arch, regsNum,
1348                     (instrOpMask & ~INSTROP_VOP3MODS) | INSTROP_PARSEWITHNEG, regField);
1349        }
1350       
1351        // checking closing of VOP modifiers
1352        if (operand.vopMods & VOPOP_ABS)
1353        {
1354            skipSpacesToEnd(linePtr, end);
1355            if (linePtr!=end && ((*linePtr==')' && !llvmAbs) ||
1356                        (*linePtr=='|' && llvmAbs)))
1357                linePtr++;
1358            else
1359                ASM_FAIL_BY_ERROR(linePtr, "Unterminated abs() modifier")
1360        }
1361        if (operand.vopMods & VOPOP_SEXT)
1362        {
1363            skipSpacesToEnd(linePtr, end);
1364            if (linePtr!=end && *linePtr==')')
1365                linePtr++;
1366            else
1367                ASM_FAIL_BY_ERROR(linePtr, "Unterminated sext() modifier")
1368        }
1369        return good;
1370    }
1371    skipSpacesToEnd(linePtr, end);
1372    const char* negPlace = linePtr;
1373    if (instrOpMask & INSTROP_VOP3NEG)
1374        operand.vopMods = 0; // clear modifier (VOP3NEG)
1375        /// PARSEWITHNEG used to continuing operand parsing with modifiers
1376    if (instrOpMask & (INSTROP_PARSEWITHNEG|INSTROP_VOP3NEG))
1377    {
1378        if (linePtr!=end && *linePtr=='-')
1379        {
1380            skipCharAndSpacesToEnd(linePtr, end);
1381            operand.vopMods |= VOPOP_NEG;
1382        }
1383    }
1384   
1385    // otherwise, we try parse scalar register
1386    if (instrOpMask & INSTROP_SREGS)
1387    {
1388        if (!parseSRegRange(asmr, linePtr, operand.range, arch, regsNum, regField,
1389                        false, optionFlags))
1390            return false;
1391        if (operand)
1392            return true;
1393    }
1394    // otherwise try parse vector register
1395    if (instrOpMask & INSTROP_VREGS)
1396    {
1397        if (!parseVRegRange(asmr, linePtr, operand.range, regsNum, regField,
1398                        false, optionFlags))
1399            return false;
1400        if (operand)
1401            return true;
1402    }
1403    // if still is not this, try parse symbol regrange
1404    if (instrOpMask & (INSTROP_SREGS|INSTROP_VREGS))
1405    {
1406        if (!parseSymRegRange(asmr, linePtr, operand.range, arch, regsNum, regField,
1407                  (instrOpMask&((INSTROP_SREGS|INSTROP_VREGS|INSTROP_SSOURCE))) |
1408                    optionFlags, false))
1409            return false;
1410        if (operand)
1411            return true;
1412    }
1413   
1414    skipSpacesToEnd(linePtr, end);
1415   
1416    if ((instrOpMask & INSTROP_SSOURCE)!=0)
1417    {
1418        // try parse scalar source (vcc, scc, literals)
1419        char regName[25];
1420        const char* regNamePlace = linePtr;
1421        if (getNameArg(asmr, 25, regName, linePtr, "register name", false, true))
1422        {
1423            toLowerString(regName);
1424            operand.range = {0, 0};
1425           
1426            auto regNameTblEnd = isGCN14 ?
1427                        ssourceNamesGCN14Tbl + ssourceNamesGCN14TblSize :
1428                        ssourceNamesTbl + ssourceNamesTblSize;
1429            auto regNameIt = binaryMapFind(
1430                    isGCN14 ? ssourceNamesGCN14Tbl : ssourceNamesTbl,
1431                    regNameTblEnd, regName, CStringLess());
1432           
1433            // if found in table
1434            if (regNameIt != regNameTblEnd)
1435            {
1436                operand.range = { regNameIt->second, regNameIt->second+1 };
1437                return true;
1438            }
1439            // if lds or src_lds_direct, lds_direct
1440            else if ((instrOpMask&INSTROP_LDS)!=0 &&
1441                (::strcmp(regName, "lds")==0 || ::strcmp(regName, "lds_direct")==0 ||
1442                    ::strcmp(regName, "src_lds_direct")==0))
1443            {
1444                operand.range = { 254, 255 };
1445                return true;
1446            }
1447            if (operand)
1448            {
1449                if (regsNum!=0 && regsNum!=1 && regsNum!=2)
1450                {
1451                    printXRegistersRequired(asmr, regNamePlace, "scalar", regsNum);
1452                    return false;
1453                }
1454                return true;
1455            }
1456            /* check expression, back to before regName */
1457            linePtr = negPlace;
1458        }
1459        // treat argument as expression
1460        bool forceExpression = false;
1461        if (linePtr!=end && *linePtr=='@')
1462        {
1463            forceExpression = true;
1464            skipCharAndSpacesToEnd(linePtr, end);
1465        }
1466        if (linePtr==end || *linePtr==',')
1467            ASM_FAIL_BY_ERROR(linePtr, "Expected instruction operand")
1468        const char* exprPlace = linePtr;
1469       
1470        uint64_t value;
1471        operand.vopMods = 0; // zeroing operand modifiers
1472        FloatLitType fpType;
1473       
1474        bool exprToResolve = false;
1475        bool encodeAsLiteral = false;
1476        // if 'lit(' in this place
1477        if (linePtr+4<end && toLower(linePtr[0])=='l' && toLower(linePtr[1])=='i' &&
1478            toLower(linePtr[2])=='t' && (isSpace(linePtr[3]) || linePtr[3]=='('))
1479        {
1480            // lit() - force encoding as literal
1481            linePtr+=3;
1482            const char* oldLinePtr = linePtr;
1483            skipSpacesToEnd(linePtr, end);
1484            // space between lit and (
1485            if (linePtr!=end && *linePtr=='(')
1486            {
1487                encodeAsLiteral = true;
1488                linePtr++;
1489                skipSpacesToEnd(linePtr, end);
1490                negPlace = linePtr; // later treaten as next part of operand
1491            }
1492            else // back to expression start
1493                linePtr = oldLinePtr;
1494        }
1495       
1496        FloatLitType defaultFPType = (instrOpMask & INSTROP_TYPE_MASK)!=INSTROP_F16?
1497                    ((instrOpMask & INSTROP_TYPE_MASK)!=INSTROP_V64BIT?
1498                        FLTT_F32:FLTT_F64):FLTT_F16;
1499        if (!forceExpression && isOnlyFloat(negPlace, end, defaultFPType, fpType))
1500        {
1501            // if only floating point value
1502            /* if floating point literal can be processed */
1503            linePtr = negPlace;
1504            try
1505            {
1506                if (fpType==FLTT_F16)
1507                {
1508                    // parse FP16
1509                    value = cstrtohCStyle(linePtr, end, linePtr);
1510                    // skip suffix if needed
1511                    if (linePtr!=end && toLower(*linePtr)=='h')
1512                        linePtr++;
1513                   
1514                    if (!encodeAsLiteral)
1515                    {
1516                        if (asmr.buggyFPLit && value == 0)
1517                        {
1518                            // old buggy behaviour (to 0.1.2 version)
1519                            operand.range = { 128, 0 };
1520                            return true;
1521                        }
1522                    }
1523                }
1524                else if (fpType==FLTT_F32) /* otherwise, FLOAT */
1525                {
1526                    union FloatUnion { uint32_t i; float f; };
1527                    FloatUnion v;
1528                    v.f = cstrtovCStyle<float>(linePtr, end, linePtr);
1529                    // skip suffix if needed
1530                    if (linePtr!=end && toLower(*linePtr)=='s')
1531                        linePtr++;
1532                    value = v.i;
1533                    /// simplify to float constant immediate (-0.5, 0.5, 1.0, 2.0,...)
1534                    /// constant immediates converted only to single floating points
1535                    if (!encodeAsLiteral && asmr.buggyFPLit)
1536                        switch (value)
1537                        {
1538                            case 0x0:
1539                                operand.range = { 128, 0 };
1540                                return true;
1541                            case 0x3f000000: // 0.5
1542                                operand.range = { 240, 0 };
1543                                return true;
1544                            case 0xbf000000: // -0.5
1545                                operand.range = { 241, 0 };
1546                                return true;
1547                            case 0x3f800000: // 1.0
1548                                operand.range = { 242, 0 };
1549                                return true;
1550                            case 0xbf800000: // -1.0
1551                                operand.range = { 243, 0 };
1552                                return true;
1553                            case 0x40000000: // 2.0
1554                                operand.range = { 244, 0 };
1555                                return true;
1556                            case 0xc0000000: // -2.0
1557                                operand.range = { 245, 0 };
1558                                return true;
1559                            case 0x40800000: // 4.0
1560                                operand.range = { 246, 0 };
1561                                return true;
1562                            case 0xc0800000: // -4.0
1563                                operand.range = { 247, 0 };
1564                                return true;
1565                            case 0x3e22f983: // 1/(2*PI)
1566                                if (isGCN12)
1567                                {
1568                                    operand.range = { 248, 0 };
1569                                    return true;
1570                                }
1571                        }
1572                }
1573                else
1574                {
1575                    uint32_t v = cstrtofXCStyle(linePtr, end, linePtr, 11, 20);
1576                    // skip suffix if needed
1577                    if (linePtr!=end && toLower(*linePtr)=='l')
1578                        linePtr++;
1579                    value = v;
1580                }
1581               
1582                /// simplify to float constant immediate (-0.5, 0.5, 1.0, 2.0,...)
1583                /// constant immediates converted only to single floating points
1584                /// new behaviour
1585                if (!asmr.buggyFPLit && !encodeAsLiteral && fpType==defaultFPType)
1586                {
1587                    if (defaultFPType==FLTT_F16)
1588                        // simplify FP16 to constant immediate
1589                        switch (value)
1590                        {
1591                            case 0x0:
1592                                operand.range = { 128, 0 };
1593                                return true;
1594                            case 0x3800: // 0.5
1595                                operand.range = { 240, 0 };
1596                                return true;
1597                            case 0xb800: // -0.5
1598                                operand.range = { 241, 0 };
1599                                return true;
1600                            case 0x3c00: // 1.0
1601                                operand.range = { 242, 0 };
1602                                return true;
1603                            case 0xbc00: // -1.0
1604                                operand.range = { 243, 0 };
1605                                return true;
1606                            case 0x4000: // 2.0
1607                                operand.range = { 244, 0 };
1608                                return true;
1609                            case 0xc000: // -2.0
1610                                operand.range = { 245, 0 };
1611                                return true;
1612                            case 0x4400: // 4.0
1613                                operand.range = { 246, 0 };
1614                                return true;
1615                            case 0xc400: // -4.0
1616                                operand.range = { 247, 0 };
1617                                return true;
1618                            case 0x3118: // 1/(2*PI)
1619                                if (isGCN12)
1620                                {
1621                                    operand.range = { 248, 0 };
1622                                    return true;
1623                                }
1624                        }
1625                    else if (defaultFPType==FLTT_F32)
1626                        // simplify FP32 to constant immediate
1627                        switch (value)
1628                        {
1629                            case 0x0:
1630                                operand.range = { 128, 0 };
1631                                return true;
1632                            case 0x3f000000: // 0.5
1633                                operand.range = { 240, 0 };
1634                                return true;
1635                            case 0xbf000000: // -0.5
1636                                operand.range = { 241, 0 };
1637                                return true;
1638                            case 0x3f800000: // 1.0
1639                                operand.range = { 242, 0 };
1640                                return true;
1641                            case 0xbf800000: // -1.0
1642                                operand.range = { 243, 0 };
1643                                return true;
1644                            case 0x40000000: // 2.0
1645                                operand.range = { 244, 0 };
1646                                return true;
1647                            case 0xc0000000: // -2.0
1648                                operand.range = { 245, 0 };
1649                                return true;
1650                            case 0x40800000: // 4.0
1651                                operand.range = { 246, 0 };
1652                                return true;
1653                            case 0xc0800000: // -4.0
1654                                operand.range = { 247, 0 };
1655                                return true;
1656                            case 0x3e22f983: // 1/(2*PI)
1657                                if (isGCN12)
1658                                {
1659                                    operand.range = { 248, 0 };
1660                                    return true;
1661                                }
1662                        }
1663                    else /* FP64 */
1664                        // simplify FP64 (only high part) to constant immediate
1665                        switch (value)
1666                        {
1667                            case 0x0:
1668                                operand.range = { 128, 0 };
1669                                return true;
1670                            case 0x3fe00000: // 0.5
1671                                operand.range = { 240, 0 };
1672                                return true;
1673                            case 0xbfe00000: // -0.5
1674                                operand.range = { 241, 0 };
1675                                return true;
1676                            case 0x3ff00000: // 1.0
1677                                operand.range = { 242, 0 };
1678                                return true;
1679                            case 0xbff00000: // -1.0
1680                                operand.range = { 243, 0 };
1681                                return true;
1682                            case 0x40000000: // 2.0
1683                                operand.range = { 244, 0 };
1684                                return true;
1685                            case 0xc0000000: // -2.0
1686                                operand.range = { 245, 0 };
1687                                return true;
1688                            case 0x40100000: // 4.0
1689                                operand.range = { 246, 0 };
1690                                return true;
1691                            case 0xc0100000: // -4.0
1692                                operand.range = { 247, 0 };
1693                                return true;
1694                            case 0x3fc45f30: // 1/(2*PI)
1695                                if (isGCN12)
1696                                {
1697                                    operand.range = { 248, 0 };
1698                                    return true;
1699                                }
1700                        }
1701                }
1702            }
1703            catch(const ParseException& ex)
1704            {
1705                asmr.printError(linePtr, ex.what());
1706                return false;
1707            }
1708        }
1709        else
1710        {
1711            if (!AsmExpression::fastExprEvaluate(asmr, linePtr, value))
1712            {
1713            // if expression
1714            std::unique_ptr<AsmExpression> expr(AsmExpression::parse(asmr, linePtr));
1715            if (expr==nullptr) // error
1716                return false;
1717            if (expr->isEmpty())
1718                ASM_FAIL_BY_ERROR(exprPlace, "Expected expression")
1719           
1720            bool tryLater = false;
1721            if (expr->getSymOccursNum()==0)
1722            {
1723                // resolved now
1724                cxuint sectionId; // for getting
1725                AsmTryStatus evalStatus = expr->tryEvaluate(asmr, value, sectionId,
1726                                        asmr.withSectionDiffs());
1727               
1728                if (evalStatus == AsmTryStatus::FAILED) // failed evaluation!
1729                    return false;
1730                else if (evalStatus == AsmTryStatus::TRY_LATER)
1731                {
1732                    if (outTargetExpr!=nullptr)
1733                        asmr.unevalExpressions.push_back(expr.get());
1734                    tryLater = true;
1735                }
1736                else if (sectionId != ASMSECT_ABS)
1737                    // if not absolute value
1738                    ASM_FAIL_BY_ERROR(exprPlace, "Expression must be absolute!")
1739            }
1740            else
1741                tryLater = true;
1742           
1743            if (tryLater)
1744            {
1745                // return output expression with symbols to resolve
1746                if ((instrOpMask & INSTROP_ONLYINLINECONSTS)!=0)
1747                {
1748                    // error
1749                    if ((instrOpMask & INSTROP_NOLITERALERROR)!=0)
1750                        asmr.printError(regNamePlace, "Literal in VOP3 is illegal");
1751                    else if ((instrOpMask & INSTROP_NOLITERALERRORMUBUF)!=0)
1752                        asmr.printError(regNamePlace, "Literal in MUBUF is illegal");
1753                    else
1754                        asmr.printError(regNamePlace,
1755                                "Only one literal can be used in instruction");
1756                    return false;
1757                }
1758                if (outTargetExpr!=nullptr)
1759                    *outTargetExpr = std::move(expr);
1760                operand.range = { 255, 0 };
1761                exprToResolve = true;
1762            }
1763            }
1764           
1765            if (!encodeAsLiteral && !exprToResolve)
1766            {
1767                // if literal can be a constant immediate
1768                if (value <= 64)
1769                {
1770                    operand.range = { 128+value, 0 };
1771                    return true;
1772                }
1773                else if (int64_t(value) >= -16 && int64_t(value) < 0)
1774                {
1775                    operand.range = { 192-value, 0 };
1776                    return true;
1777                }
1778            }
1779        }
1780        if (encodeAsLiteral)
1781        {
1782            /* finish lit function */
1783            skipSpacesToEnd(linePtr, end);
1784            if (linePtr==end || *linePtr!=')')
1785                ASM_FAIL_BY_ERROR(linePtr, "Expected ')' after expression at 'lit'")
1786            else // skip end of lit
1787                linePtr++;
1788        }
1789        if (exprToResolve) // finish if expression to resolve
1790            return true;
1791       
1792        if ((instrOpMask & INSTROP_ONLYINLINECONSTS)!=0)
1793        {
1794            // error
1795            if ((instrOpMask & INSTROP_NOLITERALERROR)!=0)
1796                asmr.printError(regNamePlace, "Literal in VOP3 is illegal");
1797            else if ((instrOpMask & INSTROP_NOLITERALERRORMUBUF)!=0)
1798                asmr.printError(regNamePlace, "Literal in MUBUF is illegal");
1799            else
1800                asmr.printError(regNamePlace,
1801                        "Only one literal can be used in instruction");
1802            return false;
1803        }
1804       
1805        // not in range
1806        asmr.printWarningForRange(32, value, asmr.getSourcePos(regNamePlace));
1807        operand = { { 255, 0 }, uint32_t(value), operand.vopMods };
1808        return true;
1809    }
1810   
1811    // check otherwise
1812    asmr.printError(linePtr, "Unknown operand");
1813    return false;
1814}
1815
1816// used while parsing op_sel or op_sel_hi, neg_lo, neg_hi
1817bool GCNAsmUtils::parseImmWithBoolArray(Assembler& asmr, const char*& linePtr,
1818            uint32_t& value, cxuint bits, cxbyte signess)
1819{
1820    const char* end = asmr.line + asmr.lineSize;
1821    skipSpacesToEnd(linePtr, end);
1822    if (linePtr == end || *linePtr != '[')
1823        return parseImm(asmr, linePtr, value, nullptr, bits, signess);
1824    // array of boolean values
1825    linePtr++;
1826    bool good = true;
1827    uint32_t inVal = 0;
1828    for (cxuint i = 0; i < bits; i++)
1829    {
1830        uint32_t v = 0;
1831        // parse boolean immediate (0 or 1)
1832        good &= parseImm(asmr, linePtr, v, nullptr, 1, WS_UNSIGNED);
1833        inVal |= v<<i;
1834        skipSpacesToEnd(linePtr, end);
1835        if (i+1 < bits)
1836        {
1837            // next bool will be, try parse ','
1838            if (linePtr==end || *linePtr!=',')
1839            {
1840                ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ',' before bit value")
1841                break;
1842            }
1843            else
1844                ++linePtr;
1845        }
1846        else
1847        {
1848            // end of array, try parse ']'
1849            if (linePtr == end || *linePtr!=']')
1850                ASM_NOTGOOD_BY_ERROR(linePtr, "Unterminated bit array")
1851            else
1852                ++linePtr;
1853        }
1854    }
1855    if (good)
1856        value = inVal;
1857    return good;
1858}
1859
1860bool GCNAsmUtils::parseSingleOMODCLAMP(Assembler& asmr, const char*& linePtr,
1861                    const char* modPlace, const char* mod, GPUArchMask arch,
1862                    cxbyte& mods, VOPOpModifiers& opMods, cxuint modOperands,
1863                    cxuint flags, bool& haveAbs, bool& haveNeg,
1864                    bool& alreadyModDefined, bool& good)
1865{
1866    const char* end = asmr.line+asmr.lineSize;
1867    const bool vop3p = (flags & PARSEVOP_VOP3P)!=0;
1868    if (!vop3p && ::strcmp(mod, "mul")==0)
1869    {
1870        // if 'mul:xx'
1871        skipSpacesToEnd(linePtr, end);
1872        if (linePtr!=end && *linePtr==':')
1873        {
1874            skipCharAndSpacesToEnd(linePtr, end);
1875            cxbyte count = cstrtobyte(linePtr, end);
1876            if (count==2)
1877            {
1878                alreadyModDefined = mods&3;
1879                mods = (mods&~3) | VOP3_MUL2;
1880            }
1881            else if (count==4)
1882            {
1883                alreadyModDefined = mods&3;
1884                mods = (mods&~3) | VOP3_MUL4;
1885            }
1886            else
1887                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP3 mul:X modifier")
1888        }
1889        else
1890            ASM_NOTGOOD_BY_ERROR(linePtr,
1891                        "Expected ':' before multiplier number")
1892    }
1893    else if (!vop3p && ::strcmp(mod, "div")==0)
1894    {
1895        // if 'div:2'
1896        skipSpacesToEnd(linePtr, end);
1897        if (linePtr!=end && *linePtr==':')
1898        {
1899            skipCharAndSpacesToEnd(linePtr, end);
1900            cxbyte count = cstrtobyte(linePtr, end);
1901            if (count==2)
1902            {
1903                alreadyModDefined = mods&3;
1904                mods = (mods&~3) | VOP3_DIV2;
1905            }
1906            else
1907                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP3 div:X modifier")
1908        }
1909        else
1910            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before divider number")
1911    }
1912    else if (!vop3p && ::strcmp(mod, "omod")==0)
1913    {
1914        // if omod (parametrization of div or mul)
1915        skipSpacesToEnd(linePtr, end);
1916        if (linePtr!=end && *linePtr==':')
1917        {
1918            linePtr++;
1919            cxbyte omod = 0;
1920            if (parseImm(asmr, linePtr, omod, nullptr, 2, WS_UNSIGNED))
1921                mods = (mods & ~3) | omod;
1922            else
1923                good = false;
1924        }
1925        else
1926            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before omod")
1927    }
1928    else if (::strcmp(mod, "clamp")==0) // clamp
1929    {
1930        bool clamp = false;
1931        good &= parseModEnable(asmr, linePtr, clamp, "clamp modifier");
1932        if (flags & PARSEVOP_WITHCLAMP)
1933            mods = (mods & ~VOP3_CLAMP) | (clamp ? VOP3_CLAMP : 0);
1934        else
1935            ASM_NOTGOOD_BY_ERROR(modPlace, "Modifier CLAMP in VOP3B is illegal")
1936    }
1937    else if (!vop3p && modOperands>1 && ::strcmp(mod, "abs")==0)
1938    {
1939        // abs modifiers for source operands (bit per operand in array)
1940        uint32_t absVal = 0;
1941        if (linePtr!=end && *linePtr==':')
1942        {
1943            linePtr++;
1944            if (parseImmWithBoolArray(asmr, linePtr, absVal, modOperands-1,
1945                        WS_UNSIGNED))
1946            {
1947                opMods.absMod = absVal;
1948                if (haveAbs)
1949                    asmr.printWarning(modPlace, "Abs is already defined");
1950                haveAbs = true;
1951            }
1952        }
1953        else
1954            good = false;
1955    }
1956    else if (modOperands>1 && (::strcmp(mod, "neg")==0 ||
1957            (vop3p && ::strcmp(mod, "neg_lo")==0)))
1958    {
1959        // neg or neg_lo modifiers for source operands
1960        // (bit per operand in array)
1961        uint32_t negVal = 0;
1962        if (linePtr!=end && *linePtr==':')
1963        {
1964            linePtr++;
1965            if (parseImmWithBoolArray(asmr, linePtr, negVal, modOperands-1,
1966                            WS_UNSIGNED))
1967            {
1968                opMods.negMod = (opMods.negMod&0xf0) | negVal;
1969                if (haveNeg)
1970                    asmr.printWarning(modPlace, "Neg is already defined");
1971                haveNeg = true;
1972            }
1973        }
1974        else
1975            good = false;
1976    }
1977    else // other modifier
1978        return false;
1979    return true;
1980}
1981
1982// sorted list of VOP SDWA DST_SEL names
1983static const std::pair<const char*, cxuint> vopSDWADSTSelNamesMap[] =
1984{
1985    { "b0", 0 },
1986    { "b1", 1 },
1987    { "b2", 2 },
1988    { "b3", 3 },
1989    { "byte0", 0 },
1990    { "byte1", 1 },
1991    { "byte2", 2 },
1992    { "byte3", 3 },
1993    { "byte_0", 0 },
1994    { "byte_1", 1 },
1995    { "byte_2", 2 },
1996    { "byte_3", 3 },
1997    { "dword", 6 },
1998    { "w0", 4 },
1999    { "w1", 5 },
2000    { "word0", 4 },
2001    { "word1", 5 },
2002    { "word_0", 4 },
2003    { "word_1", 5 }
2004};
2005
2006static const size_t vopSDWADSTSelNamesNum = sizeof(vopSDWADSTSelNamesMap)/
2007            sizeof(std::pair<const char*, cxuint>);
2008           
2009/* main routine to parse VOP modifiers: basic modifiers stored in mods parameter,
2010 * modifier specific for VOP_SDWA and VOP_DPP stored in extraMods structure
2011 * withSDWAOperands - specify number of operand for that modifier will be parsed */
2012bool GCNAsmUtils::parseVOPModifiers(Assembler& asmr, const char*& linePtr,
2013                GPUArchMask arch, cxbyte& mods, VOPOpModifiers& opMods, cxuint modOperands,
2014                VOPExtraModifiers* extraMods, cxuint flags, cxuint withSDWAOperands)
2015{
2016    const char* end = asmr.line+asmr.lineSize;
2017    //bool haveSDWAMod = false, haveDPPMod = false;
2018    bool haveDstSel = false, haveSrc0Sel = false, haveSrc1Sel = false;
2019    bool haveDstUnused = false;
2020    bool haveBankMask = false, haveRowMask = false;
2021    bool haveBoundCtrl = false, haveDppCtrl = false;
2022    bool haveNeg = false, haveAbs = false;
2023    bool haveSext = false, haveOpsel = false;
2024    bool haveNegHi = false, haveOpselHi = false;
2025    bool haveFi = false, haveDPP8 = false;
2026    bool haveDPP = false, haveSDWA = false;
2027   
2028    // set default VOP extra modifiers (SDWA/DPP)
2029    if (extraMods!=nullptr)
2030        *extraMods = { 6, 0, cxbyte((withSDWAOperands>=2)?6:0),
2031                    cxbyte((withSDWAOperands>=3)?6:0),
2032                    15, 15, 0xe4 /* TODO: why not 0xe4? */, false, false, false };
2033   
2034    skipSpacesToEnd(linePtr, end);
2035    const char* modsPlace = linePtr;
2036    bool good = true;
2037    mods = 0;
2038    const bool vop3p = (flags & PARSEVOP_VOP3P)!=0;
2039   
2040    const bool isGCN15 = (arch & ARCH_GCN_1_5)!=0;
2041   
2042    // main loop
2043    while (linePtr != end)
2044    {
2045        skipSpacesToEnd(linePtr, end);
2046        if (linePtr == end)
2047            break;
2048        char mod[20];
2049        const char* modPlace = linePtr;
2050        if (getNameArgS(asmr, 20, mod, linePtr, "VOP modifier"))
2051        {
2052            toLowerString(mod);
2053            try
2054            {
2055                // check what is VOP modifier
2056                bool alreadyModDefined = false;
2057                if (parseSingleOMODCLAMP(asmr, linePtr, modPlace, mod, arch, mods,
2058                        opMods, modOperands, flags, haveAbs, haveNeg,
2059                        alreadyModDefined, good))
2060                {   // do nothing
2061                }
2062                else if (modOperands>1 && vop3p && ::strcmp(mod, "neg_hi")==0)
2063                {
2064                    // neg_hi modifiers for source operands (bit per operand in array)
2065                    uint32_t negVal = 0;
2066                    if (linePtr!=end && *linePtr==':')
2067                    {
2068                        linePtr++;
2069                        if (parseImmWithBoolArray(asmr, linePtr, negVal, modOperands-1,
2070                                        WS_UNSIGNED))
2071                        {
2072                            opMods.negMod = (opMods.negMod&15) | (negVal<<4);
2073                            if (haveNegHi)
2074                                asmr.printWarning(modPlace, "Neg_hi is already defined");
2075                            haveNegHi = true;
2076                        }
2077                    }
2078                    else
2079                        good = false;
2080                }
2081                else if (!vop3p &&(arch & ARCH_GCN_1_2_4_5) &&
2082                        (flags & PARSEVOP_WITHSEXT)!=0 &&
2083                         modOperands>1 && ::strcmp(mod, "sext")==0)
2084                {
2085                    // neg_hi modifiers for source operands (bit per operand in array)
2086                    uint32_t sextVal = 0;
2087                    if (linePtr!=end && *linePtr==':')
2088                    {
2089                        linePtr++;
2090                        if (parseImmWithBoolArray(asmr, linePtr, sextVal, modOperands-1,
2091                                    WS_UNSIGNED))
2092                        {
2093                            opMods.sextMod = sextVal;
2094                            if (haveSext)
2095                                asmr.printWarning(modPlace, "Sext is already defined");
2096                            haveSext = true;
2097                        }
2098                    }
2099                    else
2100                        good = false;
2101                }
2102                else if ((flags & PARSEVOP_WITHOPSEL) != 0 && modOperands>1 &&
2103                            ::strcmp(mod, "op_sel")==0)
2104                {
2105                    // op_sel (bit per operand in array)
2106                    uint32_t opselVal = 0;
2107                    if (linePtr!=end && *linePtr==':')
2108                    {
2109                        linePtr++;
2110                        // for VOP3P encoding is one less bit in array
2111                        if (parseImmWithBoolArray(asmr, linePtr, opselVal,
2112                                (vop3p ? modOperands-1 : modOperands),
2113                                    WS_UNSIGNED))
2114                        {
2115                            if (!vop3p && modOperands<4)
2116                                /* move last bit to dest (fourth bit)
2117                                 * if less than 4 operands */
2118                                opselVal = (opselVal & ((1U<<(modOperands-1))-1)) |
2119                                        ((opselVal & (1U<<(modOperands-1))) ? 8 : 0);
2120                            opMods.opselMod = (opMods.opselMod&0xf0) | opselVal;
2121                            if (haveOpsel)
2122                                asmr.printWarning(modPlace, "Opsel is already defined");
2123                            haveOpsel = true;
2124                        }
2125                    }
2126                    else
2127                        good = false;
2128                }
2129                else if (vop3p && (flags & PARSEVOP_WITHOPSEL) != 0 && modOperands>1 &&
2130                            ::strcmp(mod, "op_sel_hi")==0)
2131                {
2132                    // op_sel_hi (bit per operand in array)
2133                    uint32_t opselVal = 0;
2134                    if (linePtr!=end && *linePtr==':')
2135                    {
2136                        linePtr++;
2137                        // for VOP3P encoding is one less bit in array
2138                        if (parseImmWithBoolArray(asmr, linePtr, opselVal, modOperands-1,
2139                                    WS_UNSIGNED))
2140                        {
2141                            opMods.opselMod = (opMods.opselMod&15) | (opselVal<<4);
2142                            if (haveOpselHi)
2143                                asmr.printWarning(modPlace, "Opsel_hi is already defined");
2144                            haveOpselHi = true;
2145                        }
2146                    }
2147                    else
2148                        good = false;
2149                }
2150                else if (::strcmp(mod, "vop3")==0)
2151                {
2152                    bool vop3 = false;
2153                    good &= parseModEnable(asmr, linePtr, vop3, "vop3 modifier");
2154                    mods = (mods & ~VOP3_VOP3) | (vop3 ? VOP3_VOP3 : 0);
2155                }
2156                else if (extraMods!=nullptr)
2157                {
2158                    /* parse specific modofier from VOP_SDWA or VOP_DPP encoding */
2159                    if (withSDWAOperands>=1 && (flags&PARSEVOP_NODSTMODS)==0 &&
2160                            ::strcmp(mod, "dst_sel")==0)
2161                    {
2162                        // dstsel
2163                        skipSpacesToEnd(linePtr, end);
2164                        if (linePtr!=end && *linePtr==':')
2165                        {
2166                            linePtr++;
2167                            cxuint dstSel = 0;
2168                            if (linePtr == end || *linePtr!='@')
2169                            {
2170                                // parse name of dst_sel
2171                                if (getEnumeration(asmr, linePtr, "dst_sel",
2172                                            vopSDWADSTSelNamesNum,
2173                                            vopSDWADSTSelNamesMap, dstSel))
2174                                {
2175                                    extraMods->dstSel = dstSel;
2176                                    if (haveDstSel)
2177                                        asmr.printWarning(modPlace,
2178                                                "Dst_sel is already defined");
2179                                    haveDstSel = true;
2180                                }
2181                                else
2182                                    good = false;
2183                            }
2184                            else
2185                            {
2186                                /* parametrize (if in form '@value') */
2187                                linePtr++;
2188                                if (parseImm(asmr, linePtr, dstSel, nullptr,
2189                                                 3, WS_UNSIGNED))
2190                                {
2191                                    extraMods->dstSel = dstSel;
2192                                    if (haveDstSel)
2193                                        asmr.printWarning(modPlace,
2194                                                "Dst_sel is already defined");
2195                                    haveDstSel = true;
2196                                }
2197                                else
2198                                    good = false;
2199                            }
2200                        }
2201                        else
2202                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before dst_sel")
2203                    }
2204                    else if (withSDWAOperands>=1 && (flags&PARSEVOP_NODSTMODS)==0 &&
2205                        (::strcmp(mod, "dst_unused")==0 || ::strcmp(mod, "dst_un")==0))
2206                    {
2207                        /* dst_unused modifer */
2208                        skipSpacesToEnd(linePtr, end);
2209                        if (linePtr!=end && *linePtr==':')
2210                        {
2211                            skipCharAndSpacesToEnd(linePtr, end);
2212                            cxbyte unused = 0;
2213                            if (linePtr == end || *linePtr!='@')
2214                            {
2215                                // parse name of dst_unused
2216                                char name[20];
2217                                const char* enumPlace = linePtr;
2218                                if (getNameArg(asmr, 20, name, linePtr, "dst_unused"))
2219                                {
2220                                    toLowerString(name);
2221                                    size_t namePos =
2222                                            (::strncmp(name, "unused_", 7)==0) ?7 : 0;
2223                                    if (::strcmp(name+namePos, "sext")==0)
2224                                        unused = 1;
2225                                    else if (::strcmp(name+namePos, "preserve")==0)
2226                                        unused = 2;
2227                                    else if (::strcmp(name+namePos, "pad")!=0)
2228                                        ASM_NOTGOOD_BY_ERROR(enumPlace,
2229                                                    "Unknown dst_unused")
2230                                    extraMods->dstUnused = unused;
2231                                    if (haveDstUnused)
2232                                        asmr.printWarning(modPlace,
2233                                                        "Dst_unused is already defined");
2234                                    haveDstUnused = true;
2235                                }
2236                                else
2237                                    good = false;
2238                            }
2239                            else
2240                            {
2241                                /* '@' in dst_unused: parametrization */
2242                                linePtr++;
2243                                if (parseImm(asmr, linePtr, unused, nullptr,
2244                                                 2, WS_UNSIGNED))
2245                                {
2246                                    extraMods->dstUnused = unused;
2247                                    if (haveDstUnused)
2248                                        asmr.printWarning(modPlace,
2249                                                        "Dst_unused is already defined");
2250                                    haveDstUnused = true;
2251                                }
2252                                else
2253                                    good = false;
2254                            }
2255                        }
2256                        else
2257                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before dst_unused")
2258                    }
2259                    else if (withSDWAOperands>=2 && ::strcmp(mod, "src0_sel")==0)
2260                    {
2261                        /* src0_sel modifier */
2262                        skipSpacesToEnd(linePtr, end);
2263                        if (linePtr!=end && *linePtr==':')
2264                        {
2265                            linePtr++;
2266                            cxuint src0Sel = 0;
2267                            if (linePtr == end || *linePtr!='@')
2268                            {
2269                                // parse source selection (name)
2270                                if (getEnumeration(asmr, linePtr, "src0_sel",
2271                                            vopSDWADSTSelNamesNum,
2272                                            vopSDWADSTSelNamesMap, src0Sel))
2273                                {
2274                                    extraMods->src0Sel = src0Sel;
2275                                    if (haveSrc0Sel)
2276                                        asmr.printWarning(modPlace,
2277                                                        "Src0_sel is already defined");
2278                                    haveSrc0Sel = true;
2279                                }
2280                                else
2281                                    good = false;
2282                            }
2283                            else
2284                            {
2285                                /* parametrize (if in form '@value') */
2286                                linePtr++;
2287                                if (parseImm(asmr, linePtr, src0Sel, nullptr,
2288                                                 3, WS_UNSIGNED))
2289                                {
2290                                    extraMods->src0Sel = src0Sel;
2291                                    if (haveSrc0Sel)
2292                                        asmr.printWarning(modPlace,
2293                                                        "Src0_sel is already defined");
2294                                    haveSrc0Sel = true;
2295                                }
2296                                else
2297                                    good = false;
2298                            }
2299                        }
2300                        else
2301                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before src0_sel")
2302                    }
2303                    else if (withSDWAOperands>=3 && ::strcmp(mod, "src1_sel")==0)
2304                    {
2305                        // src1_sel modifier
2306                        skipSpacesToEnd(linePtr, end);
2307                        if (linePtr!=end && *linePtr==':')
2308                        {
2309                            linePtr++;
2310                            cxuint src1Sel = 0;
2311                            if (linePtr == end || *linePtr!='@')
2312                            {
2313                                // parse source selection (name)
2314                                if (getEnumeration(asmr, linePtr, "src1_sel",
2315                                            vopSDWADSTSelNamesNum,
2316                                            vopSDWADSTSelNamesMap, src1Sel))
2317                                {
2318                                    extraMods->src1Sel = src1Sel;
2319                                    if (haveSrc1Sel)
2320                                        asmr.printWarning(modPlace,
2321                                                        "Src1_sel is already defined");
2322                                    haveSrc1Sel = true;
2323                                }
2324                                else
2325                                    good = false;
2326                            }
2327                            else
2328                            {
2329                                /* parametrize by '@' */
2330                                linePtr++;
2331                                if (parseImm(asmr, linePtr, src1Sel, nullptr,
2332                                                 3, WS_UNSIGNED))
2333                                {
2334                                    extraMods->src1Sel = src1Sel;
2335                                    if (haveSrc1Sel)
2336                                        asmr.printWarning(modPlace,
2337                                                        "Src1_sel is already defined");
2338                                    haveSrc1Sel = true;
2339                                }
2340                                else
2341                                    good = false;
2342                            }
2343                        }
2344                        else
2345                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before src1_sel")
2346                    }
2347                    else if (::strcmp(mod, "quad_perm")==0)
2348                    {
2349                        skipSpacesToEnd(linePtr, end);
2350                        if (linePtr!=end && *linePtr==':')
2351                        {
2352                            // parse quad_perm array
2353                            bool goodMod = true;
2354                            skipCharAndSpacesToEnd(linePtr, end);
2355                            if (linePtr==end || *linePtr!='[')
2356                            {
2357                                ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2358                                        "Expected '[' before quad_perm list")
2359                                continue;
2360                            }
2361                            cxbyte quadPerm = 0;
2362                            linePtr++;
2363                            // parse four 2-bit values
2364                            for (cxuint k = 0; k < 4; k++)
2365                            {
2366                                skipSpacesToEnd(linePtr, end);
2367                                cxbyte qpv = 0;
2368                                good &= parseImm(asmr, linePtr, qpv, nullptr,
2369                                        2, WS_UNSIGNED);
2370                                quadPerm |= qpv<<(k<<1);
2371                                skipSpacesToEnd(linePtr, end);
2372                                if (k!=3)
2373                                {
2374                                    // skip ',' before next value
2375                                    if (linePtr==end || *linePtr!=',')
2376                                    {
2377                                        ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2378                                            "Expected ',' before quad_perm component")
2379                                        break;
2380                                    }
2381                                    else
2382                                        ++linePtr;
2383                                }
2384                                else if (linePtr==end || *linePtr!=']')
2385                                {
2386                                    // unterminated quad_perm
2387                                    asmr.printError(linePtr, "Unterminated quad_perm");
2388                                    goodMod = good = false;
2389                                }
2390                                else
2391                                    ++linePtr;
2392                            }
2393                            if (goodMod)
2394                            {
2395                                // set up quad perm
2396                                extraMods->dppCtrl = quadPerm;
2397                                if (haveDppCtrl)
2398                                    asmr.printWarning(modPlace,
2399                                              "DppCtrl is already defined");
2400                                haveDppCtrl = true;
2401                            }
2402                        }
2403                        else
2404                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before quad_perm")
2405                    }
2406                    else if (::strcmp(mod, "bank_mask")==0)
2407                    {
2408                        // parse bank_mask with 4-bit value
2409                        skipSpacesToEnd(linePtr, end);
2410                        if (linePtr!=end && *linePtr==':')
2411                        {
2412                            linePtr++;
2413                            cxbyte bankMask = 0;
2414                            if (parseImm(asmr, linePtr, bankMask, nullptr, 4, WS_UNSIGNED))
2415                            {
2416                                extraMods->bankMask = bankMask;
2417                                if (haveBankMask)
2418                                    asmr.printWarning(modPlace,
2419                                              "Bank_mask is already defined");
2420                                haveBankMask = true;
2421                            }
2422                            else
2423                                good = false;
2424                        }
2425                        else
2426                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before bank_mask")
2427                    }
2428                    else if (::strcmp(mod, "row_mask")==0)
2429                    {
2430                        // parse row_mask with 4-bit value
2431                        skipSpacesToEnd(linePtr, end);
2432                        if (linePtr!=end && *linePtr==':')
2433                        {
2434                            linePtr++;
2435                            cxbyte rowMask = 0;
2436                            if (parseImm(asmr, linePtr, rowMask, nullptr, 4, WS_UNSIGNED))
2437                            {
2438                                extraMods->rowMask = rowMask;
2439                                if (haveRowMask)
2440                                    asmr.printWarning(modPlace,
2441                                              "Row_mask is already defined");
2442                                haveRowMask = true;
2443                            }
2444                            else
2445                                good = false;
2446                        }
2447                        else
2448                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before row_mask")
2449                    }
2450                    else if (::strcmp(mod, "bound_ctrl")==0)
2451                    {
2452                        bool modGood = true;
2453                        skipSpacesToEnd(linePtr, end);
2454                        if (linePtr!=end && *linePtr==':')
2455                        {
2456                            skipCharAndSpacesToEnd(linePtr, end);
2457                            if (linePtr!=end && (*linePtr=='0' || *linePtr=='1'))
2458                            {
2459                                // accept '0' and '1' as enabled (old and new syntax)
2460                                bool boundCtrl = false;
2461                                linePtr++;
2462                                good &= parseModEnable(asmr, linePtr, boundCtrl,
2463                                        "bound_ctrl modifier");
2464                                mods = (mods & ~VOP3_BOUNDCTRL) |
2465                                        (boundCtrl ? VOP3_BOUNDCTRL : 0);
2466                            }
2467                            else
2468                            {
2469                                asmr.printError(linePtr, "Value must be '0' or '1'");
2470                                modGood = good = false;
2471                            }
2472                        }
2473                        else // just enable boundctrl
2474                            mods |= VOP3_BOUNDCTRL;
2475                        if (modGood)
2476                        {
2477                            // bound_ctrl is defined
2478                            if (haveBoundCtrl)
2479                                asmr.printWarning(modPlace, "BoundCtrl is already defined");
2480                            haveBoundCtrl = true;
2481                            extraMods->needDPP = true;
2482                        }
2483                    }
2484                    else if (mod[0]=='r' && mod[1]=='o' && mod[2]=='w' && mod[3]=='_' &&
2485                            (::strcmp(mod+4, "shl")==0 || ::strcmp(mod+4, "shr")==0 ||
2486                                ::strcmp(mod+4, "ror")==0))
2487                    {
2488                        // row_XXX (shl, shr, ror) modifier (shift is in 1-15)
2489                        skipSpacesToEnd(linePtr, end);
2490                        if (linePtr!=end && *linePtr==':')
2491                        {
2492                            skipCharAndSpacesToEnd(linePtr, end);
2493                            const char* shiftPlace = linePtr;
2494                            cxbyte shift = 0;
2495                            if (parseImm(asmr, linePtr, shift , nullptr, 4, WS_UNSIGNED))
2496                            {
2497                                if (shift == 0)
2498                                {
2499                                    ASM_NOTGOOD_BY_ERROR(shiftPlace,
2500                                            "Illegal zero shift for row_XXX shift")
2501                                    continue;
2502                                }
2503                                if (haveDppCtrl)
2504                                    asmr.printWarning(modPlace,
2505                                              "DppCtrl is already defined");
2506                                haveDppCtrl = true;
2507                                /* retrieve dppCtrl code from mod name:
2508                                 * shl - 0, shr - 0x10, ror - 0x20 */
2509                                extraMods->dppCtrl = 0x100U | ((mod[4]=='r') ? 0x20 :
2510                                    (mod[4]=='s' && mod[6]=='r') ? 0x10 : 0) | shift;
2511                            }
2512                            else
2513                                good = false;
2514                        }
2515                        else
2516                            ASM_NOTGOOD_BY_ERROR(linePtr, (std::string(
2517                                        "Expected ':' before ")+mod).c_str())
2518                    }
2519                    else if (!isGCN15 && memcmp(mod, "wave_", 5)==0 &&
2520                        (::strcmp(mod+5, "shl")==0 || ::strcmp(mod+5, "shr")==0 ||
2521                            ::strcmp(mod+5, "rol")==0 || ::strcmp(mod+5, "ror")==0))
2522                    {
2523                        // wave_XXX (shl,shr,rol,ror)
2524                        bool modGood = true;
2525                        skipSpacesToEnd(linePtr, end);
2526                        if (linePtr!=end && *linePtr==':')
2527                        {
2528                            // accept only 1 at parameter
2529                            skipCharAndSpacesToEnd(linePtr, end);
2530                            if (linePtr!=end && *linePtr=='1')
2531                                ++linePtr;
2532                            else
2533                                ASM_NOTGOOD_BY_ERROR1(modGood = good, linePtr,
2534                                            "Value must be '1'")
2535                        }
2536                        if (mod[5]=='s')
2537                            extraMods->dppCtrl = 0x100 | ((mod[7]=='l') ? 0x30 : 0x38);
2538                        else if (mod[5]=='r')
2539                            extraMods->dppCtrl = 0x100 | ((mod[7]=='l') ? 0x34 : 0x3c);
2540                        if (modGood)
2541                        {
2542                            // dpp_ctrl is defined
2543                            if (haveDppCtrl)
2544                                asmr.printWarning(modPlace, "DppCtrl is already defined");
2545                            haveDppCtrl = true;
2546                        }
2547                    }
2548                    else if (::strcmp(mod, "row_mirror")==0 ||
2549                        ::strcmp(mod, "row_half_mirror")==0 ||
2550                        ::strcmp(mod, "row_hmirror")==0)
2551                    {
2552                        // row_mirror, row_half_mirror
2553                        extraMods->dppCtrl = (mod[4]=='h') ? 0x141 : 0x140;
2554                        if (haveDppCtrl)
2555                            asmr.printWarning(modPlace, "DppCtrl is already defined");
2556                        haveDppCtrl = true;
2557                    }
2558                    else if (!isGCN15 && ::strncmp(mod, "row_bcast", 9)==0 && (
2559                        (mod[9]=='1' && mod[10]=='5' && mod[11]==0) ||
2560                        (mod[9]=='3' && mod[10]=='1' && mod[11]==0) || mod[9]==0))
2561                    {
2562                        // row_bcast15 and row_bast31 modifier
2563                        bool modGood = true;
2564                        if (mod[9] =='1') // if row_bcast15
2565                            extraMods->dppCtrl = 0x142;
2566                        else if (mod[9] =='3') // if row_bcast31
2567                            extraMods->dppCtrl = 0x143;
2568                        else
2569                        {
2570                            // get number
2571                            skipSpacesToEnd(linePtr, end);
2572                            if (linePtr!=end && *linePtr==':')
2573                            {
2574                                skipCharAndSpacesToEnd(linePtr, end);
2575                                const char* numPlace = linePtr;
2576                                cxbyte value = cstrtobyte(linePtr, end);
2577                                // parse row_bcast:15 or row_bcast:31
2578                                if (value == 31)
2579                                    extraMods->dppCtrl = 0x143;
2580                                else if (value == 15)
2581                                    extraMods->dppCtrl = 0x142;
2582                                else
2583                                    ASM_NOTGOOD_BY_ERROR1(modGood = good, numPlace,
2584                                            "Thread to broadcast must be 15 or 31")
2585                            }
2586                            else
2587                                ASM_NOTGOOD_BY_ERROR1(modGood = good, linePtr,
2588                                            "Expected ':' before row_bcast")
2589                        }
2590                        if (modGood)
2591                        {
2592                            if (haveDppCtrl)
2593                                asmr.printWarning(modPlace, "DppCtrl is already defined");
2594                            haveDppCtrl = true;
2595                        }
2596                    }
2597                    else if (isGCN15 && (::strcmp(mod, "row_share")==0 || ::strcmp(mod, "row_xmask")==0))
2598                    {
2599                        // row_XXX (shl, shr, ror) modifier (shift is in 1-15)
2600                        skipSpacesToEnd(linePtr, end);
2601                        if (linePtr!=end && *linePtr==':')
2602                        {
2603                            skipCharAndSpacesToEnd(linePtr, end);
2604                            cxbyte shift = 0;
2605                            if (parseImm(asmr, linePtr, shift , nullptr, 4, WS_UNSIGNED))
2606                            {
2607                                if (haveDppCtrl)
2608                                    asmr.printWarning(modPlace,
2609                                              "DppCtrl is already defined");
2610                                haveDppCtrl = true;
2611                                extraMods->dppCtrl = (0x150U +
2612                                            (mod[4]=='x' ? 0x10 : 0)) | shift;
2613                            }
2614                            else
2615                                good = false;
2616                        }
2617                        else
2618                            ASM_NOTGOOD_BY_ERROR(linePtr, (std::string(
2619                                        "Expected ':' before ")+mod).c_str())
2620                    }
2621                    else if (isGCN15 && ::strcmp(mod, "fi")==0)
2622                    {
2623                        bool fi = false;
2624                        good &= parseModEnable(asmr, linePtr, fi, "vop3 modifier");
2625                        extraMods->fi = fi;
2626                    }
2627                    else if (isGCN15 && ::strcmp(mod, "dpp8")==0)
2628                    {
2629                        skipSpacesToEnd(linePtr, end);
2630                        if (linePtr!=end && *linePtr==':')
2631                        {
2632                            // parse dpp8 array
2633                            bool goodMod = true;
2634                            skipCharAndSpacesToEnd(linePtr, end);
2635                            if (linePtr==end || *linePtr!='[')
2636                            {
2637                                ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2638                                        "Expected '[' before dpp8 list")
2639                                continue;
2640                            }
2641                            uint32_t dpp8 = 0;
2642                            linePtr++;
2643                            // parse four 3-bit values
2644                            for (cxuint k = 0; k < 8; k++)
2645                            {
2646                                skipSpacesToEnd(linePtr, end);
2647                                cxbyte qpv = 0;
2648                                good &= parseImm(asmr, linePtr, qpv, nullptr,
2649                                        3, WS_UNSIGNED);
2650                                dpp8 |= qpv<<(k*3);
2651                                skipSpacesToEnd(linePtr, end);
2652                                if (k!=7)
2653                                {
2654                                    // skip ',' before next value
2655                                    if (linePtr==end || *linePtr!=',')
2656                                    {
2657                                        ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2658                                            "Expected ',' before dpp8 component")
2659                                        break;
2660                                    }
2661                                    else
2662                                        ++linePtr;
2663                                }
2664                                else if (linePtr==end || *linePtr!=']')
2665                                {
2666                                    // unterminated dpp8
2667                                    asmr.printError(linePtr, "Unterminated dpp8");
2668                                    goodMod = good = false;
2669                                }
2670                                else
2671                                    ++linePtr;
2672                            }
2673                            if (goodMod)
2674                            {
2675                                // set up dpp8
2676                                extraMods->dpp8Value = dpp8;
2677                                if (haveDPP8)
2678                                    asmr.printWarning(modPlace,
2679                                              "DPP8 is already defined");
2680                                haveDPP8 = true;
2681                            }
2682                        }
2683                        else
2684                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before quad_perm")
2685                    }
2686                    else if (::strcmp(mod, "sdwa")==0)
2687                    {
2688                        // SDWA - force SDWA encoding
2689                        bool sdwa = false;
2690                        good &= parseModEnable(asmr, linePtr, sdwa, "vop3 modifier");
2691                        haveSDWA = sdwa;
2692                    }
2693                    else if (::strcmp(mod, "dpp")==0)
2694                    {
2695                        // DPP - force DPP encoding
2696                        bool dpp = false;
2697                        good &= parseModEnable(asmr, linePtr, dpp, "vop3 modifier");
2698                        haveDPP = dpp;
2699                    }
2700                    else    /// unknown modifier
2701                        ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP modifier")
2702                }
2703                else /// unknown modifier
2704                    ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP modifier")
2705               
2706                if (alreadyModDefined)
2707                    asmr.printWarning(modPlace, "OMOD is already defined");
2708            }
2709            catch(const ParseException& ex)
2710            {
2711                asmr.printError(linePtr, ex.what());
2712                good = false;
2713            }
2714        }
2715        else
2716            good = false;
2717    }
2718   
2719    // determine what encoding will be needed to encode instruction
2720    const bool vopSDWA = (haveDstSel || haveDstUnused || haveSrc0Sel || haveSrc1Sel ||
2721        opMods.sextMod!=0 || haveSDWA);
2722    const bool vopDPP = (haveDppCtrl || haveBoundCtrl || haveBankMask || haveRowMask ||
2723            haveDPP || (haveFi && !haveDPP8));
2724    const bool vopDPP8 = (haveFi || haveDPP8);
2725    const bool isGCN14 = (arch & ARCH_GCN_1_4_5) != 0;
2726    // mul/div modifier does not apply to vop3 if RXVEGA (this case will be checked later)
2727    const bool vop3 = (mods & ((isGCN14 ? 0 : 3)|VOP3_VOP3))!=0 ||
2728                ((opMods.opselMod&15)!=0);
2729    if (extraMods!=nullptr)
2730    {
2731        extraMods->needSDWA = vopSDWA;
2732        extraMods->needDPP = vopDPP;
2733        extraMods->needDPP8 = vopDPP8;
2734    }
2735    if ((int(vop3)+vopSDWA+vopDPP+vopDPP8)>1 ||
2736                // RXVEGA: mul/div modifier are accepted in VOP_SDWA but not for VOP_DPP
2737                (isGCN14 && (mods & 3)!=0 && (vopDPP||vopDPP8)) ||
2738                ((mods&VOP3_CLAMP)!=0 && (vopDPP||vopDPP8)))
2739        ASM_FAIL_BY_ERROR(modsPlace, "Mixing modifiers from different encodings is illegal")
2740    return good;
2741}
2742
2743static const char* vintrpParamsTbl[] =
2744{ "p10", "p20", "p0" };
2745
2746// parse interpolation (P0,P10,P20) parameter for VINTRP instructions
2747bool GCNAsmUtils::parseVINTRP0P10P20(Assembler& asmr, const char*& linePtr, RegRange& reg)
2748{
2749    const char* end = asmr.line+asmr.lineSize;
2750    skipSpacesToEnd(linePtr, end);
2751    const char* p0Place = linePtr;
2752    char pxName[5];
2753    if (getNameArg(asmr, 5, pxName, linePtr, "VINTRP parameter"))
2754    {
2755        cxuint p0Code = 0;
2756        toLowerString(pxName);
2757        for (p0Code = 0; p0Code < 3; p0Code++)
2758            if (::strcmp(vintrpParamsTbl[p0Code], pxName)==0)
2759                break;
2760        if (p0Code < 3) // as srcReg
2761            reg = { p0Code, p0Code+1 };
2762        else
2763            ASM_FAIL_BY_ERROR(p0Place, "Unknown VINTRP parameter")
2764        return true;
2765    }
2766    return false;
2767}
2768
2769bool GCNAsmUtils::parseVINTRPAttr(Assembler& asmr, const char*& linePtr, cxbyte& attr)
2770{
2771    bool good = true;
2772    const char* end = asmr.line+asmr.lineSize;
2773    skipSpacesToEnd(linePtr, end);
2774    bool goodAttr = true;
2775    const char* attrPlace = linePtr;
2776    if (linePtr+4 > end)
2777        ASM_NOTGOOD_BY_ERROR1(goodAttr = good, attrPlace, "Expected 'attr' keyword")
2778    char buf[5];
2779    if (goodAttr)
2780    {
2781        std::transform(linePtr, linePtr+4, buf, toLower); // to lowercase
2782        // parse attr:
2783        if (::strncmp(buf, "attr", 4)!=0)
2784        {
2785            // skip spaces (to next element)
2786            while (linePtr!=end && *linePtr!=' ') linePtr++;
2787            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, attrPlace, "Expected 'attr' keyword")
2788        }
2789        else
2790            linePtr+=4;
2791    }
2792   
2793    cxbyte attrVal = 0;
2794    if (goodAttr)
2795    {
2796        // parse only attribute value if no error before
2797        const char* attrNumPlace = linePtr;
2798        try
2799        { attrVal = cstrtobyte(linePtr, end); }
2800        catch(const ParseException& ex)
2801        {
2802            asmr.printError(linePtr, ex.what());
2803            goodAttr = good = false;
2804        }
2805        if (attrVal >= 64)
2806            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, attrNumPlace,
2807                        "Attribute number out of range (0-63)")
2808    }
2809    if (goodAttr)
2810    {
2811        // parse again if no error before
2812        skipSpacesToEnd(linePtr, end);
2813        if (linePtr==end || *linePtr!='.')
2814            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, linePtr,
2815                            "Expected '.' after attribute number")
2816        else
2817            ++linePtr;
2818    }
2819    if (goodAttr)
2820    {
2821        // parse again if no error before
2822        skipSpacesToEnd(linePtr, end);
2823        if (linePtr==end)
2824            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, linePtr, "Expected attribute component")
2825    }
2826    char attrCmpName = 0;
2827    if (goodAttr)
2828    {
2829        // parse only attribute component if no error before
2830        attrCmpName = toLower(*linePtr);
2831        if (attrCmpName!='x' && attrCmpName!='y' && attrCmpName!='z' && attrCmpName!='w')
2832            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected attribute component")
2833        linePtr++;
2834    }
2835   
2836    attr = (attrVal<<2) | ((attrCmpName=='x') ? 0 : (attrCmpName=='y') ? 1 :
2837            (attrCmpName=='z') ? 2 : 3);
2838    return good;
2839}
2840
2841/* special version of getNameArg for MUBUF format name.
2842 * this version accepts digits at first character of format name */
2843bool GCNAsmUtils::getMUBUFFmtNameArg(Assembler& asmr, size_t maxOutStrSize, char* outStr,
2844               const char*& linePtr, const char* objName)
2845{
2846    const char* end = asmr.line + asmr.lineSize;
2847    skipSpacesToEnd(linePtr, end);
2848    if (linePtr == end)
2849        ASM_FAIL_BY_ERROR(linePtr, (std::string("Expected ")+objName).c_str())
2850    const char* nameStr = linePtr;
2851    if (isAlnum(*linePtr) || *linePtr == '_' || *linePtr == '.')
2852    {
2853        linePtr++;
2854        while (linePtr != end && (isAlnum(*linePtr) ||
2855            *linePtr == '_' || *linePtr == '.')) linePtr++;
2856    }
2857    else
2858    {
2859        asmr.printError(linePtr, (std::string("Some garbages at ")+objName+
2860                " place").c_str());
2861        while (linePtr != end && !isSpace(*linePtr)) linePtr++;
2862        return false;
2863    }
2864    if (maxOutStrSize-1 < size_t(linePtr-nameStr))
2865        ASM_FAIL_BY_ERROR(linePtr, (std::string(objName)+" is too long").c_str())
2866    const size_t outStrSize = std::min(maxOutStrSize-1, size_t(linePtr-nameStr));
2867    std::copy(nameStr, nameStr+outStrSize, outStr);
2868    outStr[outStrSize] = 0; // null-char
2869    return true;
2870}
2871
2872bool GCNAsmUtils::checkGCNEncodingSize(Assembler& asmr, const char* insnPtr,
2873                     GCNEncSize gcnEncSize, uint32_t wordsNum)
2874{
2875    if (gcnEncSize==GCNEncSize::BIT32 && wordsNum!=1)
2876        ASM_FAIL_BY_ERROR(insnPtr, "32-bit encoding specified when 64-bit encoding")
2877    if (gcnEncSize==GCNEncSize::BIT64 && wordsNum!=2)
2878        ASM_FAIL_BY_ERROR(insnPtr, "64-bit encoding specified when 32-bit encoding")
2879    return true;
2880}
2881
2882bool GCNAsmUtils::checkGCNVOPEncoding(Assembler& asmr, GPUArchMask arch, const char* insnPtr,
2883            GCNVOPEnc vopEnc, GCNInsnMode insnMode, const VOPExtraModifiers* modifiers)
2884{
2885    if (vopEnc==GCNVOPEnc::DPP && (!modifiers->needDPP && !modifiers->needDPP8))
2886        ASM_FAIL_BY_ERROR(insnPtr, "DPP encoding specified when DPP not present")
2887    if (vopEnc==GCNVOPEnc::SDWA && !modifiers->needSDWA)
2888        ASM_FAIL_BY_ERROR(insnPtr, "SDWA encoding specified when SDWA not present")
2889    if ((modifiers->needDPP || modifiers->needDPP8) && (insnMode&GCN_VOP_NODPP)!=0)
2890        ASM_FAIL_BY_ERROR(insnPtr, "DPP encoding is illegal for this instruction")
2891    if (modifiers->needSDWA && (insnMode&GCN_VOP_NOSDWA)!=0)
2892        ASM_FAIL_BY_ERROR(insnPtr, "SDWA encoding is illegal for this instruction")
2893    if (modifiers->needSDWA && (insnMode&GCN_VOP_NOSDWAVEGA)!=0 && (arch & ARCH_GCN_1_4)!=0)
2894        ASM_FAIL_BY_ERROR(insnPtr, "SDWA encoding is illegal for this instruction")
2895    return true;
2896}
2897
2898bool GCNAsmUtils::checkGCNVOPExtraModifers(Assembler& asmr, GPUArchMask arch, bool needImm,
2899                 bool sextFlags, bool vop3, GCNVOPEnc gcnVOPEnc, const GCNOperand& src0Op,
2900                 VOPExtraModifiers& extraMods, bool absNegFlags, const char* instrPlace)
2901{
2902    if (needImm)
2903        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SDWA or DPP word is illegal")
2904    if ((arch & ARCH_GCN_1_4_5)==0 && !src0Op.range.isVGPR())
2905        ASM_FAIL_BY_ERROR(instrPlace, "SRC0 must be a vector register with "
2906                    "SDWA or DPP word")
2907    if ((arch & ARCH_GCN_1_4_5)!=0 && (extraMods.needDPP || extraMods.needDPP8) &&
2908                    !src0Op.range.isVGPR())
2909        ASM_FAIL_BY_ERROR(instrPlace, "SRC0 must be a vector register with DPP word")
2910    if (vop3)
2911        // if VOP3 and (VOP_DPP or VOP_SDWA)
2912        ASM_FAIL_BY_ERROR(instrPlace, "Mixing VOP3 with SDWA or WORD is illegal")
2913    if (sextFlags && (extraMods.needDPP || extraMods.needDPP8))
2914        ASM_FAIL_BY_ERROR(instrPlace, "SEXT modifiers is unavailable for DPP word")
2915    if (absNegFlags && extraMods.needDPP8)
2916        ASM_FAIL_BY_ERROR(instrPlace, "ABS and NEG modifiers is unavailable for DPP8 word")
2917    if (!extraMods.needSDWA && !extraMods.needDPP && !extraMods.needDPP8)
2918    {
2919        if (gcnVOPEnc!=GCNVOPEnc::DPP)
2920            extraMods.needSDWA = true; // by default we choose SDWA word
2921        else
2922            extraMods.needDPP = true;
2923    }
2924    return true;
2925}
2926
2927};
Note: See TracBrowser for help on using the repository browser.