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

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

CLRadeonExtender: GCNAsm: Small fix in parseSMRDImm.

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            skipSpacesToEnd(linePtr, end);
995            if (linePtr==end || *linePtr!=')')
996                ASM_FAIL_BY_ERROR(linePtr, "Expected ')' after expression at 'lit'")
997            else // skip end of lit
998                linePtr++;
999        }
1000        litimm = haveLit || (outValue >= 0x100);
1001        return true;
1002    }
1003   
1004    std::unique_ptr<AsmExpression> expr(AsmExpression::parse(asmr, linePtr));
1005    if (expr==nullptr) // error
1006        return false;
1007    if (expr->isEmpty())
1008        ASM_FAIL_BY_ERROR(exprPlace, "Expected expression")
1009    if (expr->getSymOccursNum()==0)
1010    {
1011        // resolved now
1012        cxuint sectionId; // for getting
1013        if (!expr->evaluate(asmr, value, sectionId)) // failed evaluation!
1014            return false;
1015        else if (sectionId != ASMSECT_ABS)
1016            // if not absolute value
1017            ASM_FAIL_BY_ERROR(exprPlace, "Expression must be absolute!")
1018       
1019        asmr.printWarningForRange(32, value,
1020                        asmr.getSourcePos(exprPlace), WS_BOTH);
1021        outValue = value & ((1ULL<<32)-1ULL);
1022    }
1023    else
1024    {
1025        // return output expression with symbols to resolve
1026        if (outTargetExpr!=nullptr)
1027            *outTargetExpr = std::move(expr);
1028        else
1029            ASM_FAIL_BY_ERROR(exprPlace, "Unresolved expression is illegal in this place")
1030    }
1031    litimm = haveLit || (outValue >= 0x100);
1032    // end of 'lit('
1033    if (haveLit)
1034    {
1035        skipSpacesToEnd(linePtr, end);
1036        if (linePtr==end || *linePtr!=')')
1037            ASM_FAIL_BY_ERROR(linePtr, "Expected ')' after expression at 'lit'")
1038        else // skip end of lit
1039            linePtr++;
1040    }
1041    return true;
1042}
1043
1044enum FloatLitType
1045{
1046    FLTT_F16,
1047    FLTT_F32,
1048    FLTT_F64
1049};
1050
1051// determinr float literal type from suffix
1052static FloatLitType getFloatLitType(const char* str, const char* end,
1053                        FloatLitType defaultFPType)
1054{
1055    if (str==end)
1056        return defaultFPType; // end of string and no replacing suffix
1057    else if (toLower(*str)=='l')
1058        return FLTT_F64;
1059    else if (toLower(*str)=='s')
1060        return FLTT_F32;
1061    else if (toLower(*str)=='h')
1062        return FLTT_F16;
1063    else
1064        return defaultFPType;
1065}
1066
1067/* check whether string is exclusively floating point value
1068 * (only floating point, and neither integer and nor symbol) */
1069static bool isOnlyFloat(const char* str, const char* end, FloatLitType defaultFPType,
1070                        FloatLitType& outFPType)
1071{
1072    if (str == end)
1073        return false;
1074    if (*str=='-' || *str=='+')
1075        str++; // skip '-' or '+'
1076    if (str+2 < end && *str=='0' && (str[1]=='X' || str[1]=='x'))
1077    {
1078        // hexadecimal
1079        str += 2;
1080        const char* beforeComma = str;
1081        while (str!=end && isXDigit(*str)) str++;
1082        const char* point = str;
1083        if (str == end || *str!='.')
1084        {
1085            if (beforeComma-point!=0 && str!=end && (*str=='p' || *str=='P'))
1086            {
1087                // handle part P[+|-]exp
1088                str++;
1089                if (str!=end && (*str=='-' || *str=='+'))
1090                    str++;
1091                const char* expPlace = str;
1092                while (str!=end && isDigit(*str)) str++;
1093                if (str-expPlace!=0)
1094                {
1095                    outFPType = getFloatLitType(str, end, defaultFPType);
1096                    return true; // if 'XXXp[+|-]XXX'
1097                }
1098            }
1099            return false; // no '.'
1100        }
1101        // if XXX.XXX
1102        str++;
1103        while (str!=end && isXDigit(*str)) str++;
1104        const char* afterComma = str;
1105       
1106        if (point-beforeComma!=0 || afterComma-(point+1)!=0)
1107        {
1108            if (beforeComma-point!=0 && str!=end && (*str=='p' || *str=='P'))
1109            {
1110                // handle part P[+|-]exp
1111                str++;
1112                if (str!=end && (*str=='-' || *str=='+'))
1113                    str++;
1114                while (str!=end && isDigit(*str)) str++;
1115            }
1116            outFPType = getFloatLitType(str, end, defaultFPType);
1117            return true;
1118        }
1119    }
1120    else
1121    {
1122        // decimal
1123        const char* beforeComma = str;
1124        while (str!=end && isDigit(*str)) str++;
1125        const char* point = str;
1126        if (str == end || *str!='.')
1127        {
1128            if (beforeComma-point!=0 && str!=end && (*str=='e' || *str=='E'))
1129            {
1130                // handle part E[+|-]exp
1131                str++;
1132                if (str!=end && (*str=='-' || *str=='+'))
1133                    str++;
1134                const char* expPlace = str;
1135                while (str!=end && isDigit(*str)) str++;
1136                if (str-expPlace!=0)
1137                {
1138                    outFPType = getFloatLitType(str, end, defaultFPType);
1139                    return true; // if 'XXXe[+|-]XXX'
1140                }
1141            }
1142            return false; // no '.'
1143        }
1144        // if XXX.XXX
1145        str++;
1146        while (str!=end && isDigit(*str)) str++;
1147        const char* afterComma = str;
1148       
1149        if (point-beforeComma!=0 || afterComma-(point+1)!=0)
1150        {
1151            if (beforeComma-point!=0 && str!=end && (*str=='e' || *str=='E'))
1152            {
1153                // handle part E[+|-]exp
1154                str++;
1155                if (str!=end && (*str=='-' || *str=='+'))
1156                    str++;
1157                while (str!=end && isDigit(*str)) str++;
1158            }
1159            outFPType = getFloatLitType(str, end, defaultFPType);
1160            return true;
1161        }
1162    }
1163    return false;
1164}
1165
1166bool GCNAsmUtils::parseLiteralImm(Assembler& asmr, const char*& linePtr, uint32_t& value,
1167            std::unique_ptr<AsmExpression>* outTargetExpr, Flags instropMask)
1168{
1169    if (outTargetExpr!=nullptr)
1170        outTargetExpr->reset();
1171    const char* end = asmr.line+asmr.lineSize;
1172    skipSpacesToEnd(linePtr, end);
1173    FloatLitType fpType;
1174    FloatLitType defaultFpType = (instropMask&INSTROP_TYPE_MASK)!=INSTROP_F16 ?
1175            ((instropMask&INSTROP_TYPE_MASK)==INSTROP_V64BIT ?
1176            FLTT_F64 : FLTT_F32) : FLTT_F16;
1177   
1178    // before parsing, we check that is floating point and integer value
1179    if (isOnlyFloat(linePtr, end, defaultFpType, fpType))
1180    {
1181        // try to parse floating point FP16, FP32 or high 32-bit part of FP64
1182        try
1183        {
1184        if (fpType==FLTT_F16)
1185        {
1186            value = cstrtohCStyle(linePtr, end, linePtr);
1187            if (linePtr!=end && toLower(*linePtr)=='h')
1188                linePtr++;
1189        }
1190        else if (fpType==FLTT_F32)
1191        {
1192            union FloatUnion { uint32_t i; float f; };
1193            FloatUnion v;
1194            v.f = cstrtovCStyle<float>(linePtr, end, linePtr);
1195            if (linePtr!=end && toLower(*linePtr)=='s')
1196                linePtr++;
1197            value = v.i;
1198        }
1199        else
1200        {
1201            /* 64-bit (high 32-bits) */
1202            uint32_t v = cstrtofXCStyle(linePtr, end, linePtr, 11, 20);
1203            if (linePtr!=end && toLower(*linePtr)=='l')
1204                linePtr++;
1205            value = v;
1206        }
1207        }
1208        catch(const ParseException& ex)
1209        {
1210            // error
1211            asmr.printError(linePtr, ex.what());
1212            return false;
1213        }
1214        return true;
1215    }
1216    return parseImm(asmr, linePtr, value, outTargetExpr);
1217}
1218
1219// source register names (EXECZ,SCC,VCCZ, ...) for GCN1.0/1.1/1.2
1220static const std::pair<const char*, uint16_t> ssourceNamesTbl[] =
1221{
1222    { "execz", 252 },
1223    { "scc", 253 },
1224    { "src_execz", 252 },
1225    { "src_scc", 253 },
1226    { "src_vccz", 251 },
1227    { "vccz", 251 }
1228};
1229
1230static const size_t ssourceNamesTblSize = sizeof(ssourceNamesTbl) /
1231        sizeof(std::pair<const char*, uint16_t>);
1232
1233// source register names (EXECZ,SCC,VCCZ, ...) for GCN1.4
1234static const std::pair<const char*, uint16_t> ssourceNamesGCN14Tbl[] =
1235{
1236    { "execz", 252 },
1237    { "pops_exiting_wave_id", 0xef },
1238    { "private_base", 0xed },
1239    { "private_limit", 0xee },
1240    { "scc", 253 },
1241    { "shared_base", 0xeb },
1242    { "shared_limit", 0xec },
1243    { "src_execz", 252 },
1244    { "src_pops_exiting_wave_id", 0xef },
1245    { "src_private_base", 0xed },
1246    { "src_private_limit", 0xee },
1247    { "src_scc", 253 },
1248    { "src_shared_base", 0xeb },
1249    { "src_shared_limit", 0xec },
1250    { "src_vccz", 251 },
1251    { "vccz", 251 }
1252};
1253
1254static const size_t ssourceNamesGCN14TblSize = sizeof(ssourceNamesGCN14Tbl) /
1255        sizeof(std::pair<const char*, uint16_t>);
1256
1257// main routine to parse operand
1258bool GCNAsmUtils::parseOperand(Assembler& asmr, const char*& linePtr, GCNOperand& operand,
1259             std::unique_ptr<AsmExpression>* outTargetExpr, GPUArchMask arch,
1260             cxuint regsNum, Flags instrOpMask, AsmRegField regField)
1261{
1262    const bool isGCN12 = (arch & ARCH_GCN_1_2_4_5)!=0;
1263    const bool isGCN14 = (arch & ARCH_GCN_1_4_5)!=0;
1264   
1265    if (outTargetExpr!=nullptr)
1266        outTargetExpr->reset();
1267   
1268    if (asmr.buggyFPLit && (instrOpMask&INSTROP_TYPE_MASK)==INSTROP_V64BIT)
1269        // buggy fplit does not accept 64-bit values (high 32-bits)
1270        instrOpMask = (instrOpMask&~INSTROP_TYPE_MASK) | INSTROP_INT;
1271   
1272    const Flags optionFlags = (instrOpMask & (INSTROP_UNALIGNED|INSTROP_ACCESS_MASK));
1273    // fast path to parse only VGPR or SGPR
1274    if ((instrOpMask&~INSTROP_UNALIGNED) == INSTROP_SREGS)
1275        return parseSRegRange(asmr, linePtr, operand.range, arch, regsNum, regField, true,
1276                              INSTROP_SYMREGRANGE | optionFlags);
1277    else if ((instrOpMask&~INSTROP_UNALIGNED) == INSTROP_VREGS)
1278        return parseVRegRange(asmr, linePtr, operand.range, regsNum, regField, true,
1279                              INSTROP_SYMREGRANGE | optionFlags);
1280   
1281    // otherwise we must include modifiers and other registers or literals/constants
1282    const char* end = asmr.line+asmr.lineSize;
1283    if (instrOpMask & INSTROP_VOP3MODS)
1284    {
1285        operand.vopMods = 0;
1286        skipSpacesToEnd(linePtr, end);
1287        if (linePtr!=end && *linePtr=='@') // treat this operand as expression
1288            return parseOperand(asmr, linePtr, operand, outTargetExpr, arch, regsNum,
1289                             instrOpMask & ~INSTROP_VOP3MODS, regField);
1290       
1291        if (isGCN12 && (instrOpMask & (INSTROP_NOSEXT|INSTROP_VOP3P))==0 &&
1292            linePtr+4 <= end && strncasecmp(linePtr, "sext", 4)==0)
1293        {
1294            /* sext */
1295            linePtr += 4;
1296            skipSpacesToEnd(linePtr, end);
1297            if (linePtr!=end && *linePtr=='(')
1298            {
1299                operand.vopMods |= VOPOP_SEXT;
1300                ++linePtr;
1301            }
1302            else
1303                ASM_FAIL_BY_ERROR(linePtr, "Expected '(' after sext")
1304        }
1305       
1306        const char* negPlace = linePtr;
1307        // parse negation
1308        if (linePtr!=end && *linePtr=='-')
1309        {
1310            operand.vopMods |= VOPOP_NEG;
1311            skipCharAndSpacesToEnd(linePtr, end);
1312        }
1313        // parse abs modifier
1314        bool llvmAbs = false;
1315        if (linePtr+3 <= end && (instrOpMask & INSTROP_VOP3P)==0 &&
1316            toLower(linePtr[0])=='a' &&
1317            toLower(linePtr[1])=='b' && toLower(linePtr[2])=='s')
1318        {
1319            // if 'abs' operand modifier
1320            linePtr += 3;
1321            skipSpacesToEnd(linePtr, end);
1322            if (linePtr!=end && *linePtr=='(')
1323            {
1324                operand.vopMods |= VOPOP_ABS;
1325                ++linePtr;
1326            }
1327            else
1328                ASM_FAIL_BY_ERROR(linePtr, "Expected '(' after abs")
1329        }
1330        else if (linePtr<=end && linePtr[0]=='|')
1331        {
1332            // LLVM like syntax for abs modifier
1333            linePtr++;
1334            skipSpacesToEnd(linePtr, end);
1335            operand.vopMods |= VOPOP_ABS;
1336            llvmAbs = true;
1337        }
1338       
1339        bool good;
1340        // now we parse operand except VOP modifiers
1341        if ((operand.vopMods&(VOPOP_NEG|VOPOP_ABS)) != VOPOP_NEG)
1342            good = parseOperand(asmr, linePtr, operand, outTargetExpr, arch, regsNum,
1343                                     instrOpMask & ~INSTROP_VOP3MODS, regField);
1344        else //
1345        {
1346            // parse with negation if neg (it can be literal with negation)
1347            linePtr = negPlace;
1348            good = parseOperand(asmr, linePtr, operand, outTargetExpr, arch, regsNum,
1349                     (instrOpMask & ~INSTROP_VOP3MODS) | INSTROP_PARSEWITHNEG, regField);
1350        }
1351       
1352        // checking closing of VOP modifiers
1353        if (operand.vopMods & VOPOP_ABS)
1354        {
1355            skipSpacesToEnd(linePtr, end);
1356            if (linePtr!=end && ((*linePtr==')' && !llvmAbs) ||
1357                        (*linePtr=='|' && llvmAbs)))
1358                linePtr++;
1359            else
1360                ASM_FAIL_BY_ERROR(linePtr, "Unterminated abs() modifier")
1361        }
1362        if (operand.vopMods & VOPOP_SEXT)
1363        {
1364            skipSpacesToEnd(linePtr, end);
1365            if (linePtr!=end && *linePtr==')')
1366                linePtr++;
1367            else
1368                ASM_FAIL_BY_ERROR(linePtr, "Unterminated sext() modifier")
1369        }
1370        return good;
1371    }
1372    skipSpacesToEnd(linePtr, end);
1373    const char* negPlace = linePtr;
1374    if (instrOpMask & INSTROP_VOP3NEG)
1375        operand.vopMods = 0; // clear modifier (VOP3NEG)
1376        /// PARSEWITHNEG used to continuing operand parsing with modifiers
1377    if (instrOpMask & (INSTROP_PARSEWITHNEG|INSTROP_VOP3NEG))
1378    {
1379        if (linePtr!=end && *linePtr=='-')
1380        {
1381            skipCharAndSpacesToEnd(linePtr, end);
1382            operand.vopMods |= VOPOP_NEG;
1383        }
1384    }
1385   
1386    // otherwise, we try parse scalar register
1387    if (instrOpMask & INSTROP_SREGS)
1388    {
1389        if (!parseSRegRange(asmr, linePtr, operand.range, arch, regsNum, regField,
1390                        false, optionFlags))
1391            return false;
1392        if (operand)
1393            return true;
1394    }
1395    // otherwise try parse vector register
1396    if (instrOpMask & INSTROP_VREGS)
1397    {
1398        if (!parseVRegRange(asmr, linePtr, operand.range, regsNum, regField,
1399                        false, optionFlags))
1400            return false;
1401        if (operand)
1402            return true;
1403    }
1404    // if still is not this, try parse symbol regrange
1405    if (instrOpMask & (INSTROP_SREGS|INSTROP_VREGS))
1406    {
1407        if (!parseSymRegRange(asmr, linePtr, operand.range, arch, regsNum, regField,
1408                  (instrOpMask&((INSTROP_SREGS|INSTROP_VREGS|INSTROP_SSOURCE))) |
1409                    optionFlags, false))
1410            return false;
1411        if (operand)
1412            return true;
1413    }
1414   
1415    skipSpacesToEnd(linePtr, end);
1416   
1417    if ((instrOpMask & INSTROP_SSOURCE)!=0)
1418    {
1419        // try parse scalar source (vcc, scc, literals)
1420        char regName[25];
1421        const char* regNamePlace = linePtr;
1422        if (getNameArg(asmr, 25, regName, linePtr, "register name", false, true))
1423        {
1424            toLowerString(regName);
1425            operand.range = {0, 0};
1426           
1427            auto regNameTblEnd = isGCN14 ?
1428                        ssourceNamesGCN14Tbl + ssourceNamesGCN14TblSize :
1429                        ssourceNamesTbl + ssourceNamesTblSize;
1430            auto regNameIt = binaryMapFind(
1431                    isGCN14 ? ssourceNamesGCN14Tbl : ssourceNamesTbl,
1432                    regNameTblEnd, regName, CStringLess());
1433           
1434            // if found in table
1435            if (regNameIt != regNameTblEnd)
1436            {
1437                operand.range = { regNameIt->second, regNameIt->second+1 };
1438                return true;
1439            }
1440            // if lds or src_lds_direct, lds_direct
1441            else if ((instrOpMask&INSTROP_LDS)!=0 &&
1442                (::strcmp(regName, "lds")==0 || ::strcmp(regName, "lds_direct")==0 ||
1443                    ::strcmp(regName, "src_lds_direct")==0))
1444            {
1445                operand.range = { 254, 255 };
1446                return true;
1447            }
1448            if (operand)
1449            {
1450                if (regsNum!=0 && regsNum!=1 && regsNum!=2)
1451                {
1452                    printXRegistersRequired(asmr, regNamePlace, "scalar", regsNum);
1453                    return false;
1454                }
1455                return true;
1456            }
1457            /* check expression, back to before regName */
1458            linePtr = negPlace;
1459        }
1460        // treat argument as expression
1461        bool forceExpression = false;
1462        if (linePtr!=end && *linePtr=='@')
1463        {
1464            forceExpression = true;
1465            skipCharAndSpacesToEnd(linePtr, end);
1466        }
1467        if (linePtr==end || *linePtr==',')
1468            ASM_FAIL_BY_ERROR(linePtr, "Expected instruction operand")
1469        const char* exprPlace = linePtr;
1470       
1471        uint64_t value;
1472        operand.vopMods = 0; // zeroing operand modifiers
1473        FloatLitType fpType;
1474       
1475        bool exprToResolve = false;
1476        bool encodeAsLiteral = false;
1477        // if 'lit(' in this place
1478        if (linePtr+4<end && toLower(linePtr[0])=='l' && toLower(linePtr[1])=='i' &&
1479            toLower(linePtr[2])=='t' && (isSpace(linePtr[3]) || linePtr[3]=='('))
1480        {
1481            // lit() - force encoding as literal
1482            linePtr+=3;
1483            const char* oldLinePtr = linePtr;
1484            skipSpacesToEnd(linePtr, end);
1485            // space between lit and (
1486            if (linePtr!=end && *linePtr=='(')
1487            {
1488                encodeAsLiteral = true;
1489                linePtr++;
1490                skipSpacesToEnd(linePtr, end);
1491                negPlace = linePtr; // later treaten as next part of operand
1492            }
1493            else // back to expression start
1494                linePtr = oldLinePtr;
1495        }
1496       
1497        FloatLitType defaultFPType = (instrOpMask & INSTROP_TYPE_MASK)!=INSTROP_F16?
1498                    ((instrOpMask & INSTROP_TYPE_MASK)!=INSTROP_V64BIT?
1499                        FLTT_F32:FLTT_F64):FLTT_F16;
1500        if (!forceExpression && isOnlyFloat(negPlace, end, defaultFPType, fpType))
1501        {
1502            // if only floating point value
1503            /* if floating point literal can be processed */
1504            linePtr = negPlace;
1505            try
1506            {
1507                if (fpType==FLTT_F16)
1508                {
1509                    // parse FP16
1510                    value = cstrtohCStyle(linePtr, end, linePtr);
1511                    // skip suffix if needed
1512                    if (linePtr!=end && toLower(*linePtr)=='h')
1513                        linePtr++;
1514                   
1515                    if (!encodeAsLiteral)
1516                    {
1517                        if (asmr.buggyFPLit && value == 0)
1518                        {
1519                            // old buggy behaviour (to 0.1.2 version)
1520                            operand.range = { 128, 0 };
1521                            return true;
1522                        }
1523                    }
1524                }
1525                else if (fpType==FLTT_F32) /* otherwise, FLOAT */
1526                {
1527                    union FloatUnion { uint32_t i; float f; };
1528                    FloatUnion v;
1529                    v.f = cstrtovCStyle<float>(linePtr, end, linePtr);
1530                    // skip suffix if needed
1531                    if (linePtr!=end && toLower(*linePtr)=='s')
1532                        linePtr++;
1533                    value = v.i;
1534                    /// simplify to float constant immediate (-0.5, 0.5, 1.0, 2.0,...)
1535                    /// constant immediates converted only to single floating points
1536                    if (!encodeAsLiteral && asmr.buggyFPLit)
1537                        switch (value)
1538                        {
1539                            case 0x0:
1540                                operand.range = { 128, 0 };
1541                                return true;
1542                            case 0x3f000000: // 0.5
1543                                operand.range = { 240, 0 };
1544                                return true;
1545                            case 0xbf000000: // -0.5
1546                                operand.range = { 241, 0 };
1547                                return true;
1548                            case 0x3f800000: // 1.0
1549                                operand.range = { 242, 0 };
1550                                return true;
1551                            case 0xbf800000: // -1.0
1552                                operand.range = { 243, 0 };
1553                                return true;
1554                            case 0x40000000: // 2.0
1555                                operand.range = { 244, 0 };
1556                                return true;
1557                            case 0xc0000000: // -2.0
1558                                operand.range = { 245, 0 };
1559                                return true;
1560                            case 0x40800000: // 4.0
1561                                operand.range = { 246, 0 };
1562                                return true;
1563                            case 0xc0800000: // -4.0
1564                                operand.range = { 247, 0 };
1565                                return true;
1566                            case 0x3e22f983: // 1/(2*PI)
1567                                if (isGCN12)
1568                                {
1569                                    operand.range = { 248, 0 };
1570                                    return true;
1571                                }
1572                        }
1573                }
1574                else
1575                {
1576                    uint32_t v = cstrtofXCStyle(linePtr, end, linePtr, 11, 20);
1577                    // skip suffix if needed
1578                    if (linePtr!=end && toLower(*linePtr)=='l')
1579                        linePtr++;
1580                    value = v;
1581                }
1582               
1583                /// simplify to float constant immediate (-0.5, 0.5, 1.0, 2.0,...)
1584                /// constant immediates converted only to single floating points
1585                /// new behaviour
1586                if (!asmr.buggyFPLit && !encodeAsLiteral && fpType==defaultFPType)
1587                {
1588                    if (defaultFPType==FLTT_F16)
1589                        // simplify FP16 to constant immediate
1590                        switch (value)
1591                        {
1592                            case 0x0:
1593                                operand.range = { 128, 0 };
1594                                return true;
1595                            case 0x3800: // 0.5
1596                                operand.range = { 240, 0 };
1597                                return true;
1598                            case 0xb800: // -0.5
1599                                operand.range = { 241, 0 };
1600                                return true;
1601                            case 0x3c00: // 1.0
1602                                operand.range = { 242, 0 };
1603                                return true;
1604                            case 0xbc00: // -1.0
1605                                operand.range = { 243, 0 };
1606                                return true;
1607                            case 0x4000: // 2.0
1608                                operand.range = { 244, 0 };
1609                                return true;
1610                            case 0xc000: // -2.0
1611                                operand.range = { 245, 0 };
1612                                return true;
1613                            case 0x4400: // 4.0
1614                                operand.range = { 246, 0 };
1615                                return true;
1616                            case 0xc400: // -4.0
1617                                operand.range = { 247, 0 };
1618                                return true;
1619                            case 0x3118: // 1/(2*PI)
1620                                if (isGCN12)
1621                                {
1622                                    operand.range = { 248, 0 };
1623                                    return true;
1624                                }
1625                        }
1626                    else if (defaultFPType==FLTT_F32)
1627                        // simplify FP32 to constant immediate
1628                        switch (value)
1629                        {
1630                            case 0x0:
1631                                operand.range = { 128, 0 };
1632                                return true;
1633                            case 0x3f000000: // 0.5
1634                                operand.range = { 240, 0 };
1635                                return true;
1636                            case 0xbf000000: // -0.5
1637                                operand.range = { 241, 0 };
1638                                return true;
1639                            case 0x3f800000: // 1.0
1640                                operand.range = { 242, 0 };
1641                                return true;
1642                            case 0xbf800000: // -1.0
1643                                operand.range = { 243, 0 };
1644                                return true;
1645                            case 0x40000000: // 2.0
1646                                operand.range = { 244, 0 };
1647                                return true;
1648                            case 0xc0000000: // -2.0
1649                                operand.range = { 245, 0 };
1650                                return true;
1651                            case 0x40800000: // 4.0
1652                                operand.range = { 246, 0 };
1653                                return true;
1654                            case 0xc0800000: // -4.0
1655                                operand.range = { 247, 0 };
1656                                return true;
1657                            case 0x3e22f983: // 1/(2*PI)
1658                                if (isGCN12)
1659                                {
1660                                    operand.range = { 248, 0 };
1661                                    return true;
1662                                }
1663                        }
1664                    else /* FP64 */
1665                        // simplify FP64 (only high part) to constant immediate
1666                        switch (value)
1667                        {
1668                            case 0x0:
1669                                operand.range = { 128, 0 };
1670                                return true;
1671                            case 0x3fe00000: // 0.5
1672                                operand.range = { 240, 0 };
1673                                return true;
1674                            case 0xbfe00000: // -0.5
1675                                operand.range = { 241, 0 };
1676                                return true;
1677                            case 0x3ff00000: // 1.0
1678                                operand.range = { 242, 0 };
1679                                return true;
1680                            case 0xbff00000: // -1.0
1681                                operand.range = { 243, 0 };
1682                                return true;
1683                            case 0x40000000: // 2.0
1684                                operand.range = { 244, 0 };
1685                                return true;
1686                            case 0xc0000000: // -2.0
1687                                operand.range = { 245, 0 };
1688                                return true;
1689                            case 0x40100000: // 4.0
1690                                operand.range = { 246, 0 };
1691                                return true;
1692                            case 0xc0100000: // -4.0
1693                                operand.range = { 247, 0 };
1694                                return true;
1695                            case 0x3fc45f30: // 1/(2*PI)
1696                                if (isGCN12)
1697                                {
1698                                    operand.range = { 248, 0 };
1699                                    return true;
1700                                }
1701                        }
1702                }
1703            }
1704            catch(const ParseException& ex)
1705            {
1706                asmr.printError(linePtr, ex.what());
1707                return false;
1708            }
1709        }
1710        else
1711        {
1712            if (!AsmExpression::fastExprEvaluate(asmr, linePtr, value))
1713            {
1714            // if expression
1715            std::unique_ptr<AsmExpression> expr(AsmExpression::parse(asmr, linePtr));
1716            if (expr==nullptr) // error
1717                return false;
1718            if (expr->isEmpty())
1719                ASM_FAIL_BY_ERROR(exprPlace, "Expected expression")
1720           
1721            bool tryLater = false;
1722            if (expr->getSymOccursNum()==0)
1723            {
1724                // resolved now
1725                cxuint sectionId; // for getting
1726                AsmTryStatus evalStatus = expr->tryEvaluate(asmr, value, sectionId,
1727                                        asmr.withSectionDiffs());
1728               
1729                if (evalStatus == AsmTryStatus::FAILED) // failed evaluation!
1730                    return false;
1731                else if (evalStatus == AsmTryStatus::TRY_LATER)
1732                {
1733                    if (outTargetExpr!=nullptr)
1734                        asmr.unevalExpressions.push_back(expr.get());
1735                    tryLater = true;
1736                }
1737                else if (sectionId != ASMSECT_ABS)
1738                    // if not absolute value
1739                    ASM_FAIL_BY_ERROR(exprPlace, "Expression must be absolute!")
1740            }
1741            else
1742                tryLater = true;
1743           
1744            if (tryLater)
1745            {
1746                // return output expression with symbols to resolve
1747                if ((instrOpMask & INSTROP_ONLYINLINECONSTS)!=0)
1748                {
1749                    // error
1750                    if ((instrOpMask & INSTROP_NOLITERALERROR)!=0)
1751                        asmr.printError(regNamePlace, "Literal in VOP3 is illegal");
1752                    else if ((instrOpMask & INSTROP_NOLITERALERRORMUBUF)!=0)
1753                        asmr.printError(regNamePlace, "Literal in MUBUF is illegal");
1754                    else
1755                        asmr.printError(regNamePlace,
1756                                "Only one literal can be used in instruction");
1757                    return false;
1758                }
1759                if (outTargetExpr!=nullptr)
1760                    *outTargetExpr = std::move(expr);
1761                operand.range = { 255, 0 };
1762                exprToResolve = true;
1763            }
1764            }
1765           
1766            if (!encodeAsLiteral && !exprToResolve)
1767            {
1768                // if literal can be a constant immediate
1769                if (value <= 64)
1770                {
1771                    operand.range = { 128+value, 0 };
1772                    return true;
1773                }
1774                else if (int64_t(value) >= -16 && int64_t(value) < 0)
1775                {
1776                    operand.range = { 192-value, 0 };
1777                    return true;
1778                }
1779            }
1780        }
1781        if (encodeAsLiteral)
1782        {
1783            /* finish lit function */
1784            skipSpacesToEnd(linePtr, end);
1785            if (linePtr==end || *linePtr!=')')
1786                ASM_FAIL_BY_ERROR(linePtr, "Expected ')' after expression at 'lit'")
1787            else // skip end of lit
1788                linePtr++;
1789        }
1790        if (exprToResolve) // finish if expression to resolve
1791            return true;
1792       
1793        if ((instrOpMask & INSTROP_ONLYINLINECONSTS)!=0)
1794        {
1795            // error
1796            if ((instrOpMask & INSTROP_NOLITERALERROR)!=0)
1797                asmr.printError(regNamePlace, "Literal in VOP3 is illegal");
1798            else if ((instrOpMask & INSTROP_NOLITERALERRORMUBUF)!=0)
1799                asmr.printError(regNamePlace, "Literal in MUBUF is illegal");
1800            else
1801                asmr.printError(regNamePlace,
1802                        "Only one literal can be used in instruction");
1803            return false;
1804        }
1805       
1806        // not in range
1807        asmr.printWarningForRange(32, value, asmr.getSourcePos(regNamePlace));
1808        operand = { { 255, 0 }, uint32_t(value), operand.vopMods };
1809        return true;
1810    }
1811   
1812    // check otherwise
1813    asmr.printError(linePtr, "Unknown operand");
1814    return false;
1815}
1816
1817// used while parsing op_sel or op_sel_hi, neg_lo, neg_hi
1818bool GCNAsmUtils::parseImmWithBoolArray(Assembler& asmr, const char*& linePtr,
1819            uint32_t& value, cxuint bits, cxbyte signess)
1820{
1821    const char* end = asmr.line + asmr.lineSize;
1822    skipSpacesToEnd(linePtr, end);
1823    if (linePtr == end || *linePtr != '[')
1824        return parseImm(asmr, linePtr, value, nullptr, bits, signess);
1825    // array of boolean values
1826    linePtr++;
1827    bool good = true;
1828    uint32_t inVal = 0;
1829    for (cxuint i = 0; i < bits; i++)
1830    {
1831        uint32_t v = 0;
1832        // parse boolean immediate (0 or 1)
1833        good &= parseImm(asmr, linePtr, v, nullptr, 1, WS_UNSIGNED);
1834        inVal |= v<<i;
1835        skipSpacesToEnd(linePtr, end);
1836        if (i+1 < bits)
1837        {
1838            // next bool will be, try parse ','
1839            if (linePtr==end || *linePtr!=',')
1840            {
1841                ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ',' before bit value")
1842                break;
1843            }
1844            else
1845                ++linePtr;
1846        }
1847        else
1848        {
1849            // end of array, try parse ']'
1850            if (linePtr == end || *linePtr!=']')
1851                ASM_NOTGOOD_BY_ERROR(linePtr, "Unterminated bit array")
1852            else
1853                ++linePtr;
1854        }
1855    }
1856    if (good)
1857        value = inVal;
1858    return good;
1859}
1860
1861bool GCNAsmUtils::parseSingleOMODCLAMP(Assembler& asmr, const char*& linePtr,
1862                    const char* modPlace, const char* mod, GPUArchMask arch,
1863                    cxbyte& mods, VOPOpModifiers& opMods, cxuint modOperands,
1864                    cxuint flags, bool& haveAbs, bool& haveNeg,
1865                    bool& alreadyModDefined, bool& good)
1866{
1867    const char* end = asmr.line+asmr.lineSize;
1868    const bool vop3p = (flags & PARSEVOP_VOP3P)!=0;
1869    if (!vop3p && ::strcmp(mod, "mul")==0)
1870    {
1871        // if 'mul:xx'
1872        skipSpacesToEnd(linePtr, end);
1873        if (linePtr!=end && *linePtr==':')
1874        {
1875            skipCharAndSpacesToEnd(linePtr, end);
1876            cxbyte count = cstrtobyte(linePtr, end);
1877            if (count==2)
1878            {
1879                alreadyModDefined = mods&3;
1880                mods = (mods&~3) | VOP3_MUL2;
1881            }
1882            else if (count==4)
1883            {
1884                alreadyModDefined = mods&3;
1885                mods = (mods&~3) | VOP3_MUL4;
1886            }
1887            else
1888                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP3 mul:X modifier")
1889        }
1890        else
1891            ASM_NOTGOOD_BY_ERROR(linePtr,
1892                        "Expected ':' before multiplier number")
1893    }
1894    else if (!vop3p && ::strcmp(mod, "div")==0)
1895    {
1896        // if 'div:2'
1897        skipSpacesToEnd(linePtr, end);
1898        if (linePtr!=end && *linePtr==':')
1899        {
1900            skipCharAndSpacesToEnd(linePtr, end);
1901            cxbyte count = cstrtobyte(linePtr, end);
1902            if (count==2)
1903            {
1904                alreadyModDefined = mods&3;
1905                mods = (mods&~3) | VOP3_DIV2;
1906            }
1907            else
1908                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP3 div:X modifier")
1909        }
1910        else
1911            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before divider number")
1912    }
1913    else if (!vop3p && ::strcmp(mod, "omod")==0)
1914    {
1915        // if omod (parametrization of div or mul)
1916        skipSpacesToEnd(linePtr, end);
1917        if (linePtr!=end && *linePtr==':')
1918        {
1919            linePtr++;
1920            cxbyte omod = 0;
1921            if (parseImm(asmr, linePtr, omod, nullptr, 2, WS_UNSIGNED))
1922                mods = (mods & ~3) | omod;
1923            else
1924                good = false;
1925        }
1926        else
1927            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before omod")
1928    }
1929    else if (::strcmp(mod, "clamp")==0) // clamp
1930    {
1931        bool clamp = false;
1932        good &= parseModEnable(asmr, linePtr, clamp, "clamp modifier");
1933        if (flags & PARSEVOP_WITHCLAMP)
1934            mods = (mods & ~VOP3_CLAMP) | (clamp ? VOP3_CLAMP : 0);
1935        else
1936            ASM_NOTGOOD_BY_ERROR(modPlace, "Modifier CLAMP in VOP3B is illegal")
1937    }
1938    else if (!vop3p && modOperands>1 && ::strcmp(mod, "abs")==0)
1939    {
1940        // abs modifiers for source operands (bit per operand in array)
1941        uint32_t absVal = 0;
1942        if (linePtr!=end && *linePtr==':')
1943        {
1944            linePtr++;
1945            if (parseImmWithBoolArray(asmr, linePtr, absVal, modOperands-1,
1946                        WS_UNSIGNED))
1947            {
1948                opMods.absMod = absVal;
1949                if (haveAbs)
1950                    asmr.printWarning(modPlace, "Abs is already defined");
1951                haveAbs = true;
1952            }
1953        }
1954        else
1955            good = false;
1956    }
1957    else if (modOperands>1 && (::strcmp(mod, "neg")==0 ||
1958            (vop3p && ::strcmp(mod, "neg_lo")==0)))
1959    {
1960        // neg or neg_lo modifiers for source operands
1961        // (bit per operand in array)
1962        uint32_t negVal = 0;
1963        if (linePtr!=end && *linePtr==':')
1964        {
1965            linePtr++;
1966            if (parseImmWithBoolArray(asmr, linePtr, negVal, modOperands-1,
1967                            WS_UNSIGNED))
1968            {
1969                opMods.negMod = (opMods.negMod&0xf0) | negVal;
1970                if (haveNeg)
1971                    asmr.printWarning(modPlace, "Neg is already defined");
1972                haveNeg = true;
1973            }
1974        }
1975        else
1976            good = false;
1977    }
1978    else // other modifier
1979        return false;
1980    return true;
1981}
1982
1983// sorted list of VOP SDWA DST_SEL names
1984static const std::pair<const char*, cxuint> vopSDWADSTSelNamesMap[] =
1985{
1986    { "b0", 0 },
1987    { "b1", 1 },
1988    { "b2", 2 },
1989    { "b3", 3 },
1990    { "byte0", 0 },
1991    { "byte1", 1 },
1992    { "byte2", 2 },
1993    { "byte3", 3 },
1994    { "byte_0", 0 },
1995    { "byte_1", 1 },
1996    { "byte_2", 2 },
1997    { "byte_3", 3 },
1998    { "dword", 6 },
1999    { "w0", 4 },
2000    { "w1", 5 },
2001    { "word0", 4 },
2002    { "word1", 5 },
2003    { "word_0", 4 },
2004    { "word_1", 5 }
2005};
2006
2007static const size_t vopSDWADSTSelNamesNum = sizeof(vopSDWADSTSelNamesMap)/
2008            sizeof(std::pair<const char*, cxuint>);
2009           
2010/* main routine to parse VOP modifiers: basic modifiers stored in mods parameter,
2011 * modifier specific for VOP_SDWA and VOP_DPP stored in extraMods structure
2012 * withSDWAOperands - specify number of operand for that modifier will be parsed */
2013bool GCNAsmUtils::parseVOPModifiers(Assembler& asmr, const char*& linePtr,
2014                GPUArchMask arch, cxbyte& mods, VOPOpModifiers& opMods, cxuint modOperands,
2015                VOPExtraModifiers* extraMods, cxuint flags, cxuint withSDWAOperands)
2016{
2017    const char* end = asmr.line+asmr.lineSize;
2018    //bool haveSDWAMod = false, haveDPPMod = false;
2019    bool haveDstSel = false, haveSrc0Sel = false, haveSrc1Sel = false;
2020    bool haveDstUnused = false;
2021    bool haveBankMask = false, haveRowMask = false;
2022    bool haveBoundCtrl = false, haveDppCtrl = false;
2023    bool haveNeg = false, haveAbs = false;
2024    bool haveSext = false, haveOpsel = false;
2025    bool haveNegHi = false, haveOpselHi = false;
2026    bool haveFi = false, haveDPP8 = false;
2027    bool haveDPP = false, haveSDWA = false;
2028   
2029    // set default VOP extra modifiers (SDWA/DPP)
2030    if (extraMods!=nullptr)
2031        *extraMods = { 6, 0, cxbyte((withSDWAOperands>=2)?6:0),
2032                    cxbyte((withSDWAOperands>=3)?6:0),
2033                    15, 15, 0xe4 /* TODO: why not 0xe4? */, false, false, false };
2034   
2035    skipSpacesToEnd(linePtr, end);
2036    const char* modsPlace = linePtr;
2037    bool good = true;
2038    mods = 0;
2039    const bool vop3p = (flags & PARSEVOP_VOP3P)!=0;
2040   
2041    const bool isGCN15 = (arch & ARCH_GCN_1_5)!=0;
2042   
2043    // main loop
2044    while (linePtr != end)
2045    {
2046        skipSpacesToEnd(linePtr, end);
2047        if (linePtr == end)
2048            break;
2049        char mod[20];
2050        const char* modPlace = linePtr;
2051        if (getNameArgS(asmr, 20, mod, linePtr, "VOP modifier"))
2052        {
2053            toLowerString(mod);
2054            try
2055            {
2056                // check what is VOP modifier
2057                bool alreadyModDefined = false;
2058                if (parseSingleOMODCLAMP(asmr, linePtr, modPlace, mod, arch, mods,
2059                        opMods, modOperands, flags, haveAbs, haveNeg,
2060                        alreadyModDefined, good))
2061                {   // do nothing
2062                }
2063                else if (modOperands>1 && vop3p && ::strcmp(mod, "neg_hi")==0)
2064                {
2065                    // neg_hi modifiers for source operands (bit per operand in array)
2066                    uint32_t negVal = 0;
2067                    if (linePtr!=end && *linePtr==':')
2068                    {
2069                        linePtr++;
2070                        if (parseImmWithBoolArray(asmr, linePtr, negVal, modOperands-1,
2071                                        WS_UNSIGNED))
2072                        {
2073                            opMods.negMod = (opMods.negMod&15) | (negVal<<4);
2074                            if (haveNegHi)
2075                                asmr.printWarning(modPlace, "Neg_hi is already defined");
2076                            haveNegHi = true;
2077                        }
2078                    }
2079                    else
2080                        good = false;
2081                }
2082                else if (!vop3p &&(arch & ARCH_GCN_1_2_4_5) &&
2083                        (flags & PARSEVOP_WITHSEXT)!=0 &&
2084                         modOperands>1 && ::strcmp(mod, "sext")==0)
2085                {
2086                    // neg_hi modifiers for source operands (bit per operand in array)
2087                    uint32_t sextVal = 0;
2088                    if (linePtr!=end && *linePtr==':')
2089                    {
2090                        linePtr++;
2091                        if (parseImmWithBoolArray(asmr, linePtr, sextVal, modOperands-1,
2092                                    WS_UNSIGNED))
2093                        {
2094                            opMods.sextMod = sextVal;
2095                            if (haveSext)
2096                                asmr.printWarning(modPlace, "Sext is already defined");
2097                            haveSext = true;
2098                        }
2099                    }
2100                    else
2101                        good = false;
2102                }
2103                else if ((flags & PARSEVOP_WITHOPSEL) != 0 && modOperands>1 &&
2104                            ::strcmp(mod, "op_sel")==0)
2105                {
2106                    // op_sel (bit per operand in array)
2107                    uint32_t opselVal = 0;
2108                    if (linePtr!=end && *linePtr==':')
2109                    {
2110                        linePtr++;
2111                        // for VOP3P encoding is one less bit in array
2112                        if (parseImmWithBoolArray(asmr, linePtr, opselVal,
2113                                (vop3p ? modOperands-1 : modOperands),
2114                                    WS_UNSIGNED))
2115                        {
2116                            if (!vop3p && modOperands<4)
2117                                /* move last bit to dest (fourth bit)
2118                                 * if less than 4 operands */
2119                                opselVal = (opselVal & ((1U<<(modOperands-1))-1)) |
2120                                        ((opselVal & (1U<<(modOperands-1))) ? 8 : 0);
2121                            opMods.opselMod = (opMods.opselMod&0xf0) | opselVal;
2122                            if (haveOpsel)
2123                                asmr.printWarning(modPlace, "Opsel is already defined");
2124                            haveOpsel = true;
2125                        }
2126                    }
2127                    else
2128                        good = false;
2129                }
2130                else if (vop3p && (flags & PARSEVOP_WITHOPSEL) != 0 && modOperands>1 &&
2131                            ::strcmp(mod, "op_sel_hi")==0)
2132                {
2133                    // op_sel_hi (bit per operand in array)
2134                    uint32_t opselVal = 0;
2135                    if (linePtr!=end && *linePtr==':')
2136                    {
2137                        linePtr++;
2138                        // for VOP3P encoding is one less bit in array
2139                        if (parseImmWithBoolArray(asmr, linePtr, opselVal, modOperands-1,
2140                                    WS_UNSIGNED))
2141                        {
2142                            opMods.opselMod = (opMods.opselMod&15) | (opselVal<<4);
2143                            if (haveOpselHi)
2144                                asmr.printWarning(modPlace, "Opsel_hi is already defined");
2145                            haveOpselHi = true;
2146                        }
2147                    }
2148                    else
2149                        good = false;
2150                }
2151                else if (::strcmp(mod, "vop3")==0)
2152                {
2153                    bool vop3 = false;
2154                    good &= parseModEnable(asmr, linePtr, vop3, "vop3 modifier");
2155                    mods = (mods & ~VOP3_VOP3) | (vop3 ? VOP3_VOP3 : 0);
2156                }
2157                else if (extraMods!=nullptr)
2158                {
2159                    /* parse specific modofier from VOP_SDWA or VOP_DPP encoding */
2160                    if (withSDWAOperands>=1 && (flags&PARSEVOP_NODSTMODS)==0 &&
2161                            ::strcmp(mod, "dst_sel")==0)
2162                    {
2163                        // dstsel
2164                        skipSpacesToEnd(linePtr, end);
2165                        if (linePtr!=end && *linePtr==':')
2166                        {
2167                            linePtr++;
2168                            cxuint dstSel = 0;
2169                            if (linePtr == end || *linePtr!='@')
2170                            {
2171                                // parse name of dst_sel
2172                                if (getEnumeration(asmr, linePtr, "dst_sel",
2173                                            vopSDWADSTSelNamesNum,
2174                                            vopSDWADSTSelNamesMap, dstSel))
2175                                {
2176                                    extraMods->dstSel = dstSel;
2177                                    if (haveDstSel)
2178                                        asmr.printWarning(modPlace,
2179                                                "Dst_sel is already defined");
2180                                    haveDstSel = true;
2181                                }
2182                                else
2183                                    good = false;
2184                            }
2185                            else
2186                            {
2187                                /* parametrize (if in form '@value') */
2188                                linePtr++;
2189                                if (parseImm(asmr, linePtr, dstSel, nullptr,
2190                                                 3, WS_UNSIGNED))
2191                                {
2192                                    extraMods->dstSel = dstSel;
2193                                    if (haveDstSel)
2194                                        asmr.printWarning(modPlace,
2195                                                "Dst_sel is already defined");
2196                                    haveDstSel = true;
2197                                }
2198                                else
2199                                    good = false;
2200                            }
2201                        }
2202                        else
2203                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before dst_sel")
2204                    }
2205                    else if (withSDWAOperands>=1 && (flags&PARSEVOP_NODSTMODS)==0 &&
2206                        (::strcmp(mod, "dst_unused")==0 || ::strcmp(mod, "dst_un")==0))
2207                    {
2208                        /* dst_unused modifer */
2209                        skipSpacesToEnd(linePtr, end);
2210                        if (linePtr!=end && *linePtr==':')
2211                        {
2212                            skipCharAndSpacesToEnd(linePtr, end);
2213                            cxbyte unused = 0;
2214                            if (linePtr == end || *linePtr!='@')
2215                            {
2216                                // parse name of dst_unused
2217                                char name[20];
2218                                const char* enumPlace = linePtr;
2219                                if (getNameArg(asmr, 20, name, linePtr, "dst_unused"))
2220                                {
2221                                    toLowerString(name);
2222                                    size_t namePos =
2223                                            (::strncmp(name, "unused_", 7)==0) ?7 : 0;
2224                                    if (::strcmp(name+namePos, "sext")==0)
2225                                        unused = 1;
2226                                    else if (::strcmp(name+namePos, "preserve")==0)
2227                                        unused = 2;
2228                                    else if (::strcmp(name+namePos, "pad")!=0)
2229                                        ASM_NOTGOOD_BY_ERROR(enumPlace,
2230                                                    "Unknown dst_unused")
2231                                    extraMods->dstUnused = unused;
2232                                    if (haveDstUnused)
2233                                        asmr.printWarning(modPlace,
2234                                                        "Dst_unused is already defined");
2235                                    haveDstUnused = true;
2236                                }
2237                                else
2238                                    good = false;
2239                            }
2240                            else
2241                            {
2242                                /* '@' in dst_unused: parametrization */
2243                                linePtr++;
2244                                if (parseImm(asmr, linePtr, unused, nullptr,
2245                                                 2, WS_UNSIGNED))
2246                                {
2247                                    extraMods->dstUnused = unused;
2248                                    if (haveDstUnused)
2249                                        asmr.printWarning(modPlace,
2250                                                        "Dst_unused is already defined");
2251                                    haveDstUnused = true;
2252                                }
2253                                else
2254                                    good = false;
2255                            }
2256                        }
2257                        else
2258                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before dst_unused")
2259                    }
2260                    else if (withSDWAOperands>=2 && ::strcmp(mod, "src0_sel")==0)
2261                    {
2262                        /* src0_sel modifier */
2263                        skipSpacesToEnd(linePtr, end);
2264                        if (linePtr!=end && *linePtr==':')
2265                        {
2266                            linePtr++;
2267                            cxuint src0Sel = 0;
2268                            if (linePtr == end || *linePtr!='@')
2269                            {
2270                                // parse source selection (name)
2271                                if (getEnumeration(asmr, linePtr, "src0_sel",
2272                                            vopSDWADSTSelNamesNum,
2273                                            vopSDWADSTSelNamesMap, src0Sel))
2274                                {
2275                                    extraMods->src0Sel = src0Sel;
2276                                    if (haveSrc0Sel)
2277                                        asmr.printWarning(modPlace,
2278                                                        "Src0_sel is already defined");
2279                                    haveSrc0Sel = true;
2280                                }
2281                                else
2282                                    good = false;
2283                            }
2284                            else
2285                            {
2286                                /* parametrize (if in form '@value') */
2287                                linePtr++;
2288                                if (parseImm(asmr, linePtr, src0Sel, nullptr,
2289                                                 3, WS_UNSIGNED))
2290                                {
2291                                    extraMods->src0Sel = src0Sel;
2292                                    if (haveSrc0Sel)
2293                                        asmr.printWarning(modPlace,
2294                                                        "Src0_sel is already defined");
2295                                    haveSrc0Sel = true;
2296                                }
2297                                else
2298                                    good = false;
2299                            }
2300                        }
2301                        else
2302                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before src0_sel")
2303                    }
2304                    else if (withSDWAOperands>=3 && ::strcmp(mod, "src1_sel")==0)
2305                    {
2306                        // src1_sel modifier
2307                        skipSpacesToEnd(linePtr, end);
2308                        if (linePtr!=end && *linePtr==':')
2309                        {
2310                            linePtr++;
2311                            cxuint src1Sel = 0;
2312                            if (linePtr == end || *linePtr!='@')
2313                            {
2314                                // parse source selection (name)
2315                                if (getEnumeration(asmr, linePtr, "src1_sel",
2316                                            vopSDWADSTSelNamesNum,
2317                                            vopSDWADSTSelNamesMap, src1Sel))
2318                                {
2319                                    extraMods->src1Sel = src1Sel;
2320                                    if (haveSrc1Sel)
2321                                        asmr.printWarning(modPlace,
2322                                                        "Src1_sel is already defined");
2323                                    haveSrc1Sel = true;
2324                                }
2325                                else
2326                                    good = false;
2327                            }
2328                            else
2329                            {
2330                                /* parametrize by '@' */
2331                                linePtr++;
2332                                if (parseImm(asmr, linePtr, src1Sel, nullptr,
2333                                                 3, WS_UNSIGNED))
2334                                {
2335                                    extraMods->src1Sel = src1Sel;
2336                                    if (haveSrc1Sel)
2337                                        asmr.printWarning(modPlace,
2338                                                        "Src1_sel is already defined");
2339                                    haveSrc1Sel = true;
2340                                }
2341                                else
2342                                    good = false;
2343                            }
2344                        }
2345                        else
2346                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before src1_sel")
2347                    }
2348                    else if (::strcmp(mod, "quad_perm")==0)
2349                    {
2350                        skipSpacesToEnd(linePtr, end);
2351                        if (linePtr!=end && *linePtr==':')
2352                        {
2353                            // parse quad_perm array
2354                            bool goodMod = true;
2355                            skipCharAndSpacesToEnd(linePtr, end);
2356                            if (linePtr==end || *linePtr!='[')
2357                            {
2358                                ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2359                                        "Expected '[' before quad_perm list")
2360                                continue;
2361                            }
2362                            cxbyte quadPerm = 0;
2363                            linePtr++;
2364                            // parse four 2-bit values
2365                            for (cxuint k = 0; k < 4; k++)
2366                            {
2367                                skipSpacesToEnd(linePtr, end);
2368                                cxbyte qpv = 0;
2369                                good &= parseImm(asmr, linePtr, qpv, nullptr,
2370                                        2, WS_UNSIGNED);
2371                                quadPerm |= qpv<<(k<<1);
2372                                skipSpacesToEnd(linePtr, end);
2373                                if (k!=3)
2374                                {
2375                                    // skip ',' before next value
2376                                    if (linePtr==end || *linePtr!=',')
2377                                    {
2378                                        ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2379                                            "Expected ',' before quad_perm component")
2380                                        break;
2381                                    }
2382                                    else
2383                                        ++linePtr;
2384                                }
2385                                else if (linePtr==end || *linePtr!=']')
2386                                {
2387                                    // unterminated quad_perm
2388                                    asmr.printError(linePtr, "Unterminated quad_perm");
2389                                    goodMod = good = false;
2390                                }
2391                                else
2392                                    ++linePtr;
2393                            }
2394                            if (goodMod)
2395                            {
2396                                // set up quad perm
2397                                extraMods->dppCtrl = quadPerm;
2398                                if (haveDppCtrl)
2399                                    asmr.printWarning(modPlace,
2400                                              "DppCtrl is already defined");
2401                                haveDppCtrl = true;
2402                            }
2403                        }
2404                        else
2405                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before quad_perm")
2406                    }
2407                    else if (::strcmp(mod, "bank_mask")==0)
2408                    {
2409                        // parse bank_mask with 4-bit value
2410                        skipSpacesToEnd(linePtr, end);
2411                        if (linePtr!=end && *linePtr==':')
2412                        {
2413                            linePtr++;
2414                            cxbyte bankMask = 0;
2415                            if (parseImm(asmr, linePtr, bankMask, nullptr, 4, WS_UNSIGNED))
2416                            {
2417                                extraMods->bankMask = bankMask;
2418                                if (haveBankMask)
2419                                    asmr.printWarning(modPlace,
2420                                              "Bank_mask is already defined");
2421                                haveBankMask = true;
2422                            }
2423                            else
2424                                good = false;
2425                        }
2426                        else
2427                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before bank_mask")
2428                    }
2429                    else if (::strcmp(mod, "row_mask")==0)
2430                    {
2431                        // parse row_mask with 4-bit value
2432                        skipSpacesToEnd(linePtr, end);
2433                        if (linePtr!=end && *linePtr==':')
2434                        {
2435                            linePtr++;
2436                            cxbyte rowMask = 0;
2437                            if (parseImm(asmr, linePtr, rowMask, nullptr, 4, WS_UNSIGNED))
2438                            {
2439                                extraMods->rowMask = rowMask;
2440                                if (haveRowMask)
2441                                    asmr.printWarning(modPlace,
2442                                              "Row_mask is already defined");
2443                                haveRowMask = true;
2444                            }
2445                            else
2446                                good = false;
2447                        }
2448                        else
2449                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before row_mask")
2450                    }
2451                    else if (::strcmp(mod, "bound_ctrl")==0)
2452                    {
2453                        bool modGood = true;
2454                        skipSpacesToEnd(linePtr, end);
2455                        if (linePtr!=end && *linePtr==':')
2456                        {
2457                            skipCharAndSpacesToEnd(linePtr, end);
2458                            if (linePtr!=end && (*linePtr=='0' || *linePtr=='1'))
2459                            {
2460                                // accept '0' and '1' as enabled (old and new syntax)
2461                                bool boundCtrl = false;
2462                                linePtr++;
2463                                good &= parseModEnable(asmr, linePtr, boundCtrl,
2464                                        "bound_ctrl modifier");
2465                                mods = (mods & ~VOP3_BOUNDCTRL) |
2466                                        (boundCtrl ? VOP3_BOUNDCTRL : 0);
2467                            }
2468                            else
2469                            {
2470                                asmr.printError(linePtr, "Value must be '0' or '1'");
2471                                modGood = good = false;
2472                            }
2473                        }
2474                        else // just enable boundctrl
2475                            mods |= VOP3_BOUNDCTRL;
2476                        if (modGood)
2477                        {
2478                            // bound_ctrl is defined
2479                            if (haveBoundCtrl)
2480                                asmr.printWarning(modPlace, "BoundCtrl is already defined");
2481                            haveBoundCtrl = true;
2482                            extraMods->needDPP = true;
2483                        }
2484                    }
2485                    else if (mod[0]=='r' && mod[1]=='o' && mod[2]=='w' && mod[3]=='_' &&
2486                            (::strcmp(mod+4, "shl")==0 || ::strcmp(mod+4, "shr")==0 ||
2487                                ::strcmp(mod+4, "ror")==0))
2488                    {
2489                        // row_XXX (shl, shr, ror) modifier (shift is in 1-15)
2490                        skipSpacesToEnd(linePtr, end);
2491                        if (linePtr!=end && *linePtr==':')
2492                        {
2493                            skipCharAndSpacesToEnd(linePtr, end);
2494                            const char* shiftPlace = linePtr;
2495                            cxbyte shift = 0;
2496                            if (parseImm(asmr, linePtr, shift , nullptr, 4, WS_UNSIGNED))
2497                            {
2498                                if (shift == 0)
2499                                {
2500                                    ASM_NOTGOOD_BY_ERROR(shiftPlace,
2501                                            "Illegal zero shift for row_XXX shift")
2502                                    continue;
2503                                }
2504                                if (haveDppCtrl)
2505                                    asmr.printWarning(modPlace,
2506                                              "DppCtrl is already defined");
2507                                haveDppCtrl = true;
2508                                /* retrieve dppCtrl code from mod name:
2509                                 * shl - 0, shr - 0x10, ror - 0x20 */
2510                                extraMods->dppCtrl = 0x100U | ((mod[4]=='r') ? 0x20 :
2511                                    (mod[4]=='s' && mod[6]=='r') ? 0x10 : 0) | shift;
2512                            }
2513                            else
2514                                good = false;
2515                        }
2516                        else
2517                            ASM_NOTGOOD_BY_ERROR(linePtr, (std::string(
2518                                        "Expected ':' before ")+mod).c_str())
2519                    }
2520                    else if (!isGCN15 && memcmp(mod, "wave_", 5)==0 &&
2521                        (::strcmp(mod+5, "shl")==0 || ::strcmp(mod+5, "shr")==0 ||
2522                            ::strcmp(mod+5, "rol")==0 || ::strcmp(mod+5, "ror")==0))
2523                    {
2524                        // wave_XXX (shl,shr,rol,ror)
2525                        bool modGood = true;
2526                        skipSpacesToEnd(linePtr, end);
2527                        if (linePtr!=end && *linePtr==':')
2528                        {
2529                            // accept only 1 at parameter
2530                            skipCharAndSpacesToEnd(linePtr, end);
2531                            if (linePtr!=end && *linePtr=='1')
2532                                ++linePtr;
2533                            else
2534                                ASM_NOTGOOD_BY_ERROR1(modGood = good, linePtr,
2535                                            "Value must be '1'")
2536                        }
2537                        if (mod[5]=='s')
2538                            extraMods->dppCtrl = 0x100 | ((mod[7]=='l') ? 0x30 : 0x38);
2539                        else if (mod[5]=='r')
2540                            extraMods->dppCtrl = 0x100 | ((mod[7]=='l') ? 0x34 : 0x3c);
2541                        if (modGood)
2542                        {
2543                            // dpp_ctrl is defined
2544                            if (haveDppCtrl)
2545                                asmr.printWarning(modPlace, "DppCtrl is already defined");
2546                            haveDppCtrl = true;
2547                        }
2548                    }
2549                    else if (::strcmp(mod, "row_mirror")==0 ||
2550                        ::strcmp(mod, "row_half_mirror")==0 ||
2551                        ::strcmp(mod, "row_hmirror")==0)
2552                    {
2553                        // row_mirror, row_half_mirror
2554                        extraMods->dppCtrl = (mod[4]=='h') ? 0x141 : 0x140;
2555                        if (haveDppCtrl)
2556                            asmr.printWarning(modPlace, "DppCtrl is already defined");
2557                        haveDppCtrl = true;
2558                    }
2559                    else if (!isGCN15 && ::strncmp(mod, "row_bcast", 9)==0 && (
2560                        (mod[9]=='1' && mod[10]=='5' && mod[11]==0) ||
2561                        (mod[9]=='3' && mod[10]=='1' && mod[11]==0) || mod[9]==0))
2562                    {
2563                        // row_bcast15 and row_bast31 modifier
2564                        bool modGood = true;
2565                        if (mod[9] =='1') // if row_bcast15
2566                            extraMods->dppCtrl = 0x142;
2567                        else if (mod[9] =='3') // if row_bcast31
2568                            extraMods->dppCtrl = 0x143;
2569                        else
2570                        {
2571                            // get number
2572                            skipSpacesToEnd(linePtr, end);
2573                            if (linePtr!=end && *linePtr==':')
2574                            {
2575                                skipCharAndSpacesToEnd(linePtr, end);
2576                                const char* numPlace = linePtr;
2577                                cxbyte value = cstrtobyte(linePtr, end);
2578                                // parse row_bcast:15 or row_bcast:31
2579                                if (value == 31)
2580                                    extraMods->dppCtrl = 0x143;
2581                                else if (value == 15)
2582                                    extraMods->dppCtrl = 0x142;
2583                                else
2584                                    ASM_NOTGOOD_BY_ERROR1(modGood = good, numPlace,
2585                                            "Thread to broadcast must be 15 or 31")
2586                            }
2587                            else
2588                                ASM_NOTGOOD_BY_ERROR1(modGood = good, linePtr,
2589                                            "Expected ':' before row_bcast")
2590                        }
2591                        if (modGood)
2592                        {
2593                            if (haveDppCtrl)
2594                                asmr.printWarning(modPlace, "DppCtrl is already defined");
2595                            haveDppCtrl = true;
2596                        }
2597                    }
2598                    else if (isGCN15 && (::strcmp(mod, "row_share")==0 || ::strcmp(mod, "row_xmask")==0))
2599                    {
2600                        // row_XXX (shl, shr, ror) modifier (shift is in 1-15)
2601                        skipSpacesToEnd(linePtr, end);
2602                        if (linePtr!=end && *linePtr==':')
2603                        {
2604                            skipCharAndSpacesToEnd(linePtr, end);
2605                            cxbyte shift = 0;
2606                            if (parseImm(asmr, linePtr, shift , nullptr, 4, WS_UNSIGNED))
2607                            {
2608                                if (haveDppCtrl)
2609                                    asmr.printWarning(modPlace,
2610                                              "DppCtrl is already defined");
2611                                haveDppCtrl = true;
2612                                extraMods->dppCtrl = (0x150U +
2613                                            (mod[4]=='x' ? 0x10 : 0)) | shift;
2614                            }
2615                            else
2616                                good = false;
2617                        }
2618                        else
2619                            ASM_NOTGOOD_BY_ERROR(linePtr, (std::string(
2620                                        "Expected ':' before ")+mod).c_str())
2621                    }
2622                    else if (isGCN15 && ::strcmp(mod, "fi")==0)
2623                    {
2624                        bool fi = false;
2625                        good &= parseModEnable(asmr, linePtr, fi, "vop3 modifier");
2626                        extraMods->fi = fi;
2627                    }
2628                    else if (isGCN15 && ::strcmp(mod, "dpp8")==0)
2629                    {
2630                        skipSpacesToEnd(linePtr, end);
2631                        if (linePtr!=end && *linePtr==':')
2632                        {
2633                            // parse dpp8 array
2634                            bool goodMod = true;
2635                            skipCharAndSpacesToEnd(linePtr, end);
2636                            if (linePtr==end || *linePtr!='[')
2637                            {
2638                                ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2639                                        "Expected '[' before dpp8 list")
2640                                continue;
2641                            }
2642                            uint32_t dpp8 = 0;
2643                            linePtr++;
2644                            // parse four 3-bit values
2645                            for (cxuint k = 0; k < 8; k++)
2646                            {
2647                                skipSpacesToEnd(linePtr, end);
2648                                cxbyte qpv = 0;
2649                                good &= parseImm(asmr, linePtr, qpv, nullptr,
2650                                        3, WS_UNSIGNED);
2651                                dpp8 |= qpv<<(k*3);
2652                                skipSpacesToEnd(linePtr, end);
2653                                if (k!=7)
2654                                {
2655                                    // skip ',' before next value
2656                                    if (linePtr==end || *linePtr!=',')
2657                                    {
2658                                        ASM_NOTGOOD_BY_ERROR1(goodMod = good, linePtr,
2659                                            "Expected ',' before dpp8 component")
2660                                        break;
2661                                    }
2662                                    else
2663                                        ++linePtr;
2664                                }
2665                                else if (linePtr==end || *linePtr!=']')
2666                                {
2667                                    // unterminated dpp8
2668                                    asmr.printError(linePtr, "Unterminated dpp8");
2669                                    goodMod = good = false;
2670                                }
2671                                else
2672                                    ++linePtr;
2673                            }
2674                            if (goodMod)
2675                            {
2676                                // set up dpp8
2677                                extraMods->dpp8Value = dpp8;
2678                                if (haveDPP8)
2679                                    asmr.printWarning(modPlace,
2680                                              "DPP8 is already defined");
2681                                haveDPP8 = true;
2682                            }
2683                        }
2684                        else
2685                            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before quad_perm")
2686                    }
2687                    else if (::strcmp(mod, "sdwa")==0)
2688                    {
2689                        // SDWA - force SDWA encoding
2690                        bool sdwa = false;
2691                        good &= parseModEnable(asmr, linePtr, sdwa, "vop3 modifier");
2692                        haveSDWA = sdwa;
2693                    }
2694                    else if (::strcmp(mod, "dpp")==0)
2695                    {
2696                        // DPP - force DPP encoding
2697                        bool dpp = false;
2698                        good &= parseModEnable(asmr, linePtr, dpp, "vop3 modifier");
2699                        haveDPP = dpp;
2700                    }
2701                    else    /// unknown modifier
2702                        ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP modifier")
2703                }
2704                else /// unknown modifier
2705                    ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VOP modifier")
2706               
2707                if (alreadyModDefined)
2708                    asmr.printWarning(modPlace, "OMOD is already defined");
2709            }
2710            catch(const ParseException& ex)
2711            {
2712                asmr.printError(linePtr, ex.what());
2713                good = false;
2714            }
2715        }
2716        else
2717            good = false;
2718    }
2719   
2720    // determine what encoding will be needed to encode instruction
2721    const bool vopSDWA = (haveDstSel || haveDstUnused || haveSrc0Sel || haveSrc1Sel ||
2722        opMods.sextMod!=0 || haveSDWA);
2723    const bool vopDPP = (haveDppCtrl || haveBoundCtrl || haveBankMask || haveRowMask ||
2724            haveDPP || (haveFi && !haveDPP8));
2725    const bool vopDPP8 = (haveFi || haveDPP8);
2726    const bool isGCN14 = (arch & ARCH_GCN_1_4_5) != 0;
2727    // mul/div modifier does not apply to vop3 if RXVEGA (this case will be checked later)
2728    const bool vop3 = (mods & ((isGCN14 ? 0 : 3)|VOP3_VOP3))!=0 ||
2729                ((opMods.opselMod&15)!=0);
2730    if (extraMods!=nullptr)
2731    {
2732        extraMods->needSDWA = vopSDWA;
2733        extraMods->needDPP = vopDPP;
2734        extraMods->needDPP8 = vopDPP8;
2735    }
2736    if ((int(vop3)+vopSDWA+vopDPP+vopDPP8)>1 ||
2737                // RXVEGA: mul/div modifier are accepted in VOP_SDWA but not for VOP_DPP
2738                (isGCN14 && (mods & 3)!=0 && (vopDPP||vopDPP8)) ||
2739                ((mods&VOP3_CLAMP)!=0 && (vopDPP||vopDPP8)))
2740        ASM_FAIL_BY_ERROR(modsPlace, "Mixing modifiers from different encodings is illegal")
2741    return good;
2742}
2743
2744static const char* vintrpParamsTbl[] =
2745{ "p10", "p20", "p0" };
2746
2747// parse interpolation (P0,P10,P20) parameter for VINTRP instructions
2748bool GCNAsmUtils::parseVINTRP0P10P20(Assembler& asmr, const char*& linePtr, RegRange& reg)
2749{
2750    const char* end = asmr.line+asmr.lineSize;
2751    skipSpacesToEnd(linePtr, end);
2752    const char* p0Place = linePtr;
2753    char pxName[5];
2754    if (getNameArg(asmr, 5, pxName, linePtr, "VINTRP parameter"))
2755    {
2756        cxuint p0Code = 0;
2757        toLowerString(pxName);
2758        for (p0Code = 0; p0Code < 3; p0Code++)
2759            if (::strcmp(vintrpParamsTbl[p0Code], pxName)==0)
2760                break;
2761        if (p0Code < 3) // as srcReg
2762            reg = { p0Code, p0Code+1 };
2763        else
2764            ASM_FAIL_BY_ERROR(p0Place, "Unknown VINTRP parameter")
2765        return true;
2766    }
2767    return false;
2768}
2769
2770bool GCNAsmUtils::parseVINTRPAttr(Assembler& asmr, const char*& linePtr, cxbyte& attr)
2771{
2772    bool good = true;
2773    const char* end = asmr.line+asmr.lineSize;
2774    skipSpacesToEnd(linePtr, end);
2775    bool goodAttr = true;
2776    const char* attrPlace = linePtr;
2777    if (linePtr+4 > end)
2778        ASM_NOTGOOD_BY_ERROR1(goodAttr = good, attrPlace, "Expected 'attr' keyword")
2779    char buf[5];
2780    if (goodAttr)
2781    {
2782        std::transform(linePtr, linePtr+4, buf, toLower); // to lowercase
2783        // parse attr:
2784        if (::strncmp(buf, "attr", 4)!=0)
2785        {
2786            // skip spaces (to next element)
2787            while (linePtr!=end && *linePtr!=' ') linePtr++;
2788            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, attrPlace, "Expected 'attr' keyword")
2789        }
2790        else
2791            linePtr+=4;
2792    }
2793   
2794    cxbyte attrVal = 0;
2795    if (goodAttr)
2796    {
2797        // parse only attribute value if no error before
2798        const char* attrNumPlace = linePtr;
2799        try
2800        { attrVal = cstrtobyte(linePtr, end); }
2801        catch(const ParseException& ex)
2802        {
2803            asmr.printError(linePtr, ex.what());
2804            goodAttr = good = false;
2805        }
2806        if (attrVal >= 64)
2807            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, attrNumPlace,
2808                        "Attribute number out of range (0-63)")
2809    }
2810    if (goodAttr)
2811    {
2812        // parse again if no error before
2813        skipSpacesToEnd(linePtr, end);
2814        if (linePtr==end || *linePtr!='.')
2815            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, linePtr,
2816                            "Expected '.' after attribute number")
2817        else
2818            ++linePtr;
2819    }
2820    if (goodAttr)
2821    {
2822        // parse again if no error before
2823        skipSpacesToEnd(linePtr, end);
2824        if (linePtr==end)
2825            ASM_NOTGOOD_BY_ERROR1(goodAttr = good, linePtr, "Expected attribute component")
2826    }
2827    char attrCmpName = 0;
2828    if (goodAttr)
2829    {
2830        // parse only attribute component if no error before
2831        attrCmpName = toLower(*linePtr);
2832        if (attrCmpName!='x' && attrCmpName!='y' && attrCmpName!='z' && attrCmpName!='w')
2833            ASM_NOTGOOD_BY_ERROR(linePtr, "Expected attribute component")
2834        linePtr++;
2835    }
2836   
2837    attr = (attrVal<<2) | ((attrCmpName=='x') ? 0 : (attrCmpName=='y') ? 1 :
2838            (attrCmpName=='z') ? 2 : 3);
2839    return good;
2840}
2841
2842/* special version of getNameArg for MUBUF format name.
2843 * this version accepts digits at first character of format name */
2844bool GCNAsmUtils::getMUBUFFmtNameArg(Assembler& asmr, size_t maxOutStrSize, char* outStr,
2845               const char*& linePtr, const char* objName)
2846{
2847    const char* end = asmr.line + asmr.lineSize;
2848    skipSpacesToEnd(linePtr, end);
2849    if (linePtr == end)
2850        ASM_FAIL_BY_ERROR(linePtr, (std::string("Expected ")+objName).c_str())
2851    const char* nameStr = linePtr;
2852    if (isAlnum(*linePtr) || *linePtr == '_' || *linePtr == '.')
2853    {
2854        linePtr++;
2855        while (linePtr != end && (isAlnum(*linePtr) ||
2856            *linePtr == '_' || *linePtr == '.')) linePtr++;
2857    }
2858    else
2859    {
2860        asmr.printError(linePtr, (std::string("Some garbages at ")+objName+
2861                " place").c_str());
2862        while (linePtr != end && !isSpace(*linePtr)) linePtr++;
2863        return false;
2864    }
2865    if (maxOutStrSize-1 < size_t(linePtr-nameStr))
2866        ASM_FAIL_BY_ERROR(linePtr, (std::string(objName)+" is too long").c_str())
2867    const size_t outStrSize = std::min(maxOutStrSize-1, size_t(linePtr-nameStr));
2868    std::copy(nameStr, nameStr+outStrSize, outStr);
2869    outStr[outStrSize] = 0; // null-char
2870    return true;
2871}
2872
2873bool GCNAsmUtils::checkGCNEncodingSize(Assembler& asmr, const char* insnPtr,
2874                     GCNEncSize gcnEncSize, uint32_t wordsNum)
2875{
2876    if (gcnEncSize==GCNEncSize::BIT32 && wordsNum!=1)
2877        ASM_FAIL_BY_ERROR(insnPtr, "32-bit encoding specified when 64-bit encoding")
2878    if (gcnEncSize==GCNEncSize::BIT64 && wordsNum!=2)
2879        ASM_FAIL_BY_ERROR(insnPtr, "64-bit encoding specified when 32-bit encoding")
2880    return true;
2881}
2882
2883bool GCNAsmUtils::checkGCNVOPEncoding(Assembler& asmr, GPUArchMask arch, const char* insnPtr,
2884            GCNVOPEnc vopEnc, GCNInsnMode insnMode, const VOPExtraModifiers* modifiers)
2885{
2886    if (vopEnc==GCNVOPEnc::DPP && (!modifiers->needDPP && !modifiers->needDPP8))
2887        ASM_FAIL_BY_ERROR(insnPtr, "DPP encoding specified when DPP not present")
2888    if (vopEnc==GCNVOPEnc::SDWA && !modifiers->needSDWA)
2889        ASM_FAIL_BY_ERROR(insnPtr, "SDWA encoding specified when SDWA not present")
2890    if ((modifiers->needDPP || modifiers->needDPP8) && (insnMode&GCN_VOP_NODPP)!=0)
2891        ASM_FAIL_BY_ERROR(insnPtr, "DPP encoding is illegal for this instruction")
2892    if (modifiers->needSDWA && (insnMode&GCN_VOP_NOSDWA)!=0)
2893        ASM_FAIL_BY_ERROR(insnPtr, "SDWA encoding is illegal for this instruction")
2894    if (modifiers->needSDWA && (insnMode&GCN_VOP_NOSDWAVEGA)!=0 && (arch & ARCH_GCN_1_4)!=0)
2895        ASM_FAIL_BY_ERROR(insnPtr, "SDWA encoding is illegal for this instruction")
2896    return true;
2897}
2898
2899bool GCNAsmUtils::checkGCNVOPExtraModifers(Assembler& asmr, GPUArchMask arch, bool needImm,
2900                 bool sextFlags, bool vop3, GCNVOPEnc gcnVOPEnc, const GCNOperand& src0Op,
2901                 VOPExtraModifiers& extraMods, bool absNegFlags, const char* instrPlace)
2902{
2903    if (needImm)
2904        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SDWA or DPP word is illegal")
2905    if ((arch & ARCH_GCN_1_4_5)==0 && !src0Op.range.isVGPR())
2906        ASM_FAIL_BY_ERROR(instrPlace, "SRC0 must be a vector register with "
2907                    "SDWA or DPP word")
2908    if ((arch & ARCH_GCN_1_4_5)!=0 && (extraMods.needDPP || extraMods.needDPP8) &&
2909                    !src0Op.range.isVGPR())
2910        ASM_FAIL_BY_ERROR(instrPlace, "SRC0 must be a vector register with DPP word")
2911    if (vop3)
2912        // if VOP3 and (VOP_DPP or VOP_SDWA)
2913        ASM_FAIL_BY_ERROR(instrPlace, "Mixing VOP3 with SDWA or WORD is illegal")
2914    if (sextFlags && (extraMods.needDPP || extraMods.needDPP8))
2915        ASM_FAIL_BY_ERROR(instrPlace, "SEXT modifiers is unavailable for DPP word")
2916    if (absNegFlags && extraMods.needDPP8)
2917        ASM_FAIL_BY_ERROR(instrPlace, "ABS and NEG modifiers is unavailable for DPP8 word")
2918    if (!extraMods.needSDWA && !extraMods.needDPP && !extraMods.needDPP8)
2919    {
2920        if (gcnVOPEnc!=GCNVOPEnc::DPP)
2921            extraMods.needSDWA = true; // by default we choose SDWA word
2922        else
2923            extraMods.needDPP = true;
2924    }
2925    return true;
2926}
2927
2928};
Note: See TracBrowser for help on using the repository browser.