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

Last change on this file since 3096 was 3096, checked in by matszpk, 2 years ago

CLRadeonExtender: GCNAsm: Add parametrizable modifers: abs, neg, sext.

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