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

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

CLRadeonExtender: GCNAsm: update. remove bitfields from VOPExtraModifiers. Add negMod and absMod to VOPExtraModifiers.

File size: 93.4 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, VOPExtraModifiers* extraMods, bool withClamp,
1538                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   
1548    if (extraMods!=nullptr)
1549        *extraMods = { 6, 0, cxbyte((withSDWAOperands>=2)?6:0),
1550                    cxbyte((withSDWAOperands>=3)?6:0),
1551                    15, 15, 0xe4 /* TODO: why not 0xe4? */, false, false, 0U, 0U };
1552   
1553    skipSpacesToEnd(linePtr, end);
1554    const char* modsPlace = linePtr;
1555    bool good = true;
1556    mods = 0;
1557    while (linePtr != end)
1558    {
1559        skipSpacesToEnd(linePtr, end);
1560        if (linePtr == end)
1561            break;
1562        char mod[20];
1563        const char* modPlace = linePtr;
1564        if (getNameArgS(asmr, 20, mod, linePtr, "VOP modifier"))
1565        {
1566            toLowerString(mod);
1567            try
1568            {
1569                bool alreadyModDefined = false;
1570                if (::strcmp(mod, "mul")==0)
1571                {
1572                    skipSpacesToEnd(linePtr, end);
1573                    if (linePtr!=end && *linePtr==':')
1574                    {
1575                        skipCharAndSpacesToEnd(linePtr, end);
1576                        cxbyte count = cstrtobyte(linePtr, end);
1577                        if (count==2)
1578                        {
1579                            alreadyModDefined = mods&3;
1580                            mods = (mods&~3) | VOP3_MUL2;
1581                        }
1582                        else if (count==4)
1583                        {
1584                            alreadyModDefined = mods&3;
1585                            mods = (mods&~3) | VOP3_MUL4;
1586                        }
1587                        else
1588                        {
1589                            asmr.printError(modPlace, "Unknown VOP3 mul:X modifier");
1590                            good = false;
1591                        }
1592                    }
1593                    else
1594                    {
1595                        asmr.printError(linePtr, "Expected ':' before multiplier number");
1596                        good = false;
1597                    }
1598                }
1599                else if (::strcmp(mod, "div")==0)
1600                {
1601                    skipSpacesToEnd(linePtr, end);
1602                    if (linePtr!=end && *linePtr==':')
1603                    {
1604                        skipCharAndSpacesToEnd(linePtr, end);
1605                        cxbyte count = cstrtobyte(linePtr, end);
1606                        if (count==2)
1607                        {
1608                            alreadyModDefined = mods&3;
1609                            mods = (mods&~3) | VOP3_DIV2;
1610                        }
1611                        else
1612                        {
1613                            asmr.printError(modPlace, "Unknown VOP3 div:X modifier");
1614                            good = false;
1615                        }
1616                    }
1617                    else
1618                    {
1619                        asmr.printError(linePtr, "Expected ':' before divider number");
1620                        good = false;
1621                    }
1622                }
1623                else if (::strcmp(mod, "omod")==0)
1624                {
1625                    skipSpacesToEnd(linePtr, end);
1626                    if (linePtr!=end && *linePtr==':')
1627                    {
1628                        linePtr++;
1629                        cxbyte omod = 0;
1630                        if (parseImm(asmr, linePtr, omod, nullptr, 2, WS_UNSIGNED))
1631                            mods = (mods & ~3) | omod;
1632                        else
1633                            good = false;
1634                    }
1635                    else
1636                    {
1637                        asmr.printError(linePtr, "Expected ':' before omod");
1638                        good = false;
1639                    }
1640                }
1641                else if (::strcmp(mod, "clamp")==0) // clamp
1642                {
1643                    bool clamp = false;
1644                    good &= parseModEnable(asmr, linePtr, clamp, "clamp modifier");
1645                    if (withClamp)
1646                        mods = (mods & ~VOP3_CLAMP) | (clamp ? VOP3_CLAMP : 0);
1647                    else
1648                    {
1649                        asmr.printError(modPlace, "Modifier CLAMP in VOP3B is illegal");
1650                        good = false;
1651                    }
1652                }
1653                /*else if (::strcmp(mod, "abs")==0)
1654                {
1655                    cxbyte absVal = 0;
1656                    if (linePtr!=end && *linePtr==':')
1657                    {
1658                        linePtr++;
1659                        if (parseImm(asmr, linePtr, absVal, nullptr, 2, WS_UNSIGNED))
1660                        {
1661                            extraMods->absMod = absVal;
1662                            if (haveAbs)
1663                                asmr.printWarning(modPlace, "Abs is already defined");
1664                            haveAbs = true;
1665                        }
1666                    }
1667                    else
1668                        good = false;
1669                }
1670                else if (::strcmp(mod, "neg")==0)
1671                {
1672                    cxbyte negVal = 0;
1673                    if (linePtr!=end && *linePtr==':')
1674                    {
1675                        linePtr++;
1676                        if (parseImm(asmr, linePtr, negVal, nullptr, 2, WS_UNSIGNED))
1677                        {
1678                            extraMods->negMod = negVal;
1679                            if (haveNeg)
1680                                asmr.printWarning(modPlace, "Neg is already defined");
1681                            haveNeg = true;
1682                        }
1683                    }
1684                    else
1685                        good = false;
1686                }*/
1687                else if (::strcmp(mod, "vop3")==0)
1688                {
1689                    bool vop3 = false;
1690                    good &= parseModEnable(asmr, linePtr, vop3, "vop3 modifier");
1691                    mods = (mods & ~VOP3_VOP3) | (vop3 ? VOP3_VOP3 : 0);
1692                }
1693                else if (extraMods!=nullptr)
1694                {   /* parse specific modofier from VOP_SDWA or VOP_DPP encoding */
1695                    if (withSDWAOperands>=1 && ::strcmp(mod, "dst_sel")==0)
1696                    {   // dstsel
1697                        skipSpacesToEnd(linePtr, end);
1698                        if (linePtr!=end && *linePtr==':')
1699                        {
1700                            linePtr++;
1701                            cxuint dstSel = 0;
1702                            if (linePtr == end || *linePtr!='@')
1703                            {
1704                                if (getEnumeration(asmr, linePtr, "dst_sel",
1705                                            vopSDWADSTSelNamesNum,
1706                                            vopSDWADSTSelNamesMap, dstSel))
1707                                {
1708                                    extraMods->dstSel = dstSel;
1709                                    if (haveDstSel)
1710                                        asmr.printWarning(modPlace,
1711                                                "Dst_sel is already defined");
1712                                    haveDstSel = true;
1713                                }
1714                                else
1715                                    good = false;
1716                            }
1717                            else
1718                            {   /* parametrize */
1719                                linePtr++;
1720                                if (parseImm(asmr, linePtr, dstSel, nullptr,
1721                                                 3, WS_UNSIGNED))
1722                                {
1723                                    extraMods->dstSel = dstSel;
1724                                    if (haveDstSel)
1725                                        asmr.printWarning(modPlace,
1726                                                "Dst_sel is already defined");
1727                                    haveDstSel = true;
1728                                }
1729                                else
1730                                    good = false;
1731                            }
1732                        }
1733                        else
1734                        {
1735                            asmr.printError(linePtr, "Expected ':' before dst_sel");
1736                            good = false;
1737                        }
1738                    }
1739                    else if (withSDWAOperands>=1 &&
1740                        (::strcmp(mod, "dst_unused")==0 || ::strcmp(mod, "dst_un")==0))
1741                    {
1742                        skipSpacesToEnd(linePtr, end);
1743                        if (linePtr!=end && *linePtr==':')
1744                        {
1745                            skipCharAndSpacesToEnd(linePtr, end);
1746                            cxbyte unused = 0;
1747                            if (linePtr == end || *linePtr!='@')
1748                            {
1749                                char name[20];
1750                                const char* enumPlace = linePtr;
1751                                if (getNameArg(asmr, 20, name, linePtr, "dst_unused"))
1752                                {
1753                                    toLowerString(name);
1754                                    size_t namePos =
1755                                            (::strncmp(name, "unused_", 7)==0) ?7 : 0;
1756                                    if (::strcmp(name+namePos, "sext")==0)
1757                                        unused = 1;
1758                                    else if (::strcmp(name+namePos, "preserve")==0)
1759                                        unused = 2;
1760                                    else if (::strcmp(name+namePos, "pad")!=0)
1761                                    {
1762                                        asmr.printError(enumPlace, "Unknown dst_unused");
1763                                        good = false;
1764                                    }
1765                                    extraMods->dstUnused = unused;
1766                                    if (haveDstUnused)
1767                                        asmr.printWarning(modPlace,
1768                                                        "Dst_unused is already defined");
1769                                    haveDstUnused = true;
1770                                }
1771                                else
1772                                    good = false;
1773                            }
1774                            else
1775                            {
1776                                linePtr++;
1777                                if (parseImm(asmr, linePtr, unused, nullptr,
1778                                                 2, WS_UNSIGNED))
1779                                {
1780                                    extraMods->dstUnused = unused;
1781                                    if (haveDstUnused)
1782                                        asmr.printWarning(modPlace,
1783                                                        "Dst_unused is already defined");
1784                                    haveDstUnused = true;
1785                                }
1786                                else
1787                                    good = false;
1788                            }
1789                        }
1790                        else
1791                        {
1792                            asmr.printError(linePtr, "Expected ':' before dst_unused");
1793                            good = false;
1794                        }
1795                    }
1796                    else if (withSDWAOperands>=2 && ::strcmp(mod, "src0_sel")==0)
1797                    {
1798                        skipSpacesToEnd(linePtr, end);
1799                        if (linePtr!=end && *linePtr==':')
1800                        {
1801                            linePtr++;
1802                            cxuint src0Sel = 0;
1803                            if (linePtr == end || *linePtr!='@')
1804                            {
1805                                if (getEnumeration(asmr, linePtr, "src0_sel",
1806                                            vopSDWADSTSelNamesNum,
1807                                            vopSDWADSTSelNamesMap, src0Sel))
1808                                {
1809                                    extraMods->src0Sel = src0Sel;
1810                                    if (haveSrc0Sel)
1811                                        asmr.printWarning(modPlace,
1812                                                        "Src0_sel is already defined");
1813                                    haveSrc0Sel = true;
1814                                }
1815                                else
1816                                    good = false;
1817                            }
1818                            else
1819                            {   /* parametrize */
1820                                linePtr++;
1821                                if (parseImm(asmr, linePtr, src0Sel, nullptr,
1822                                                 3, WS_UNSIGNED))
1823                                {
1824                                    extraMods->src0Sel = src0Sel;
1825                                    if (haveSrc0Sel)
1826                                        asmr.printWarning(modPlace,
1827                                                        "Src0_sel is already defined");
1828                                    haveSrc0Sel = true;
1829                                }
1830                                else
1831                                    good = false;
1832                            }
1833                        }
1834                        else
1835                        {
1836                            asmr.printError(linePtr, "Expected ':' before src0_sel");
1837                            good = false;
1838                        }
1839                    }
1840                    else if (withSDWAOperands>=3 && ::strcmp(mod, "src1_sel")==0)
1841                    {
1842                        skipSpacesToEnd(linePtr, end);
1843                        if (linePtr!=end && *linePtr==':')
1844                        {
1845                            linePtr++;
1846                            cxuint src1Sel = 0;
1847                            if (linePtr == end || *linePtr!='@')
1848                            {
1849                                if (getEnumeration(asmr, linePtr, "src1_sel",
1850                                            vopSDWADSTSelNamesNum,
1851                                            vopSDWADSTSelNamesMap, src1Sel))
1852                                {
1853                                    extraMods->src1Sel = src1Sel;
1854                                    if (haveSrc1Sel)
1855                                        asmr.printWarning(modPlace,
1856                                                        "Src1_sel is already defined");
1857                                    haveSrc1Sel = true;
1858                                }
1859                                else
1860                                    good = false;
1861                            }
1862                            else
1863                            {   /* parametrize */
1864                                linePtr++;
1865                                if (parseImm(asmr, linePtr, src1Sel, nullptr,
1866                                                 3, WS_UNSIGNED))
1867                                {
1868                                    extraMods->src1Sel = src1Sel;
1869                                    if (haveSrc1Sel)
1870                                        asmr.printWarning(modPlace,
1871                                                        "Src1_sel is already defined");
1872                                    haveSrc1Sel = true;
1873                                }
1874                                else
1875                                    good = false;
1876                            }
1877                        }
1878                        else
1879                        {
1880                            asmr.printError(linePtr, "Expected ':' before src1_sel");
1881                            good = false;
1882                        }
1883                    }
1884                    else if (::strcmp(mod, "quad_perm")==0)
1885                    {
1886                        skipSpacesToEnd(linePtr, end);
1887                        if (linePtr!=end && *linePtr==':')
1888                        {
1889                            bool goodMod = true;
1890                            skipCharAndSpacesToEnd(linePtr, end);
1891                            if (linePtr==end || *linePtr!='[')
1892                            {
1893                                asmr.printError(linePtr,
1894                                        "Expected '[' before quad_perm list");
1895                                goodMod = good = false;
1896                                continue;
1897                            }
1898                            cxbyte quadPerm = 0;
1899                            linePtr++;
1900                            for (cxuint k = 0; k < 4; k++)
1901                            {
1902                                skipSpacesToEnd(linePtr, end);
1903                                try
1904                                {
1905                                    cxbyte qpv = 0;
1906                                    good &= parseImm(asmr, linePtr, qpv, nullptr,
1907                                            2, WS_UNSIGNED);
1908                                    quadPerm |= qpv<<(k<<1);
1909                                }
1910                                catch(const ParseException& ex)
1911                                {
1912                                    asmr.printError(linePtr, ex.what());
1913                                    goodMod = good = false;
1914                                }
1915                                skipSpacesToEnd(linePtr, end);
1916                                if (k!=3)
1917                                {
1918                                    if (linePtr==end || *linePtr!=',')
1919                                    {
1920                                        asmr.printError(linePtr,
1921                                            "Expected ',' before quad_perm component");
1922                                        goodMod = good = false;
1923                                        break;
1924                                    }
1925                                    else
1926                                        ++linePtr;
1927                                }
1928                                else if (linePtr==end || *linePtr!=']')
1929                                {   // unterminated quad_perm
1930                                    asmr.printError(linePtr, "Unterminated quad_perm");
1931                                    goodMod = good = false;
1932                                }
1933                                else
1934                                    ++linePtr;
1935                            }
1936                            if (goodMod)
1937                            {
1938                                extraMods->dppCtrl = quadPerm;
1939                                if (haveDppCtrl)
1940                                    asmr.printWarning(modPlace,
1941                                              "DppCtrl is already defined");
1942                                haveDppCtrl = true;
1943                            }
1944                        }
1945                        else
1946                        {
1947                            asmr.printError(linePtr, "Expected ':' before quad_perm");
1948                            good = false;
1949                        }
1950                    }
1951                    else if (::strcmp(mod, "bank_mask")==0)
1952                    {
1953                        skipSpacesToEnd(linePtr, end);
1954                        if (linePtr!=end && *linePtr==':')
1955                        {
1956                            linePtr++;
1957                            cxbyte bankMask = 0;
1958                            if (parseImm(asmr, linePtr, bankMask, nullptr, 4, WS_UNSIGNED))
1959                            {
1960                                extraMods->bankMask = bankMask;
1961                                if (haveBankMask)
1962                                    asmr.printWarning(modPlace,
1963                                              "Bank_mask is already defined");
1964                                haveBankMask = true;
1965                            }
1966                            else
1967                                good = false;
1968                        }
1969                        else
1970                        {
1971                            asmr.printError(linePtr, "Expected ':' before bank_mask");
1972                            good = false;
1973                        }
1974                    }
1975                    else if (::strcmp(mod, "row_mask")==0)
1976                    {
1977                        skipSpacesToEnd(linePtr, end);
1978                        if (linePtr!=end && *linePtr==':')
1979                        {
1980                            linePtr++;
1981                            cxbyte rowMask = 0;
1982                            if (parseImm(asmr, linePtr, rowMask, nullptr, 4, WS_UNSIGNED))
1983                            {
1984                                extraMods->rowMask = rowMask;
1985                                if (haveRowMask)
1986                                    asmr.printWarning(modPlace,
1987                                              "Row_mask is already defined");
1988                                haveRowMask = true;
1989                            }
1990                            else
1991                                good = false;
1992                        }
1993                        else
1994                        {
1995                            asmr.printError(linePtr, "Expected ':' before row_mask");
1996                            good = false;
1997                        }
1998                    }
1999                    else if (::strcmp(mod, "bound_ctrl")==0)
2000                    {
2001                        bool modGood = true;
2002                        skipSpacesToEnd(linePtr, end);
2003                        if (linePtr!=end && *linePtr==':')
2004                        {
2005                            skipCharAndSpacesToEnd(linePtr, end);
2006                            if (linePtr!=end && (*linePtr=='0' || *linePtr=='1'))
2007                            {
2008                                bool boundCtrl = false;
2009                                linePtr++;
2010                                good &= parseModEnable(asmr, linePtr, boundCtrl,
2011                                        "bound_ctrl modifier");
2012                                mods = (mods & ~VOP3_BOUNDCTRL) |
2013                                        (boundCtrl ? VOP3_BOUNDCTRL : 0);
2014                            }
2015                            else
2016                            {
2017                                asmr.printError(linePtr, "Value must be '0' or '1'");
2018                                modGood = good = false;
2019                            }
2020                        }
2021                        else // just enable boundctrl
2022                            mods |= VOP3_BOUNDCTRL;
2023                        if (modGood)
2024                        {   // bound_ctrl is defined
2025                            if (haveBoundCtrl)
2026                                asmr.printWarning(modPlace, "BoundCtrl is already defined");
2027                            haveBoundCtrl = true;
2028                            extraMods->needDPP = true;
2029                        }
2030                    }
2031                    else if (mod[0]=='r' && mod[1]=='o' && mod[2]=='w' && mod[3]=='_' &&
2032                            (::strcmp(mod+4, "shl")==0 || ::strcmp(mod+4, "shr")==0 ||
2033                                ::strcmp(mod+4, "ror")==0))
2034                    {   //
2035                        skipSpacesToEnd(linePtr, end);
2036                        if (linePtr!=end && *linePtr==':')
2037                        {
2038                            skipCharAndSpacesToEnd(linePtr, end);
2039                            const char* shiftPlace = linePtr;
2040                            cxbyte shift = 0;
2041                            if (parseImm(asmr, linePtr, shift , nullptr, 4, WS_UNSIGNED))
2042                            {
2043                                if (shift == 0)
2044                                {
2045                                    asmr.printError(shiftPlace,
2046                                            "Illegal zero shift for row_XXX shift");
2047                                    good = false;
2048                                    continue;
2049                                }
2050                                if (haveDppCtrl)
2051                                    asmr.printWarning(modPlace,
2052                                              "DppCtrl is already defined");
2053                                haveDppCtrl = true;
2054                                /* retrieve dppCtrl code from mod name:
2055                                 * shl - 0, shr - 0x10, ror - 0x20 */
2056                                extraMods->dppCtrl = 0x100U | ((mod[4]=='r') ? 0x20 :
2057                                    (mod[4]=='s' && mod[6]=='r') ? 0x10 : 0) | shift;
2058                            }
2059                            else
2060                                good = false;
2061                        }
2062                        else
2063                        {
2064                            asmr.printError(linePtr, (std::string(
2065                                        "Expected ':' before ")+mod).c_str());
2066                            good = false;
2067                        }
2068                    }
2069                    else if (memcmp(mod, "wave_", 5)==0 &&
2070                        (::strcmp(mod+5, "shl")==0 || ::strcmp(mod+5, "shr")==0 ||
2071                            ::strcmp(mod+5, "rol")==0 || ::strcmp(mod+5, "ror")==0))
2072                    {
2073                        bool modGood = true;
2074                        skipSpacesToEnd(linePtr, end);
2075                        if (linePtr!=end && *linePtr==':')
2076                        {
2077                            skipCharAndSpacesToEnd(linePtr, end);
2078                            if (linePtr!=end && *linePtr=='1')
2079                                ++linePtr;
2080                            else
2081                            {
2082                                asmr.printError(linePtr, "Value must be '1'");
2083                                modGood = good = false;
2084                            }
2085                        }
2086                        if (mod[5]=='s')
2087                            extraMods->dppCtrl = 0x100 | ((mod[7]=='l') ? 0x30 : 0x38);
2088                        else if (mod[5]=='r')
2089                            extraMods->dppCtrl = 0x100 | ((mod[7]=='l') ? 0x34 : 0x3c);
2090                        if (modGood)
2091                        {   // dpp_ctrl is defined
2092                            if (haveDppCtrl)
2093                                asmr.printWarning(modPlace, "DppCtrl is already defined");
2094                            haveDppCtrl = true;
2095                        }
2096                    }
2097                    else if (::strcmp(mod, "row_mirror")==0 ||
2098                        ::strcmp(mod, "row_half_mirror")==0 ||
2099                        ::strcmp(mod, "row_hmirror")==0)
2100                    {
2101                        extraMods->dppCtrl = (mod[4]=='h') ? 0x141 : 0x140;
2102                        if (haveDppCtrl)
2103                            asmr.printWarning(modPlace, "DppCtrl is already defined");
2104                        haveDppCtrl = true;
2105                    }
2106                    else if (::strncmp(mod, "row_bcast", 9)==0 && (
2107                        (mod[9]=='1' && mod[10]=='5' && mod[11]==0) ||
2108                        (mod[9]=='3' && mod[10]=='1' && mod[11]==0) || mod[9]==0))
2109                    {
2110                        bool modGood = true;
2111                        if (mod[9] =='1') // if row_bcast15
2112                            extraMods->dppCtrl = 0x142;
2113                        else if (mod[9] =='3') // if row_bcast31
2114                            extraMods->dppCtrl = 0x143;
2115                        else
2116                        { // get number
2117                            skipSpacesToEnd(linePtr, end);
2118                            if (linePtr!=end && *linePtr==':')
2119                            {
2120                                skipCharAndSpacesToEnd(linePtr, end);
2121                                const char* numPlace = linePtr;
2122                                cxbyte value = cstrtobyte(linePtr, end);
2123                                // parse row_bcast:15 or row_bcast:31
2124                                if (value == 31)
2125                                    extraMods->dppCtrl = 0x143;
2126                                else if (value == 15)
2127                                    extraMods->dppCtrl = 0x142;
2128                                else
2129                                {
2130                                    asmr.printError(numPlace, "Thread to broadcast must be"
2131                                                " 15 or 31");
2132                                    modGood = good = false;
2133                                }
2134                            }
2135                            else
2136                            {
2137                                asmr.printError(linePtr, "Expected ':' before row_bcast");
2138                                modGood = good = false;
2139                            }
2140                        }
2141                        if (modGood)
2142                        {
2143                            if (haveDppCtrl)
2144                                asmr.printWarning(modPlace, "DppCtrl is already defined");
2145                            haveDppCtrl = true;
2146                        }
2147                    }
2148                    else
2149                    {   /// unknown modifier
2150                        asmr.printError(modPlace, "Unknown VOP modifier");
2151                        good = false;
2152                    }
2153                }
2154                else
2155                {   /// unknown modifier
2156                    asmr.printError(modPlace, "Unknown VOP modifier");
2157                    good = false;
2158                }
2159               
2160                if (alreadyModDefined)
2161                    asmr.printWarning(modPlace, "OMOD is already defined");
2162            }
2163            catch(const ParseException& ex)
2164            {
2165                asmr.printError(linePtr, ex.what());
2166                good = false;
2167            }
2168        }
2169        else
2170            good = false;
2171    }
2172    const bool vopSDWA = (haveDstSel || haveDstUnused || haveSrc0Sel || haveSrc1Sel);
2173    const bool vopDPP = (haveDppCtrl || haveBoundCtrl || haveBankMask || haveRowMask);
2174    const bool isGCN14 = (arch & ARCH_RXVEGA) != 0;
2175    // mul/div modifier does not apply to vop3 if RXVEGA (this case will be checked later)
2176    const bool vop3 = (mods & ((isGCN14 ? 0 : 3)|VOP3_VOP3))!=0;
2177    if (extraMods!=nullptr)
2178    {
2179        extraMods->needSDWA = vopSDWA;
2180        extraMods->needDPP = vopDPP;
2181    }
2182    if ((int(vop3)+vopSDWA+vopDPP)>1 ||
2183                // RXVEGA: mul/div modifier are accepted in VOP_SDWA but not for VOP_DPP
2184                (isGCN14 && (mods & 3)!=0 && vopDPP) ||
2185                ((mods&VOP3_CLAMP)!=0 && vopDPP))
2186    {
2187        asmr.printError(modsPlace, "Mixing modifiers from different encodings is illegal");
2188        return false;
2189    }
2190    return good;
2191}
2192
2193static const char* vintrpParamsTbl[] =
2194{ "p10", "p20", "p0" };
2195
2196// parse interpolation (P0,P10,P20) parameter for VINTRP instructions
2197bool GCNAsmUtils::parseVINTRP0P10P20(Assembler& asmr, const char*& linePtr, RegRange& reg)
2198{
2199    const char* end = asmr.line+asmr.lineSize;
2200    skipSpacesToEnd(linePtr, end);
2201    const char* p0Place = linePtr;
2202    char pxName[5];
2203    if (getNameArg(asmr, 5, pxName, linePtr, "VINTRP parameter"))
2204    {
2205        cxuint p0Code = 0;
2206        toLowerString(pxName);
2207        for (p0Code = 0; p0Code < 3; p0Code++)
2208            if (::strcmp(vintrpParamsTbl[p0Code], pxName)==0)
2209                break;
2210        if (p0Code < 3) // as srcReg
2211            reg = { p0Code, p0Code+1 };
2212        else
2213        {
2214            asmr.printError(p0Place, "Unknown VINTRP parameter");
2215            return false;
2216        }
2217        return true;
2218    }
2219    return false;
2220}
2221
2222bool GCNAsmUtils::parseVINTRPAttr(Assembler& asmr, const char*& linePtr, cxbyte& attr)
2223{
2224    bool good = true;
2225    const char* end = asmr.line+asmr.lineSize;
2226    skipSpacesToEnd(linePtr, end);
2227    bool goodAttr = true;
2228    const char* attrPlace = linePtr;
2229    if (linePtr+4 > end)
2230    {
2231        asmr.printError(attrPlace, "Expected 'attr' keyword");
2232        goodAttr = good = false;
2233    }
2234    char buf[5];
2235    if (goodAttr)
2236    {
2237        std::transform(linePtr, linePtr+4, buf, toLower);
2238        if (::strncmp(buf, "attr", 4)!=0)
2239        {
2240            while (linePtr!=end && *linePtr!=' ') linePtr++;
2241            asmr.printError(attrPlace, "Expected 'attr' keyword");
2242            goodAttr = good = false;
2243        }
2244        else
2245            linePtr+=4;
2246    }
2247   
2248    cxbyte attrVal = 0;
2249    if (goodAttr)
2250    {   // parse only attribute value if no error before
2251        const char* attrNumPlace = linePtr;
2252        try
2253        { attrVal = cstrtobyte(linePtr, end); }
2254        catch(const ParseException& ex)
2255        {
2256            asmr.printError(linePtr, ex.what());
2257            goodAttr = good = false;
2258        }
2259        if (attrVal >= 64)
2260        {
2261            asmr.printError(attrNumPlace, "Attribute number out of range (0-63)");
2262            goodAttr = good = false;
2263        }
2264    }
2265    if (goodAttr)
2266    {   // parse again if no error before
2267        skipSpacesToEnd(linePtr, end);
2268        if (linePtr==end || *linePtr!='.')
2269        {
2270            asmr.printError(linePtr, "Expected '.' after attribute number");
2271            goodAttr = good = false;
2272        }
2273        else
2274            ++linePtr;
2275    }
2276    if (goodAttr)
2277    {   // parse again if no error before
2278        skipSpacesToEnd(linePtr, end);
2279        if (linePtr==end)
2280        {
2281            asmr.printError(linePtr, "Expected attribute component");
2282            goodAttr = good = false;
2283        }
2284    }
2285    char attrCmpName = 0;
2286    if (goodAttr)
2287    {   // parse only attribute component if no error before
2288        attrCmpName = toLower(*linePtr);
2289        if (attrCmpName!='x' && attrCmpName!='y' && attrCmpName!='z' && attrCmpName!='w')
2290        {
2291            asmr.printError(linePtr, "Expected attribute component");
2292            good = false;
2293        }
2294        linePtr++;
2295    }
2296   
2297    attr = (attrVal<<2) | ((attrCmpName=='x') ? 0 : (attrCmpName=='y') ? 1 :
2298            (attrCmpName=='z') ? 2 : 3);
2299    return good;
2300}
2301
2302/* special version of getNameArg for MUBUF format name.
2303 * this version accepts digits at first character of format name */
2304bool GCNAsmUtils::getMUBUFFmtNameArg(Assembler& asmr, size_t maxOutStrSize, char* outStr,
2305               const char*& linePtr, const char* objName)
2306{
2307    const char* end = asmr.line + asmr.lineSize;
2308    skipSpacesToEnd(linePtr, end);
2309    if (linePtr == end)
2310    {
2311        asmr.printError(linePtr, (std::string("Expected ")+objName).c_str());
2312        return false;
2313    }
2314    const char* nameStr = linePtr;
2315    if (isAlnum(*linePtr) || *linePtr == '_' || *linePtr == '.')
2316    {
2317        linePtr++;
2318        while (linePtr != end && (isAlnum(*linePtr) ||
2319            *linePtr == '_' || *linePtr == '.')) linePtr++;
2320    }
2321    else
2322    {
2323        asmr.printError(linePtr, (std::string("Some garbages at ")+objName+
2324                " place").c_str());
2325        while (linePtr != end && !isSpace(*linePtr)) linePtr++;
2326        return false;
2327    }
2328    if (maxOutStrSize-1 < size_t(linePtr-nameStr))
2329    {
2330        asmr.printError(linePtr, (std::string(objName)+" is too long").c_str());
2331        return false;
2332    }
2333    const size_t outStrSize = std::min(maxOutStrSize-1, size_t(linePtr-nameStr));
2334    std::copy(nameStr, nameStr+outStrSize, outStr);
2335    outStr[outStrSize] = 0; // null-char
2336    return true;
2337}
2338
2339bool GCNAsmUtils::checkGCNEncodingSize(Assembler& asmr, const char* insnPtr,
2340                     GCNEncSize gcnEncSize, uint32_t wordsNum)
2341{
2342    if (gcnEncSize==GCNEncSize::BIT32 && wordsNum!=1)
2343    {
2344        asmr.printError(insnPtr, "32-bit encoding specified when 64-bit encoding");
2345        return false;
2346    }
2347    if (gcnEncSize==GCNEncSize::BIT64 && wordsNum!=2)
2348    {
2349        asmr.printError(insnPtr, "64-bit encoding specified when 32-bit encoding");
2350        return false;
2351    }
2352    return true;
2353}
2354
2355bool GCNAsmUtils::checkGCNVOPEncoding(Assembler& asmr, const char* insnPtr,
2356                     GCNVOPEnc vopEnc, const VOPExtraModifiers* modifiers)
2357{
2358    if (vopEnc==GCNVOPEnc::DPP && !modifiers->needDPP)
2359    {
2360        asmr.printError(insnPtr, "DPP encoding specified when DPP not present");
2361        return false;
2362    }
2363    if (vopEnc==GCNVOPEnc::SDWA && !modifiers->needSDWA)
2364    {
2365        asmr.printError(insnPtr, "DPP encoding specified when DPP not present");
2366        return false;
2367    }
2368    return true;
2369}
2370
2371bool GCNAsmUtils::checkGCNVOPExtraModifers(Assembler& asmr, bool needImm, bool sextFlags,
2372                 bool vop3, GCNVOPEnc gcnVOPEnc, const GCNOperand& src0Op,
2373                 VOPExtraModifiers& extraMods, const char* instrPlace)
2374{
2375    if (needImm)
2376    {
2377        asmr.printError(instrPlace, "Literal with SDWA or DPP word is illegal");
2378        return false;
2379    }
2380    if (!src0Op.range.isVGPR())
2381    {
2382        asmr.printError(instrPlace, "SRC0 must be a vector register with "
2383                    "SDWA or DPP word");
2384        return false;
2385    }
2386    if (vop3)
2387    {   // if VOP3 and (VOP_DPP or VOP_SDWA)
2388        asmr.printError(instrPlace, "Mixing VOP3 with SDWA or WORD is illegal");
2389        return false;
2390    }
2391    if (sextFlags & extraMods.needDPP)
2392    {
2393        asmr.printError(instrPlace, "SEXT modifiers is unavailable for DPP word");
2394        return false;
2395    }
2396    if (!extraMods.needSDWA && !extraMods.needDPP)
2397    {
2398        if (gcnVOPEnc!=GCNVOPEnc::DPP)
2399            extraMods.needSDWA = true; // by default we choose SDWA word
2400        else
2401            extraMods.needDPP = true;
2402    }
2403    return true;
2404}
2405
2406};
Note: See TracBrowser for help on using the repository browser.