source: CLRX/CLRadeonExtender/trunk/amdasm/GCNAssembler.cpp @ 4084

Last change on this file since 4084 was 4084, checked in by matszpk, 12 months ago

CLRadeonExtender: GCNAsm: Simplify VOP2/VOP1/VOPC encoding part (use encodeVOPWords).

File size: 169.6 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2018 Mateusz Szpakowski
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <CLRX/Config.h>
21#include <cstdio>
22#include <vector>
23#include <memory>
24#include <cstring>
25#include <algorithm>
26#include <mutex>
27#include <CLRX/amdasm/Assembler.h>
28#include <CLRX/utils/Utilities.h>
29#include "GCNAsmInternals.h"
30
31using namespace CLRX;
32
33static OnceFlag clrxGCNAssemblerOnceFlag;
34static Array<GCNAsmInstruction> gcnInstrSortedTable;
35
36static void initializeGCNAssembler()
37{
38    size_t tableSize = 0;
39    while (gcnInstrsTable[tableSize].mnemonic!=nullptr)
40        tableSize++;
41    gcnInstrSortedTable.resize(tableSize);
42    for (cxuint i = 0; i < tableSize; i++)
43    {
44        const GCNInstruction& insn = gcnInstrsTable[i];
45        gcnInstrSortedTable[i] = {insn.mnemonic, insn.encoding, insn.mode,
46                    insn.code, UINT16_MAX, insn.archMask};
47    }
48   
49    // sort GCN instruction table by mnemonic, encoding and architecture
50    std::sort(gcnInstrSortedTable.begin(), gcnInstrSortedTable.end(),
51            [](const GCNAsmInstruction& instr1, const GCNAsmInstruction& instr2)
52            {
53                // compare mnemonic and if mnemonic
54                int r = ::strcmp(instr1.mnemonic, instr2.mnemonic);
55                return (r < 0) || (r==0 && instr1.encoding < instr2.encoding) ||
56                            (r == 0 && instr1.encoding == instr2.encoding &&
57                             instr1.archMask < instr2.archMask);
58            });
59   
60    cxuint j = 0;
61    std::unique_ptr<uint16_t[]> oldArchMasks(new uint16_t[tableSize]);
62    /* join VOP3A instr with VOP2/VOPC/VOP1 instr together to faster encoding. */
63    for (cxuint i = 0; i < tableSize; i++)
64    {
65        GCNAsmInstruction insn = gcnInstrSortedTable[i];
66        if (insn.encoding == GCNENC_VOP3A || insn.encoding == GCNENC_VOP3B)
67        {
68            // check duplicates
69            cxuint k = j-1;
70            while (::strcmp(gcnInstrSortedTable[k].mnemonic, insn.mnemonic)==0 &&
71                    (oldArchMasks[k] & insn.archMask)!=insn.archMask) k--;
72           
73            if (::strcmp(gcnInstrSortedTable[k].mnemonic, insn.mnemonic)==0 &&
74                (oldArchMasks[k] & insn.archMask)==insn.archMask)
75            {
76                // we found duplicate, we apply
77                if (gcnInstrSortedTable[k].code2==UINT16_MAX)
78                {
79                    // if second slot for opcode is not filled
80                    gcnInstrSortedTable[k].code2 = insn.code1;
81                    gcnInstrSortedTable[k].archMask = oldArchMasks[k] & insn.archMask;
82                }
83                else
84                {
85                    // if filled we create new entry
86                    oldArchMasks[j] = gcnInstrSortedTable[j].archMask;
87                    gcnInstrSortedTable[j] = gcnInstrSortedTable[k];
88                    gcnInstrSortedTable[j].archMask = oldArchMasks[k] & insn.archMask;
89                    gcnInstrSortedTable[j++].code2 = insn.code1;
90                }
91            }
92            else // not found
93            {
94                oldArchMasks[j] = insn.archMask;
95                gcnInstrSortedTable[j++] = insn;
96            }
97        }
98        else if (insn.encoding == GCNENC_VINTRP)
99        {
100            // check duplicates
101            cxuint k = j-1;
102            oldArchMasks[j] = insn.archMask;
103            gcnInstrSortedTable[j++] = insn;
104            while (::strcmp(gcnInstrSortedTable[k].mnemonic, insn.mnemonic)==0 &&
105                    gcnInstrSortedTable[k].encoding!=GCNENC_VOP3A) k--;
106            if (::strcmp(gcnInstrSortedTable[k].mnemonic, insn.mnemonic)==0 &&
107                gcnInstrSortedTable[k].encoding==GCNENC_VOP3A)
108                // we found VINTRP duplicate, set up second code (VINTRP)
109                gcnInstrSortedTable[k].code2 = insn.code1;
110        }
111        else // normal instruction
112        {
113            oldArchMasks[j] = insn.archMask;
114            gcnInstrSortedTable[j++] = insn;
115        }
116    }
117    gcnInstrSortedTable.resize(j); // final size
118}
119
120// GCN Usage handler
121
122GCNUsageHandler::GCNUsageHandler(const std::vector<cxbyte>& content,
123                 uint16_t _archMask) : ISAUsageHandler(content), archMask(_archMask)
124{
125    defaultInstrSize = 4;
126}
127GCNUsageHandler::~GCNUsageHandler()
128{ }
129
130ISAUsageHandler* GCNUsageHandler::copy() const
131{
132    return new GCNUsageHandler(*this);
133}
134
135// get read-write flags from current position
136cxbyte GCNUsageHandler::getRwFlags(AsmRegField regField,
137                   uint16_t rstart, uint16_t rend) const
138{
139    uint16_t regSize = rend-rstart-1;
140    cxbyte flags;
141    switch (regField)
142    {
143        case GCNFIELD_SMRD_SBASE:
144            flags = (regSize>>1)<<ASMRVU_REGSIZE_SHIFT;
145            break;
146        case GCNFIELD_SMRD_SDST:
147        {
148            cxbyte out = 0;
149            regSize += 1;
150            for (uint16_t v = 1; v < regSize; v<<=1, out++);
151            flags = out<<ASMRVU_REGSIZE_SHIFT;
152            break;
153        }
154        case GCNFIELD_M_SRSRC:
155        case GCNFIELD_MIMG_SSAMP:
156            flags = (regSize>>2)<<ASMRVU_REGSIZE_SHIFT; // 4
157            break;
158        default:
159            flags = regSize<<ASMRVU_REGSIZE_SHIFT;
160            break;
161    }
162    return flags;
163}
164
165/* get register pair from specified field from instruction in current code position */
166std::pair<uint16_t,uint16_t> GCNUsageHandler::getRegPair(AsmRegField regField,
167                 cxbyte rwFlags) const
168{
169    cxbyte regSize = ((rwFlags >> ASMRVU_REGSIZE_SHIFT) & 15) + 1;
170    uint16_t rstart;
171    uint32_t code1 = 0, code2 = 0;
172    if (readOffset+4 <= content.size())
173        code1 = ULEV(*reinterpret_cast<const uint32_t*>(content.data()+readOffset));
174    if (readOffset+8 <= content.size())
175        code2 = ULEV(*reinterpret_cast<const uint32_t*>(content.data()+readOffset+4));
176   
177    const bool isGCN12 = (archMask & ARCH_GCN_1_2_4)!=0;
178   
179    switch(regField)
180    {
181        case GCNFIELD_SSRC0:
182            rstart = code1&0xff;
183            break;
184        case GCNFIELD_SSRC1:
185            rstart = (code1>>8)&0xff;
186            break;
187        case GCNFIELD_SDST:
188            rstart = (code1>>16)&0x7f;
189            break;
190        case GCNFIELD_SMRD_SBASE:
191            if (isGCN12)
192                rstart = (code1<<1) & 0x7f;
193            else
194                rstart = (code1>>8) & 0x7e;
195            regSize<<=1; // 2 or 4
196            break;
197        case GCNFIELD_SMRD_SDST:
198        case GCNFIELD_SMRD_SDSTH:
199            if (isGCN12)
200                rstart = (code1>>6) & 0x7f;
201            else
202                rstart = (code1>>15) & 0x7f;
203            regSize = 1U<<(regSize-1);
204            if (regField == GCNFIELD_SMRD_SDSTH)
205                rstart += regSize;
206            break;
207        case GCNFIELD_SMRD_SOFFSET:
208            if (isGCN12)
209                rstart = (code2&0x7f);
210            else
211                rstart = (code1&0x7f);
212            break;
213        case GCNFIELD_VOP_SRC0:
214            rstart = code1&0x1ff;
215            break;
216        case GCNFIELD_VOP_VSRC1:
217            rstart = ((code1>>9) & 0xff) + 256;
218            break;
219        case GCNFIELD_VOP_SSRC1:
220            rstart = ((code1>>9) & 0xff);
221            break;
222        case GCNFIELD_VOP_VDST:
223            rstart = ((code1>>17) & 0xff) + 256;
224            break;
225        case GCNFIELD_VOP_SDST:
226            rstart = ((code1>>17) & 0xff);
227            break;
228        case GCNFIELD_VOP3_SRC0:
229            rstart = code2&0x1ff;
230            break;
231        case GCNFIELD_VOP3_SRC1:
232            rstart = (code2>>9) & 0x1ff;
233            break;
234        case GCNFIELD_VOP3_SRC2:
235            rstart = (code2>>18) & 0x1ff;
236            break;
237        case GCNFIELD_VOP3_VDST:
238        case GCNFIELD_VINTRP_VSRC0:
239            rstart = (code1&0xff) + 256;
240            break;
241        case GCNFIELD_VOP3_SDST0:
242            rstart = (code1&0xff);
243            break;
244        case GCNFIELD_VOP3_SSRC:
245            rstart = (code2>>18)&0xff;
246            break;
247        case GCNFIELD_VOP3_SDST1:
248            rstart = (code1>>8)&0xff;
249            break;
250        case GCNFIELD_VINTRP_VDST:
251            rstart = ((code1>>18) & 0xff) + 256;
252            break;
253        case GCNFIELD_DPPSDWA_SRC0:
254        case GCNFIELD_FLAT_ADDR:
255        case GCNFIELD_DS_ADDR:
256        case GCNFIELD_EXP_VSRC0:
257        case GCNFIELD_M_VADDR:
258            rstart = (code2&0xff) + 256;
259            break;
260        case GCNFIELD_FLAT_DATA:
261        case GCNFIELD_DS_DATA0:
262        case GCNFIELD_EXP_VSRC1:
263        case GCNFIELD_M_VDATA:
264            rstart = ((code2>>8)&0xff) + 256;
265            break;
266        case GCNFIELD_M_VDATAH:
267            rstart = ((code2>>8)&0xff) + 256 + regSize;
268            break;
269        case GCNFIELD_M_VDATALAST:
270            // regSize stored by fix for regusage (regvar==nullptr)
271            rstart = ((code2>>8)&0xff) + 256 + regSize;
272            return { rstart, rstart+1 };
273            break;
274        case GCNFIELD_DS_DATA1:
275        case GCNFIELD_EXP_VSRC2:
276            rstart = ((code2>>16)&0xff) + 256;
277            break;
278        case GCNFIELD_DS_VDST:
279        case GCNFIELD_FLAT_VDST:
280        case GCNFIELD_EXP_VSRC3:
281            rstart = (code2>>24) + 256;
282            break;
283        case GCNFIELD_FLAT_VDSTLAST:
284            // regSize stored by fix for regusage (regvar==nullptr)
285            rstart = (code2>>24) + 256 + regSize;
286            return { rstart, rstart+1 };
287            break;
288        case GCNFIELD_M_SRSRC:
289            rstart = (code2>>14)&0x7c;
290            regSize<<=2; // 4 or 8
291            break;
292        case GCNFIELD_MIMG_SSAMP:
293            rstart = (code2>>19)&0x7c;
294            regSize<<=2; // 4
295            break;
296        case GCNFIELD_M_SOFFSET:
297            rstart = (code2>>24)&0xff;
298            break;
299        case GCNFIELD_DPPSDWA_SSRC0:
300            rstart = code2&0xff;
301            break;
302        case GCNFIELD_SDWAB_SDST:
303            rstart = (code2>>8)&0x7f;
304            break;
305        default:
306            throw AsmException("Unknown GCNField");
307    }
308    return { rstart, rstart+regSize };
309}
310
311/// get usage dependencies
312/* linearDeps - lists of linked register fields (linked fields) */
313void GCNUsageHandler::getUsageDependencies(cxuint rvusNum, const AsmRegVarUsage* rvus,
314                cxbyte* linearDeps) const
315{
316    cxuint count = 0;
317    // linear dependencies (join fields)
318    count = 0;
319    for (cxuint i = 0; i < rvusNum; i++)
320    {
321        const AsmRegField rf = rvus[i].regField;
322        if (rf == GCNFIELD_M_VDATA || rf == GCNFIELD_M_VDATAH ||
323            rf == GCNFIELD_M_VDATALAST ||
324            rf == GCNFIELD_FLAT_VDST || rf == GCNFIELD_FLAT_VDSTLAST)
325            linearDeps[2 + count++] = i;
326    }
327    linearDeps[1] = (count >= 2) ? count : 0;
328    linearDeps[0] = (linearDeps[1] != 0);
329}
330
331/*
332 * GCN Assembler
333 */
334
335GCNAssembler::GCNAssembler(Assembler& assembler): ISAAssembler(assembler),
336        regs({0, 0}), curArchMask(1U<<cxuint(
337                    getGPUArchitectureFromDeviceType(assembler.getDeviceType())))
338{
339    callOnce(clrxGCNAssemblerOnceFlag, initializeGCNAssembler);
340}
341
342GCNAssembler::~GCNAssembler()
343{ }
344
345namespace CLRX
346{
347
348static const uint32_t constImmFloatLiterals[9] = 
349{
350    0x3f000000, 0xbf000000, 0x3f800000, 0xbf800000,
351    0x40000000, 0xc0000000, 0x40800000, 0xc0800000, 0x3e22f983
352};
353
354// used while converting 32-bit SOPx encoding to 64-bit SOPx encoding
355static void tryPromoteConstImmToLiteral(GCNOperand& src0Op, uint16_t arch)
356{
357    if (!src0Op.range.isRegVar() && src0Op.range.start>=128 && src0Op.range.start<=208)
358    {
359        // convert integer const immediates
360        src0Op.value = src0Op.range.start<193? src0Op.range.start-128 :
361                192-src0Op.range.start;
362        src0Op.range.start = 255;
363    }
364    else if (!src0Op.range.isRegVar() &&
365            ((src0Op.range.start>=240 && src0Op.range.start<248) ||
366             ((arch&ARCH_GCN_1_2_4)!=0 && src0Op.range.start==248)))
367    {
368        // floating point immediates to literal
369        src0Op.value = constImmFloatLiterals[src0Op.range.start-240];
370        src0Op.range.start = 255;
371    }
372}
373
374// check whether reg range can be equal (regvar and registers)
375static inline bool regRangeCanEqual(const RegRange& r1, const RegRange& r2)
376{
377    return r1.regVar==r2.regVar && r1.start==r2.start;
378}
379
380bool GCNAsmUtils::parseSOP2Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
381                  const char* instrPlace, const char* linePtr, uint16_t arch,
382                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
383                  GCNEncSize gcnEncSize)
384{
385    bool good = true;
386    RegRange dstReg(0, 0);
387    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
388   
389    if ((gcnInsn.mode & GCN_MASK1) != GCN_DST_NONE)
390    {
391        // parse SDST (SGPR)
392        gcnAsm->setCurrentRVU(0);
393        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
394                   (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
395                   INSTROP_SYMREGRANGE|INSTROP_WRITE);
396        if (!skipRequiredComma(asmr, linePtr))
397            return false;
398    }
399   
400    std::unique_ptr<AsmExpression> src0Expr, src1Expr;
401    // parse SRC0 (can be SGPR or scalar source)
402    GCNOperand src0Op{};
403    gcnAsm->setCurrentRVU(1);
404    good &= parseOperand(asmr, linePtr, src0Op, &src0Expr, arch,
405             (gcnInsn.mode&GCN_REG_SRC0_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
406                         INSTROP_READ, GCNFIELD_SSRC0);
407    if (!skipRequiredComma(asmr, linePtr))
408        return false;
409    GCNOperand src1Op{};
410    // parse SRC1 (can be SGPR or scalar source)
411    gcnAsm->setCurrentRVU(2);
412    good &= parseOperand(asmr, linePtr, src1Op, &src1Expr, arch,
413             (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
414             (src0Op.range.isVal(255) ? INSTROP_ONLYINLINECONSTS : 0)|INSTROP_READ,
415             GCNFIELD_SSRC1);
416   
417    /// if errors
418    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
419        return false;
420   
421    if (gcnEncSize==GCNEncSize::BIT64)
422    {
423        // try to promote constant immediate to literal
424        tryPromoteConstImmToLiteral(src0Op, arch);
425        tryPromoteConstImmToLiteral(src1Op, arch);
426    }
427    // put data
428    cxuint wordsNum = 1;
429    uint32_t words[2];
430    SLEV(words[0], 0x80000000U | (uint32_t(gcnInsn.code1)<<23) | src0Op.range.bstart() |
431            (src1Op.range.bstart()<<8) | uint32_t(dstReg.bstart())<<16);
432    if (src0Op.range.isVal(255) || src1Op.range.isVal(255))
433    {
434        // put literal value
435        if (src0Expr==nullptr && src1Expr==nullptr)
436            SLEV(words[1], src0Op.range.isVal(255) ? src0Op.value : src1Op.value);
437        else    // zero if unresolved value
438            SLEV(words[1], 0);
439        wordsNum++;
440    }
441    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
442        return false;
443    // set expression targets to resolving later
444    if (src0Expr!=nullptr)
445        src0Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
446                      output.size()));
447    else if (src1Expr!=nullptr)
448        src1Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
449                      output.size()));
450   
451    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
452            reinterpret_cast<cxbyte*>(words + wordsNum));
453    // prevent freeing expressions
454    src0Expr.release();
455    src1Expr.release();
456    // update SGPR counting and VCC usage (regflags)
457    if (dstReg && !dstReg.isRegVar())
458    {
459        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
460        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
461    }
462    if (src0Op.range && !src0Op.range.isRegVar())
463        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
464    if (src1Op.range && !src1Op.range.isRegVar())
465        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
466    return true;
467}
468
469bool GCNAsmUtils::parseSOP1Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
470                  const char* instrPlace, const char* linePtr, uint16_t arch,
471                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
472                  GCNEncSize gcnEncSize)
473{
474    bool good = true;
475    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
476    RegRange dstReg(0, 0);
477   
478    if ((gcnInsn.mode & GCN_MASK1) != GCN_DST_NONE)
479    {
480        // parse SDST (SGPR)
481        gcnAsm->setCurrentRVU(0);
482        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
483                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
484                       INSTROP_SYMREGRANGE|INSTROP_WRITE);
485        if ((gcnInsn.mode & GCN_MASK1) != GCN_SRC_NONE)
486            if (!skipRequiredComma(asmr, linePtr))
487                return false;
488    }
489   
490    GCNOperand src0Op{};
491    std::unique_ptr<AsmExpression> src0Expr;
492    if ((gcnInsn.mode & GCN_MASK1) != GCN_SRC_NONE)
493    {
494        // parse SRC0 (can be SGPR or source scalar, constant or literal)
495        gcnAsm->setCurrentRVU(1);
496        good &= parseOperand(asmr, linePtr, src0Op, &src0Expr, arch,
497                 (gcnInsn.mode&GCN_REG_SRC0_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
498                         INSTROP_READ, GCNFIELD_SSRC0);
499    }
500   
501    /// if errors
502    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
503        return false;
504   
505    if (gcnEncSize==GCNEncSize::BIT64)
506        // try to promote constant immediate to literal
507        tryPromoteConstImmToLiteral(src0Op, arch);
508    cxuint wordsNum = 1;
509    uint32_t words[2];
510    // put instruction word
511    SLEV(words[0], 0xbe800000U | (uint32_t(gcnInsn.code1)<<8) | src0Op.range.bstart() |
512            uint32_t(dstReg.bstart())<<16);
513    if (src0Op.range.start==255)
514    {
515        // put literal
516        if (src0Expr==nullptr)
517            SLEV(words[1], src0Op.value);
518        else    // zero if unresolved value
519            SLEV(words[1], 0);
520        wordsNum++;
521    }
522    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
523        return false;
524    // set expression targets
525    if (src0Expr!=nullptr)
526        src0Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
527                      output.size()));
528   
529    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
530            reinterpret_cast<cxbyte*>(words + wordsNum));
531    // prevent freeing expressions
532    src0Expr.release();
533    // update SGPR counting and VCC usage (regflags)
534    if (dstReg && !dstReg.isRegVar())
535    {
536        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
537        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
538    }
539    if (src0Op.range && !src0Op.range.isRegVar())
540        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
541    return true;
542}
543
544// hwreg names sorted by names
545static const std::pair<const char*, cxuint> hwregNamesMap[] =
546{
547    { "gpr_alloc", 5 },
548    { "hw_id", 4 },
549    { "ib_dbg0", 12 },
550    { "ib_dbg1", 13 },
551    { "ib_sts", 7 },
552    { "inst_dw0", 10 },
553    { "inst_dw1", 11 },
554    { "lds_alloc", 6 },
555    { "mode", 1 },
556    { "pc_hi", 9 },
557    { "pc_lo", 8 },
558    { "status", 2 },
559    { "trapsts", 3 }
560};
561
562static const size_t hwregNamesMapSize = sizeof(hwregNamesMap) /
563            sizeof(std::pair<const char*, uint16_t>);
564
565// update SGPR counting and VCC usage (regflags) for GCN 1.4 (VEGA)
566static const std::pair<const char*, cxuint> hwregNamesGCN14Map[] =
567{
568    { "flush_ib", 14 },
569    { "gpr_alloc", 5 },
570    { "hw_id", 4 },
571    { "ib_dbg0", 12 },
572    { "ib_dbg1", 13 },
573    { "ib_sts", 7 },
574    { "inst_dw0", 10 },
575    { "inst_dw1", 11 },
576    { "lds_alloc", 6 },
577    { "mode", 1 },
578    { "pc_hi", 9 },
579    { "pc_lo", 8 },
580    { "sh_mem_bases", 15 },
581    { "sq_shader_tba_hi", 17 },
582    { "sq_shader_tba_lo", 16 },
583    { "sq_shader_tma_hi", 19 },
584    { "sq_shader_tma_lo", 18 },
585    { "status", 2 },
586    { "trapsts", 3 }
587};
588
589static const size_t hwregNamesGCN14MapSize = sizeof(hwregNamesGCN14Map) /
590            sizeof(std::pair<const char*, uint16_t>);
591
592bool GCNAsmUtils::parseSOPKEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
593                  const char* instrPlace, const char* linePtr, uint16_t arch,
594                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
595                  GCNEncSize gcnEncSize)
596{
597    const char* end = asmr.line+asmr.lineSize;
598    bool good = true;
599    RegRange dstReg(0, 0);
600    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
601    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
602   
603    gcnAsm->setCurrentRVU(0);
604    bool doWrite = (gcnInsn.mode&GCN_MASK1) != GCN_DST_SRC &&
605            ((gcnInsn.mode&GCN_MASK1) != GCN_IMM_REL);
606    if ((gcnInsn.mode & GCN_IMM_DST) == 0)
607    {
608        // parse SDST (SGPR)
609        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
610                   (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
611                   INSTROP_SYMREGRANGE|
612                   (doWrite ? INSTROP_WRITE : INSTROP_READ));
613        if (!skipRequiredComma(asmr, linePtr))
614            return false;
615    }
616   
617    uint16_t imm16 = 0;
618    std::unique_ptr<AsmExpression> imm16Expr;
619   
620    if ((gcnInsn.mode&GCN_MASK1) == GCN_IMM_REL)
621    {
622        // parse relative address
623        uint64_t value = 0;
624        if (!getJumpValueArg(asmr, value, imm16Expr, linePtr))
625            return false;
626        if (imm16Expr==nullptr)
627        {
628            // if resolved at this time
629            int64_t offset = (int64_t(value)-int64_t(output.size())-4);
630            if (offset & 3)
631                ASM_NOTGOOD_BY_ERROR(linePtr, "Jump is not aligned to word!")
632            offset >>= 2;
633            if (offset > INT16_MAX || offset < INT16_MIN)
634                ASM_NOTGOOD_BY_ERROR(linePtr, "Jump out of range")
635            imm16 = offset;
636            // add codeflow entry
637            if (good)
638            {
639                asmr.sections[asmr.currentSection].addCodeFlowEntry({ 
640                    size_t(asmr.currentOutPos), size_t(value),
641                    (isGCN14 && gcnInsn.code1==21) ? AsmCodeFlowType::CALL :
642                            AsmCodeFlowType::CJUMP });
643            }
644        }
645    }
646    else if ((gcnInsn.mode&GCN_MASK1) == GCN_IMM_SREG)
647    {
648        // parse hwreg: hwreg(HWREG, bitstart, bitsize)
649        skipSpacesToEnd(linePtr, end);
650        char name[20];
651        const char* funcNamePlace = linePtr;
652        if (!getNameArg(asmr, 20, name, linePtr, "function name", true))
653            return false;
654        toLowerString(name);
655        skipSpacesToEnd(linePtr, end);
656        // if not hwreg
657        if (::strcmp(name, "hwreg")!=0 || linePtr==end || *linePtr!='(')
658            ASM_FAIL_BY_ERROR(funcNamePlace, "Expected hwreg function")
659        ++linePtr;
660        skipSpacesToEnd(linePtr, end);
661        cxuint hwregId = 0;
662        if (linePtr == end || *linePtr!='@')
663        {
664            // parse hwreg by name
665            const char* hwregNamePlace = linePtr;
666            // choose hwReg names map
667            const size_t regMapSize = isGCN14 ? hwregNamesGCN14MapSize : hwregNamesMapSize;
668            const std::pair<const char*, cxuint>* regMap = isGCN14 ?
669                        hwregNamesGCN14Map : hwregNamesMap;
670            good &= getEnumeration(asmr, linePtr, "HWRegister",
671                        regMapSize, regMap, hwregId, "hwreg_");
672            if (good && (arch & ARCH_GCN_1_2_4) == 0 && hwregId == 13)
673                // if ib_dgb1 in not GCN 1.2
674                ASM_NOTGOOD_BY_ERROR(hwregNamePlace, "Unknown HWRegister")
675        }
676        else
677        {
678            // parametrization (if preceded by '@')
679            linePtr++;
680            good &= parseImm(asmr, linePtr, hwregId, nullptr, 6, WS_UNSIGNED);
681        }
682       
683        if (!skipRequiredComma(asmr, linePtr))
684            return false;
685        uint64_t arg2Value = 0;
686        skipSpacesToEnd(linePtr, end);
687        const char* funcArg2Place = linePtr;
688       
689        // second argument of hwreg
690        if (getAbsoluteValueArg(asmr, arg2Value, linePtr, true))
691        {
692            if (arg2Value >= 32)
693                asmr.printWarning(funcArg2Place, "Second argument out of range (0-31)");
694        }
695        else
696            good = false;
697       
698        if (!skipRequiredComma(asmr, linePtr))
699            return false;
700        uint64_t arg3Value = 0;
701        skipSpacesToEnd(linePtr, end);
702        const char* funcArg3Place = linePtr;
703       
704        // second argument of hwreg
705        if (getAbsoluteValueArg(asmr, arg3Value, linePtr, true))
706        {
707            if (arg3Value >= 33 || arg3Value < 1)
708                asmr.printWarning(funcArg3Place, "Third argument out of range (1-32)");
709        }
710        else
711            good = false;
712       
713        skipSpacesToEnd(linePtr, end);
714        if (linePtr==end || *linePtr!=')')
715            ASM_FAIL_BY_ERROR(linePtr, "Unterminated hwreg function")
716        ++linePtr;
717        imm16 = hwregId | (arg2Value<<6) | ((arg3Value-1)<<11);
718    }
719    else // otherwise we parse expression
720        good &= parseImm(asmr, linePtr, imm16, &imm16Expr);
721   
722    uint32_t imm32 = 0;
723    std::unique_ptr<AsmExpression> imm32Expr;
724    if (gcnInsn.mode & GCN_IMM_DST)
725    {
726        // parse SDST as immediate or next source
727        if (!skipRequiredComma(asmr, linePtr))
728            return false;
729        if (gcnInsn.mode & GCN_SOPK_CONST)
730            good &= parseImm(asmr, linePtr, imm32, &imm32Expr);
731        else
732            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
733                   (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
734                   INSTROP_SYMREGRANGE|INSTROP_READ); // new field!
735    }
736   
737    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
738        return false;
739   
740    const cxuint wordsNum = (gcnInsn.mode & GCN_SOPK_CONST) ? 2 : 1;
741    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
742        return false;
743   
744    // put data (instruction words)
745    uint32_t words[2];
746    SLEV(words[0], 0xb0000000U | imm16 | (uint32_t(dstReg.bstart())<<16) |
747                uint32_t(gcnInsn.code1)<<23);
748    if (wordsNum==2)
749        SLEV(words[1], imm32);
750   
751    // setting expresion targets (for immediates)
752    if (imm32Expr!=nullptr)
753        imm32Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
754                           output.size()));
755    if (imm16Expr!=nullptr)
756        imm16Expr->setTarget(AsmExprTarget(((gcnInsn.mode&GCN_MASK1) == GCN_IMM_REL) ?
757                GCNTGT_SOPJMP : GCNTGT_SOPKSIMM16, asmr.currentSection, output.size()));
758   
759    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
760            reinterpret_cast<cxbyte*>(words + wordsNum));
761    /// prevent freeing expression
762    imm32Expr.release();
763    imm16Expr.release();
764    // update SGPR counting and VCC usage (regflags)
765    if (dstReg && !dstReg.isRegVar() && doWrite && (gcnInsn.mode & GCN_IMM_DST)==0)
766        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
767    if (dstReg && !dstReg.isRegVar())
768        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
769    return true;
770}
771
772bool GCNAsmUtils::parseSOPCEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
773                  const char* instrPlace, const char* linePtr, uint16_t arch,
774                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
775                  GCNEncSize gcnEncSize)
776{
777    bool good = true;
778    std::unique_ptr<AsmExpression> src0Expr, src1Expr;
779    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
780    GCNOperand src0Op{};
781   
782    // parse SRC0 (can be SGPR, source scalar, literal or constant
783    gcnAsm->setCurrentRVU(0);
784    good &= parseOperand(asmr, linePtr, src0Op, &src0Expr, arch,
785             (gcnInsn.mode&GCN_REG_SRC0_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
786                     INSTROP_READ, GCNFIELD_SSRC0);
787    if (!skipRequiredComma(asmr, linePtr))
788        return false;
789    GCNOperand src1Op{};
790    if ((gcnInsn.mode & GCN_SRC1_IMM) == 0)
791    {
792        // parse SRC1 (can be SGPR, source scalar, literal or constant
793        gcnAsm->setCurrentRVU(1);
794        good &= parseOperand(asmr, linePtr, src1Op, &src1Expr, arch,
795                 (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
796                 (src0Op.range.start==255 ? INSTROP_ONLYINLINECONSTS : 0)|INSTROP_READ,
797                         GCNFIELD_SSRC1);
798    }
799    else // immediate
800        good &= parseImm(asmr, linePtr, src1Op.range.start, &src1Expr, 8);
801   
802    /// if errors
803    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
804        return false;
805   
806    if (gcnEncSize==GCNEncSize::BIT64)
807    {
808        // try to promote constant immediate to literal
809        tryPromoteConstImmToLiteral(src0Op, arch);
810        tryPromoteConstImmToLiteral(src1Op, arch);
811    }
812    // put data
813    cxuint wordsNum = 1;
814    uint32_t words[2];
815    SLEV(words[0], 0xbf000000U | (uint32_t(gcnInsn.code1)<<16) | src0Op.range.bstart() |
816            (src1Op.range.bstart()<<8));
817    if (src0Op.range.start==255 ||
818        ((gcnInsn.mode & GCN_SRC1_IMM)==0 && src1Op.range.start==255))
819    {
820        // put literal
821        if (src0Expr==nullptr && src1Expr==nullptr)
822            SLEV(words[1], src0Op.range.isVal(255) ? src0Op.value : src1Op.value);
823        else    // zero if unresolved value
824            SLEV(words[1], 0);
825        wordsNum++;
826    }
827    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
828        return false;
829    // set expression targets
830    if (src0Expr!=nullptr)
831        src0Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
832                      output.size()));
833    else if (src1Expr!=nullptr)
834        src1Expr->setTarget(AsmExprTarget(
835            ((gcnInsn.mode&GCN_SRC1_IMM)) ? GCNTGT_SOPCIMM8 : GCNTGT_LITIMM,
836            asmr.currentSection, output.size()));
837   
838    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
839            reinterpret_cast<cxbyte*>(words + wordsNum));
840    // prevent freeing expressions
841    src0Expr.release();
842    src1Expr.release();
843    // update SGPR counting and VCC usage (regflags)
844    if (src0Op.range && !src0Op.range.isRegVar())
845        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
846    if (src1Op.range && !src1Op.range.isRegVar())
847        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
848    return true;
849}
850
851// message names sorted by name
852static const std::pair<const char*, uint16_t> sendMessageNamesMap[] =
853{
854    { "gs", 2 },
855    { "gs_done", 3 },
856    { "interrupt", 1 },
857    { "savewave", 4 },
858    { "sysmsg", 15 },
859    { "system", 15 }
860};
861
862static const size_t sendMessageNamesMapSize = sizeof(sendMessageNamesMap) /
863            sizeof(std::pair<const char*, uint16_t>);
864
865// message names sorted by name for GCN1.4 (VEGA)
866static const std::pair<const char*, uint16_t> sendMessageNamesGCN14Map[] =
867{
868    { "early_prim_dealloc", 8 },
869    { "get_doorbell", 10 },
870    { "gs", 2 },
871    { "gs_alloc_req", 9 },
872    { "gs_done", 3 },
873    { "halt_waves", 6 },
874    { "interrupt", 1 },
875    { "ordered_ps_done", 7 },
876    { "savewave", 4 },
877    { "stall_wave_gen", 5 },
878    { "sysmsg", 15 },
879    { "system", 15 }
880};
881
882static const size_t sendMessageNamesGCN14MapSize = sizeof(sendMessageNamesGCN14Map) /
883            sizeof(std::pair<const char*, uint16_t>);
884
885static const char* sendMsgGSOPTable[] =
886{ "nop", "cut", "emit", "emit_cut" };
887
888static const size_t sendMsgGSOPTableSize = sizeof(sendMsgGSOPTable) / sizeof(const char*);
889
890bool GCNAsmUtils::parseSOPPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
891                  const char* instrPlace, const char* linePtr, uint16_t arch,
892                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
893                  GCNEncSize gcnEncSize)
894{
895    const char* end = asmr.line+asmr.lineSize;
896    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
897    bool good = true;
898    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
899    if (gcnEncSize==GCNEncSize::BIT64)
900        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for SOPP encoding")
901   
902    uint16_t imm16 = 0;
903    std::unique_ptr<AsmExpression> imm16Expr;
904    switch (gcnInsn.mode&GCN_MASK1)
905    {
906        case GCN_IMM_REL:
907        {
908            // parse relative address
909            uint64_t value = 0;
910            if (!getJumpValueArg(asmr, value, imm16Expr, linePtr))
911                return false;
912            if (imm16Expr==nullptr)
913            {
914                int64_t offset = (int64_t(value)-int64_t(output.size())-4);
915                if (offset & 3)
916                    ASM_NOTGOOD_BY_ERROR(linePtr, "Jump is not aligned to word!")
917                offset >>= 2;
918                if (offset > INT16_MAX || offset < INT16_MIN)
919                    ASM_NOTGOOD_BY_ERROR(linePtr, "Jump out of range")
920                imm16 = offset;
921                Assembler& asmr = gcnAsm->assembler;
922                // add codeflow entry
923                if (good)
924                    asmr.sections[asmr.currentSection].addCodeFlowEntry({ 
925                        size_t(asmr.currentOutPos), size_t(value),
926                        gcnInsn.code1==2 ? AsmCodeFlowType::JUMP :
927                            AsmCodeFlowType::CJUMP });
928            }
929            break;
930        }
931        case GCN_IMM_LOCKS:
932        {
933            /* parse locks for s_waitcnt */
934            char name[20];
935            bool haveLgkmCnt = false;
936            bool haveExpCnt = false;
937            bool haveVMCnt = false;
938            imm16 = isGCN14 ? 0xcf7f : 0xf7f;
939            while (true)
940            {
941                skipSpacesToEnd(linePtr, end);
942                const char* funcNamePlace = linePtr;
943                name[0] = 0;
944                // get function name
945                good &= getNameArgS(asmr, 20, name, linePtr, "function name", true);
946                toLowerString(name);
947               
948                cxuint bitPos = 0, bitMask = UINT_MAX;
949                bool goodCnt = true;
950                bool doVMCnt = false;
951                // select bitfield for lock
952                if (::strcmp(name, "vmcnt")==0)
953                {
954                    if (haveVMCnt)
955                        asmr.printWarning(funcNamePlace, "vmcnt was already defined");
956                    bitPos = 0;
957                    bitMask = isGCN14 ? 63 : 15;
958                    doVMCnt = haveVMCnt = true;
959                }
960                else if (::strcmp(name, "lgkmcnt")==0)
961                {
962                    if (haveLgkmCnt)
963                        asmr.printWarning(funcNamePlace, "lgkmcnt was already defined");
964                    bitPos = 8;
965                    bitMask = 15;
966                    haveLgkmCnt = true;
967                }
968                else if (::strcmp(name, "expcnt")==0)
969                {
970                    if (haveExpCnt)
971                        asmr.printWarning(funcNamePlace, "expcnt was already defined");
972                    bitPos = 4;
973                    bitMask = 7;
974                    haveExpCnt = true;
975                }
976                else
977                    ASM_NOTGOOD_BY_ERROR1(goodCnt = good, funcNamePlace,
978                                    "Expected vmcnt, lgkmcnt or expcnt")
979               
980                skipSpacesToEnd(linePtr, end);
981                if (linePtr==end || *linePtr!='(')
982                {
983                    if (goodCnt) // only if cnt has been parsed (do not duplicate errors)
984                        asmr.printError(funcNamePlace, "Expected vmcnt, lgkmcnt or expcnt");
985                    return false;
986                }
987                skipCharAndSpacesToEnd(linePtr, end);
988                const char* argPlace = linePtr;
989                uint64_t value;
990                // parse value for lock
991                if (getAbsoluteValueArg(asmr, value, linePtr, true))
992                {
993                    if (value > bitMask)
994                        asmr.printWarning(argPlace, "Value out of range");
995                    if (!isGCN14 || !doVMCnt)
996                        imm16 = (imm16 & ~(bitMask<<bitPos)) | ((value&bitMask)<<bitPos);
997                    else // vmcnt for GFX9
998                        imm16 = (imm16 & 0x3ff0) | ((value&15) | ((value&0x30)<<10));
999                }
1000                else
1001                    good = false;
1002                skipSpacesToEnd(linePtr, end);
1003                if (linePtr==end || *linePtr!=')')
1004                    ASM_FAIL_BY_ERROR(linePtr, "Unterminated function")
1005                // ampersand
1006                skipCharAndSpacesToEnd(linePtr, end);
1007                if (linePtr==end)
1008                    break;
1009                if (linePtr[0] == '&')
1010                    ++linePtr;
1011            }
1012            break;
1013        }
1014        case GCN_IMM_MSGS:
1015        {
1016            char name[25];
1017            const char* funcNamePlace = linePtr;
1018            if (!getNameArg(asmr, 25, name, linePtr, "function name", true))
1019                return false;
1020            toLowerString(name);
1021            skipSpacesToEnd(linePtr, end);
1022            if (::strcmp(name, "sendmsg")!=0 || linePtr==end || *linePtr!='(')
1023                ASM_FAIL_BY_ERROR(funcNamePlace, "Expected sendmsg function")
1024            skipCharAndSpacesToEnd(linePtr, end);
1025           
1026            const char* funcArg1Place = linePtr;
1027            size_t sendMessage = 0;
1028            if (linePtr == end || *linePtr != '@')
1029            {
1030                // parse message name
1031                if (getNameArg(asmr, 25, name, linePtr, "message name", true))
1032                {
1033                    toLowerString(name);
1034                    const size_t msgNameIndex = (::strncmp(name, "msg_", 4) == 0) ? 4 : 0;
1035                    // choose message name table
1036                    auto msgMap = isGCN14 ? sendMessageNamesGCN14Map :
1037                            sendMessageNamesMap;
1038                    const size_t msgMapSize = isGCN14 ?
1039                            sendMessageNamesGCN14MapSize : sendMessageNamesMapSize;
1040                    // find message name
1041                    size_t index = binaryMapFind(msgMap, msgMap + msgMapSize,
1042                            name+msgNameIndex, CStringLess()) - msgMap;
1043                    if (index != msgMapSize &&
1044                        // save_wave only for GCN1.2
1045                        (msgMap[index].second!=4 || (arch&ARCH_GCN_1_2_4)!=0))
1046                        sendMessage = msgMap[index].second;
1047                    else
1048                        ASM_NOTGOOD_BY_ERROR(funcArg1Place, "Unknown message")
1049                }
1050                else
1051                    good = false;
1052            }
1053            else
1054            {
1055                // parametrization
1056                linePtr++;
1057                good &= parseImm(asmr, linePtr, sendMessage, nullptr, 4, WS_UNSIGNED);
1058            }
1059           
1060            cxuint gsopIndex = 0;
1061            cxuint streamId = 0;
1062            if (sendMessage == 2 || sendMessage == 3)
1063            {
1064                if (!skipRequiredComma(asmr, linePtr))
1065                    return false;
1066                skipSpacesToEnd(linePtr, end);
1067               
1068                // parse GSOP parameter
1069                if (linePtr == end || *linePtr != '@')
1070                {
1071                    const char* funcArg2Place = linePtr;
1072                    if (getNameArg(asmr, 20, name, linePtr, "GSOP", true))
1073                    {
1074                        toLowerString(name);
1075                        // handle gs_op prefix
1076                        const size_t gsopNameIndex = (::strncmp(name, "gs_op_", 6) == 0)
1077                                    ? 6 : 0;
1078                        for (gsopIndex = 0; gsopIndex < 4; gsopIndex++)
1079                            if (::strcmp(name+gsopNameIndex,
1080                                        sendMsgGSOPTable[gsopIndex])==0)
1081                                break;
1082                        if (gsopIndex==2 && gsopNameIndex==0)
1083                        {
1084                            /* 'emit-cut' handling */
1085                            if (linePtr+4<=end && ::strncasecmp(linePtr, "-cut", 4)==0 &&
1086                                (linePtr==end || (!isAlnum(*linePtr) && *linePtr!='_' &&
1087                                *linePtr!='$' && *linePtr!='.')))
1088                            {
1089                                linePtr+=4;
1090                                gsopIndex++;
1091                            }
1092                        }
1093                        if (gsopIndex == sendMsgGSOPTableSize)
1094                        {
1095                            // not found
1096                            gsopIndex = 0;
1097                            ASM_NOTGOOD_BY_ERROR(funcArg2Place, "Unknown GSOP")
1098                        }
1099                    }
1100                    else
1101                        good = false;
1102                }
1103                else
1104                {
1105                    // parametrization
1106                    linePtr++;
1107                    good &= parseImm(asmr, linePtr, gsopIndex, nullptr, 3, WS_UNSIGNED);
1108                }
1109               
1110                if (gsopIndex!=0)
1111                {
1112                    if (!skipRequiredComma(asmr, linePtr))
1113                        return false;
1114                   
1115                    uint64_t value;
1116                    skipSpacesToEnd(linePtr, end);
1117                    const char* func3ArgPlace = linePtr;
1118                    // parse STREAMID (third argument of sendmsg)
1119                    good &= getAbsoluteValueArg(asmr, value, linePtr, true);
1120                    if (value > 3)
1121                        asmr.printWarning(func3ArgPlace,
1122                                  "StreamId (3rd argument) out of range");
1123                    streamId = value&3;
1124                }
1125            }
1126            skipSpacesToEnd(linePtr, end);
1127            if (linePtr==end || *linePtr!=')')
1128                ASM_FAIL_BY_ERROR(linePtr, "Unterminated sendmsg function")
1129            ++linePtr;
1130            imm16 = sendMessage | (gsopIndex<<4) | (streamId<<8);
1131            break;
1132        }
1133        case GCN_IMM_NONE:
1134            // if s_endpgm or s_endpgm_saved then add 'end' to code flow entries
1135            if (gcnInsn.code1 == 1 || gcnInsn.code1 == 27)
1136                asmr.sections[asmr.currentSection].addCodeFlowEntry({ 
1137                    size_t(asmr.currentOutPos+4), size_t(0), AsmCodeFlowType::END });
1138            break;
1139        default:
1140            good &= parseImm(asmr, linePtr, imm16, &imm16Expr);
1141    }
1142    /// if errors
1143    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1144        return false;
1145   
1146    // put data (instruction word)
1147    uint32_t word;
1148    SLEV(word, 0xbf800000U | imm16 | (uint32_t(gcnInsn.code1)<<16));
1149   
1150    if (imm16Expr!=nullptr)
1151        imm16Expr->setTarget(AsmExprTarget(((gcnInsn.mode&GCN_MASK1) == GCN_IMM_REL) ?
1152                GCNTGT_SOPJMP : GCNTGT_SOPKSIMM16, asmr.currentSection, output.size()));
1153   
1154    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word), 
1155            reinterpret_cast<cxbyte*>(&word)+4);
1156    /// prevent freeing expression
1157    imm16Expr.release();
1158    return true;
1159}
1160
1161bool GCNAsmUtils::parseSMRDEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1162                  const char* instrPlace, const char* linePtr, uint16_t arch,
1163                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1164                  GCNEncSize gcnEncSize)
1165{
1166    const char* end = asmr.line+asmr.lineSize;
1167    bool good = true;
1168    if (gcnEncSize==GCNEncSize::BIT64)
1169        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for SMRD encoding")
1170    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1171   
1172    RegRange dstReg(0, 0);
1173    RegRange sbaseReg(0, 0);
1174    RegRange soffsetReg(0, 0);
1175    cxbyte soffsetVal = 0;
1176    std::unique_ptr<AsmExpression> soffsetExpr;
1177    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
1178    if (mode1 == GCN_SMRD_ONLYDST)
1179    {
1180        // parse SDST (SGPR)
1181        gcnAsm->setCurrentRVU(0);
1182        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1183                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SMRD_SDST, true,
1184                       INSTROP_SYMREGRANGE|INSTROP_WRITE);
1185    }
1186    else if (mode1 != GCN_ARG_NONE)
1187    {
1188        const cxuint dregsNum = 1<<((gcnInsn.mode & GCN_DSIZE_MASK)>>GCN_SHIFT2);
1189        // parse SDST (SGPR's (1-16 registers))
1190        gcnAsm->setCurrentRVU(0);
1191        good &= parseSRegRange(asmr, linePtr, dstReg, arch, dregsNum,
1192                   GCNFIELD_SMRD_SDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE);
1193        if (!skipRequiredComma(asmr, linePtr))
1194            return false;
1195       
1196        // parse SBASE (2 or 4 registers)
1197        gcnAsm->setCurrentRVU(1);
1198        good &= parseSRegRange(asmr, linePtr, sbaseReg, arch,
1199                   (gcnInsn.mode&GCN_SBASE4)?4:2, GCNFIELD_SMRD_SBASE, true,
1200                   INSTROP_SYMREGRANGE|INSTROP_READ);
1201        if (!skipRequiredComma(asmr, linePtr))
1202            return false;
1203       
1204        skipSpacesToEnd(linePtr, end);
1205        if (linePtr==end || *linePtr!='@')
1206        {
1207            // parse SOFFSET (SGPR)
1208            gcnAsm->setCurrentRVU(2);
1209            good &= parseSRegRange(asmr, linePtr, soffsetReg, arch, 1,
1210                       GCNFIELD_SMRD_SOFFSET, false, INSTROP_SYMREGRANGE|INSTROP_READ);
1211        }
1212        else // '@' prefix (treat next as expression)
1213            skipCharAndSpacesToEnd(linePtr, end);
1214       
1215        if (!soffsetReg)
1216        {
1217            // parse immediate
1218            soffsetReg.start = 255; // indicate an immediate
1219            good &= parseImm(asmr, linePtr, soffsetVal, &soffsetExpr, 0, WS_UNSIGNED);
1220        }
1221    }
1222    /// if errors
1223    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1224        return false;
1225   
1226    if (soffsetExpr!=nullptr)
1227        soffsetExpr->setTarget(AsmExprTarget(GCNTGT_SMRDOFFSET, asmr.currentSection,
1228                       output.size()));
1229   
1230    // put data (instruction word)
1231    uint32_t word;
1232    SLEV(word, 0xc0000000U | (uint32_t(gcnInsn.code1)<<22) |
1233            (uint32_t(dstReg.bstart())<<15) |
1234            ((sbaseReg.bstart()&~1U)<<8) | ((soffsetReg.isVal(255)) ? 0x100 : 0) |
1235            ((soffsetReg.isVal(255)) ? soffsetVal : soffsetReg.bstart()));
1236    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word), 
1237            reinterpret_cast<cxbyte*>(&word)+4);
1238    /// prevent freeing expression
1239    soffsetExpr.release();
1240   
1241    // update SGPR counting and VCC usage (regflags)
1242    if (dstReg && !dstReg.isRegVar())
1243    {
1244        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1245        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1246    }
1247    if (!sbaseReg.isRegVar())
1248        updateRegFlags(gcnRegs.regFlags, sbaseReg.start, arch);
1249    if (!soffsetReg.isRegVar())
1250        updateRegFlags(gcnRegs.regFlags, soffsetReg.start, arch);
1251    return true;
1252}
1253
1254bool GCNAsmUtils::parseSMEMEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1255                  const char* instrPlace, const char* linePtr, uint16_t arch,
1256                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1257                  GCNEncSize gcnEncSize)
1258{
1259    const char* end = asmr.line+asmr.lineSize;
1260    bool good = true;
1261    if (gcnEncSize==GCNEncSize::BIT32)
1262        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for SMEM encoding")
1263   
1264    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1265    RegRange dataReg(0, 0);
1266    RegRange sbaseReg(0, 0);
1267    RegRange soffsetReg(0, 0);
1268    uint32_t soffsetVal = 0;
1269    std::unique_ptr<AsmExpression> soffsetExpr;
1270    std::unique_ptr<AsmExpression> simm7Expr;
1271    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
1272    const bool isGCN14 = (arch & ARCH_RXVEGA) != 0;
1273   
1274    const char* soffsetPlace = nullptr;
1275    AsmSourcePos soffsetPos;
1276   
1277    if (mode1 == GCN_SMRD_ONLYDST)
1278    {
1279        // parse SDST (SGPR)
1280        gcnAsm->setCurrentRVU(0);
1281        good &= parseSRegRange(asmr, linePtr, dataReg, arch,
1282                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SMRD_SDST, true,
1283                       INSTROP_SYMREGRANGE|INSTROP_WRITE);
1284    }
1285    else if (mode1 != GCN_ARG_NONE)
1286    {
1287        const cxuint dregsNum = 1<<((gcnInsn.mode & GCN_DSIZE_MASK)>>GCN_SHIFT2);
1288        // parse SDST (SGPR's (1-16 registers))
1289        gcnAsm->setCurrentRVU(0);
1290        if ((mode1 & GCN_SMEM_SDATA_IMM)==0)
1291            good &= parseSRegRange(asmr, linePtr, dataReg, arch, dregsNum,
1292                    GCNFIELD_SMRD_SDST, true, INSTROP_SYMREGRANGE|
1293                    ((gcnInsn.mode & GCN_MLOAD) != 0 ? INSTROP_WRITE : INSTROP_READ));
1294        else
1295            good &= parseImm(asmr, linePtr, dataReg.start, &simm7Expr, 7);
1296        if (!skipRequiredComma(asmr, linePtr))
1297            return false;
1298       
1299        // parse SBASE (2 or 4 SGPR's)
1300        gcnAsm->setCurrentRVU(1);
1301        good &= parseSRegRange(asmr, linePtr, sbaseReg, arch,
1302                   (gcnInsn.mode&GCN_SBASE4)?4:2, GCNFIELD_SMRD_SBASE, true,
1303                   INSTROP_SYMREGRANGE|INSTROP_READ);
1304        if (!skipRequiredComma(asmr, linePtr))
1305            return false;
1306       
1307        skipSpacesToEnd(linePtr, end);
1308        if (linePtr==end || *linePtr!='@')
1309        {
1310            // parse SOFFSET (SGPR)
1311            gcnAsm->setCurrentRVU(2);
1312            const char* soffsetPlace = linePtr;
1313            good &= parseSRegRange(asmr, linePtr, soffsetReg, arch, 1,
1314                       GCNFIELD_SMRD_SOFFSET, false, INSTROP_SYMREGRANGE|INSTROP_READ);
1315            if (good && !isGCN14 && (gcnInsn.mode & GCN_MLOAD) == 0 && soffsetReg &&
1316                    !soffsetReg.isVal(124))
1317                // if no M0 register
1318                ASM_NOTGOOD_BY_ERROR(soffsetPlace,
1319                        "Store/Atomic SMEM instructions accepts only M0 register")
1320        }
1321        else // '@' prefix (treat next as expression)
1322            skipCharAndSpacesToEnd(linePtr, end);
1323       
1324        if (!soffsetReg)
1325        {
1326            // parse immediate
1327            soffsetReg.start = 255; // indicate an immediate
1328            skipSpacesToEnd(linePtr, end);
1329            soffsetPlace = linePtr;
1330            soffsetPos = asmr.getSourcePos(soffsetPlace);
1331            good &= parseImm(asmr, linePtr, soffsetVal, &soffsetExpr,
1332                        isGCN14 ? 21 : 20, isGCN14 ? WS_BOTH : WS_UNSIGNED);
1333        }
1334    }
1335    bool haveGlc = false;
1336    bool haveNv = false;
1337    bool haveOffset = false;
1338    // parse modifiers
1339    while (linePtr != end)
1340    {
1341        skipSpacesToEnd(linePtr, end);
1342        if (linePtr == end)
1343            break;
1344        const char* modPlace = linePtr;
1345        char name[10];
1346        if (getNameArgS(asmr, 10, name, linePtr, "modifier"))
1347        {
1348            toLowerString(name);
1349            if (::strcmp(name, "glc")==0)
1350                good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
1351            else if (isGCN14 && ::strcmp(name, "nv")==0)
1352                good &= parseModEnable(asmr, linePtr, haveNv, "nv modifier");
1353            else if (isGCN14 && ::strcmp(name, "offset")==0)
1354            {
1355                // parse offset and it parameter: offset:XXX
1356                if (parseModImm(asmr, linePtr, soffsetVal, &soffsetExpr, "offset",
1357                        21, WS_BOTH))
1358                {
1359                    if (haveOffset)
1360                        asmr.printWarning(modPlace, "Offset is already defined");
1361                    haveOffset = true;
1362                    if (soffsetReg.isVal(255))
1363                        // illegal second offset
1364                        ASM_NOTGOOD_BY_ERROR(modPlace, "Illegal second offset");
1365                }
1366                else
1367                    good = false;
1368            }
1369            else
1370                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown SMEM modifier")
1371        }
1372        else
1373            good = false;
1374    }
1375    /// if errors
1376    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1377        return false;
1378   
1379    // set expression target for offsets and immediates
1380    if (soffsetExpr!=nullptr)
1381        soffsetExpr->setTarget(AsmExprTarget(isGCN14 ?
1382                    GCNTGT_SMEMOFFSETVEGA : GCNTGT_SMEMOFFSET,
1383                    asmr.currentSection, output.size()));
1384    if (simm7Expr!=nullptr)
1385        simm7Expr->setTarget(AsmExprTarget(GCNTGT_SMEMIMM, asmr.currentSection,
1386                       output.size()));
1387    // TODO: add RVU modification for atomics
1388    bool dataToRead = false;
1389    bool dataToWrite = false;
1390    if (dataReg)
1391    {
1392        dataToWrite = ((gcnInsn.mode&GCN_MLOAD) != 0 ||
1393                ((gcnInsn.mode&GCN_MATOMIC)!=0 && haveGlc));
1394        dataToRead = (gcnInsn.mode&GCN_MLOAD)==0 || (gcnInsn.mode&GCN_MATOMIC)!=0;
1395    }
1396   
1397    gcnAsm->instrRVUs[0].rwFlags = (dataToRead ? ASMRVU_READ : 0) |
1398            (dataToWrite ? ASMRVU_WRITE : 0);
1399    // check fcmpswap
1400    if ((gcnInsn.mode & GCN_MHALFWRITE) != 0 && dataToWrite &&
1401            gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
1402    {
1403        // fix access
1404        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
1405        uint16_t size = rvu.rend-rvu.rstart;
1406        rvu.rend = rvu.rstart + (size>>1);
1407        AsmRegVarUsage& nextRvu = gcnAsm->instrRVUs[3];
1408        nextRvu = rvu;
1409        nextRvu.regField = GCNFIELD_SMRD_SDSTH;
1410        nextRvu.rstart += (size>>1);
1411        nextRvu.rend = rvu.rstart + size;
1412        nextRvu.rwFlags = ASMRVU_READ;
1413        nextRvu.align = 0;
1414    }
1415   
1416    // put data (2 instruction words)
1417    uint32_t words[2];
1418    SLEV(words[0], 0xc0000000U | (uint32_t(gcnInsn.code1)<<18) | (dataReg.bstart()<<6) |
1419            (sbaseReg.bstart()>>1) |
1420            // enable IMM if soffset is immediate or haveOffset with SGPR
1421            ((soffsetReg.isVal(255) || haveOffset) ? 0x20000 : 0) |
1422            (haveGlc ? 0x10000 : 0) | (haveNv ? 0x8000 : 0) | (haveOffset ? 0x4000 : 0));
1423    SLEV(words[1], (
1424            // store IMM OFFSET if offset: or IMM offset instead SGPR
1425            ((soffsetReg.isVal(255) || haveOffset) ? soffsetVal : soffsetReg.bstart())) |
1426            // store SGPR in SOFFSET if have offset and have SGPR offset
1427                ((haveOffset && !soffsetReg.isVal(255)) ? (soffsetReg.bstart()<<25) : 0));
1428   
1429    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
1430            reinterpret_cast<cxbyte*>(words+2));
1431    /// prevent freeing expression
1432    soffsetExpr.release();
1433    simm7Expr.release();
1434   
1435    // update SGPR counting and VCC usage (regflags)
1436    if (!dataReg.isRegVar() && dataToWrite)
1437    {
1438        updateSGPRsNum(gcnRegs.sgprsNum, dataReg.end-1, arch);
1439        updateRegFlags(gcnRegs.regFlags, dataReg.start, arch);
1440    }
1441    if (!sbaseReg.isRegVar())
1442        updateRegFlags(gcnRegs.regFlags, sbaseReg.start, arch);
1443    if (!soffsetReg.isRegVar())
1444        updateRegFlags(gcnRegs.regFlags, soffsetReg.start, arch);
1445    return true;
1446}
1447
1448// choose between 64-bit immediate (FP64) and 32-bit immediate
1449static Flags correctOpType(uint32_t regsNum, Flags typeMask)
1450{
1451    return (regsNum==2 && (typeMask==INSTROP_FLOAT || typeMask==INSTROP_INT)) ?
1452        INSTROP_V64BIT : typeMask;
1453}
1454
1455static void encodeVOPWords(uint32_t vop0Word, cxbyte modifiers,
1456        const VOPExtraModifiers& extraMods, const GCNOperand& src0Op,
1457        const GCNOperand& src1Op, uint32_t immValue, uint16_t mode1,
1458        // dstMod - (16bits lower value, 16bit - use dstMod instead std encoding
1459        uint32_t inDstMod, cxuint& wordsNum, uint32_t* words)
1460{
1461    // VOP2 encoding
1462    cxuint src0out = src0Op.range.bstart();
1463    if (extraMods.needSDWA)
1464        src0out = 0xf9;
1465    else if (extraMods.needDPP)
1466        src0out = 0xfa;
1467    SLEV(words[0], vop0Word | uint32_t(src0out));
1468    if (extraMods.needSDWA)
1469    {
1470        const uint32_t dstMod = (inDstMod & 0x10000) ? (inDstMod&0xff00) :
1471                    ((uint32_t(extraMods.dstSel)<<8) |
1472                    (uint32_t(extraMods.dstUnused)<<11) |
1473                    ((modifiers & VOP3_CLAMP) ? 0x2000 : 0) |
1474                    (uint32_t(modifiers & 3) << 14));
1475        // if SDWA encoding
1476        SLEV(words[wordsNum++], dstMod | (src0Op.range.bstart()&0xff) |
1477                (uint32_t(extraMods.src0Sel)<<16) |
1478                ((src0Op.vopMods&VOPOP_SEXT) ? (1U<<19) : 0) |
1479                ((src0Op.vopMods&VOPOP_NEG) ? (1U<<20) : 0) |
1480                ((src0Op.vopMods&VOPOP_ABS) ? (1U<<21) : 0) |
1481                (uint32_t(extraMods.src1Sel)<<24) |
1482                ((src1Op.vopMods&VOPOP_SEXT) ? (1U<<27) : 0) |
1483                ((src1Op.vopMods&VOPOP_NEG) ? (1U<<28) : 0) |
1484                ((src1Op.vopMods&VOPOP_ABS) ? (1U<<29) : 0) |
1485                (src0Op.range.isNonVGPR() ? (1U<<23) : 0) |
1486                (src1Op.range.isNonVGPR() ? (1U<<31) : 0));
1487    }
1488    else if (extraMods.needDPP)
1489        // DPP encoding
1490        SLEV(words[wordsNum++], (src0Op.range.bstart()&0xff) | (extraMods.dppCtrl<<8) |
1491                ((modifiers&VOP3_BOUNDCTRL) ? (1U<<19) : 0) |
1492                ((src0Op.vopMods&VOPOP_NEG) ? (1U<<20) : 0) |
1493                ((src0Op.vopMods&VOPOP_ABS) ? (1U<<21) : 0) |
1494                ((src1Op.vopMods&VOPOP_NEG) ? (1U<<22) : 0) |
1495                ((src1Op.vopMods&VOPOP_ABS) ? (1U<<23) : 0) |
1496                (uint32_t(extraMods.bankMask)<<24) |
1497                (uint32_t(extraMods.rowMask)<<28));
1498    else if (src0Op.range.isVal(255)) // otherwise we check for immediate/literal value
1499        SLEV(words[wordsNum++], src0Op.value);
1500    else if (src1Op.range.isVal(255))
1501        // literal from SRC1
1502        SLEV(words[wordsNum++], src1Op.value);
1503    else if (mode1 == GCN_ARG1_IMM || mode1 == GCN_ARG2_IMM)
1504        SLEV(words[wordsNum++], immValue);
1505}
1506
1507bool GCNAsmUtils::parseVOP2Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1508                  const char* instrPlace, const char* linePtr, uint16_t arch,
1509                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1510                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1511{
1512    const char* end = asmr.line+asmr.lineSize;
1513    bool good = true;
1514    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
1515    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
1516    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1517    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
1518    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1519   
1520    RegRange dstReg(0, 0);
1521    RegRange dstCCReg(0, 0);
1522    RegRange srcCCReg(0, 0);
1523    gcnAsm->setCurrentRVU(0);
1524    if (mode1 == GCN_DS1_SGPR)
1525        // if SGPRS as destination
1526        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1527                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_SDST, true,
1528                       INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1529    else
1530    {
1531         // if VGPRS as destination
1532        bool v_mac = ::strncmp(gcnInsn.mnemonic, "v_mac_", 6)==0;
1533        good &= parseVRegRange(asmr, linePtr, dstReg, (gcnInsn.mode&GCN_REG_DST_64)?2:1,
1534                        GCNFIELD_VOP_VDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE|
1535                              (v_mac?INSTROP_READ:0));
1536    }
1537   
1538    const bool haveDstCC = mode1 == GCN_DS2_VCC || mode1 == GCN_DST_VCC;
1539    const bool haveSrcCC = mode1 == GCN_DS2_VCC || mode1 == GCN_SRC2_VCC;
1540    if (haveDstCC) /* VOP3b */
1541    {
1542        if (!skipRequiredComma(asmr, linePtr))
1543            return false;
1544        // parse SDST (in place VCC) (2 SGPR's)
1545        gcnAsm->setCurrentRVU(1);
1546        good &= parseSRegRange(asmr, linePtr, dstCCReg, arch, 2, GCNFIELD_VOP3_SDST1, true,
1547                               INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1548    }
1549   
1550    GCNOperand src0Op{}, src1Op{};
1551    std::unique_ptr<AsmExpression> src0OpExpr, src1OpExpr;
1552    const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1553            (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1554   
1555    const Flags vopOpModFlags = ((haveDstCC && !isGCN12) ?
1556                    INSTROP_VOP3NEG : INSTROP_VOP3MODS);
1557    if (!skipRequiredComma(asmr, linePtr))
1558        return false;
1559    cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1560    // parse SRC0 (can be VGPR, SGPR, scalar source, LDS, literal or constant)
1561    gcnAsm->setCurrentRVU(2);
1562    good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1563            correctOpType(regsNum, literalConstsFlags) | vopOpModFlags |
1564            INSTROP_SGPR_UNALIGNED|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1565            INSTROP_READ, GCNFIELD_VOP_SRC0);
1566   
1567    uint32_t immValue = 0;
1568    std::unique_ptr<AsmExpression> immExpr;
1569    if (mode1 == GCN_ARG1_IMM)
1570    {
1571        // for V_MADMK_FXxx instruction
1572        if (!skipRequiredComma(asmr, linePtr))
1573            return false;
1574        good &= parseLiteralImm(asmr, linePtr, immValue, &immExpr, literalConstsFlags);
1575    }
1576   
1577    if (!skipRequiredComma(asmr, linePtr))
1578        return false;
1579   
1580    bool sgprRegInSrc1 = mode1 == GCN_DS1_SGPR || mode1 == GCN_SRC1_SGPR;
1581    skipSpacesToEnd(linePtr, end);
1582    regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
1583    gcnAsm->setCurrentRVU(3);
1584    // parse SRC1 (can be VGPR, SGPR, scalar source, LDS, literal or constant)
1585    //  except when SGPR for SRC1 when instructions accepts SGPR in this place
1586    good &= parseOperand(asmr, linePtr, src1Op, &src1OpExpr, arch, regsNum,
1587            correctOpType(regsNum, literalConstsFlags) | vopOpModFlags |
1588            (!sgprRegInSrc1 ? INSTROP_VREGS : 0)|INSTROP_SSOURCE|INSTROP_SREGS|
1589            INSTROP_SGPR_UNALIGNED |
1590            (src0Op.range.start==255 ? INSTROP_ONLYINLINECONSTS : 0)|
1591            INSTROP_READ, (!sgprRegInSrc1) ? GCNFIELD_VOP_VSRC1 : GCNFIELD_VOP_SSRC1);
1592   
1593    if (mode1 == GCN_ARG2_IMM)
1594    {
1595        // for V_MADAK_Fxx instruction
1596        if (!skipRequiredComma(asmr, linePtr))
1597            return false;
1598        good &= parseLiteralImm(asmr, linePtr, immValue, &immExpr, literalConstsFlags);
1599    }
1600    else if (haveSrcCC)
1601    {
1602        if (!skipRequiredComma(asmr, linePtr))
1603            return false;
1604        gcnAsm->setCurrentRVU(4);
1605        // parse SSRC (VCC) (2 SGPR's)
1606        good &= parseSRegRange(asmr, linePtr, srcCCReg, arch, 2, GCNFIELD_VOP3_SSRC, true,
1607                       INSTROP_SYMREGRANGE|INSTROP_UNALIGNED|INSTROP_READ);
1608    }
1609   
1610    // modifiers
1611    cxbyte modifiers = 0;
1612    VOPExtraModifiers extraMods{};
1613    VOPOpModifiers opMods{};
1614    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, 3,
1615                    (isGCN12) ? &extraMods : nullptr,
1616                    ((!haveDstCC || isGCN12) ? PARSEVOP_WITHCLAMP : 0)|PARSEVOP_WITHSEXT|
1617                    ((isGCN14 && !haveDstCC) ? PARSEVOP_WITHOPSEL : 0));
1618    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1619        return false;
1620   
1621    // apply VOP modifiers (abs,neg,sext) to operands from modifiers
1622    if (src0Op)
1623        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1624                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1625                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1626    if (src1Op)
1627        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
1628                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
1629                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
1630   
1631    extraMods.needSDWA |= ((src0Op.vopMods | src1Op.vopMods) & VOPOP_SEXT) != 0;
1632    // determine whether VOP3 encoding is needed
1633    bool vop3 = /* src1=sgprs and not (DS1_SGPR|src1_SGPR) */
1634        //((src1Op.range.start<256) ^ sgprRegInSrc1) ||
1635        ((!isGCN14 || !extraMods.needSDWA) &&
1636                (src1Op.range.isNonVGPR() ^ sgprRegInSrc1)) ||
1637        (!isGCN12 && (src0Op.vopMods!=0 || src1Op.vopMods!=0)) ||
1638        (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1639            /* exclude OMOD if RXVEGA and SDWA used */
1640            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0 ||
1641        /* srcCC!=VCC or dstCC!=VCC */
1642        //(haveDstCC && dstCCReg.start!=106) || (haveSrcCC && srcCCReg.start!=106) ||
1643        (haveDstCC && !dstCCReg.isVal(106)) || (haveSrcCC && !srcCCReg.isVal(106)) ||
1644        ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1645   
1646    if ((src0Op.range.isVal(255) || src1Op.range.isVal(255)) &&
1647        (src0Op.range.isSGPR() || src0Op.range.isVal(124) ||
1648         src1Op.range.isSGPR() || src1Op.range.isVal(124)))
1649        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SGPR or M0 is illegal")
1650   
1651    if (vop3) // modify fields in reg usage
1652    {
1653        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1654        if (rvus[0].regField != ASMFIELD_NONE)
1655            rvus[0].regField = (rvus[0].regField==GCNFIELD_VOP_VDST) ? GCNFIELD_VOP3_VDST :
1656                            GCNFIELD_VOP3_SDST0;
1657        if (rvus[2].regField != ASMFIELD_NONE)
1658            rvus[2].regField = GCNFIELD_VOP3_SRC0;
1659        if (rvus[3].regField != ASMFIELD_NONE)
1660            rvus[3].regField = GCNFIELD_VOP3_SRC1;
1661    }
1662   
1663    // count number SGPR operands readed by instruction
1664    cxuint sgprsReaded = 0;
1665    if (src0Op.range.isSGPR())
1666        sgprsReaded++;
1667    if (src1Op.range.isSGPR() && !regRangeCanEqual(src0Op.range, src1Op.range))
1668        sgprsReaded++;
1669    if (haveSrcCC)
1670        // check for third operand (SSRC)
1671        if (!regRangeCanEqual(src0Op.range, srcCCReg) &&
1672            !regRangeCanEqual(src1Op.range, srcCCReg))
1673            sgprsReaded++;
1674   
1675    if (sgprsReaded >= 2)
1676        /* include VCCs (???) */
1677        ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
1678   
1679    const bool needImm = (src0Op.range.start==255 || src1Op.range.start==255 ||
1680             mode1 == GCN_ARG1_IMM || mode1 == GCN_ARG2_IMM);
1681   
1682    bool sextFlags = ((src0Op.vopMods|src1Op.vopMods) & VOPOP_SEXT);
1683    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || sextFlags ||
1684                gcnVOPEnc!=GCNVOPEnc::NORMAL))
1685    {
1686        /* if VOP_SDWA or VOP_DPP is required */
1687        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1688                    gcnVOPEnc, src0Op, extraMods, instrPlace))
1689            return false;
1690        if (gcnAsm->instrRVUs[2].regField != ASMFIELD_NONE)
1691            gcnAsm->instrRVUs[2].regField = GCNFIELD_DPPSDWA_SRC0;
1692       
1693        if (extraMods.needSDWA && isGCN14)
1694        {
1695            // fix for extra type operand from SDWA
1696            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1697            if (rvus[2].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1698                rvus[2].regField = GCNFIELD_DPPSDWA_SSRC0;
1699            if (rvus[3].regField != ASMFIELD_NONE && src1Op.range.isNonVGPR())
1700                rvus[3].regField = GCNFIELD_VOP_SSRC1;
1701        }
1702    }
1703    else if (isGCN12 && ((src0Op.vopMods|src1Op.vopMods) & ~VOPOP_SEXT)!=0 && !sextFlags)
1704        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1705        vop3 = true;
1706   
1707    if (isGCN12 && vop3 && haveDstCC && ((src0Op.vopMods|src1Op.vopMods) & VOPOP_ABS) != 0)
1708        ASM_FAIL_BY_ERROR(instrPlace, "Abs modifier is illegal for VOP3B encoding")
1709    if (vop3 && needImm)
1710        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1711   
1712    if (!checkGCNVOPEncoding(asmr, instrPlace, gcnVOPEnc, &extraMods))
1713        return false;
1714   
1715    // set target expressions if needed
1716    if (src0OpExpr!=nullptr)
1717        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1718                      output.size()));
1719    if (src1OpExpr!=nullptr)
1720        src1OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1721                      output.size()));
1722    if (immExpr!=nullptr)
1723        immExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1724                      output.size()));
1725   
1726    // put data (instruction words)
1727    cxuint wordsNum = 1;
1728    uint32_t words[2];
1729    if (!vop3)
1730        // VOP2 encoding
1731        encodeVOPWords((uint32_t(gcnInsn.code1)<<25) |
1732                (uint32_t(src1Op.range.bstart()&0xff)<<9) |
1733                (uint32_t(dstReg.bstart()&0xff)<<17),
1734                modifiers, extraMods, src0Op, src1Op, immValue, mode1,
1735                0, wordsNum, words);
1736    else
1737    {
1738        // VOP3 encoding
1739        uint32_t code = (isGCN12) ?
1740                (uint32_t(gcnInsn.code2)<<16) | ((modifiers&VOP3_CLAMP) ? 0x8000 : 0) :
1741                (uint32_t(gcnInsn.code2)<<17) | ((modifiers&VOP3_CLAMP) ? 0x800 : 0);
1742        if (haveDstCC) // if VOP3B
1743            SLEV(words[0], 0xd0000000U | code |
1744                (dstReg.bstart()&0xff) | (uint32_t(dstCCReg.bstart())<<8));
1745        else // if VOP3A
1746            SLEV(words[0], 0xd0000000U | code | (dstReg.bstart()&0xff) |
1747                ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
1748                ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
1749                ((opMods.opselMod&15) << 11));
1750        // second dword
1751        SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
1752            (uint32_t(srcCCReg.bstart())<<18) | (uint32_t(modifiers & 3) << 27) |
1753            ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
1754            ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0));
1755        wordsNum++;
1756    }
1757    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1758        return false;
1759   
1760    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1761            reinterpret_cast<cxbyte*>(words + wordsNum));
1762    /// prevent freeing expression
1763    src0OpExpr.release();
1764    src1OpExpr.release();
1765    immExpr.release();
1766    // update register pool (VGPR and SGPR counting)
1767    if (!dstReg.isRegVar())
1768    {
1769        if (dstReg.start>=256)
1770            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
1771        else // sgprs
1772        {
1773            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1774            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1775        }
1776    }
1777    // for SRC operands
1778    if (src0Op.range && !src0Op.range.isRegVar())
1779        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1780    if (src1Op.range && !src1Op.range.isRegVar())
1781        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
1782    if (dstCCReg && !dstCCReg.isRegVar())
1783    {
1784        updateSGPRsNum(gcnRegs.sgprsNum, dstCCReg.end-1, arch);
1785        updateRegFlags(gcnRegs.regFlags, dstCCReg.start, arch);
1786    }
1787    if (srcCCReg && !srcCCReg.isRegVar())
1788        updateRegFlags(gcnRegs.regFlags, srcCCReg.start, arch);
1789    return true;
1790}
1791
1792bool GCNAsmUtils::parseVOP1Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1793                  const char* instrPlace, const char* linePtr, uint16_t arch,
1794                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1795                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1796{
1797    bool good = true;
1798    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
1799    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
1800    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1801    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
1802   
1803    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1804    RegRange dstReg(0, 0);
1805    GCNOperand src0Op{};
1806    std::unique_ptr<AsmExpression> src0OpExpr;
1807    cxbyte modifiers = 0;
1808    if (mode1 != GCN_VOP_ARG_NONE)
1809    {
1810        gcnAsm->setCurrentRVU(0);
1811        if (mode1 == GCN_DST_SGPR)
1812            // if SGPRS as destination
1813            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1814                           (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_SDST, true,
1815                           INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1816        else // if VGPRS as destination
1817            good &= parseVRegRange(asmr, linePtr, dstReg,
1818                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_VDST, true,
1819                                  INSTROP_SYMREGRANGE|INSTROP_WRITE);
1820       
1821        const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1822                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1823       
1824        if (!skipRequiredComma(asmr, linePtr))
1825            return false;
1826        cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1827        gcnAsm->setCurrentRVU(1);
1828        // parse SRC0 (can be VGPR, SGPR, source scalar, literal or cosntant, LDS
1829        good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1830                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
1831                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1832                    INSTROP_VOP3MODS|INSTROP_READ, GCNFIELD_VOP_SRC0);
1833    }
1834    // modifiers
1835    VOPExtraModifiers extraMods{};
1836    VOPOpModifiers opMods{};
1837    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods,
1838                  (mode1!=GCN_VOP_ARG_NONE) ? 2 : 0, (isGCN12)?&extraMods:nullptr,
1839                  PARSEVOP_WITHCLAMP|PARSEVOP_WITHSEXT|
1840                  (isGCN14 ? PARSEVOP_WITHOPSEL : 0), (mode1!=GCN_VOP_ARG_NONE) ? 2 : 0);
1841    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1842        return false;
1843   
1844    if (src0Op)
1845        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1846                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1847                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1848   
1849    extraMods.needSDWA |= ((src0Op.vopMods) & VOPOP_SEXT) != 0;
1850    // check whether VOP3 encoding is needed
1851    bool vop3 = ((!isGCN12 && src0Op.vopMods!=0) ||
1852            (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1853            /* exclude OMOD if RXVEGA and SDWA used */
1854            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0) ||
1855            ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1856    if (vop3) // modify fields in reg usage
1857    {
1858        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1859        if (rvus[0].regField != ASMFIELD_NONE)
1860            rvus[0].regField = (rvus[0].regField==GCNFIELD_VOP_VDST) ?
1861                        GCNFIELD_VOP3_VDST : GCNFIELD_VOP3_SDST0;
1862        if (rvus[1].regField != ASMFIELD_NONE)
1863            rvus[1].regField = GCNFIELD_VOP3_SRC0;
1864    }
1865   
1866    bool sextFlags = (src0Op.vopMods & VOPOP_SEXT);
1867    bool needImm = (src0Op && src0Op.range.isVal(255));
1868    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || sextFlags ||
1869                gcnVOPEnc!=GCNVOPEnc::NORMAL))
1870    {
1871        /* if VOP_SDWA or VOP_DPP is required */
1872        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1873                    gcnVOPEnc, src0Op, extraMods, instrPlace))
1874            return false;
1875        if (gcnAsm->instrRVUs[1].regField != ASMFIELD_NONE)
1876            gcnAsm->instrRVUs[1].regField = GCNFIELD_DPPSDWA_SRC0;
1877        if (extraMods.needSDWA && isGCN14)
1878        {
1879            // fix for extra type operand from SDWA
1880            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1881            if (rvus[1].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1882                rvus[1].regField = GCNFIELD_DPPSDWA_SSRC0;
1883        }
1884    }
1885    else if (isGCN12 && (src0Op.vopMods & ~VOPOP_SEXT)!=0 && !sextFlags)
1886        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1887        vop3 = true;
1888   
1889    if (vop3 && src0Op.range.isVal(255))
1890        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1891   
1892    if (!checkGCNVOPEncoding(asmr, instrPlace, gcnVOPEnc, &extraMods))
1893        return false;
1894   
1895    if (src0OpExpr!=nullptr)
1896        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1897                      output.size()));
1898   
1899    // put data (instruction word)
1900    cxuint wordsNum = 1;
1901    uint32_t words[2];
1902    if (!vop3)
1903        // VOP1 encoding
1904        encodeVOPWords(0x7e000000U | (uint32_t(gcnInsn.code1)<<9) |
1905                (uint32_t(dstReg.bstart()&0xff)<<17),
1906                modifiers, extraMods, src0Op, GCNOperand{ { 256, 257 } }, 0, mode1,
1907                0, wordsNum, words);
1908    else
1909    {
1910        // VOP3 encoding
1911        uint32_t code = (isGCN12) ?
1912                (uint32_t(gcnInsn.code2)<<16) | ((modifiers&VOP3_CLAMP) ? 0x8000 : 0) :
1913                (uint32_t(gcnInsn.code2)<<17) | ((modifiers&VOP3_CLAMP) ? 0x800 : 0);
1914        SLEV(words[0], 0xd0000000U | code | (dstReg.bstart()&0xff) |
1915            ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
1916            ((opMods.opselMod&15) << 11));
1917        SLEV(words[1], src0Op.range.bstart() | (uint32_t(modifiers & 3) << 27) |
1918            ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0));
1919        wordsNum++;
1920    }
1921    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1922        return false;
1923   
1924    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1925            reinterpret_cast<cxbyte*>(words + wordsNum));
1926    /// prevent freeing expression
1927    src0OpExpr.release();
1928    // update register pool (VGPR and SGPR counting)
1929    if (dstReg && !dstReg.isRegVar())
1930    {
1931        if (dstReg.start>=256)
1932            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
1933        else // sgprs
1934        {
1935            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1936            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1937        }
1938    }
1939    if (src0Op.range && !src0Op.range.isRegVar())
1940        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1941    return true;
1942}
1943
1944bool GCNAsmUtils::parseVOPCEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1945                  const char* instrPlace, const char* linePtr, uint16_t arch,
1946                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1947                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1948{
1949    bool good = true;
1950    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
1951    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1952    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
1953   
1954    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1955    RegRange dstReg(0, 0);
1956    GCNOperand src0Op{};
1957    std::unique_ptr<AsmExpression> src0OpExpr;
1958    GCNOperand src1Op{};
1959    std::unique_ptr<AsmExpression> src1OpExpr;
1960    cxbyte modifiers = 0;
1961   
1962    // parse SDST (2 SGPR's)
1963    gcnAsm->setCurrentRVU(0);
1964    good &= parseSRegRange(asmr, linePtr, dstReg, arch, 2, GCNFIELD_VOP3_SDST0, true,
1965                           INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1966    if (!skipRequiredComma(asmr, linePtr))
1967        return false;
1968   
1969    const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1970                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1971    cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1972    gcnAsm->setCurrentRVU(1);
1973    // parse SRC0 (can VGPR, SGPR, scalar source,literal or constant)
1974    good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1975                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
1976                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1977                    INSTROP_VOP3MODS|INSTROP_READ, GCNFIELD_VOP_SRC0);
1978   
1979    if (!skipRequiredComma(asmr, linePtr))
1980        return false;
1981    regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
1982    gcnAsm->setCurrentRVU(2);
1983    // parse SRC1 (can VGPR, SGPR, scalar source,literal or constant)
1984    good &= parseOperand(asmr, linePtr, src1Op, &src1OpExpr, arch, regsNum,
1985                correctOpType(regsNum, literalConstsFlags) | INSTROP_VOP3MODS|
1986                INSTROP_SGPR_UNALIGNED|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|
1987                INSTROP_READ| (src0Op.range.isVal(255) ? INSTROP_ONLYINLINECONSTS : 0),
1988                GCNFIELD_VOP_VSRC1);
1989    // modifiers
1990    VOPExtraModifiers extraMods{};
1991    VOPOpModifiers opMods{};
1992    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, 3,
1993                (isGCN12)?&extraMods:nullptr, (isGCN14 ? PARSEVOP_NODSTMODS : 0)|
1994                PARSEVOP_WITHCLAMP|PARSEVOP_WITHSEXT|(isGCN14 ? PARSEVOP_WITHOPSEL : 0));
1995    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1996        return false;
1997   
1998    // set VOP operand modifiers to src operands
1999    if (src0Op)
2000        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
2001                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
2002                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
2003    if (src1Op)
2004        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
2005                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
2006                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
2007   
2008    // determine whether SDWA is needed or VOP3 encoding needed
2009    extraMods.needSDWA |= ((src0Op.vopMods | src1Op.vopMods) & VOPOP_SEXT) != 0;
2010    bool vop3 = //(dstReg.start!=106) || (src1Op.range.start<256) ||
2011        ((!isGCN14 || !extraMods.needSDWA) && !dstReg.isVal(106)) ||
2012        ((!isGCN14 || !extraMods.needSDWA) && src1Op.range.isNonVGPR()) ||
2013        (!isGCN12 && (src0Op.vopMods!=0 || src1Op.vopMods!=0)) ||
2014        (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
2015            /* exclude OMOD if RXVEGA and SDWA used */
2016            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0 ||
2017        ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
2018   
2019    if ((src0Op.range.isVal(255) || src1Op.range.isVal(255)) &&
2020        (src0Op.range.isSGPR() || src0Op.range.isVal(124) ||
2021         src1Op.range.isSGPR() || src1Op.range.isVal(124)))
2022        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SGPR or M0 is illegal")
2023    if (src0Op.range.isSGPR() && src1Op.range.isSGPR() &&
2024        !regRangeCanEqual(src0Op.range, src1Op.range))
2025        /* include VCCs (???) */
2026        ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
2027   
2028    if (vop3)
2029    {
2030        // modify fields in reg usage
2031        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
2032        if (rvus[1].regField != ASMFIELD_NONE)
2033            rvus[1].regField = GCNFIELD_VOP3_SRC0;
2034        if (rvus[2].regField != ASMFIELD_NONE)
2035            rvus[2].regField = GCNFIELD_VOP3_SRC1;
2036    }
2037   
2038    const bool needImm = src0Op.range.start==255 || src1Op.range.start==255;
2039   
2040    bool sextFlags = ((src0Op.vopMods|src1Op.vopMods) & VOPOP_SEXT);
2041    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || sextFlags ||
2042                gcnVOPEnc!=GCNVOPEnc::NORMAL))
2043    {
2044        /* if VOP_SDWA or VOP_DPP is required */
2045        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
2046                    gcnVOPEnc, src0Op, extraMods, instrPlace))
2047            return false;
2048        if (gcnAsm->instrRVUs[1].regField != ASMFIELD_NONE)
2049            gcnAsm->instrRVUs[1].regField = GCNFIELD_DPPSDWA_SRC0;
2050       
2051        if (extraMods.needSDWA && isGCN14)
2052        {
2053            // fix for extra type operand from SDWA
2054            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
2055            if (rvus[0].regField != ASMFIELD_NONE)
2056                rvus[0].regField = GCNFIELD_SDWAB_SDST;
2057            if (rvus[1].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
2058                rvus[1].regField = GCNFIELD_DPPSDWA_SSRC0;
2059            if (rvus[2].regField != ASMFIELD_NONE && src1Op.range.isNonVGPR())
2060                rvus[2].regField = GCNFIELD_VOP_SSRC1;
2061        }
2062    }
2063    else if (isGCN12 && ((src0Op.vopMods|src1Op.vopMods) & ~VOPOP_SEXT)!=0 && !sextFlags)
2064        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
2065        vop3 = true;
2066   
2067    if (vop3 && (src0Op.range.isVal(255) || src1Op.range.isVal(255)))
2068        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
2069   
2070    if (!checkGCNVOPEncoding(asmr, instrPlace, gcnVOPEnc, &extraMods))
2071        return false;
2072   
2073    if (isGCN14 && extraMods.needSDWA && ((modifiers & VOP3_CLAMP)!=0 || (modifiers&3)!=0))
2074        ASM_FAIL_BY_ERROR(instrPlace, "Modifiers CLAMP and OMOD is illegal in SDWAB")
2075   
2076    if (src0OpExpr!=nullptr)
2077        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
2078                      output.size()));
2079    if (src1OpExpr!=nullptr)
2080        src1OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
2081                      output.size()));
2082   
2083    // put data (instruction words)
2084    cxuint wordsNum = 1;
2085    uint32_t words[2];
2086    if (!vop3)
2087    {
2088        // VOPC encoding
2089        const uint32_t dstMods = (isGCN14 ? 0x10000 : 0) |
2090                ((isGCN14 && !dstReg.isVal(106)) ? ((dstReg.bstart()|0x80)<<8) : 0);
2091       
2092        encodeVOPWords(0x7c000000U | (uint32_t(gcnInsn.code1)<<17) |
2093                (uint32_t(src1Op.range.bstart()&0xff)<<9),
2094                modifiers, extraMods, src0Op, src1Op, 0, 0,
2095                dstMods, wordsNum, words);
2096    }
2097    else
2098    {
2099        // VOP3 encoding
2100        uint32_t code = (isGCN12) ?
2101                (uint32_t(gcnInsn.code2)<<16) | ((modifiers&VOP3_CLAMP) ? 0x8000 : 0) :
2102                (uint32_t(gcnInsn.code2)<<17) | ((modifiers&VOP3_CLAMP) ? 0x800 : 0);
2103        SLEV(words[0], 0xd0000000U | code | (dstReg.bstart()&0xff) |
2104                ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2105                ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2106                ((opMods.opselMod&15) << 11));
2107        SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
2108            (uint32_t(modifiers & 3) << 27) |
2109            ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
2110            ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0));
2111        wordsNum++;
2112    }
2113    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
2114        return false;
2115    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2116            reinterpret_cast<cxbyte*>(words + wordsNum));
2117    /// prevent freeing expression
2118    src0OpExpr.release();
2119    src1OpExpr.release();
2120    // update register pool (VGPR and SGPR counting)
2121    if (dstReg && !dstReg.isRegVar())
2122    {
2123        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
2124        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
2125    }
2126    if (src0Op.range && !src0Op.range.isRegVar())
2127        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
2128    if (src1Op.range && !src1Op.range.isRegVar())
2129        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
2130    return true;
2131}
2132
2133bool GCNAsmUtils::parseVOP3Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2134                  const char* instrPlace, const char* linePtr, uint16_t arch,
2135                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2136                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
2137{
2138    bool good = true;
2139    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
2140    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
2141    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
2142    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
2143    const bool vop3p = (gcnInsn.mode & GCN_VOP3_VOP3P) != 0;
2144    if (gcnVOPEnc!=GCNVOPEnc::NORMAL)
2145        ASM_FAIL_BY_ERROR(instrPlace, "DPP and SDWA encoding is illegal for VOP3")
2146   
2147    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2148    RegRange dstReg(0, 0);
2149    RegRange sdstReg(0, 0);
2150    GCNOperand src0Op{};
2151    GCNOperand src1Op{};
2152    GCNOperand src2Op{};
2153   
2154    const bool is128Ops = (gcnInsn.mode & 0x7000) == GCN_VOP3_DS2_128;
2155    bool modHigh = false;
2156    cxbyte modifiers = 0;
2157    const Flags vop3Mods = ((gcnInsn.encoding == GCNENC_VOP3B) ?
2158            INSTROP_VOP3NEG : INSTROP_VOP3MODS | INSTROP_NOSEXT) |
2159            (vop3p ? INSTROP_VOP3P : 0);
2160   
2161    // by default OPSEL_HI is [1,1,1] in vop3p instructions
2162    VOPOpModifiers opMods{ 0, 0, 0, cxbyte(vop3p ? 7<<4 : 0) };
2163    cxuint operands = 1;
2164    if (mode1 != GCN_VOP_ARG_NONE)
2165    {
2166        gcnAsm->setCurrentRVU(0);
2167        // parse DST (
2168        if ((gcnInsn.mode&GCN_VOP3_DST_SGPR)==0)
2169            good &= parseVRegRange(asmr, linePtr, dstReg,
2170                       (is128Ops) ? 4 : ((gcnInsn.mode&GCN_REG_DST_64)?2:1),
2171                       GCNFIELD_VOP3_VDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE);
2172        else // SGPRS as dest
2173            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
2174                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP3_SDST0, true,
2175                       INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
2176        if (!skipRequiredComma(asmr, linePtr))
2177            return false;
2178       
2179        if (gcnInsn.encoding == GCNENC_VOP3B &&
2180            (mode1 == GCN_DS2_VCC || mode1 == GCN_DST_VCC || mode1 == GCN_DST_VCC_VSRC2 ||
2181             mode1 == GCN_S0EQS12)) /* VOP3b */
2182        {
2183            // SDST (VCC) (2 SGPR's)
2184            gcnAsm->setCurrentRVU(1);
2185            good &= parseSRegRange(asmr, linePtr, sdstReg, arch, 2, GCNFIELD_VOP3_SDST1,
2186                       true, INSTROP_SYMREGRANGE|INSTROP_WRITE|INSTROP_SGPR_UNALIGNED);
2187            if (!skipRequiredComma(asmr, linePtr))
2188                return false;
2189        }
2190        const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
2191                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
2192       
2193        cxuint regsNum;
2194        if (mode2 != GCN_VOP3_VINTRP)
2195        {
2196            // parse SRC0 (can be VGPR, SGPR, scalar source, constant, LDS)
2197            gcnAsm->setCurrentRVU(2);
2198            regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
2199            good &= parseOperand(asmr, linePtr, src0Op, nullptr, arch, regsNum,
2200                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
2201                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
2202                    vop3Mods|INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR|INSTROP_READ,
2203                    GCNFIELD_VOP3_SRC0);
2204            operands++;
2205        }
2206       
2207        if (mode2 == GCN_VOP3_VINTRP)
2208        {
2209            // if VINTRP instruction
2210            gcnAsm->setCurrentRVU(3);
2211            if (mode1 != GCN_P0_P10_P20)
2212            {
2213                good &= parseOperand(asmr, linePtr, src1Op, nullptr, arch, 1,
2214                        INSTROP_VREGS|vop3Mods|INSTROP_READ, GCNFIELD_VOP3_SRC1);
2215            }
2216            else /* P0_P10_P20 */
2217                good &= parseVINTRP0P10P20(asmr, linePtr, src1Op.range);
2218           
2219            if (!skipRequiredComma(asmr, linePtr))
2220                return false;
2221           
2222            cxbyte attr;
2223            good &= parseVINTRPAttr(asmr, linePtr, attr);
2224            attr = ((attr&3)<<6) | ((attr&0xfc)>>2);
2225            src0Op.range = { attr, uint16_t(attr+1) };
2226           
2227            if ((gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2)
2228            {
2229                if (!skipRequiredComma(asmr, linePtr))
2230                    return false;
2231                // parse SRC2 (VGPR, SGPR)
2232                gcnAsm->setCurrentRVU(4);
2233                good &= parseOperand(asmr, linePtr, src2Op, nullptr, arch,
2234                    (gcnInsn.mode&GCN_REG_SRC2_64)?2:1, vop3Mods|INSTROP_SGPR_UNALIGNED|
2235                    INSTROP_VREGS|INSTROP_SREGS|INSTROP_READ, GCNFIELD_VOP3_SRC2);
2236            }
2237            // high and vop3
2238            const char* end = asmr.line+asmr.lineSize;
2239            bool haveOpsel = false;
2240            bool haveNeg = false, haveAbs = false;
2241            // own parse VINTRP modifiers with some VOP3 modifiers
2242            while (true)
2243            {
2244                bool alreadyModDefined = false;
2245                skipSpacesToEnd(linePtr, end);
2246                if (linePtr==end)
2247                    break;
2248                char modName[10];
2249                const char* modPlace = linePtr;
2250                if (!getNameArgS(asmr, 10, modName, linePtr, "VINTRP modifier"))
2251                    continue;
2252                if (::strcmp(modName, "high")==0)
2253                    good &= parseModEnable(asmr, linePtr, modHigh, "high modifier");
2254                else if (::strcmp(modName, "vop3")==0)
2255                {
2256                    bool vop3Mod = false;
2257                    good &= parseModEnable(asmr, linePtr, vop3Mod, "vop3 modifier");
2258                    modifiers = (modifiers & ~VOP3_VOP3) | (vop3Mod ? VOP3_VOP3 : 0);
2259                }
2260                else if (parseSingleOMODCLAMP(asmr, linePtr, modPlace, modName, arch,
2261                        modifiers, opMods, 
2262                        (gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2 ? 4 : 3, PARSEVOP_WITHCLAMP, haveAbs, haveNeg,
2263                        alreadyModDefined, good))
2264                {   // do nothing
2265                }
2266                else if (::strcmp(modName, "op_sel")==0)
2267                {
2268                    // op_sel with boolean array
2269                    uint32_t opselVal = 0;
2270                    if (linePtr!=end && *linePtr==':')
2271                    {
2272                        linePtr++;
2273                        if (parseImmWithBoolArray(asmr, linePtr, opselVal, 4, WS_UNSIGNED))
2274                        {
2275                            opMods.opselMod = opselVal;
2276                            if (haveOpsel)
2277                                asmr.printWarning(modPlace, "Opsel is already defined");
2278                            haveOpsel = true;
2279                            opMods.opselMod = opselVal;
2280                        }
2281                    }
2282                    else
2283                        good = false;
2284                }
2285                else
2286                    ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VINTRP modifier")
2287            }
2288            if (modHigh)
2289            {
2290                src0Op.range.start+=0x100;
2291                src0Op.range.end+=0x100;
2292            }
2293        }
2294        else if (mode1 != GCN_SRC12_NONE)
2295        {
2296            // if encoding have two or three source operands
2297            if (!skipRequiredComma(asmr, linePtr))
2298                return false;
2299            regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
2300            // parse SRC1 (can be VGPR, SGPR, source scalar, constant)
2301            gcnAsm->setCurrentRVU(3);
2302            good &= parseOperand(asmr, linePtr, src1Op, nullptr, arch, regsNum,
2303                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
2304                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|vop3Mods|
2305                    INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR|INSTROP_READ,
2306                    GCNFIELD_VOP3_SRC1);
2307            operands++;
2308           
2309            if (mode1 != GCN_SRC2_NONE && mode1 != GCN_DST_VCC)
2310            {
2311                if (!skipRequiredComma(asmr, linePtr))
2312                    return false;
2313                regsNum = (gcnInsn.mode&GCN_REG_SRC2_64)?2:1;
2314                // parse SRC2 (can be VGPR, SGPR, source scalar, constant)
2315                gcnAsm->setCurrentRVU(4);
2316                good &= parseOperand(asmr, linePtr, src2Op, nullptr, arch,
2317                        is128Ops ? 4 : regsNum,
2318                        correctOpType(regsNum, literalConstsFlags)|INSTROP_SGPR_UNALIGNED|
2319                        INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_READ|
2320                        vop3Mods|INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR,
2321                        GCNFIELD_VOP3_SRC2);
2322                operands++;
2323            }
2324        }
2325    }
2326    // modifiers
2327    if (mode2 != GCN_VOP3_VINTRP)
2328        good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, operands,
2329                    nullptr, ((isGCN12 || gcnInsn.encoding!=GCNENC_VOP3B) ?
2330                            PARSEVOP_WITHCLAMP : 0) |
2331                    ((isGCN14 && gcnInsn.encoding!=GCNENC_VOP3B) ?
2332                            PARSEVOP_WITHOPSEL : 0) | (vop3p ? PARSEVOP_VOP3P : 0), 3);
2333    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2334        return false;
2335   
2336    // apply VOP modifiers (abs,neg,sext) to operands from modifiers
2337    if (src0Op)
2338        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
2339                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
2340                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
2341    if (src1Op)
2342        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
2343                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
2344                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
2345    if (src2Op)
2346        src2Op.vopMods |= ((opMods.absMod&4) ? VOPOP_ABS : 0) |
2347                ((opMods.negMod&4) ? VOPOP_NEG : 0);
2348   
2349    if (mode2 != GCN_VOP3_VINTRP)
2350    {
2351        // count SGPR operands readed by instruction
2352        cxuint numSgprToRead = 0;
2353        //if (src0Op.range.start<108)
2354        if (src0Op.range.isSGPR())
2355            numSgprToRead++;
2356        //if (src1Op && src1Op.range.start<108 &&
2357        if (src1Op && src1Op.range.isSGPR() &&
2358                    !regRangeCanEqual(src0Op.range, src1Op.range))
2359            numSgprToRead++;
2360        //if (src2Op && src2Op.range.start<108 &&
2361        if (src2Op && src2Op.range.isSGPR())
2362        {
2363            if (!regRangeCanEqual(src0Op.range, src2Op.range) &&
2364                !regRangeCanEqual(src1Op.range, src2Op.range))
2365                numSgprToRead++;
2366        }
2367       
2368        if (numSgprToRead>=2)
2369            ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
2370    }
2371   
2372    // put data (instruction words)
2373    uint32_t words[2];
2374    cxuint wordsNum = 2;
2375    if (gcnInsn.encoding == GCNENC_VOP3B)
2376    {
2377        // VOP3B encoding
2378        if (!isGCN12)
2379            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<17) |
2380                (dstReg.bstart()&0xff) | (uint32_t(sdstReg.bstart())<<8));
2381        else
2382            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<16) |
2383                (dstReg.bstart()&0xff) | (uint32_t(sdstReg.bstart())<<8) |
2384                ((modifiers&VOP3_CLAMP) ? 0x8000 : 0));
2385    }
2386    else
2387    {
2388        // VOP3A
2389        if (!isGCN12)
2390            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<17) |
2391                (dstReg.bstart()&0xff) | ((modifiers&VOP3_CLAMP) ? 0x800: 0) |
2392                ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2393                ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2394                ((src2Op.vopMods & VOPOP_ABS) ? 0x400 : 0));
2395        else if (mode2 != GCN_VOP3_VINTRP || mode1 == GCN_NEW_OPCODE ||
2396            (gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2 ||
2397            (modifiers & VOP3_VOP3)!=0 || (src0Op.range.bstart()&0x100)!=0/* high */ ||
2398            (modifiers & (VOP3_CLAMP|3)) != 0 || opMods.opselMod != 0 ||
2399            src1Op.vopMods!=0 || src2Op.vopMods!=0)
2400            // new VOP3 for GCN 1.2
2401            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<16) |
2402                (dstReg.bstart()&0xff) | ((modifiers&VOP3_CLAMP) ? 0x8000: 0) |
2403                (vop3p ? (uint32_t(opMods.negMod>>4) << 8) /* VOP3P NEG_HI */ :
2404                    ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2405                    ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2406                    ((src2Op.vopMods & VOPOP_ABS) ? 0x400 : 0)) |
2407                (((opMods.opselMod & 64) !=0) ? 0x4000 : 0) |
2408                ((opMods.opselMod&15) << 11));
2409        else // VINTRP
2410        {
2411            SLEV(words[0], 0xd4000000U | (src1Op.range.bstart()&0xff) |
2412                (uint32_t(src0Op.range.bstart()>>6)<<8) |
2413                (uint32_t(src0Op.range.bstart()&63)<<10) |
2414                (uint32_t(gcnInsn.code2)<<16) | (uint32_t(dstReg.bstart()&0xff)<<18));
2415            // VOP3 VINTRP have only one word instruction
2416            wordsNum--;
2417        }
2418    }
2419    if (wordsNum==2)
2420        // second instruction's word
2421        SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
2422                (uint32_t(src2Op.range.bstart())<<18) |
2423                (vop3p ? ((uint32_t(opMods.opselMod>>4)&3)<<27) :
2424                 (uint32_t(modifiers & 3) << 27)) |
2425                /* in VOP3P is also NEG_LO */
2426                ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
2427                ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0) |
2428                ((src2Op.vopMods & VOPOP_NEG) ? (1U<<31) : 0));
2429   
2430    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
2431        return false;
2432    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2433            reinterpret_cast<cxbyte*>(words + wordsNum));
2434   
2435    // update register pool (VGPR and SGPR counting)
2436    if (dstReg && !dstReg.isRegVar())
2437    {
2438        if (dstReg.start>=256)
2439            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2440        else // sgprs
2441        {
2442            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
2443            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
2444        }
2445    }
2446    if (sdstReg && !sdstReg.isRegVar())
2447    {
2448        updateSGPRsNum(gcnRegs.sgprsNum, sdstReg.end-1, arch);
2449        updateRegFlags(gcnRegs.regFlags, sdstReg.start, arch);
2450    }
2451    if (mode2 != GCN_VOP3_VINTRP)
2452    {
2453        // count for SSRC0 and SSRC1 for VOP3A/B encoding (not VINTRP) ???
2454        if (src0Op.range && !src0Op.range.isRegVar() && src0Op.range.start < 256)
2455            updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
2456        if (src1Op.range && !src1Op.range.isRegVar() && src1Op.range.start < 256)
2457            updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
2458    }
2459    if (src2Op.range && !src2Op.range.isRegVar() && src2Op.range.start < 256)
2460        updateRegFlags(gcnRegs.regFlags, src2Op.range.start, arch);
2461    return true;
2462}
2463
2464bool GCNAsmUtils::parseVINTRPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2465                  const char* instrPlace, const char* linePtr, uint16_t arch,
2466                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2467                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
2468{
2469    bool good = true;
2470    RegRange dstReg(0, 0);
2471    RegRange srcReg(0, 0);
2472    if (gcnEncSize==GCNEncSize::BIT64)
2473        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for VINTRP encoding")
2474    if (gcnVOPEnc!=GCNVOPEnc::NORMAL)
2475        ASM_FAIL_BY_ERROR(instrPlace, "DPP and SDWA encoding is illegal for VOP3")
2476   
2477    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2478   
2479    // parse DST (VGPR)
2480    gcnAsm->setCurrentRVU(0);
2481    good &= parseVRegRange(asmr, linePtr, dstReg, 1, GCNFIELD_VINTRP_VDST, true,
2482                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
2483    if (!skipRequiredComma(asmr, linePtr))
2484        return false;
2485   
2486    if ((gcnInsn.mode & GCN_MASK1) == GCN_P0_P10_P20)
2487        good &= parseVINTRP0P10P20(asmr, linePtr, srcReg);
2488    else
2489    {
2490        // regular vector register
2491        gcnAsm->setCurrentRVU(1);
2492        good &= parseVRegRange(asmr, linePtr, srcReg, 1, GCNFIELD_VINTRP_VSRC0, true,
2493                        INSTROP_SYMREGRANGE|INSTROP_READ);
2494    }
2495   
2496    if (!skipRequiredComma(asmr, linePtr))
2497        return false;
2498   
2499    cxbyte attrVal;
2500    good &= parseVINTRPAttr(asmr, linePtr, attrVal);
2501   
2502    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2503        return false;
2504    /* put data (instruction word */
2505    uint32_t word;
2506    SLEV(word, 0xc8000000U | (srcReg.bstart()&0xff) | (uint32_t(attrVal&0xff)<<8) |
2507            (uint32_t(gcnInsn.code1)<<16) | (uint32_t(dstReg.bstart()&0xff)<<18));
2508    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word),
2509            reinterpret_cast<cxbyte*>(&word)+4);
2510    // update register pool (VGPR counting)
2511    if (!dstReg.isRegVar())
2512        updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2513    return true;
2514}
2515
2516bool GCNAsmUtils::parseDSEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2517                  const char* instrPlace, const char* linePtr, uint16_t arch,
2518                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2519                  GCNEncSize gcnEncSize)
2520{
2521    const char* end = asmr.line+asmr.lineSize;
2522    bool good = true;
2523    if (gcnEncSize==GCNEncSize::BIT32)
2524        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for DS encoding")
2525    RegRange dstReg(0, 0);
2526    RegRange addrReg(0, 0);
2527    RegRange data0Reg(0, 0), data1Reg(0, 0);
2528   
2529    bool beforeData = false;
2530    bool vdstUsed = false;
2531   
2532    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2533   
2534    if (((gcnInsn.mode & GCN_ADDR_SRC) != 0 || (gcnInsn.mode & GCN_ONLYDST) != 0) &&
2535            (gcnInsn.mode & GCN_ONLY_SRC) == 0)
2536    {
2537        /* vdst is dst */
2538        cxuint regsNum = (gcnInsn.mode&GCN_REG_DST_64)?2:1;
2539        if ((gcnInsn.mode&GCN_DS_96) != 0)
2540            regsNum = 3;
2541        if ((gcnInsn.mode&GCN_DS_128) != 0 || (gcnInsn.mode&GCN_DST128) != 0)
2542            regsNum = 4;
2543        gcnAsm->setCurrentRVU(0);
2544        good &= parseVRegRange(asmr, linePtr, dstReg, regsNum, GCNFIELD_DS_VDST, true,
2545                    INSTROP_SYMREGRANGE|INSTROP_WRITE);
2546        vdstUsed = beforeData = true;
2547    }
2548   
2549    if ((gcnInsn.mode & GCN_ONLYDST) == 0 && (gcnInsn.mode & GCN_ONLY_SRC) == 0)
2550    {
2551        // parse ADDR as first (VGPR)
2552        if (vdstUsed)
2553            if (!skipRequiredComma(asmr, linePtr))
2554                return false;
2555        gcnAsm->setCurrentRVU(1);
2556        good &= parseVRegRange(asmr, linePtr, addrReg, 1, GCNFIELD_DS_ADDR, true,
2557                    INSTROP_SYMREGRANGE|INSTROP_READ);
2558        beforeData = true;
2559    }
2560   
2561    const uint16_t srcMode = (gcnInsn.mode & GCN_SRCS_MASK);
2562   
2563    if ((gcnInsn.mode & GCN_ONLYDST) == 0 &&
2564        (gcnInsn.mode & (GCN_ADDR_DST|GCN_ADDR_SRC)) != 0 && srcMode != GCN_NOSRC)
2565    {
2566        /* two vdata */
2567        if (beforeData)
2568            if (!skipRequiredComma(asmr, linePtr))
2569                return false;
2570       
2571        cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
2572        if ((gcnInsn.mode&GCN_DS_96) != 0)
2573            regsNum = 3;
2574        if ((gcnInsn.mode&GCN_DS_128) != 0)
2575            regsNum = 4;
2576        // parse VDATA0 (VGPR)
2577        gcnAsm->setCurrentRVU(2);
2578        good &= parseVRegRange(asmr, linePtr, data0Reg, regsNum, GCNFIELD_DS_DATA0, true,
2579                    INSTROP_SYMREGRANGE|INSTROP_READ);
2580        if (srcMode == GCN_2SRCS)
2581        {
2582            // insturction have second source
2583            if (!skipRequiredComma(asmr, linePtr))
2584                return false;
2585            // parse VDATA0 (VGPR)
2586            gcnAsm->setCurrentRVU(3);
2587            good &= parseVRegRange(asmr, linePtr, data1Reg,
2588                       (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, GCNFIELD_DS_DATA1, true,
2589                               INSTROP_SYMREGRANGE|INSTROP_READ);
2590        }
2591    }
2592   
2593    bool haveGds = false;
2594    std::unique_ptr<AsmExpression> offsetExpr, offset2Expr;
2595    char name[10];
2596    uint16_t offset = 0;
2597    cxbyte offset1 = 0, offset2 = 0;
2598    bool haveOffset = false, haveOffset2 = false;
2599    // parse DS modifiers
2600    while (linePtr!=end)
2601    {
2602        skipSpacesToEnd(linePtr, end);
2603        if (linePtr==end)
2604            break;
2605        const char* modPlace = linePtr;
2606        if (!getNameArgS(asmr, 10, name, linePtr, "DS modifier"))
2607        {
2608            good = false;
2609            continue;
2610        }
2611        toLowerString(name);
2612        if (::strcmp(name, "gds")==0)
2613            good &= parseModEnable(asmr, linePtr, haveGds, "gds modifier");
2614        else if ((gcnInsn.mode & GCN_2OFFSETS) == 0) /* single offset */
2615        {
2616            // single offset
2617            if (::strcmp(name, "offset") == 0)
2618            {
2619                if (parseModImm(asmr, linePtr, offset, &offsetExpr, "offset",
2620                            0, WS_UNSIGNED))
2621                {
2622                    // detect multiple occurrences
2623                    if (haveOffset)
2624                        asmr.printWarning(modPlace, "Offset is already defined");
2625                    haveOffset = true;
2626                }
2627                else
2628                    good = false;
2629            }
2630            else
2631                ASM_NOTGOOD_BY_ERROR(modPlace, "Expected 'offset'")
2632        }
2633        else
2634        {
2635            // two offsets (offset0, offset1)
2636            if (::memcmp(name, "offset", 6)==0 &&
2637                (name[6]=='0' || name[6]=='1') && name[7]==0)
2638            {
2639                skipSpacesToEnd(linePtr, end);
2640                if (linePtr!=end && *linePtr==':')
2641                {
2642                    skipCharAndSpacesToEnd(linePtr, end);
2643                    if (name[6]=='0')
2644                    {
2645                        /* offset0 */
2646                        if (parseImm(asmr, linePtr, offset1, &offsetExpr, 0, WS_UNSIGNED))
2647                        {
2648                            // detect multiple occurrences
2649                            if (haveOffset)
2650                                asmr.printWarning(modPlace, "Offset0 is already defined");
2651                            haveOffset = true;
2652                        }
2653                        else
2654                            good = false;
2655                    }
2656                    else
2657                    {
2658                        /* offset1 */
2659                        if (parseImm(asmr, linePtr, offset2, &offset2Expr, 0, WS_UNSIGNED))
2660                        {
2661                            // detect multiple occurrences
2662                            if (haveOffset2)
2663                                asmr.printWarning(modPlace, "Offset1 is already defined");
2664                            haveOffset2 = true;
2665                        }
2666                        else
2667                            good = false;
2668                    }
2669                }
2670                else
2671                    ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before offset")
2672            }
2673            else
2674                ASM_NOTGOOD_BY_ERROR(modPlace,
2675                                "Expected 'offset', 'offset0' or 'offset1'")
2676        }
2677    }
2678   
2679    if ((gcnInsn.mode & GCN_2OFFSETS) != 0)
2680        offset = offset1 | (offset2<<8);
2681   
2682    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2683        return false;
2684   
2685    if ((gcnInsn.mode&GCN_ONLYGDS) != 0 && !haveGds)
2686        ASM_FAIL_BY_ERROR(instrPlace, "Instruction requires GDS modifier")
2687   
2688    // set target expressions for offsets (if needed)
2689    if (offsetExpr!=nullptr)
2690        offsetExpr->setTarget(AsmExprTarget((gcnInsn.mode & GCN_2OFFSETS) ?
2691                    GCNTGT_DSOFFSET8_0 : GCNTGT_DSOFFSET16, asmr.currentSection,
2692                    output.size()));
2693    if (offset2Expr!=nullptr)
2694        offset2Expr->setTarget(AsmExprTarget(GCNTGT_DSOFFSET8_1, asmr.currentSection,
2695                    output.size()));
2696    // put data (two instruction words)
2697    uint32_t words[2];
2698    if ((arch & ARCH_GCN_1_2_4)==0)
2699        SLEV(words[0], 0xd8000000U | uint32_t(offset) | (haveGds ? 0x20000U : 0U) |
2700                (uint32_t(gcnInsn.code1)<<18));
2701    else
2702        SLEV(words[0], 0xd8000000U | uint32_t(offset) | (haveGds ? 0x10000U : 0U) |
2703                (uint32_t(gcnInsn.code1)<<17));
2704    SLEV(words[1], (addrReg.bstart()&0xff) | (uint32_t(data0Reg.bstart()&0xff)<<8) |
2705            (uint32_t(data1Reg.bstart()&0xff)<<16) | (uint32_t(dstReg.bstart()&0xff)<<24));
2706    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2707            reinterpret_cast<cxbyte*>(words + 2));
2708   
2709    offsetExpr.release();
2710    offset2Expr.release();
2711    // update register pool (VGPR counting)
2712    if (dstReg && !dstReg.isRegVar())
2713        updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2714    return true;
2715}
2716
2717// data format names (sorted by names) (for MUBUF/MTBUF)
2718static const std::pair<const char*, uint16_t> mtbufDFMTNamesMap[] =
2719{
2720    { "10_10_10_2", 8 },
2721    { "10_11_11", 6 },
2722    { "11_11_10", 7 },
2723    { "16", 2 },
2724    { "16_16", 5 },
2725    { "16_16_16_16", 12 },
2726    { "2_10_10_10", 9 },
2727    { "32", 4 },
2728    { "32_32", 11 },
2729    { "32_32_32", 13 },
2730    { "32_32_32_32", 14 },
2731    { "8", 1 },
2732    { "8_8", 3 },
2733    { "8_8_8_8", 10 }
2734};
2735
2736// number format names (sorted by names) (for MUBUF/MTBUF)
2737static const std::pair<const char*, cxuint> mtbufNFMTNamesMap[] =
2738{
2739    { "float", 7 },
2740    { "sint", 5 },
2741    { "snorm", 1 },
2742    { "snorm_ogl", 6 },
2743    { "sscaled", 3 },
2744    { "uint", 4 },
2745    { "unorm", 0 },
2746    { "uscaled", 2 }
2747};
2748
2749bool GCNAsmUtils::parseMUBUFEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2750                  const char* instrPlace, const char* linePtr, uint16_t arch,
2751                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2752                  GCNEncSize gcnEncSize)
2753{
2754    const char* end = asmr.line+asmr.lineSize;
2755    bool good = true;
2756    if (gcnEncSize==GCNEncSize::BIT32)
2757        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for MUBUF/MTBUF encoding")
2758    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
2759    RegRange vaddrReg(0, 0);
2760    RegRange vdataReg(0, 0);
2761    GCNOperand soffsetOp{};
2762    RegRange srsrcReg(0, 0);
2763    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
2764    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
2765    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2766   
2767    skipSpacesToEnd(linePtr, end);
2768    const char* vdataPlace = linePtr;
2769    const char* vaddrPlace = nullptr;
2770    bool parsedVaddr = false;
2771    if (mode1 != GCN_ARG_NONE)
2772    {
2773        if (mode1 != GCN_MUBUF_NOVAD)
2774        {
2775            gcnAsm->setCurrentRVU(0);
2776            // parse VDATA (various VGPR number, verified later)
2777            good &= parseVRegRange(asmr, linePtr, vdataReg, 0, GCNFIELD_M_VDATA, true,
2778                        INSTROP_SYMREGRANGE|INSTROP_READ);
2779            if (!skipRequiredComma(asmr, linePtr))
2780                return false;
2781           
2782            skipSpacesToEnd(linePtr, end);
2783            vaddrPlace = linePtr;
2784            gcnAsm->setCurrentRVU(1);
2785            // parse VADDR (1 or 2 VGPR's) (optional)
2786            if (!parseVRegRange(asmr, linePtr, vaddrReg, 0, GCNFIELD_M_VADDR, false,
2787                        INSTROP_SYMREGRANGE|INSTROP_READ))
2788                good = false;
2789            if (vaddrReg) // only if vaddr is
2790            {
2791                parsedVaddr = true;
2792                if (!skipRequiredComma(asmr, linePtr))
2793                    return false;
2794            }
2795            else
2796            {
2797                // if not, default is v0, then parse off
2798                if (linePtr+3<=end && ::strncasecmp(linePtr, "off", 3)==0 &&
2799                    (isSpace(linePtr[3]) || linePtr[3]==','))
2800                {
2801                    linePtr+=3;
2802                    if (!skipRequiredComma(asmr, linePtr))
2803                        return false;
2804                }
2805                vaddrReg = {256, 257};
2806            }
2807        }
2808        // parse SRSREG (4 SGPR's)
2809        gcnAsm->setCurrentRVU(2);
2810        good &= parseSRegRange(asmr, linePtr, srsrcReg, arch, 4, GCNFIELD_M_SRSRC, true,
2811                        INSTROP_SYMREGRANGE|INSTROP_READ);
2812        if (!skipRequiredComma(asmr, linePtr))
2813            return false;
2814        // parse SOFFSET (SGPR or scalar source or constant)
2815        gcnAsm->setCurrentRVU(3);
2816        good &= parseOperand(asmr, linePtr, soffsetOp, nullptr, arch, 1,
2817                 INSTROP_SREGS|INSTROP_SSOURCE|INSTROP_ONLYINLINECONSTS|INSTROP_READ|
2818                 INSTROP_NOLITERALERRORMUBUF, GCNFIELD_M_SOFFSET);
2819    }
2820   
2821    bool haveOffset = false, haveFormat = false;
2822    cxuint dfmt = 1, nfmt = 0;
2823    cxuint offset = 0;
2824    std::unique_ptr<AsmExpression> offsetExpr;
2825    bool haveAddr64 = false, haveTfe = false, haveSlc = false, haveLds = false;
2826    bool haveGlc = false, haveOffen = false, haveIdxen = false;
2827    const char* modName = (gcnInsn.encoding==GCNENC_MTBUF) ?
2828            "MTBUF modifier" : "MUBUF modifier";
2829   
2830    // main loop to parsing MUBUF/MTBUF modifiers
2831    while(linePtr!=end)
2832    {
2833        skipSpacesToEnd(linePtr, end);
2834        if (linePtr==end)
2835            break;
2836        char name[10];
2837        const char* modPlace = linePtr;
2838        if (!getNameArgS(asmr, 10, name, linePtr, modName))
2839        {
2840            good = false;
2841            continue;
2842        }
2843        toLowerString(name);
2844       
2845        if (name[0] == 'o')
2846        {
2847            // offen, offset
2848            if (::strcmp(name+1, "ffen")==0)
2849                good &= parseModEnable(asmr, linePtr, haveOffen, "offen modifier");
2850            else if (::strcmp(name+1, "ffset")==0)
2851            {
2852                // parse offset
2853                if (parseModImm(asmr, linePtr, offset, &offsetExpr, "offset",
2854                                12, WS_UNSIGNED))
2855                {
2856                    if (haveOffset)
2857                        asmr.printWarning(modPlace, "Offset is already defined");
2858                    haveOffset = true;
2859                }
2860                else
2861                    good = false;
2862            }
2863            else
2864                ASM_NOTGOOD_BY_ERROR(modPlace, (gcnInsn.encoding==GCNENC_MUBUF) ? 
2865                    "Unknown MUBUF modifier" : "Unknown MTBUF modifier")
2866        }
2867        else if (gcnInsn.encoding==GCNENC_MTBUF && ::strcmp(name, "format")==0)
2868        {
2869            // parse format
2870            bool modGood = true;
2871            skipSpacesToEnd(linePtr, end);
2872            if (linePtr==end || *linePtr!=':')
2873            {
2874                ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before format")
2875                continue;
2876            }
2877            skipCharAndSpacesToEnd(linePtr, end);
2878           
2879            // parse [DATA_FORMAT:NUMBER_FORMAT]
2880            if (linePtr==end || *linePtr!='[')
2881                ASM_NOTGOOD_BY_ERROR1(modGood = good, modPlace,
2882                                "Expected '[' before format")
2883            if (modGood)
2884            {
2885                skipCharAndSpacesToEnd(linePtr, end);
2886                const char* fmtPlace = linePtr;
2887                char fmtName[30];
2888                bool haveNFMT = false;
2889                if (linePtr != end && *linePtr=='@')
2890                {
2891                    // expression, parse DATA_FORMAT
2892                    linePtr++;
2893                    if (!parseImm(asmr, linePtr, dfmt, nullptr, 4, WS_UNSIGNED))
2894                        modGood = good = false;
2895                }
2896                else if (getMUBUFFmtNameArg(
2897                            asmr, 30, fmtName, linePtr, "data/number format"))
2898                {
2899                    toLowerString(fmtName);
2900                    size_t dfmtNameIndex = (::strncmp(fmtName,
2901                                 "buf_data_format_", 16)==0) ? 16 : 0;
2902                    size_t dfmtIdx = binaryMapFind(mtbufDFMTNamesMap, mtbufDFMTNamesMap+14,
2903                                fmtName+dfmtNameIndex, CStringLess())-mtbufDFMTNamesMap;
2904                    if (dfmtIdx != 14)
2905                        dfmt = mtbufDFMTNamesMap[dfmtIdx].second;
2906                    else
2907                    {
2908                        // nfmt (if not found, then try parse number format)
2909                        size_t nfmtNameIndex = (::strncmp(fmtName,
2910                                 "buf_num_format_", 15)==0) ? 15 : 0;
2911                        size_t nfmtIdx = binaryMapFind(mtbufNFMTNamesMap,
2912                               mtbufNFMTNamesMap+8, fmtName+nfmtNameIndex,
2913                               CStringLess())-mtbufNFMTNamesMap;
2914                        // check if found
2915                        if (nfmtIdx!=8)
2916                        {
2917                            nfmt = mtbufNFMTNamesMap[nfmtIdx].second;
2918                            haveNFMT = true;
2919                        }
2920                        else
2921                            ASM_NOTGOOD_BY_ERROR1(modGood = good, fmtPlace,
2922                                        "Unknown data/number format")
2923                    }
2924                }
2925                else
2926                    modGood = good = false;
2927               
2928                skipSpacesToEnd(linePtr, end);
2929                if (!haveNFMT && linePtr!=end && *linePtr==',')
2930                {
2931                    skipCharAndSpacesToEnd(linePtr, end);
2932                    if (linePtr != end && *linePtr=='@')
2933                    {
2934                        // expression (number format)
2935                        linePtr++;
2936                        if (!parseImm(asmr, linePtr, nfmt, nullptr, 3, WS_UNSIGNED))
2937                            modGood = good = false;
2938                    }
2939                    else
2940                    {
2941                        // parse NUMBER format from name
2942                        fmtPlace = linePtr;
2943                        good &= getEnumeration(asmr, linePtr, "number format",
2944                                8, mtbufNFMTNamesMap, nfmt, "buf_num_format_");
2945                    }
2946                }
2947                skipSpacesToEnd(linePtr, end);
2948                // close format
2949                if (linePtr!=end && *linePtr==']')
2950                    linePtr++;
2951                else
2952                    ASM_NOTGOOD_BY_ERROR(linePtr, "Unterminated format modifier")
2953                if (modGood)
2954                {
2955                    if (haveFormat)
2956                        asmr.printWarning(modPlace, "Format is already defined");
2957                    haveFormat = true;
2958                }
2959            }
2960        }
2961        // other modifiers
2962        else if (!isGCN12 && ::strcmp(name, "addr64")==0)
2963            good &= parseModEnable(asmr, linePtr, haveAddr64, "addr64 modifier");
2964        else if (::strcmp(name, "tfe")==0)
2965            good &= parseModEnable(asmr, linePtr, haveTfe, "tfe modifier");
2966        else if (::strcmp(name, "glc")==0)
2967            good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
2968        else if (::strcmp(name, "slc")==0)
2969            good &= parseModEnable(asmr, linePtr, haveSlc, "slc modifier");
2970        else if (gcnInsn.encoding==GCNENC_MUBUF && ::strcmp(name, "lds")==0)
2971            good &= parseModEnable(asmr, linePtr, haveLds, "lds modifier");
2972        else if (::strcmp(name, "idxen")==0)
2973            good &= parseModEnable(asmr, linePtr, haveIdxen, "idxen modifier");
2974        else
2975            ASM_NOTGOOD_BY_ERROR(modPlace, (gcnInsn.encoding==GCNENC_MUBUF) ? 
2976                    "Unknown MUBUF modifier" : "Unknown MTBUF modifier")
2977    }
2978   
2979    /* checking addr range and vdata range */
2980    bool vdataToRead = false;
2981    bool vdataToWrite = false;
2982    if (vdataReg)
2983    {
2984        vdataToWrite = ((gcnInsn.mode&GCN_MLOAD) != 0 ||
2985                ((gcnInsn.mode&GCN_MATOMIC)!=0 && haveGlc));
2986        vdataToRead = (gcnInsn.mode&GCN_MLOAD)==0 ||
2987                (gcnInsn.mode&GCN_MATOMIC)!=0;
2988        // check register range (number of register) in VDATA
2989        cxuint dregsNum = (((gcnInsn.mode&GCN_DSIZE_MASK)>>GCN_SHIFT2)+1);
2990        if ((gcnInsn.mode & GCN_MUBUF_D16)!=0 && isGCN14)
2991            // 16-bit values packed into half of number of registers
2992            dregsNum = (dregsNum+1)>>1;
2993        dregsNum += (haveTfe);
2994        if (!isXRegRange(vdataReg, dregsNum))
2995        {
2996            char errorMsg[40];
2997            snprintf(errorMsg, 40, "Required %u vector register%s", dregsNum,
2998                     (dregsNum>1) ? "s" : "");
2999            ASM_NOTGOOD_BY_ERROR(vdataPlace, errorMsg)
3000        }
3001    }
3002    if (vaddrReg)
3003    {
3004        if (!parsedVaddr && (haveIdxen || haveOffen || haveAddr64))
3005            // no vaddr in instruction
3006            ASM_NOTGOOD_BY_ERROR(vaddrPlace, "VADDR is required if idxen, offen "
3007                    "or addr64 is enabled")
3008        else
3009        {
3010            const cxuint vaddrSize = ((haveOffen&&haveIdxen) || haveAddr64) ? 2 : 1;
3011            // check register range (number of register) in VADDR
3012            if (!isXRegRange(vaddrReg, vaddrSize))
3013                ASM_NOTGOOD_BY_ERROR(vaddrPlace, (vaddrSize==2) ?
3014                        "Required 2 vector registers" : "Required 1 vector register")
3015        }
3016    }
3017    // fix access for VDATA field
3018    gcnAsm->instrRVUs[0].rwFlags = (vdataToWrite ? ASMRVU_WRITE : 0) |
3019            (vdataToRead ? ASMRVU_READ : 0);
3020    // check fcmpswap
3021    bool vdataDivided = false;
3022    if ((gcnInsn.mode & GCN_MHALFWRITE) != 0 && vdataToWrite && !haveLds &&
3023        gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3024    {
3025        // fix access
3026        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
3027        uint16_t size = rvu.rend-rvu.rstart;
3028        rvu.rend = rvu.rstart + (size>>1);
3029        AsmRegVarUsage& nextRvu = gcnAsm->instrRVUs[4];
3030        nextRvu = rvu;
3031        nextRvu.regField = GCNFIELD_M_VDATAH;
3032        nextRvu.rstart += (size>>1);
3033        nextRvu.rend = rvu.rstart + size;
3034        nextRvu.rwFlags = ASMRVU_READ;
3035        vdataDivided = true;
3036    }
3037    // do not read vaddr if no offen and idxen and no addr64
3038    if (!haveAddr64 && !haveOffen && !haveIdxen)
3039        gcnAsm->instrRVUs[1].regField = ASMFIELD_NONE; // ignore this
3040   
3041    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3042        return false;
3043   
3044    /* checking modifiers conditions */
3045    if (haveAddr64 && (haveOffen || haveIdxen))
3046        ASM_FAIL_BY_ERROR(instrPlace, "Idxen and offen must be zero in 64-bit address mode")
3047    if (haveTfe && haveLds)
3048        ASM_FAIL_BY_ERROR(instrPlace, "Both LDS and TFE is illegal")
3049   
3050    // ignore vdata if LDS
3051    if (haveLds)
3052        gcnAsm->instrRVUs[0].regField = ASMFIELD_NONE;
3053   
3054    if (haveTfe && (vdataDivided ||
3055            gcnAsm->instrRVUs[0].rwFlags!=(ASMRVU_READ|ASMRVU_WRITE)) &&
3056            gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3057    {
3058        // fix for tfe
3059        const cxuint rvuId = (vdataDivided ? 4 : 0);
3060        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[rvuId];
3061        AsmRegVarUsage& lastRvu = gcnAsm->instrRVUs[5];
3062        lastRvu = rvu;
3063        lastRvu.rstart = lastRvu.rend-1;
3064        lastRvu.rwFlags = ASMRVU_READ|ASMRVU_WRITE;
3065        lastRvu.regField = GCNFIELD_M_VDATALAST;
3066        if (lastRvu.regVar==nullptr) // fix for regusage
3067        {
3068            // to save register size for VDATALAST
3069            lastRvu.rstart = gcnAsm->instrRVUs[0].rstart;
3070            lastRvu.rend--;
3071        }
3072        rvu.rend--;
3073    }
3074   
3075    if (offsetExpr!=nullptr)
3076        offsetExpr->setTarget(AsmExprTarget(GCNTGT_MXBUFOFFSET, asmr.currentSection,
3077                    output.size()));
3078   
3079    // put data (instruction words)
3080    uint32_t words[2];
3081    if (gcnInsn.encoding==GCNENC_MUBUF)
3082        SLEV(words[0], 0xe0000000U | offset | (haveOffen ? 0x1000U : 0U) |
3083                (haveIdxen ? 0x2000U : 0U) | (haveGlc ? 0x4000U : 0U) |
3084                ((haveAddr64 && !isGCN12) ? 0x8000U : 0U) | (haveLds ? 0x10000U : 0U) |
3085                ((haveSlc && isGCN12) ? 0x20000U : 0) | (uint32_t(gcnInsn.code1)<<18));
3086    else
3087    {
3088        // MTBUF encoding
3089        uint32_t code = (isGCN12) ? (uint32_t(gcnInsn.code1)<<15) :
3090                (uint32_t(gcnInsn.code1)<<16);
3091        SLEV(words[0], 0xe8000000U | offset | (haveOffen ? 0x1000U : 0U) |
3092                (haveIdxen ? 0x2000U : 0U) | (haveGlc ? 0x4000U : 0U) |
3093                ((haveAddr64 && !isGCN12) ? 0x8000U : 0U) | code |
3094                (uint32_t(dfmt)<<19) | (uint32_t(nfmt)<<23));
3095    }
3096    // second word
3097    SLEV(words[1], (vaddrReg.bstart()&0xff) | (uint32_t(vdataReg.bstart()&0xff)<<8) |
3098            (uint32_t(srsrcReg.bstart()>>2)<<16) |
3099            ((haveSlc && (!isGCN12 || gcnInsn.encoding==GCNENC_MTBUF)) ? (1U<<22) : 0) |
3100            (haveTfe ? (1U<<23) : 0) | (uint32_t(soffsetOp.range.bstart())<<24));
3101   
3102    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3103            reinterpret_cast<cxbyte*>(words + 2));
3104   
3105    offsetExpr.release();
3106    // update register pool (instr loads or save old value) */
3107    if (vdataReg && !vdataReg.isRegVar() && (vdataToWrite || haveTfe) && !haveLds)
3108        updateVGPRsNum(gcnRegs.vgprsNum, vdataReg.end-257);
3109    if (soffsetOp.range && !soffsetOp.range.isRegVar())
3110        updateRegFlags(gcnRegs.regFlags, soffsetOp.range.start, arch);
3111    return true;
3112}
3113
3114bool GCNAsmUtils::parseMIMGEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
3115                  const char* instrPlace, const char* linePtr, uint16_t arch,
3116                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
3117                  GCNEncSize gcnEncSize)
3118{
3119    const char* end = asmr.line+asmr.lineSize;
3120    if (gcnEncSize==GCNEncSize::BIT32)
3121        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for MIMG encoding")
3122    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
3123    bool good = true;
3124    RegRange vaddrReg(0, 0);
3125    RegRange vdataReg(0, 0);
3126    RegRange ssampReg(0, 0);
3127    RegRange srsrcReg(0, 0);
3128    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
3129   
3130    skipSpacesToEnd(linePtr, end);
3131    const char* vdataPlace = linePtr;
3132    gcnAsm->setCurrentRVU(0);
3133    // parse VDATA (various VGPR number, verified later)
3134    good &= parseVRegRange(asmr, linePtr, vdataReg, 0, GCNFIELD_M_VDATA, true,
3135                    INSTROP_SYMREGRANGE|INSTROP_READ);
3136    if (!skipRequiredComma(asmr, linePtr))
3137        return false;
3138   
3139    skipSpacesToEnd(linePtr, end);
3140    const char* vaddrPlace = linePtr;
3141    gcnAsm->setCurrentRVU(1);
3142    // // parse VADDR (various VGPR number, verified later)
3143    good &= parseVRegRange(asmr, linePtr, vaddrReg, 0, GCNFIELD_M_VADDR, true,
3144                    INSTROP_SYMREGRANGE|INSTROP_READ);
3145    cxuint geRegRequired = (gcnInsn.mode&GCN_MIMG_VA_MASK)+1;
3146    cxuint vaddrRegsNum = vaddrReg.end-vaddrReg.start;
3147    cxuint vaddrMaxExtraRegs = (gcnInsn.mode&GCN_MIMG_VADERIV) ? 7 : 3;
3148    if (vaddrRegsNum < geRegRequired || vaddrRegsNum > geRegRequired+vaddrMaxExtraRegs)
3149    {
3150        char buf[60];
3151        snprintf(buf, 60, "Required (%u-%u) vector registers", geRegRequired,
3152                 geRegRequired+vaddrMaxExtraRegs);
3153        ASM_NOTGOOD_BY_ERROR(vaddrPlace, buf)
3154    }
3155   
3156    if (!skipRequiredComma(asmr, linePtr))
3157        return false;
3158    skipSpacesToEnd(linePtr, end);
3159    const char* srsrcPlace = linePtr;
3160    gcnAsm->setCurrentRVU(2);
3161    // parse SRSRC (4 or 8 SGPR's) number of register verified later
3162    good &= parseSRegRange(asmr, linePtr, srsrcReg, arch, 0, GCNFIELD_M_SRSRC, true,
3163                    INSTROP_SYMREGRANGE|INSTROP_READ);
3164   
3165    if ((gcnInsn.mode & GCN_MIMG_SAMPLE) != 0)
3166    {
3167        if (!skipRequiredComma(asmr, linePtr))
3168            return false;
3169        gcnAsm->setCurrentRVU(3);
3170        // parse SSAMP (4 SGPR's)
3171        good &= parseSRegRange(asmr, linePtr, ssampReg, arch, 4, GCNFIELD_MIMG_SSAMP,
3172                               true, INSTROP_SYMREGRANGE|INSTROP_READ);
3173    }
3174   
3175    bool haveTfe = false, haveSlc = false, haveGlc = false;
3176    bool haveDa = false, haveR128 = false, haveLwe = false, haveUnorm = false;
3177    bool haveDMask = false, haveD16 = false, haveA16 = false;
3178    cxbyte dmask = 0x1;
3179    /* modifiers and modifiers */
3180    while(linePtr!=end)
3181    {
3182        skipSpacesToEnd(linePtr, end);
3183        if (linePtr==end)
3184            break;
3185        char name[10];
3186        const char* modPlace = linePtr;
3187        if (!getNameArgS(asmr, 10, name, linePtr, "MIMG modifier"))
3188        {
3189            good = false;
3190            continue;
3191        }
3192        toLowerString(name);
3193       
3194        if (name[0] == 'd')
3195        {
3196            if (name[1]=='a' && name[2]==0)
3197                // DA modifier
3198                good &= parseModEnable(asmr, linePtr, haveDa, "da modifier");
3199            else if ((arch & ARCH_GCN_1_2_4)!=0 && name[1]=='1' &&
3200                name[2]=='6' && name[3]==0)
3201                // D16 modifier
3202                good &= parseModEnable(asmr, linePtr, haveD16, "d16 modifier");
3203            else if (::strcmp(name+1, "mask")==0)
3204            {
3205                // parse dmask
3206                skipSpacesToEnd(linePtr, end);
3207                if (linePtr!=end && *linePtr==':')
3208                {
3209                    /* parse dmask immediate */
3210                    skipCharAndSpacesToEnd(linePtr, end);
3211                    const char* valuePlace = linePtr;
3212                    uint64_t value;
3213                    if (getAbsoluteValueArg(asmr, value, linePtr, true))
3214                    {
3215                        // detect multiple occurrences
3216                        if (haveDMask)
3217                            asmr.printWarning(modPlace, "Dmask is already defined");
3218                        haveDMask = true;
3219                        if (value>0xf)
3220                            asmr.printWarning(valuePlace, "Dmask out of range (0-15)");
3221                        dmask = value&0xf;
3222                        if (dmask == 0)
3223                            ASM_NOTGOOD_BY_ERROR(valuePlace, "Zero in dmask is illegal")
3224                    }
3225                    else
3226                        good = false;
3227                }
3228                else
3229                    ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before dmask")
3230            }
3231            else
3232                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown MIMG modifier")
3233        }
3234        else if (name[0] < 's')
3235        {
3236            // glc, lwe, r128, a16 modifiers
3237            if (::strcmp(name, "glc")==0)
3238                good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
3239            else if (::strcmp(name, "lwe")==0)
3240                good &= parseModEnable(asmr, linePtr, haveLwe, "lwe modifier");
3241            else if (!isGCN14 && ::strcmp(name, "r128")==0)
3242                good &= parseModEnable(asmr, linePtr, haveR128, "r128 modifier");
3243            else if (isGCN14 && ::strcmp(name, "a16")==0)
3244                good &= parseModEnable(asmr, linePtr, haveA16, "a16 modifier");
3245            else
3246                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown MIMG modifier")
3247        }
3248        // other modifiers
3249        else if (::strcmp(name, "tfe")==0)
3250            good &= parseModEnable(asmr, linePtr, haveTfe, "tfe modifier");
3251        else if (::strcmp(name, "slc")==0)
3252            good &= parseModEnable(asmr, linePtr, haveSlc, "slc modifier");
3253        else if (::strcmp(name, "unorm")==0)
3254            good &= parseModEnable(asmr, linePtr, haveUnorm, "unorm modifier");
3255        else
3256            ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown MIMG modifier")
3257    }
3258   
3259    cxuint dregsNum = 4;
3260    // check number of registers in VDATA
3261    if ((gcnInsn.mode & GCN_MIMG_VDATA4) == 0)
3262        dregsNum = ((dmask & 1)?1:0) + ((dmask & 2)?1:0) + ((dmask & 4)?1:0) +
3263                ((dmask & 8)?1:0) + (haveTfe);
3264    if (dregsNum!=0 && !isXRegRange(vdataReg, dregsNum))
3265    {
3266        char errorMsg[40];
3267        snprintf(errorMsg, 40, "Required %u vector register%s", dregsNum,
3268                 (dregsNum>1) ? "s" : "");
3269        ASM_NOTGOOD_BY_ERROR(vdataPlace, errorMsg)
3270    }
3271    // check number of registers in SRSRC
3272    if (!isXRegRange(srsrcReg, (haveR128)?4:8))
3273        ASM_NOTGOOD_BY_ERROR(srsrcPlace, (haveR128) ? "Required 4 scalar registers" :
3274                    "Required 8 scalar registers")
3275   
3276    const bool vdataToWrite = ((gcnInsn.mode&GCN_MLOAD) != 0 ||
3277                ((gcnInsn.mode&GCN_MATOMIC)!=0 && haveGlc));
3278    const bool vdataToRead = ((gcnInsn.mode&GCN_MLOAD) == 0 ||
3279                ((gcnInsn.mode&GCN_MATOMIC)!=0));
3280   
3281    // fix access for VDATA field
3282    gcnAsm->instrRVUs[0].rwFlags = (vdataToWrite ? ASMRVU_WRITE : 0) |
3283            (vdataToRead ? ASMRVU_READ : 0);
3284    // fix alignment
3285    if (gcnAsm->instrRVUs[2].regVar != nullptr)
3286        gcnAsm->instrRVUs[2].align = 4;
3287   
3288    // check fcmpswap
3289    bool vdataDivided = false;
3290    if ((gcnInsn.mode & GCN_MHALFWRITE) != 0 && vdataToWrite &&
3291        gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3292    {
3293        // fix access
3294        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
3295        uint16_t size = rvu.rend-rvu.rstart;
3296        rvu.rend = rvu.rstart + (size>>1);
3297        AsmRegVarUsage& nextRvu = gcnAsm->instrRVUs[4];
3298        nextRvu = rvu;
3299        nextRvu.regField = GCNFIELD_M_VDATAH;
3300        nextRvu.rstart += (size>>1);
3301        nextRvu.rend = rvu.rstart + size;
3302        nextRvu.rwFlags = ASMRVU_READ;
3303        vdataDivided = true;
3304    }
3305   
3306    if (haveTfe && (vdataDivided ||
3307            gcnAsm->instrRVUs[0].rwFlags!=(ASMRVU_READ|ASMRVU_WRITE)) &&
3308       gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3309    {
3310        // fix for tfe
3311        const cxuint rvuId = (vdataDivided ? 4 : 0);
3312        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[rvuId];
3313        AsmRegVarUsage& lastRvu = gcnAsm->instrRVUs[5];
3314        lastRvu = rvu;
3315        lastRvu.rstart = lastRvu.rend-1;
3316        lastRvu.rwFlags = ASMRVU_READ|ASMRVU_WRITE;
3317        lastRvu.regField = GCNFIELD_M_VDATALAST;
3318        if (lastRvu.regVar==nullptr) // fix for regusage
3319        {
3320            // to save register size for VDATALAST
3321            lastRvu.rstart = gcnAsm->instrRVUs[0].rstart;
3322            lastRvu.rend--;
3323        }
3324        rvu.rend--;
3325    }
3326   
3327    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3328        return false;
3329   
3330    /* checking modifiers conditions */
3331    if (!haveUnorm && ((gcnInsn.mode&GCN_MLOAD) == 0 || (gcnInsn.mode&GCN_MATOMIC)!=0))
3332        // unorm is not set for this instruction
3333        ASM_FAIL_BY_ERROR(instrPlace, "Unorm is not set for store or atomic instruction")
3334   
3335    // put instruction words
3336    uint32_t words[2];
3337    SLEV(words[0], 0xf0000000U | (uint32_t(dmask&0xf)<<8) | (haveUnorm ? 0x1000U : 0) |
3338        (haveGlc ? 0x2000U : 0) | (haveDa ? 0x4000U : 0) |
3339        (haveR128|haveA16 ? 0x8000U : 0) |
3340        (haveTfe ? 0x10000U : 0) | (haveLwe ? 0x20000U : 0) |
3341        (uint32_t(gcnInsn.code1)<<18) | (haveSlc ? (1U<<25) : 0));
3342    SLEV(words[1], (vaddrReg.bstart()&0xff) | (uint32_t(vdataReg.bstart()&0xff)<<8) |
3343            (uint32_t(srsrcReg.bstart()>>2)<<16) | (uint32_t(ssampReg.bstart()>>2)<<21) |
3344            (haveD16 ? (1U<<31) : 0));
3345    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3346            reinterpret_cast<cxbyte*>(words + 2));
3347   
3348    // update register pool (instr loads or save old value) */
3349    if (vdataReg && !vdataReg.isRegVar() && (vdataToWrite || haveTfe))
3350        updateVGPRsNum(gcnRegs.vgprsNum, vdataReg.end-257);
3351    return true;
3352}
3353
3354bool GCNAsmUtils::parseEXPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
3355                  const char* instrPlace, const char* linePtr, uint16_t arch,
3356                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
3357                  GCNEncSize gcnEncSize)
3358{
3359    const char* end = asmr.line+asmr.lineSize;
3360    if (gcnEncSize==GCNEncSize::BIT32)
3361        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for EXP encoding")
3362    bool good = true;
3363    cxbyte enMask = 0xf;
3364    cxbyte target = 0;
3365    RegRange vsrcsReg[4];
3366    const char* vsrcPlaces[4];
3367    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
3368   
3369    char name[20];
3370    skipSpacesToEnd(linePtr, end);
3371    const char* targetPlace = linePtr;
3372   
3373    try
3374    {
3375    if (getNameArg(asmr, 20, name, linePtr, "target"))
3376    {
3377        size_t nameSize = linePtr-targetPlace;
3378        const char* nameStart = name;
3379        toLowerString(name);
3380        // parse mrt / mrtz / mrt0 - mrt7
3381        if (name[0]=='m' && name[1]=='r' && name[2]=='t')
3382        {
3383            // parse mrtX target
3384            if (name[3]!='z' || name[4]!=0)
3385            {
3386                nameStart+=3;
3387                target = cstrtobyte(nameStart, name+nameSize);
3388                if (target>=8)
3389                    ASM_NOTGOOD_BY_ERROR(targetPlace, "MRT number out of range (0-7)")
3390            }
3391            else
3392                target = 8; // mrtz
3393        }
3394        // parse pos0 - pos3
3395        else if (name[0]=='p' && name[1]=='o' && name[2]=='s')
3396        {
3397            // parse pos target
3398            nameStart+=3;
3399            cxbyte posNum = cstrtobyte(nameStart, name+nameSize);
3400            if (posNum>=4)
3401                ASM_NOTGOOD_BY_ERROR(targetPlace, "Pos number out of range (0-3)")
3402            else
3403                target = posNum+12;
3404        }
3405        else if (strcmp(name, "null")==0)
3406            target = 9;
3407        // param0 - param 31
3408        else if (memcmp(name, "param", 5)==0)
3409        {
3410            nameStart+=5;
3411            cxbyte posNum = cstrtobyte(nameStart, name+nameSize);
3412            if (posNum>=32)
3413                ASM_NOTGOOD_BY_ERROR(targetPlace, "Param number out of range (0-31)")
3414            else
3415                target = posNum+32;
3416        }
3417        else
3418            ASM_NOTGOOD_BY_ERROR(targetPlace, "Unknown EXP target")
3419    }
3420    else
3421        good = false;
3422    }
3423    catch (const ParseException& ex)
3424    {
3425        // number parsing error
3426        asmr.printError(targetPlace, ex.what());
3427        good = false;
3428    }
3429   
3430    /* parse VSRC0-3 registers */
3431    for (cxuint i = 0; i < 4; i++)
3432    {
3433        if (!skipRequiredComma(asmr, linePtr))
3434            return false;
3435        skipSpacesToEnd(linePtr, end);
3436        vsrcPlaces[i] = linePtr;
3437        // if not 'off', then parse vector register
3438        if (linePtr+2>=end || toLower(linePtr[0])!='o' || toLower(linePtr[1])!='f' ||
3439            toLower(linePtr[2])!='f' || (linePtr+3!=end && isAlnum(linePtr[3])))
3440        {
3441            gcnAsm->setCurrentRVU(i);
3442            good &= parseVRegRange(asmr, linePtr, vsrcsReg[i], 1, GCNFIELD_EXP_VSRC0+i,
3443                        true, INSTROP_SYMREGRANGE|INSTROP_READ);
3444        }
3445        else
3446        {
3447            // if vsrcX is off
3448            enMask &= ~(1U<<i);
3449            vsrcsReg[i] = { 0, 0 };
3450            linePtr += 3;
3451        }
3452    }
3453   
3454    /* EXP modifiers */
3455    bool haveVM = false, haveCompr = false, haveDone = false;
3456    while(linePtr!=end)
3457    {
3458        skipSpacesToEnd(linePtr, end);
3459        if (linePtr==end)
3460            break;
3461        const char* modPlace = linePtr;
3462        if (!getNameArgS(asmr, 10, name, linePtr, "EXP modifier"))
3463        {
3464            good = false;
3465            continue;
3466        }
3467        toLowerString(name);
3468        if (name[0]=='v' && name[1]=='m' && name[2]==0)
3469            good &= parseModEnable(asmr, linePtr, haveVM, "vm modifier");
3470        else if (::strcmp(name, "done")==0)
3471            good &= parseModEnable(asmr, linePtr, haveDone, "done modifier");
3472        else if (::strcmp(name, "compr")==0)
3473            good &= parseModEnable(asmr, linePtr, haveCompr, "compr modifier");
3474        else
3475            ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown EXP modifier")
3476    }
3477   
3478    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3479        return false;
3480   
3481    // checking whether VSRC's is correct in compr mode if enabled
3482    if (haveCompr && !vsrcsReg[0].isRegVar() && !vsrcsReg[1].isRegVar() &&
3483            !vsrcsReg[0].isRegVar() && !vsrcsReg[1].isRegVar())
3484    {
3485        if (vsrcsReg[0].start!=vsrcsReg[1].start && (enMask&3)==3)
3486            // error (vsrc1!=vsrc0)
3487            ASM_FAIL_BY_ERROR(vsrcPlaces[1], "VSRC1 must be equal to VSRC0 in compr mode")
3488        if (vsrcsReg[2].start!=vsrcsReg[3].start && (enMask&12)==12)
3489            // error (vsrc3!=vsrc2)
3490            ASM_FAIL_BY_ERROR(vsrcPlaces[3], "VSRC3 must be equal to VSRC2 in compr mode")
3491        vsrcsReg[1] = vsrcsReg[2];
3492        vsrcsReg[2] = vsrcsReg[3] = { 0, 0 };
3493    }
3494   
3495    // put instruction words
3496    uint32_t words[2];
3497    SLEV(words[0], ((arch&ARCH_GCN_1_2_4) ? 0xc4000000 : 0xf8000000U) | enMask |
3498            (uint32_t(target)<<4) | (haveCompr ? 0x400 : 0) | (haveDone ? 0x800 : 0) |
3499            (haveVM ? 0x1000U : 0));
3500    SLEV(words[1], uint32_t(vsrcsReg[0].bstart()&0xff) |
3501            (uint32_t(vsrcsReg[1].bstart()&0xff)<<8) |
3502            (uint32_t(vsrcsReg[2].bstart()&0xff)<<16) |
3503            (uint32_t(vsrcsReg[3].bstart()&0xff)<<24));
3504   
3505    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3506            reinterpret_cast<cxbyte*>(words + 2));
3507    return true;
3508}
3509
3510bool GCNAsmUtils::parseFLATEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
3511                  const char* instrPlace, const char* linePtr, uint16_t arch,
3512                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
3513                  GCNEncSize gcnEncSize)
3514{
3515    const char* end = asmr.line+asmr.lineSize;
3516    if (gcnEncSize==GCNEncSize::BIT32)
3517        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for FLAT encoding")
3518    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
3519    const cxuint flatMode = (gcnInsn.mode & GCN_FLAT_MODEMASK);
3520    bool good = true;
3521    RegRange vaddrReg(0, 0);
3522    RegRange vdstReg(0, 0);
3523    RegRange vdataReg(0, 0);
3524    RegRange saddrReg(0, 0);
3525    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
3526   
3527    skipSpacesToEnd(linePtr, end);
3528    const char* vdstPlace = nullptr;
3529   
3530    bool vaddrOff = false;
3531    const cxuint dregsNum = ((gcnInsn.mode&GCN_DSIZE_MASK)>>GCN_SHIFT2)+1;
3532   
3533    const cxuint addrRegsNum = (flatMode != GCN_FLAT_SCRATCH ?
3534                (flatMode==GCN_FLAT_FLAT ? 2 : 0)  : 1);
3535    const char* addrPlace = nullptr;
3536    if ((gcnInsn.mode & GCN_FLAT_ADST) == 0)
3537    {
3538        // first is destination
3539        vdstPlace = linePtr;
3540       
3541        gcnAsm->setCurrentRVU(0);
3542        good &= parseVRegRange(asmr, linePtr, vdstReg, 0, GCNFIELD_FLAT_VDST, true,
3543                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
3544        if (!skipRequiredComma(asmr, linePtr))
3545            return false;
3546        skipSpacesToEnd(linePtr, end);
3547        addrPlace = linePtr;
3548        if (flatMode == GCN_FLAT_SCRATCH && linePtr+3<=end &&
3549            strncasecmp(linePtr, "off", 3)==0 && (linePtr+3==end || !isAlnum(linePtr[3])))
3550        {
3551            // // if 'off' word
3552            vaddrOff = true;
3553            linePtr+=3;
3554        }
3555        else
3556        {
3557            gcnAsm->setCurrentRVU(1);
3558            // parse VADDR (1 or 2 VGPR's)
3559            good &= parseVRegRange(asmr, linePtr, vaddrReg, addrRegsNum,
3560                    GCNFIELD_FLAT_ADDR, true, INSTROP_SYMREGRANGE|INSTROP_READ);
3561        }
3562    }
3563    else
3564    {
3565        // first is data
3566        skipSpacesToEnd(linePtr, end);
3567        addrPlace = linePtr;
3568        if (flatMode == GCN_FLAT_SCRATCH && linePtr+3<=end &&
3569            strncasecmp(linePtr, "off", 3)==0 && (linePtr+3==end || !isAlnum(linePtr[3])))
3570        {
3571            // if 'off' word
3572            vaddrOff = true;
3573            linePtr+=3;
3574        }
3575        else
3576        {
3577            gcnAsm->setCurrentRVU(1);
3578            // parse VADDR (1 or 2 VGPR's)
3579            good &= parseVRegRange(asmr, linePtr, vaddrReg, addrRegsNum,
3580                        GCNFIELD_FLAT_ADDR, true, INSTROP_SYMREGRANGE|INSTROP_READ);
3581        }
3582        if ((gcnInsn.mode & GCN_FLAT_NODST) == 0)
3583        {
3584            if (!skipRequiredComma(asmr, linePtr))
3585                return false;
3586            skipSpacesToEnd(linePtr, end);
3587            vdstPlace = linePtr;
3588            gcnAsm->setCurrentRVU(0);
3589            // parse VDST (VGPRs, various number of register, verified later)
3590            good &= parseVRegRange(asmr, linePtr, vdstReg, 0, GCNFIELD_FLAT_VDST, true,
3591                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
3592        }
3593    }
3594   
3595    if ((gcnInsn.mode & GCN_FLAT_NODATA) == 0) /* print data */
3596    {
3597        if (!skipRequiredComma(asmr, linePtr))
3598            return false;
3599        gcnAsm->setCurrentRVU(2);
3600        // parse VDATA (VGPRS, 1-4 registers)
3601        good &= parseVRegRange(asmr, linePtr, vdataReg, dregsNum, GCNFIELD_FLAT_DATA,
3602                               true, INSTROP_SYMREGRANGE|INSTROP_READ);
3603    }
3604   
3605    bool saddrOff = false;
3606    if (flatMode != 0)
3607    {
3608        // SADDR
3609        if (!skipRequiredComma(asmr, linePtr))
3610            return false;
3611        skipSpacesToEnd(linePtr, end);
3612        if (flatMode != 0 && linePtr+3<=end && strncasecmp(linePtr, "off", 3)==0 &&
3613            (linePtr+3==end || !isAlnum(linePtr[3])))
3614        {  // if 'off' word
3615            saddrOff = true;
3616            linePtr+=3;
3617        }
3618        else
3619        {
3620            gcnAsm->setCurrentRVU(3);
3621            good &= parseSRegRange(asmr, linePtr, saddrReg, arch,
3622                        (flatMode==GCN_FLAT_SCRATCH ? 1 : 2), GCNFIELD_FLAT_SADDR, true,
3623                        INSTROP_SYMREGRANGE|INSTROP_READ);
3624        }
3625    }
3626   
3627    if (addrRegsNum == 0)
3628    {
3629        // check size of addrRange
3630        // if SADDR then 1 VADDR offset register, otherwise 2 VADDR VGPRs
3631        cxuint reqAddrRegsNum = saddrOff ? 2 : 1;
3632        if (!isXRegRange(vaddrReg, reqAddrRegsNum))
3633        {
3634            char errorMsg[40];
3635            snprintf(errorMsg, 40, "Required %u vector register%s", reqAddrRegsNum,
3636                     (reqAddrRegsNum>1) ? "s" : "");
3637            ASM_NOTGOOD_BY_ERROR(addrPlace, errorMsg)
3638        }
3639    }
3640   
3641    if (flatMode == GCN_FLAT_SCRATCH && !saddrOff && !vaddrOff)
3642        ASM_NOTGOOD_BY_ERROR(instrPlace, "Only one of VADDR and SADDR can be set in "
3643                    "SCRATCH mode")
3644   
3645    if (saddrOff)
3646        saddrReg.start = 0x7f;
3647    if (vaddrOff)
3648        vaddrReg.start = 0x00;
3649   
3650    uint16_t instOffset = 0;
3651    std::unique_ptr<AsmExpression> instOffsetExpr;
3652    bool haveTfe = false, haveSlc = false, haveGlc = false;
3653    bool haveNv = false, haveLds = false, haveInstOffset = false;
3654   
3655    // main loop to parsing FLAT modifiers
3656    while(linePtr!=end)
3657    {
3658        skipSpacesToEnd(linePtr, end);
3659        if (linePtr==end)
3660            break;
3661        char name[20];
3662        const char* modPlace = linePtr;
3663        // get modifier name
3664        if (!getNameArgS(asmr, 20, name, linePtr, "FLAT modifier"))
3665        {
3666            good = false;
3667            continue;
3668        }
3669        // only GCN1.2 modifiers
3670        if (!isGCN14 && ::strcmp(name, "tfe") == 0)
3671            good &= parseModEnable(asmr, linePtr, haveTfe, "tfe modifier");
3672        // only GCN1.4 modifiers
3673        else if (isGCN14 && ::strcmp(name, "nv") == 0)
3674            good &= parseModEnable(asmr, linePtr, haveNv, "nv modifier");
3675        else if (isGCN14 && ::strcmp(name, "lds") == 0)
3676            good &= parseModEnable(asmr, linePtr, haveLds, "lds modifier");
3677        // GCN 1.2/1.4 modifiers
3678        else if (::strcmp(name, "glc") == 0)
3679            good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
3680        else if (::strcmp(name, "slc") == 0)
3681            good &= parseModEnable(asmr, linePtr, haveSlc, "slc modifier");
3682        else if (isGCN14 && ::strcmp(name, "inst_offset")==0)
3683        {
3684            // parse inst_offset, 13-bit with sign, or 12-bit unsigned
3685            if (parseModImm(asmr, linePtr, instOffset, &instOffsetExpr, "inst_offset",
3686                            flatMode!=0 ? 13 : 12, flatMode!=0 ? WS_BOTH : WS_UNSIGNED))
3687            {
3688                if (haveInstOffset)
3689                    asmr.printWarning(modPlace, "InstOffset is already defined");
3690                haveInstOffset = true;
3691            }
3692            else
3693                good = false;
3694        }
3695        else
3696            ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown FLAT modifier")
3697    }
3698    /* check register ranges */
3699    bool dstToWrite = vdstReg && ((gcnInsn.mode & GCN_MATOMIC)==0 || haveGlc);
3700    if (vdstReg)
3701    {
3702        cxuint dstRegsNum = ((gcnInsn.mode & GCN_CMPSWAP)!=0) ? (dregsNum>>1) : dregsNum;
3703        dstRegsNum = (haveTfe) ? dstRegsNum+1:dstRegsNum; // include tfe
3704        // check number of registers for VDST
3705        if (!isXRegRange(vdstReg, dstRegsNum))
3706        {
3707            char errorMsg[40];
3708            snprintf(errorMsg, 40, "Required %u vector register%s", dstRegsNum,
3709                     (dstRegsNum>1) ? "s" : "");
3710            ASM_NOTGOOD_BY_ERROR(vdstPlace, errorMsg)
3711        }
3712       
3713        if (haveTfe && vdstReg && gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3714        {
3715            // fix for tfe
3716            AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
3717            AsmRegVarUsage& lastRvu = gcnAsm->instrRVUs[3];
3718            lastRvu = rvu;
3719            lastRvu.rstart = lastRvu.rend-1;
3720            lastRvu.rwFlags = ASMRVU_READ|ASMRVU_WRITE;
3721            lastRvu.regField = GCNFIELD_FLAT_VDSTLAST;
3722            if (lastRvu.regVar==nullptr) // fix for regusage
3723            {
3724                // to save register size for VDSTLAST
3725                lastRvu.rstart = rvu.rstart;
3726                lastRvu.rend--;
3727            }
3728            rvu.rend--;
3729        }
3730       
3731        if (!dstToWrite)
3732            gcnAsm->instrRVUs[0].regField = ASMFIELD_NONE;
3733    }
3734   
3735    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3736        return false;
3737   
3738    if (instOffsetExpr!=nullptr)
3739        instOffsetExpr->setTarget(AsmExprTarget(flatMode!=0 ?
3740                    GCNTGT_INSTOFFSET_S : GCNTGT_INSTOFFSET, asmr.currentSection,
3741                    output.size()));
3742   
3743    // put data (instruction words)
3744    uint32_t words[2];
3745    SLEV(words[0], 0xdc000000U | (haveGlc ? 0x10000 : 0) | (haveSlc ? 0x20000: 0) |
3746            (uint32_t(gcnInsn.code1)<<18) | (haveLds ? 0x2000U : 0) | instOffset |
3747            (uint32_t(flatMode)<<14));
3748    SLEV(words[1], (vaddrReg.bstart()&0xff) | (uint32_t(vdataReg.bstart()&0xff)<<8) |
3749            (haveTfe|haveNv ? (1U<<23) : 0) | (uint32_t(vdstReg.bstart()&0xff)<<24) |
3750            (uint32_t(saddrReg.bstart())<<16));
3751   
3752    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3753            reinterpret_cast<cxbyte*>(words + 2));
3754   
3755    instOffsetExpr.release();
3756    // update register pool
3757    if (vdstReg && !vdstReg.isRegVar() && (dstToWrite || haveTfe))
3758        updateVGPRsNum(gcnRegs.vgprsNum, vdstReg.end-257);
3759    return true;
3760}
3761
3762};
3763
3764ISAUsageHandler* GCNAssembler::createUsageHandler(std::vector<cxbyte>& content) const
3765{
3766    return new GCNUsageHandler(content, curArchMask);
3767}
3768
3769void GCNAssembler::assemble(const CString& inMnemonic, const char* mnemPlace,
3770            const char* linePtr, const char* lineEnd, std::vector<cxbyte>& output,
3771            ISAUsageHandler* usageHandler)
3772{
3773    CString mnemonic;
3774    size_t inMnemLen = inMnemonic.size();
3775    GCNEncSize gcnEncSize = GCNEncSize::UNKNOWN;
3776    GCNVOPEnc vopEnc = GCNVOPEnc::NORMAL;
3777    // checking encoding suffixes (_e64, _e32,_dpp, _sdwa)
3778    if (inMnemLen>4 && ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e64")==0)
3779    {
3780        gcnEncSize = GCNEncSize::BIT64;
3781        mnemonic = inMnemonic.substr(0, inMnemLen-4);
3782    }
3783    else if (inMnemLen>4 && ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e32")==0)
3784    {
3785        gcnEncSize = GCNEncSize::BIT32;
3786        mnemonic = inMnemonic.substr(0, inMnemLen-4);
3787    }
3788    else if (inMnemLen>6 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
3789        ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_dpp")==0)
3790    {
3791        vopEnc = GCNVOPEnc::DPP;
3792        mnemonic = inMnemonic.substr(0, inMnemLen-4);
3793    }
3794    else if (inMnemLen>7 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
3795        ::strcasecmp(inMnemonic.c_str()+inMnemLen-5, "_sdwa")==0)
3796    {
3797        vopEnc = GCNVOPEnc::SDWA;
3798        mnemonic = inMnemonic.substr(0, inMnemLen-5);
3799    }
3800    else
3801        mnemonic = inMnemonic;
3802   
3803    // find instruction by mnemonic
3804    auto it = binaryFind(gcnInstrSortedTable.begin(), gcnInstrSortedTable.end(),
3805               GCNAsmInstruction{mnemonic.c_str()},
3806               [](const GCNAsmInstruction& instr1, const GCNAsmInstruction& instr2)
3807               { return ::strcmp(instr1.mnemonic, instr2.mnemonic)<0; });
3808   
3809    // find matched entry
3810    if (it != gcnInstrSortedTable.end() && (it->archMask & curArchMask)==0)
3811        // if not match current arch mask
3812        for (++it ;it != gcnInstrSortedTable.end() &&
3813               ::strcmp(it->mnemonic, mnemonic.c_str())==0 &&
3814               (it->archMask & curArchMask)==0; ++it);
3815
3816    if (it == gcnInstrSortedTable.end() || ::strcmp(it->mnemonic, mnemonic.c_str())!=0)
3817    {
3818        // unrecognized mnemonic
3819        printError(mnemPlace, "Unknown instruction");
3820        return;
3821    }
3822   
3823    resetInstrRVUs();
3824    setCurrentRVU(0);
3825    /* decode instruction line */
3826    bool good = false;
3827    switch(it->encoding)
3828    {
3829        case GCNENC_SOPC:
3830            good = GCNAsmUtils::parseSOPCEncoding(assembler, *it, mnemPlace, linePtr,
3831                               curArchMask, output, regs, gcnEncSize);
3832            break;
3833        case GCNENC_SOPP:
3834            good = GCNAsmUtils::parseSOPPEncoding(assembler, *it, mnemPlace, linePtr,
3835                               curArchMask, output, regs, gcnEncSize);
3836            break;
3837        case GCNENC_SOP1:
3838            good = GCNAsmUtils::parseSOP1Encoding(assembler, *it, mnemPlace, linePtr,
3839                               curArchMask, output, regs, gcnEncSize);
3840            break;
3841        case GCNENC_SOP2:
3842            good = GCNAsmUtils::parseSOP2Encoding(assembler, *it, mnemPlace, linePtr,
3843                               curArchMask, output, regs, gcnEncSize);
3844            break;
3845        case GCNENC_SOPK:
3846            good = GCNAsmUtils::parseSOPKEncoding(assembler, *it, mnemPlace, linePtr,
3847                               curArchMask, output, regs, gcnEncSize);
3848            break;
3849        case GCNENC_SMRD:
3850            if (curArchMask & ARCH_GCN_1_2_4)
3851                good = GCNAsmUtils::parseSMEMEncoding(assembler, *it, mnemPlace, linePtr,
3852                               curArchMask, output, regs, gcnEncSize);
3853            else
3854                good = GCNAsmUtils::parseSMRDEncoding(assembler, *it, mnemPlace, linePtr,
3855                               curArchMask, output, regs, gcnEncSize);
3856            break;
3857        case GCNENC_VOPC:
3858            good = GCNAsmUtils::parseVOPCEncoding(assembler, *it, mnemPlace, linePtr,
3859                           curArchMask, output, regs, gcnEncSize, vopEnc);
3860            break;
3861        case GCNENC_VOP1:
3862            good = GCNAsmUtils::parseVOP1Encoding(assembler, *it, mnemPlace, linePtr,
3863                                   curArchMask, output, regs, gcnEncSize, vopEnc);
3864            break;
3865        case GCNENC_VOP2:
3866            good = GCNAsmUtils::parseVOP2Encoding(assembler, *it, mnemPlace, linePtr,
3867                                   curArchMask, output, regs, gcnEncSize, vopEnc);
3868            break;
3869        case GCNENC_VOP3A:
3870        case GCNENC_VOP3B:
3871            good = GCNAsmUtils::parseVOP3Encoding(assembler, *it, mnemPlace, linePtr,
3872                                   curArchMask, output, regs, gcnEncSize, vopEnc);
3873            break;
3874        case GCNENC_VINTRP:
3875            good = GCNAsmUtils::parseVINTRPEncoding(assembler, *it, mnemPlace, linePtr,
3876                           curArchMask, output, regs, gcnEncSize, vopEnc);
3877            break;
3878        case GCNENC_DS:
3879            good = GCNAsmUtils::parseDSEncoding(assembler, *it, mnemPlace, linePtr,
3880                           curArchMask, output, regs, gcnEncSize);
3881            break;
3882        case GCNENC_MUBUF:
3883        case GCNENC_MTBUF:
3884            good = GCNAsmUtils::parseMUBUFEncoding(assembler, *it, mnemPlace, linePtr,
3885                           curArchMask, output, regs, gcnEncSize);
3886            break;
3887        case GCNENC_MIMG:
3888            good = GCNAsmUtils::parseMIMGEncoding(assembler, *it, mnemPlace, linePtr,
3889                           curArchMask, output, regs, gcnEncSize);
3890            break;
3891        case GCNENC_EXP:
3892            good = GCNAsmUtils::parseEXPEncoding(assembler, *it, mnemPlace, linePtr,
3893                           curArchMask, output, regs, gcnEncSize);
3894            break;
3895        case GCNENC_FLAT:
3896            good = GCNAsmUtils::parseFLATEncoding(assembler, *it, mnemPlace, linePtr,
3897                           curArchMask, output, regs, gcnEncSize);
3898            break;
3899        default:
3900            break;
3901    }
3902    // register RegVarUsage in tests, do not apply normal usage
3903    if (good && (assembler.getFlags() & ASM_TESTRUN) != 0)
3904        flushInstrRVUs(usageHandler);
3905}
3906
3907#define GCN_FAIL_BY_ERROR(PLACE, STRING) \
3908    { \
3909        printError(PLACE, STRING); \
3910        return false; \
3911    }
3912
3913// method to resolve expressions in code (in instruction in instruction field)
3914bool GCNAssembler::resolveCode(const AsmSourcePos& sourcePos, cxuint targetSectionId,
3915             cxbyte* sectionData, size_t offset, AsmExprTargetType targetType,
3916             cxuint sectionId, uint64_t value)
3917{
3918    switch(targetType)
3919    {
3920        case GCNTGT_LITIMM:
3921            // literal in instruction
3922            if (sectionId != ASMSECT_ABS)
3923                GCN_FAIL_BY_ERROR(sourcePos,
3924                        "Relative value is illegal in literal expressions")
3925            SULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4), value);
3926            printWarningForRange(32, value, sourcePos);
3927            return true;
3928        case GCNTGT_SOPKSIMM16:
3929            if (sectionId != ASMSECT_ABS)
3930                GCN_FAIL_BY_ERROR(sourcePos,
3931                        "Relative value is illegal in immediate expressions")
3932            SULEV(*reinterpret_cast<uint16_t*>(sectionData+offset), value);
3933            printWarningForRange(16, value, sourcePos);
3934            return true;
3935        case GCNTGT_SOPJMP:
3936        {
3937            if (sectionId != targetSectionId)
3938                // if jump outside current section (.text)
3939                GCN_FAIL_BY_ERROR(sourcePos, "Jump over current section!")
3940            int64_t outOffset = (int64_t(value)-int64_t(offset)-4);
3941            if (outOffset & 3)
3942                GCN_FAIL_BY_ERROR(sourcePos, "Jump is not aligned to word!")
3943            outOffset >>= 2;
3944            if (outOffset > INT16_MAX || outOffset < INT16_MIN)
3945                GCN_FAIL_BY_ERROR(sourcePos, "Jump out of range!")
3946            SULEV(*reinterpret_cast<uint16_t*>(sectionData+offset), outOffset);
3947            uint16_t insnCode = ULEV(*reinterpret_cast<uint16_t*>(sectionData+offset+2));
3948            // add codeflow entry
3949            addCodeFlowEntry(sectionId, { size_t(offset), size_t(value),
3950                    insnCode==0xbf82U ? AsmCodeFlowType::JUMP :
3951                    // CALL from S_CALL_B64
3952                    (((insnCode&0xff80)==0xba80 &&
3953                            (curArchMask&ARCH_RXVEGA)!=0) ? AsmCodeFlowType::CALL :
3954                                AsmCodeFlowType::CJUMP) });
3955            return true;
3956        }
3957        case GCNTGT_SMRDOFFSET:
3958            if (sectionId != ASMSECT_ABS)
3959                GCN_FAIL_BY_ERROR(sourcePos,
3960                            "Relative value is illegal in offset expressions")
3961            sectionData[offset] = value;
3962            printWarningForRange(8, value, sourcePos, WS_UNSIGNED);
3963            return true;
3964        case GCNTGT_DSOFFSET16:
3965            if (sectionId != ASMSECT_ABS)
3966                GCN_FAIL_BY_ERROR(sourcePos,
3967                            "Relative value is illegal in offset expressions")
3968            SULEV(*reinterpret_cast<uint16_t*>(sectionData+offset), value);
3969            printWarningForRange(16, value, sourcePos, WS_UNSIGNED);
3970            return true;
3971        case GCNTGT_DSOFFSET8_0:
3972        case GCNTGT_DSOFFSET8_1:
3973        case GCNTGT_SOPCIMM8:
3974            if (sectionId != ASMSECT_ABS)
3975                GCN_FAIL_BY_ERROR(sourcePos, (targetType != GCNTGT_SOPCIMM8) ?
3976                        "Relative value is illegal in offset expressions" :
3977                        "Relative value is illegal in immediate expressions")
3978            if (targetType==GCNTGT_DSOFFSET8_0)
3979                sectionData[offset] = value;
3980            else
3981                sectionData[offset+1] = value;
3982            printWarningForRange(8, value, sourcePos, WS_UNSIGNED);
3983            return true;
3984        case GCNTGT_MXBUFOFFSET:
3985            if (sectionId != ASMSECT_ABS)
3986                GCN_FAIL_BY_ERROR(sourcePos,
3987                            "Relative value is illegal in offset expressions")
3988            sectionData[offset] = value&0xff;
3989            sectionData[offset+1] = (sectionData[offset+1]&0xf0) | ((value>>8)&0xf);
3990            printWarningForRange(12, value, sourcePos, WS_UNSIGNED);
3991            return true;
3992        case GCNTGT_SMEMOFFSET:
3993        case GCNTGT_SMEMOFFSETVEGA:
3994            if (sectionId != ASMSECT_ABS)
3995                GCN_FAIL_BY_ERROR(sourcePos,
3996                            "Relative value is illegal in offset expressions")
3997            if (targetType==GCNTGT_SMEMOFFSETVEGA)
3998            {
3999                uint32_t oldV = ULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4));
4000                SULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4),
4001                            (oldV & 0xffe00000U) | (value&0x1fffffU));
4002            }
4003            else
4004                SULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4), value&0xfffffU);
4005            printWarningForRange(targetType==GCNTGT_SMEMOFFSETVEGA ? 21 : 20,
4006                            value, sourcePos,
4007                            targetType==GCNTGT_SMEMOFFSETVEGA ? WS_BOTH : WS_UNSIGNED);
4008            return true;
4009        case GCNTGT_SMEMIMM:
4010            if (sectionId != ASMSECT_ABS)
4011                GCN_FAIL_BY_ERROR(sourcePos,
4012                        "Relative value is illegal in immediate expressions")
4013            sectionData[offset] = (sectionData[offset]&0x3f) | ((value<<6)&0xff);
4014            sectionData[offset+1] = (sectionData[offset+1]&0xe0) | ((value>>2)&0x1f);
4015            printWarningForRange(7, value, sourcePos, WS_UNSIGNED);
4016            return true;
4017        case GCNTGT_INSTOFFSET:
4018            // FLAT unsigned inst_offset
4019            if (sectionId != ASMSECT_ABS)
4020                GCN_FAIL_BY_ERROR(sourcePos,
4021                        "Relative value is illegal in offset expressions")
4022            sectionData[offset] = value;
4023            sectionData[offset+1] = (sectionData[offset+1]&0xf0) | ((value&0xf00)>>8);
4024            printWarningForRange(12, value, sourcePos, WS_UNSIGNED);
4025            return true;
4026        case GCNTGT_INSTOFFSET_S:
4027            // FLAT signed inst_offset
4028            if (sectionId != ASMSECT_ABS)
4029                GCN_FAIL_BY_ERROR(sourcePos,
4030                        "Relative value is illegal in offset expressions")
4031            sectionData[offset] = value;
4032            sectionData[offset+1] = (sectionData[offset+1]&0xe0) |
4033                    ((value&0x1f00)>>8);
4034            printWarningForRange(13, value, sourcePos, WS_BOTH);
4035            return true;
4036        default:
4037            return false;
4038    }
4039}
4040
4041// check whether name is mnemonic (currently unused anywhere)
4042bool GCNAssembler::checkMnemonic(const CString& inMnemonic) const
4043{
4044    CString mnemonic;
4045    size_t inMnemLen = inMnemonic.size();
4046    // checking for encoding suffixes
4047    if (inMnemLen>4 &&
4048        (::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e64")==0 ||
4049            ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e32")==0))
4050        mnemonic = inMnemonic.substr(0, inMnemLen-4);
4051    else if (inMnemLen>6 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
4052        ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_dpp")==0)
4053        mnemonic = inMnemonic.substr(0, inMnemLen-4);
4054    else if (inMnemLen>7 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
4055        ::strcasecmp(inMnemonic.c_str()+inMnemLen-5, "_sdwa")==0)
4056        mnemonic = inMnemonic.substr(0, inMnemLen-5);
4057    else
4058        mnemonic = inMnemonic;
4059   
4060    return std::binary_search(gcnInstrSortedTable.begin(), gcnInstrSortedTable.end(),
4061               GCNAsmInstruction{mnemonic.c_str()},
4062               [](const GCNAsmInstruction& instr1, const GCNAsmInstruction& instr2)
4063               { return ::strcmp(instr1.mnemonic, instr2.mnemonic)<0; });
4064}
4065
4066void GCNAssembler::setAllocatedRegisters(const cxuint* inRegs, Flags inRegFlags)
4067{
4068    if (inRegs==nullptr)
4069        regs.sgprsNum = regs.vgprsNum = 0;
4070    else // if not null, just copy
4071        std::copy(inRegs, inRegs+2, regTable);
4072    regs.regFlags = inRegFlags;
4073}
4074
4075const cxuint* GCNAssembler::getAllocatedRegisters(size_t& regTypesNum,
4076              Flags& outRegFlags) const
4077{
4078    regTypesNum = 2;
4079    outRegFlags = regs.regFlags;
4080    return regTable;
4081}
4082
4083void GCNAssembler::getMaxRegistersNum(size_t& regTypesNum, cxuint* maxRegs) const
4084{
4085    maxRegs[0] = getGPUMaxRegsNumByArchMask(curArchMask, 0);
4086    maxRegs[1] = getGPUMaxRegsNumByArchMask(curArchMask, 1);
4087    regTypesNum = 2;
4088}
4089
4090void GCNAssembler::getRegisterRanges(size_t& regTypesNum, cxuint* regRanges) const
4091{
4092    regRanges[0] = 0;
4093    regRanges[1] = getGPUMaxRegsNumByArchMask(curArchMask, 0);
4094    regRanges[2] = 256; // vgpr
4095    regRanges[3] = 256+getGPUMaxRegsNumByArchMask(curArchMask, 1);
4096    regTypesNum = 2;
4097}
4098
4099// method that filling code to alignment (used by alignment pseudo-ops on code section)
4100void GCNAssembler::fillAlignment(size_t size, cxbyte* output)
4101{
4102    uint32_t value = LEV(0xbf800000U); // fill with s_nop's
4103    if ((size&3)!=0)
4104    {
4105        // first, we fill zeros
4106        const size_t toAlign4 = 4-(size&3);
4107        ::memset(output, 0, toAlign4);
4108        output += toAlign4;
4109    }
4110    std::fill((uint32_t*)output, ((uint32_t*)output) + (size>>2), value);
4111}
4112
4113bool GCNAssembler::parseRegisterRange(const char*& linePtr, cxuint& regStart,
4114          cxuint& regEnd, const AsmRegVar*& regVar)
4115{
4116    GCNOperand operand;
4117    regVar = nullptr;
4118    if (!GCNAsmUtils::parseOperand(assembler, linePtr, operand, nullptr, curArchMask, 0,
4119                INSTROP_SREGS|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_UNALIGNED,
4120                ASMFIELD_NONE))
4121        return false;
4122    regStart = operand.range.start;
4123    regEnd = operand.range.end;
4124    regVar = operand.range.regVar;
4125    return true;
4126}
4127
4128bool GCNAssembler::relocationIsFit(cxuint bits, AsmExprTargetType tgtType)
4129{
4130    if (bits==32)
4131        return tgtType==GCNTGT_SOPJMP || tgtType==GCNTGT_LITIMM;
4132    return false;
4133}
4134
4135bool GCNAssembler::parseRegisterType(const char*& linePtr, const char* end, cxuint& type)
4136{
4137    skipSpacesToEnd(linePtr, end);
4138    if (linePtr!=end)
4139    {
4140        const char c = toLower(*linePtr);
4141        if (c=='v' || c=='s')
4142        {
4143            type = c=='v' ? REGTYPE_VGPR : REGTYPE_SGPR;
4144            linePtr++;
4145            return true;
4146        }
4147        return false;
4148    }
4149    return false;
4150}
4151
4152static const bool gcnSize11Table[16] =
4153{
4154    false, // GCNENC_SMRD, // 0000
4155    false, // GCNENC_SMRD, // 0001
4156    false, // GCNENC_VINTRP, // 0010
4157    false, // GCNENC_NONE, // 0011 - illegal
4158    true,  // GCNENC_VOP3A, // 0100
4159    false, // GCNENC_NONE, // 0101 - illegal
4160    true,  // GCNENC_DS,   // 0110
4161    true,  // GCNENC_FLAT, // 0111
4162    true,  // GCNENC_MUBUF, // 1000
4163    false, // GCNENC_NONE,  // 1001 - illegal
4164    true,  // GCNENC_MTBUF, // 1010
4165    false, // GCNENC_NONE,  // 1011 - illegal
4166    true,  // GCNENC_MIMG,  // 1100
4167    false, // GCNENC_NONE,  // 1101 - illegal
4168    true,  // GCNENC_EXP,   // 1110
4169    false // GCNENC_NONE   // 1111 - illegal
4170};
4171
4172static const bool gcnSize12Table[16] =
4173{
4174    true,  // GCNENC_SMEM, // 0000
4175    true,  // GCNENC_EXP, // 0001
4176    false, // GCNENC_NONE, // 0010 - illegal
4177    false, // GCNENC_NONE, // 0011 - illegal
4178    true,  // GCNENC_VOP3A, // 0100
4179    false, // GCNENC_VINTRP, // 0101
4180    true,  // GCNENC_DS,   // 0110
4181    true,  // GCNENC_FLAT, // 0111
4182    true,  // GCNENC_MUBUF, // 1000
4183    false, // GCNENC_NONE,  // 1001 - illegal
4184    true,  // GCNENC_MTBUF, // 1010
4185    false, // GCNENC_NONE,  // 1011 - illegal
4186    true,  // GCNENC_MIMG,  // 1100
4187    false, // GCNENC_NONE,  // 1101 - illegal
4188    false, // GCNENC_NONE,  // 1110 - illegal
4189    false // GCNENC_NONE   // 1111 - illegal
4190};
4191
4192// get instruction size, used by register allocation to skip instruction
4193size_t GCNAssembler::getInstructionSize(size_t codeSize, const cxbyte* code) const
4194{
4195    if (codeSize < 4)
4196        return 0; // no instruction
4197    bool isGCN11 = (curArchMask & ARCH_RX2X0)!=0;
4198    bool isGCN12 = (curArchMask & ARCH_GCN_1_2_4)!=0;
4199    const uint32_t insnCode = ULEV(*reinterpret_cast<const uint32_t*>(code));
4200    uint32_t words = 1;
4201    if ((insnCode & 0x80000000U) != 0)
4202    {
4203        if ((insnCode & 0x40000000U) == 0)
4204        {
4205            // SOP???
4206            if  ((insnCode & 0x30000000U) == 0x30000000U)
4207            {
4208                // SOP1/SOPK/SOPC/SOPP
4209                const uint32_t encPart = (insnCode & 0x0f800000U);
4210                if (encPart == 0x0e800000U)
4211                {
4212                    // SOP1
4213                    if ((insnCode&0xff) == 0xff) // literal
4214                        words++;
4215                }
4216                else if (encPart == 0x0f000000U)
4217                {
4218                    // SOPC
4219                    if ((insnCode&0xff) == 0xff ||
4220                        (insnCode&0xff00) == 0xff00) // literal
4221                        words++;
4222                }
4223                else if (encPart != 0x0f800000U)
4224                {
4225                    // SOPK
4226                    const cxuint opcode = (insnCode>>23)&0x1f;
4227                    if ((!isGCN12 && opcode == 21) ||
4228                        (isGCN12 && opcode == 20))
4229                        words++; // additional literal
4230                }
4231            }
4232            else
4233            {
4234                // SOP2
4235                if ((insnCode&0xff) == 0xff || (insnCode&0xff00) == 0xff00)
4236                    words++;  // literal
4237            }
4238        }
4239        else
4240        {
4241            // SMRD and others
4242            const uint32_t encPart = (insnCode&0x3c000000U)>>26;
4243            if ((!isGCN12 && gcnSize11Table[encPart] && (encPart != 7 || isGCN11)) ||
4244                (isGCN12 && gcnSize12Table[encPart]))
4245                words++;
4246        }
4247    }
4248    else
4249    {
4250        // some vector instructions
4251        if ((insnCode & 0x7e000000U) == 0x7c000000U)
4252        {
4253            // VOPC
4254            if ((insnCode&0x1ff) == 0xff || // literal
4255                // SDWA, DDP
4256                (isGCN12 && ((insnCode&0x1ff) == 0xf9 || (insnCode&0x1ff) == 0xfa)))
4257                words++;
4258        }
4259        else if ((insnCode & 0x7e000000U) == 0x7e000000U)
4260        {
4261            // VOP1
4262            if ((insnCode&0x1ff) == 0xff || // literal
4263                // SDWA, DDP
4264                (isGCN12 && ((insnCode&0x1ff) == 0xf9 || (insnCode&0x1ff) == 0xfa)))
4265                words++;
4266        }
4267        else
4268        {
4269            // VOP2
4270            const cxuint opcode = (insnCode >> 25)&0x3f;
4271            if ((!isGCN12 && (opcode == 32 || opcode == 33)) ||
4272                (isGCN12 && (opcode == 23 || opcode == 24 ||
4273                opcode == 36 || opcode == 37))) // V_MADMK and V_MADAK
4274                words++;  // inline 32-bit constant
4275            else if ((insnCode&0x1ff) == 0xff || // literal
4276                // SDWA, DDP
4277                (isGCN12 && ((insnCode&0x1ff) == 0xf9 || (insnCode&0x1ff) == 0xfa)))
4278                words++;  // literal
4279        }
4280    }
4281    return words<<2;
4282}
Note: See TracBrowser for help on using the repository browser.