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

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

CLRadeonExtender: GCNAsm: Simplify VOP2/VOP1/VOPC (as VOP3) encoding part (use encodeVOP3Words).

File size: 169.1 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2018 Mateusz Szpakowski
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <CLRX/Config.h>
21#include <cstdio>
22#include <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
1507static void encodeVOP3Words(bool isGCN12, const GCNAsmInstruction& gcnInsn,
1508        cxbyte modifiers, VOPOpModifiers opMods, bool haveDstCC,
1509        const RegRange& dstReg, const RegRange& dstCCReg, const RegRange& srcCCReg,
1510        const GCNOperand& src0Op, const GCNOperand& src1Op,
1511        cxuint& wordsNum, uint32_t* words)
1512{
1513    // VOP3 encoding
1514    uint32_t code = (isGCN12) ?
1515            (uint32_t(gcnInsn.code2)<<16) | ((modifiers&VOP3_CLAMP) ? 0x8000 : 0) :
1516            (uint32_t(gcnInsn.code2)<<17) | ((modifiers&VOP3_CLAMP) ? 0x800 : 0);
1517    if (haveDstCC) // if VOP3B
1518        SLEV(words[0], 0xd0000000U | code |
1519            (dstReg.bstart()&0xff) | (uint32_t(dstCCReg.bstart())<<8));
1520    else // if VOP3A
1521        SLEV(words[0], 0xd0000000U | code | (dstReg.bstart()&0xff) |
1522            ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
1523            ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
1524            ((opMods.opselMod&15) << 11));
1525    // second dword
1526    SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
1527        (uint32_t(srcCCReg.bstart())<<18) | (uint32_t(modifiers & 3) << 27) |
1528        ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
1529        ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0));
1530    wordsNum++;
1531}
1532
1533bool GCNAsmUtils::parseVOP2Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1534                  const char* instrPlace, const char* linePtr, uint16_t arch,
1535                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1536                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1537{
1538    const char* end = asmr.line+asmr.lineSize;
1539    bool good = true;
1540    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
1541    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
1542    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1543    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
1544    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1545   
1546    RegRange dstReg(0, 0);
1547    RegRange dstCCReg(0, 0);
1548    RegRange srcCCReg(0, 0);
1549    gcnAsm->setCurrentRVU(0);
1550    if (mode1 == GCN_DS1_SGPR)
1551        // if SGPRS as destination
1552        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1553                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_SDST, true,
1554                       INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1555    else
1556    {
1557         // if VGPRS as destination
1558        bool v_mac = ::strncmp(gcnInsn.mnemonic, "v_mac_", 6)==0;
1559        good &= parseVRegRange(asmr, linePtr, dstReg, (gcnInsn.mode&GCN_REG_DST_64)?2:1,
1560                        GCNFIELD_VOP_VDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE|
1561                              (v_mac?INSTROP_READ:0));
1562    }
1563   
1564    const bool haveDstCC = mode1 == GCN_DS2_VCC || mode1 == GCN_DST_VCC;
1565    const bool haveSrcCC = mode1 == GCN_DS2_VCC || mode1 == GCN_SRC2_VCC;
1566    if (haveDstCC) /* VOP3b */
1567    {
1568        if (!skipRequiredComma(asmr, linePtr))
1569            return false;
1570        // parse SDST (in place VCC) (2 SGPR's)
1571        gcnAsm->setCurrentRVU(1);
1572        good &= parseSRegRange(asmr, linePtr, dstCCReg, arch, 2, GCNFIELD_VOP3_SDST1, true,
1573                               INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1574    }
1575   
1576    GCNOperand src0Op{}, src1Op{};
1577    std::unique_ptr<AsmExpression> src0OpExpr, src1OpExpr;
1578    const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1579            (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1580   
1581    const Flags vopOpModFlags = ((haveDstCC && !isGCN12) ?
1582                    INSTROP_VOP3NEG : INSTROP_VOP3MODS);
1583    if (!skipRequiredComma(asmr, linePtr))
1584        return false;
1585    cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1586    // parse SRC0 (can be VGPR, SGPR, scalar source, LDS, literal or constant)
1587    gcnAsm->setCurrentRVU(2);
1588    good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1589            correctOpType(regsNum, literalConstsFlags) | vopOpModFlags |
1590            INSTROP_SGPR_UNALIGNED|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1591            INSTROP_READ, GCNFIELD_VOP_SRC0);
1592   
1593    uint32_t immValue = 0;
1594    std::unique_ptr<AsmExpression> immExpr;
1595    if (mode1 == GCN_ARG1_IMM)
1596    {
1597        // for V_MADMK_FXxx instruction
1598        if (!skipRequiredComma(asmr, linePtr))
1599            return false;
1600        good &= parseLiteralImm(asmr, linePtr, immValue, &immExpr, literalConstsFlags);
1601    }
1602   
1603    if (!skipRequiredComma(asmr, linePtr))
1604        return false;
1605   
1606    bool sgprRegInSrc1 = mode1 == GCN_DS1_SGPR || mode1 == GCN_SRC1_SGPR;
1607    skipSpacesToEnd(linePtr, end);
1608    regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
1609    gcnAsm->setCurrentRVU(3);
1610    // parse SRC1 (can be VGPR, SGPR, scalar source, LDS, literal or constant)
1611    //  except when SGPR for SRC1 when instructions accepts SGPR in this place
1612    good &= parseOperand(asmr, linePtr, src1Op, &src1OpExpr, arch, regsNum,
1613            correctOpType(regsNum, literalConstsFlags) | vopOpModFlags |
1614            (!sgprRegInSrc1 ? INSTROP_VREGS : 0)|INSTROP_SSOURCE|INSTROP_SREGS|
1615            INSTROP_SGPR_UNALIGNED |
1616            (src0Op.range.start==255 ? INSTROP_ONLYINLINECONSTS : 0)|
1617            INSTROP_READ, (!sgprRegInSrc1) ? GCNFIELD_VOP_VSRC1 : GCNFIELD_VOP_SSRC1);
1618   
1619    if (mode1 == GCN_ARG2_IMM)
1620    {
1621        // for V_MADAK_Fxx instruction
1622        if (!skipRequiredComma(asmr, linePtr))
1623            return false;
1624        good &= parseLiteralImm(asmr, linePtr, immValue, &immExpr, literalConstsFlags);
1625    }
1626    else if (haveSrcCC)
1627    {
1628        if (!skipRequiredComma(asmr, linePtr))
1629            return false;
1630        gcnAsm->setCurrentRVU(4);
1631        // parse SSRC (VCC) (2 SGPR's)
1632        good &= parseSRegRange(asmr, linePtr, srcCCReg, arch, 2, GCNFIELD_VOP3_SSRC, true,
1633                       INSTROP_SYMREGRANGE|INSTROP_UNALIGNED|INSTROP_READ);
1634    }
1635   
1636    // modifiers
1637    cxbyte modifiers = 0;
1638    VOPExtraModifiers extraMods{};
1639    VOPOpModifiers opMods{};
1640    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, 3,
1641                    (isGCN12) ? &extraMods : nullptr,
1642                    ((!haveDstCC || isGCN12) ? PARSEVOP_WITHCLAMP : 0)|PARSEVOP_WITHSEXT|
1643                    ((isGCN14 && !haveDstCC) ? PARSEVOP_WITHOPSEL : 0));
1644    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1645        return false;
1646   
1647    // apply VOP modifiers (abs,neg,sext) to operands from modifiers
1648    if (src0Op)
1649        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1650                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1651                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1652    if (src1Op)
1653        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
1654                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
1655                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
1656   
1657    extraMods.needSDWA |= ((src0Op.vopMods | src1Op.vopMods) & VOPOP_SEXT) != 0;
1658    // determine whether VOP3 encoding is needed
1659    bool vop3 = /* src1=sgprs and not (DS1_SGPR|src1_SGPR) */
1660        //((src1Op.range.start<256) ^ sgprRegInSrc1) ||
1661        ((!isGCN14 || !extraMods.needSDWA) &&
1662                (src1Op.range.isNonVGPR() ^ sgprRegInSrc1)) ||
1663        (!isGCN12 && (src0Op.vopMods!=0 || src1Op.vopMods!=0)) ||
1664        (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1665            /* exclude OMOD if RXVEGA and SDWA used */
1666            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0 ||
1667        /* srcCC!=VCC or dstCC!=VCC */
1668        //(haveDstCC && dstCCReg.start!=106) || (haveSrcCC && srcCCReg.start!=106) ||
1669        (haveDstCC && !dstCCReg.isVal(106)) || (haveSrcCC && !srcCCReg.isVal(106)) ||
1670        ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1671   
1672    if ((src0Op.range.isVal(255) || src1Op.range.isVal(255)) &&
1673        (src0Op.range.isSGPR() || src0Op.range.isVal(124) ||
1674         src1Op.range.isSGPR() || src1Op.range.isVal(124)))
1675        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SGPR or M0 is illegal")
1676   
1677    if (vop3) // modify fields in reg usage
1678    {
1679        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1680        if (rvus[0].regField != ASMFIELD_NONE)
1681            rvus[0].regField = (rvus[0].regField==GCNFIELD_VOP_VDST) ? GCNFIELD_VOP3_VDST :
1682                            GCNFIELD_VOP3_SDST0;
1683        if (rvus[2].regField != ASMFIELD_NONE)
1684            rvus[2].regField = GCNFIELD_VOP3_SRC0;
1685        if (rvus[3].regField != ASMFIELD_NONE)
1686            rvus[3].regField = GCNFIELD_VOP3_SRC1;
1687    }
1688   
1689    // count number SGPR operands readed by instruction
1690    cxuint sgprsReaded = 0;
1691    if (src0Op.range.isSGPR())
1692        sgprsReaded++;
1693    if (src1Op.range.isSGPR() && !regRangeCanEqual(src0Op.range, src1Op.range))
1694        sgprsReaded++;
1695    if (haveSrcCC)
1696        // check for third operand (SSRC)
1697        if (!regRangeCanEqual(src0Op.range, srcCCReg) &&
1698            !regRangeCanEqual(src1Op.range, srcCCReg))
1699            sgprsReaded++;
1700   
1701    if (sgprsReaded >= 2)
1702        /* include VCCs (???) */
1703        ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
1704   
1705    const bool needImm = (src0Op.range.start==255 || src1Op.range.start==255 ||
1706             mode1 == GCN_ARG1_IMM || mode1 == GCN_ARG2_IMM);
1707   
1708    bool sextFlags = ((src0Op.vopMods|src1Op.vopMods) & VOPOP_SEXT);
1709    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || sextFlags ||
1710                gcnVOPEnc!=GCNVOPEnc::NORMAL))
1711    {
1712        /* if VOP_SDWA or VOP_DPP is required */
1713        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1714                    gcnVOPEnc, src0Op, extraMods, instrPlace))
1715            return false;
1716        if (gcnAsm->instrRVUs[2].regField != ASMFIELD_NONE)
1717            gcnAsm->instrRVUs[2].regField = GCNFIELD_DPPSDWA_SRC0;
1718       
1719        if (extraMods.needSDWA && isGCN14)
1720        {
1721            // fix for extra type operand from SDWA
1722            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1723            if (rvus[2].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1724                rvus[2].regField = GCNFIELD_DPPSDWA_SSRC0;
1725            if (rvus[3].regField != ASMFIELD_NONE && src1Op.range.isNonVGPR())
1726                rvus[3].regField = GCNFIELD_VOP_SSRC1;
1727        }
1728    }
1729    else if (isGCN12 && ((src0Op.vopMods|src1Op.vopMods) & ~VOPOP_SEXT)!=0 && !sextFlags)
1730        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1731        vop3 = true;
1732   
1733    if (isGCN12 && vop3 && haveDstCC && ((src0Op.vopMods|src1Op.vopMods) & VOPOP_ABS) != 0)
1734        ASM_FAIL_BY_ERROR(instrPlace, "Abs modifier is illegal for VOP3B encoding")
1735    if (vop3 && needImm)
1736        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1737   
1738    if (!checkGCNVOPEncoding(asmr, instrPlace, gcnVOPEnc, &extraMods))
1739        return false;
1740   
1741    // set target expressions if needed
1742    if (src0OpExpr!=nullptr)
1743        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1744                      output.size()));
1745    if (src1OpExpr!=nullptr)
1746        src1OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1747                      output.size()));
1748    if (immExpr!=nullptr)
1749        immExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1750                      output.size()));
1751   
1752    // put data (instruction words)
1753    cxuint wordsNum = 1;
1754    uint32_t words[2];
1755    if (!vop3)
1756        // VOP2 encoding
1757        encodeVOPWords((uint32_t(gcnInsn.code1)<<25) |
1758                (uint32_t(src1Op.range.bstart()&0xff)<<9) |
1759                (uint32_t(dstReg.bstart()&0xff)<<17),
1760                modifiers, extraMods, src0Op, src1Op, immValue, mode1,
1761                0, wordsNum, words);
1762    else
1763        // VOP3 encoding
1764        encodeVOP3Words(isGCN12, gcnInsn, modifiers, opMods, haveDstCC,
1765                dstReg, dstCCReg, srcCCReg, src0Op, src1Op, wordsNum, words);
1766   
1767    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1768        return false;
1769   
1770    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1771            reinterpret_cast<cxbyte*>(words + wordsNum));
1772    /// prevent freeing expression
1773    src0OpExpr.release();
1774    src1OpExpr.release();
1775    immExpr.release();
1776    // update register pool (VGPR and SGPR counting)
1777    if (!dstReg.isRegVar())
1778    {
1779        if (dstReg.start>=256)
1780            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
1781        else // sgprs
1782        {
1783            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1784            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1785        }
1786    }
1787    // for SRC operands
1788    if (src0Op.range && !src0Op.range.isRegVar())
1789        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1790    if (src1Op.range && !src1Op.range.isRegVar())
1791        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
1792    if (dstCCReg && !dstCCReg.isRegVar())
1793    {
1794        updateSGPRsNum(gcnRegs.sgprsNum, dstCCReg.end-1, arch);
1795        updateRegFlags(gcnRegs.regFlags, dstCCReg.start, arch);
1796    }
1797    if (srcCCReg && !srcCCReg.isRegVar())
1798        updateRegFlags(gcnRegs.regFlags, srcCCReg.start, arch);
1799    return true;
1800}
1801
1802bool GCNAsmUtils::parseVOP1Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1803                  const char* instrPlace, const char* linePtr, uint16_t arch,
1804                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1805                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1806{
1807    bool good = true;
1808    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
1809    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
1810    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1811    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
1812   
1813    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1814    RegRange dstReg(0, 0);
1815    GCNOperand src0Op{};
1816    std::unique_ptr<AsmExpression> src0OpExpr;
1817    cxbyte modifiers = 0;
1818    if (mode1 != GCN_VOP_ARG_NONE)
1819    {
1820        gcnAsm->setCurrentRVU(0);
1821        if (mode1 == GCN_DST_SGPR)
1822            // if SGPRS as destination
1823            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1824                           (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_SDST, true,
1825                           INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1826        else // if VGPRS as destination
1827            good &= parseVRegRange(asmr, linePtr, dstReg,
1828                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_VDST, true,
1829                                  INSTROP_SYMREGRANGE|INSTROP_WRITE);
1830       
1831        const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1832                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1833       
1834        if (!skipRequiredComma(asmr, linePtr))
1835            return false;
1836        cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1837        gcnAsm->setCurrentRVU(1);
1838        // parse SRC0 (can be VGPR, SGPR, source scalar, literal or cosntant, LDS
1839        good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1840                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
1841                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1842                    INSTROP_VOP3MODS|INSTROP_READ, GCNFIELD_VOP_SRC0);
1843    }
1844    // modifiers
1845    VOPExtraModifiers extraMods{};
1846    VOPOpModifiers opMods{};
1847    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods,
1848                  (mode1!=GCN_VOP_ARG_NONE) ? 2 : 0, (isGCN12)?&extraMods:nullptr,
1849                  PARSEVOP_WITHCLAMP|PARSEVOP_WITHSEXT|
1850                  (isGCN14 ? PARSEVOP_WITHOPSEL : 0), (mode1!=GCN_VOP_ARG_NONE) ? 2 : 0);
1851    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1852        return false;
1853   
1854    if (src0Op)
1855        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1856                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1857                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1858   
1859    extraMods.needSDWA |= ((src0Op.vopMods) & VOPOP_SEXT) != 0;
1860    // check whether VOP3 encoding is needed
1861    bool vop3 = ((!isGCN12 && src0Op.vopMods!=0) ||
1862            (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1863            /* exclude OMOD if RXVEGA and SDWA used */
1864            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0) ||
1865            ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1866    if (vop3) // modify fields in reg usage
1867    {
1868        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1869        if (rvus[0].regField != ASMFIELD_NONE)
1870            rvus[0].regField = (rvus[0].regField==GCNFIELD_VOP_VDST) ?
1871                        GCNFIELD_VOP3_VDST : GCNFIELD_VOP3_SDST0;
1872        if (rvus[1].regField != ASMFIELD_NONE)
1873            rvus[1].regField = GCNFIELD_VOP3_SRC0;
1874    }
1875   
1876    bool sextFlags = (src0Op.vopMods & VOPOP_SEXT);
1877    bool needImm = (src0Op && src0Op.range.isVal(255));
1878    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || sextFlags ||
1879                gcnVOPEnc!=GCNVOPEnc::NORMAL))
1880    {
1881        /* if VOP_SDWA or VOP_DPP is required */
1882        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1883                    gcnVOPEnc, src0Op, extraMods, instrPlace))
1884            return false;
1885        if (gcnAsm->instrRVUs[1].regField != ASMFIELD_NONE)
1886            gcnAsm->instrRVUs[1].regField = GCNFIELD_DPPSDWA_SRC0;
1887        if (extraMods.needSDWA && isGCN14)
1888        {
1889            // fix for extra type operand from SDWA
1890            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1891            if (rvus[1].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1892                rvus[1].regField = GCNFIELD_DPPSDWA_SSRC0;
1893        }
1894    }
1895    else if (isGCN12 && (src0Op.vopMods & ~VOPOP_SEXT)!=0 && !sextFlags)
1896        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1897        vop3 = true;
1898   
1899    if (vop3 && src0Op.range.isVal(255))
1900        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1901   
1902    if (!checkGCNVOPEncoding(asmr, instrPlace, gcnVOPEnc, &extraMods))
1903        return false;
1904   
1905    if (src0OpExpr!=nullptr)
1906        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1907                      output.size()));
1908   
1909    // put data (instruction word)
1910    cxuint wordsNum = 1;
1911    uint32_t words[2];
1912    if (!vop3)
1913        // VOP1 encoding
1914        encodeVOPWords(0x7e000000U | (uint32_t(gcnInsn.code1)<<9) |
1915                (uint32_t(dstReg.bstart()&0xff)<<17),
1916                modifiers, extraMods, src0Op, GCNOperand{ { 256, 257 } }, 0, mode1,
1917                0, wordsNum, words);
1918    else
1919        // VOP3 encoding
1920        encodeVOP3Words(isGCN12, gcnInsn, modifiers, opMods, false,
1921                dstReg, RegRange{}, RegRange{}, src0Op, GCNOperand{}, wordsNum, words);
1922   
1923    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1924        return false;
1925   
1926    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1927            reinterpret_cast<cxbyte*>(words + wordsNum));
1928    /// prevent freeing expression
1929    src0OpExpr.release();
1930    // update register pool (VGPR and SGPR counting)
1931    if (dstReg && !dstReg.isRegVar())
1932    {
1933        if (dstReg.start>=256)
1934            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
1935        else // sgprs
1936        {
1937            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1938            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1939        }
1940    }
1941    if (src0Op.range && !src0Op.range.isRegVar())
1942        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1943    return true;
1944}
1945
1946bool GCNAsmUtils::parseVOPCEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1947                  const char* instrPlace, const char* linePtr, uint16_t arch,
1948                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1949                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1950{
1951    bool good = true;
1952    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
1953    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1954    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
1955   
1956    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1957    RegRange dstReg(0, 0);
1958    GCNOperand src0Op{};
1959    std::unique_ptr<AsmExpression> src0OpExpr;
1960    GCNOperand src1Op{};
1961    std::unique_ptr<AsmExpression> src1OpExpr;
1962    cxbyte modifiers = 0;
1963   
1964    // parse SDST (2 SGPR's)
1965    gcnAsm->setCurrentRVU(0);
1966    good &= parseSRegRange(asmr, linePtr, dstReg, arch, 2, GCNFIELD_VOP3_SDST0, true,
1967                           INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1968    if (!skipRequiredComma(asmr, linePtr))
1969        return false;
1970   
1971    const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1972                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1973    cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1974    gcnAsm->setCurrentRVU(1);
1975    // parse SRC0 (can VGPR, SGPR, scalar source,literal or constant)
1976    good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1977                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
1978                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1979                    INSTROP_VOP3MODS|INSTROP_READ, GCNFIELD_VOP_SRC0);
1980   
1981    if (!skipRequiredComma(asmr, linePtr))
1982        return false;
1983    regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
1984    gcnAsm->setCurrentRVU(2);
1985    // parse SRC1 (can VGPR, SGPR, scalar source,literal or constant)
1986    good &= parseOperand(asmr, linePtr, src1Op, &src1OpExpr, arch, regsNum,
1987                correctOpType(regsNum, literalConstsFlags) | INSTROP_VOP3MODS|
1988                INSTROP_SGPR_UNALIGNED|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|
1989                INSTROP_READ| (src0Op.range.isVal(255) ? INSTROP_ONLYINLINECONSTS : 0),
1990                GCNFIELD_VOP_VSRC1);
1991    // modifiers
1992    VOPExtraModifiers extraMods{};
1993    VOPOpModifiers opMods{};
1994    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, 3,
1995                (isGCN12)?&extraMods:nullptr, (isGCN14 ? PARSEVOP_NODSTMODS : 0)|
1996                PARSEVOP_WITHCLAMP|PARSEVOP_WITHSEXT|(isGCN14 ? PARSEVOP_WITHOPSEL : 0));
1997    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1998        return false;
1999   
2000    // set VOP operand modifiers to src operands
2001    if (src0Op)
2002        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
2003                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
2004                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
2005    if (src1Op)
2006        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
2007                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
2008                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
2009   
2010    // determine whether SDWA is needed or VOP3 encoding needed
2011    extraMods.needSDWA |= ((src0Op.vopMods | src1Op.vopMods) & VOPOP_SEXT) != 0;
2012    bool vop3 = //(dstReg.start!=106) || (src1Op.range.start<256) ||
2013        ((!isGCN14 || !extraMods.needSDWA) && !dstReg.isVal(106)) ||
2014        ((!isGCN14 || !extraMods.needSDWA) && src1Op.range.isNonVGPR()) ||
2015        (!isGCN12 && (src0Op.vopMods!=0 || src1Op.vopMods!=0)) ||
2016        (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
2017            /* exclude OMOD if RXVEGA and SDWA used */
2018            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0 ||
2019        ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
2020   
2021    if ((src0Op.range.isVal(255) || src1Op.range.isVal(255)) &&
2022        (src0Op.range.isSGPR() || src0Op.range.isVal(124) ||
2023         src1Op.range.isSGPR() || src1Op.range.isVal(124)))
2024        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SGPR or M0 is illegal")
2025    if (src0Op.range.isSGPR() && src1Op.range.isSGPR() &&
2026        !regRangeCanEqual(src0Op.range, src1Op.range))
2027        /* include VCCs (???) */
2028        ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
2029   
2030    if (vop3)
2031    {
2032        // modify fields in reg usage
2033        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
2034        if (rvus[1].regField != ASMFIELD_NONE)
2035            rvus[1].regField = GCNFIELD_VOP3_SRC0;
2036        if (rvus[2].regField != ASMFIELD_NONE)
2037            rvus[2].regField = GCNFIELD_VOP3_SRC1;
2038    }
2039   
2040    const bool needImm = src0Op.range.start==255 || src1Op.range.start==255;
2041   
2042    bool sextFlags = ((src0Op.vopMods|src1Op.vopMods) & VOPOP_SEXT);
2043    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || sextFlags ||
2044                gcnVOPEnc!=GCNVOPEnc::NORMAL))
2045    {
2046        /* if VOP_SDWA or VOP_DPP is required */
2047        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
2048                    gcnVOPEnc, src0Op, extraMods, instrPlace))
2049            return false;
2050        if (gcnAsm->instrRVUs[1].regField != ASMFIELD_NONE)
2051            gcnAsm->instrRVUs[1].regField = GCNFIELD_DPPSDWA_SRC0;
2052       
2053        if (extraMods.needSDWA && isGCN14)
2054        {
2055            // fix for extra type operand from SDWA
2056            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
2057            if (rvus[0].regField != ASMFIELD_NONE)
2058                rvus[0].regField = GCNFIELD_SDWAB_SDST;
2059            if (rvus[1].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
2060                rvus[1].regField = GCNFIELD_DPPSDWA_SSRC0;
2061            if (rvus[2].regField != ASMFIELD_NONE && src1Op.range.isNonVGPR())
2062                rvus[2].regField = GCNFIELD_VOP_SSRC1;
2063        }
2064    }
2065    else if (isGCN12 && ((src0Op.vopMods|src1Op.vopMods) & ~VOPOP_SEXT)!=0 && !sextFlags)
2066        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
2067        vop3 = true;
2068   
2069    if (vop3 && (src0Op.range.isVal(255) || src1Op.range.isVal(255)))
2070        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
2071   
2072    if (!checkGCNVOPEncoding(asmr, instrPlace, gcnVOPEnc, &extraMods))
2073        return false;
2074   
2075    if (isGCN14 && extraMods.needSDWA && ((modifiers & VOP3_CLAMP)!=0 || (modifiers&3)!=0))
2076        ASM_FAIL_BY_ERROR(instrPlace, "Modifiers CLAMP and OMOD is illegal in SDWAB")
2077   
2078    if (src0OpExpr!=nullptr)
2079        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
2080                      output.size()));
2081    if (src1OpExpr!=nullptr)
2082        src1OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
2083                      output.size()));
2084   
2085    // put data (instruction words)
2086    cxuint wordsNum = 1;
2087    uint32_t words[2];
2088    if (!vop3)
2089    {
2090        // VOPC encoding
2091        const uint32_t dstMods = (isGCN14 ? 0x10000 : 0) |
2092                ((isGCN14 && !dstReg.isVal(106)) ? ((dstReg.bstart()|0x80)<<8) : 0);
2093       
2094        encodeVOPWords(0x7c000000U | (uint32_t(gcnInsn.code1)<<17) |
2095                (uint32_t(src1Op.range.bstart()&0xff)<<9),
2096                modifiers, extraMods, src0Op, src1Op, 0, 0,
2097                dstMods, wordsNum, words);
2098    }
2099    else
2100        // VOP3 encoding
2101        encodeVOP3Words(isGCN12, gcnInsn, modifiers, opMods, false,
2102                dstReg, RegRange{}, RegRange{}, src0Op, src1Op, wordsNum, words);
2103   
2104    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
2105        return false;
2106    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2107            reinterpret_cast<cxbyte*>(words + wordsNum));
2108    /// prevent freeing expression
2109    src0OpExpr.release();
2110    src1OpExpr.release();
2111    // update register pool (VGPR and SGPR counting)
2112    if (dstReg && !dstReg.isRegVar())
2113    {
2114        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
2115        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
2116    }
2117    if (src0Op.range && !src0Op.range.isRegVar())
2118        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
2119    if (src1Op.range && !src1Op.range.isRegVar())
2120        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
2121    return true;
2122}
2123
2124bool GCNAsmUtils::parseVOP3Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2125                  const char* instrPlace, const char* linePtr, uint16_t arch,
2126                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2127                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
2128{
2129    bool good = true;
2130    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
2131    const uint16_t mode2 = (gcnInsn.mode & GCN_MASK2);
2132    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
2133    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
2134    const bool vop3p = (gcnInsn.mode & GCN_VOP3_VOP3P) != 0;
2135    if (gcnVOPEnc!=GCNVOPEnc::NORMAL)
2136        ASM_FAIL_BY_ERROR(instrPlace, "DPP and SDWA encoding is illegal for VOP3")
2137   
2138    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2139    RegRange dstReg(0, 0);
2140    RegRange sdstReg(0, 0);
2141    GCNOperand src0Op{};
2142    GCNOperand src1Op{};
2143    GCNOperand src2Op{};
2144   
2145    const bool is128Ops = (gcnInsn.mode & 0x7000) == GCN_VOP3_DS2_128;
2146    bool modHigh = false;
2147    cxbyte modifiers = 0;
2148    const Flags vop3Mods = ((gcnInsn.encoding == GCNENC_VOP3B) ?
2149            INSTROP_VOP3NEG : INSTROP_VOP3MODS | INSTROP_NOSEXT) |
2150            (vop3p ? INSTROP_VOP3P : 0);
2151   
2152    // by default OPSEL_HI is [1,1,1] in vop3p instructions
2153    VOPOpModifiers opMods{ 0, 0, 0, cxbyte(vop3p ? 7<<4 : 0) };
2154    cxuint operands = 1;
2155    if (mode1 != GCN_VOP_ARG_NONE)
2156    {
2157        gcnAsm->setCurrentRVU(0);
2158        // parse DST (
2159        if ((gcnInsn.mode&GCN_VOP3_DST_SGPR)==0)
2160            good &= parseVRegRange(asmr, linePtr, dstReg,
2161                       (is128Ops) ? 4 : ((gcnInsn.mode&GCN_REG_DST_64)?2:1),
2162                       GCNFIELD_VOP3_VDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE);
2163        else // SGPRS as dest
2164            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
2165                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP3_SDST0, true,
2166                       INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
2167        if (!skipRequiredComma(asmr, linePtr))
2168            return false;
2169       
2170        if (gcnInsn.encoding == GCNENC_VOP3B &&
2171            (mode1 == GCN_DS2_VCC || mode1 == GCN_DST_VCC || mode1 == GCN_DST_VCC_VSRC2 ||
2172             mode1 == GCN_S0EQS12)) /* VOP3b */
2173        {
2174            // SDST (VCC) (2 SGPR's)
2175            gcnAsm->setCurrentRVU(1);
2176            good &= parseSRegRange(asmr, linePtr, sdstReg, arch, 2, GCNFIELD_VOP3_SDST1,
2177                       true, INSTROP_SYMREGRANGE|INSTROP_WRITE|INSTROP_SGPR_UNALIGNED);
2178            if (!skipRequiredComma(asmr, linePtr))
2179                return false;
2180        }
2181        const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
2182                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
2183       
2184        cxuint regsNum;
2185        if (mode2 != GCN_VOP3_VINTRP)
2186        {
2187            // parse SRC0 (can be VGPR, SGPR, scalar source, constant, LDS)
2188            gcnAsm->setCurrentRVU(2);
2189            regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
2190            good &= parseOperand(asmr, linePtr, src0Op, nullptr, arch, regsNum,
2191                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
2192                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
2193                    vop3Mods|INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR|INSTROP_READ,
2194                    GCNFIELD_VOP3_SRC0);
2195            operands++;
2196        }
2197       
2198        if (mode2 == GCN_VOP3_VINTRP)
2199        {
2200            // if VINTRP instruction
2201            gcnAsm->setCurrentRVU(3);
2202            if (mode1 != GCN_P0_P10_P20)
2203            {
2204                good &= parseOperand(asmr, linePtr, src1Op, nullptr, arch, 1,
2205                        INSTROP_VREGS|vop3Mods|INSTROP_READ, GCNFIELD_VOP3_SRC1);
2206            }
2207            else /* P0_P10_P20 */
2208                good &= parseVINTRP0P10P20(asmr, linePtr, src1Op.range);
2209           
2210            if (!skipRequiredComma(asmr, linePtr))
2211                return false;
2212           
2213            cxbyte attr;
2214            good &= parseVINTRPAttr(asmr, linePtr, attr);
2215            attr = ((attr&3)<<6) | ((attr&0xfc)>>2);
2216            src0Op.range = { attr, uint16_t(attr+1) };
2217           
2218            if ((gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2)
2219            {
2220                if (!skipRequiredComma(asmr, linePtr))
2221                    return false;
2222                // parse SRC2 (VGPR, SGPR)
2223                gcnAsm->setCurrentRVU(4);
2224                good &= parseOperand(asmr, linePtr, src2Op, nullptr, arch,
2225                    (gcnInsn.mode&GCN_REG_SRC2_64)?2:1, vop3Mods|INSTROP_SGPR_UNALIGNED|
2226                    INSTROP_VREGS|INSTROP_SREGS|INSTROP_READ, GCNFIELD_VOP3_SRC2);
2227            }
2228            // high and vop3
2229            const char* end = asmr.line+asmr.lineSize;
2230            bool haveOpsel = false;
2231            bool haveNeg = false, haveAbs = false;
2232            // own parse VINTRP modifiers with some VOP3 modifiers
2233            while (true)
2234            {
2235                bool alreadyModDefined = false;
2236                skipSpacesToEnd(linePtr, end);
2237                if (linePtr==end)
2238                    break;
2239                char modName[10];
2240                const char* modPlace = linePtr;
2241                if (!getNameArgS(asmr, 10, modName, linePtr, "VINTRP modifier"))
2242                    continue;
2243                if (::strcmp(modName, "high")==0)
2244                    good &= parseModEnable(asmr, linePtr, modHigh, "high modifier");
2245                else if (::strcmp(modName, "vop3")==0)
2246                {
2247                    bool vop3Mod = false;
2248                    good &= parseModEnable(asmr, linePtr, vop3Mod, "vop3 modifier");
2249                    modifiers = (modifiers & ~VOP3_VOP3) | (vop3Mod ? VOP3_VOP3 : 0);
2250                }
2251                else if (parseSingleOMODCLAMP(asmr, linePtr, modPlace, modName, arch,
2252                        modifiers, opMods, 
2253                        (gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2 ? 4 : 3, PARSEVOP_WITHCLAMP, haveAbs, haveNeg,
2254                        alreadyModDefined, good))
2255                {   // do nothing
2256                }
2257                else if (::strcmp(modName, "op_sel")==0)
2258                {
2259                    // op_sel with boolean array
2260                    uint32_t opselVal = 0;
2261                    if (linePtr!=end && *linePtr==':')
2262                    {
2263                        linePtr++;
2264                        if (parseImmWithBoolArray(asmr, linePtr, opselVal, 4, WS_UNSIGNED))
2265                        {
2266                            opMods.opselMod = opselVal;
2267                            if (haveOpsel)
2268                                asmr.printWarning(modPlace, "Opsel is already defined");
2269                            haveOpsel = true;
2270                            opMods.opselMod = opselVal;
2271                        }
2272                    }
2273                    else
2274                        good = false;
2275                }
2276                else
2277                    ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VINTRP modifier")
2278            }
2279            if (modHigh)
2280            {
2281                src0Op.range.start+=0x100;
2282                src0Op.range.end+=0x100;
2283            }
2284        }
2285        else if (mode1 != GCN_SRC12_NONE)
2286        {
2287            // if encoding have two or three source operands
2288            if (!skipRequiredComma(asmr, linePtr))
2289                return false;
2290            regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
2291            // parse SRC1 (can be VGPR, SGPR, source scalar, constant)
2292            gcnAsm->setCurrentRVU(3);
2293            good &= parseOperand(asmr, linePtr, src1Op, nullptr, arch, regsNum,
2294                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
2295                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|vop3Mods|
2296                    INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR|INSTROP_READ,
2297                    GCNFIELD_VOP3_SRC1);
2298            operands++;
2299           
2300            if (mode1 != GCN_SRC2_NONE && mode1 != GCN_DST_VCC)
2301            {
2302                if (!skipRequiredComma(asmr, linePtr))
2303                    return false;
2304                regsNum = (gcnInsn.mode&GCN_REG_SRC2_64)?2:1;
2305                // parse SRC2 (can be VGPR, SGPR, source scalar, constant)
2306                gcnAsm->setCurrentRVU(4);
2307                good &= parseOperand(asmr, linePtr, src2Op, nullptr, arch,
2308                        is128Ops ? 4 : regsNum,
2309                        correctOpType(regsNum, literalConstsFlags)|INSTROP_SGPR_UNALIGNED|
2310                        INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_READ|
2311                        vop3Mods|INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR,
2312                        GCNFIELD_VOP3_SRC2);
2313                operands++;
2314            }
2315        }
2316    }
2317    // modifiers
2318    if (mode2 != GCN_VOP3_VINTRP)
2319        good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, operands,
2320                    nullptr, ((isGCN12 || gcnInsn.encoding!=GCNENC_VOP3B) ?
2321                            PARSEVOP_WITHCLAMP : 0) |
2322                    ((isGCN14 && gcnInsn.encoding!=GCNENC_VOP3B) ?
2323                            PARSEVOP_WITHOPSEL : 0) | (vop3p ? PARSEVOP_VOP3P : 0), 3);
2324    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2325        return false;
2326   
2327    // apply VOP modifiers (abs,neg,sext) to operands from modifiers
2328    if (src0Op)
2329        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
2330                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
2331                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
2332    if (src1Op)
2333        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
2334                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
2335                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
2336    if (src2Op)
2337        src2Op.vopMods |= ((opMods.absMod&4) ? VOPOP_ABS : 0) |
2338                ((opMods.negMod&4) ? VOPOP_NEG : 0);
2339   
2340    if (mode2 != GCN_VOP3_VINTRP)
2341    {
2342        // count SGPR operands readed by instruction
2343        cxuint numSgprToRead = 0;
2344        //if (src0Op.range.start<108)
2345        if (src0Op.range.isSGPR())
2346            numSgprToRead++;
2347        //if (src1Op && src1Op.range.start<108 &&
2348        if (src1Op && src1Op.range.isSGPR() &&
2349                    !regRangeCanEqual(src0Op.range, src1Op.range))
2350            numSgprToRead++;
2351        //if (src2Op && src2Op.range.start<108 &&
2352        if (src2Op && src2Op.range.isSGPR())
2353        {
2354            if (!regRangeCanEqual(src0Op.range, src2Op.range) &&
2355                !regRangeCanEqual(src1Op.range, src2Op.range))
2356                numSgprToRead++;
2357        }
2358       
2359        if (numSgprToRead>=2)
2360            ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
2361    }
2362   
2363    // put data (instruction words)
2364    uint32_t words[2];
2365    cxuint wordsNum = 2;
2366    if (gcnInsn.encoding == GCNENC_VOP3B)
2367    {
2368        // VOP3B encoding
2369        if (!isGCN12)
2370            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<17) |
2371                (dstReg.bstart()&0xff) | (uint32_t(sdstReg.bstart())<<8));
2372        else
2373            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<16) |
2374                (dstReg.bstart()&0xff) | (uint32_t(sdstReg.bstart())<<8) |
2375                ((modifiers&VOP3_CLAMP) ? 0x8000 : 0));
2376    }
2377    else
2378    {
2379        // VOP3A
2380        if (!isGCN12)
2381            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<17) |
2382                (dstReg.bstart()&0xff) | ((modifiers&VOP3_CLAMP) ? 0x800: 0) |
2383                ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2384                ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2385                ((src2Op.vopMods & VOPOP_ABS) ? 0x400 : 0));
2386        else if (mode2 != GCN_VOP3_VINTRP || mode1 == GCN_NEW_OPCODE ||
2387            (gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2 ||
2388            (modifiers & VOP3_VOP3)!=0 || (src0Op.range.bstart()&0x100)!=0/* high */ ||
2389            (modifiers & (VOP3_CLAMP|3)) != 0 || opMods.opselMod != 0 ||
2390            src1Op.vopMods!=0 || src2Op.vopMods!=0)
2391            // new VOP3 for GCN 1.2
2392            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<16) |
2393                (dstReg.bstart()&0xff) | ((modifiers&VOP3_CLAMP) ? 0x8000: 0) |
2394                (vop3p ? (uint32_t(opMods.negMod>>4) << 8) /* VOP3P NEG_HI */ :
2395                    ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2396                    ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2397                    ((src2Op.vopMods & VOPOP_ABS) ? 0x400 : 0)) |
2398                (((opMods.opselMod & 64) !=0) ? 0x4000 : 0) |
2399                ((opMods.opselMod&15) << 11));
2400        else // VINTRP
2401        {
2402            SLEV(words[0], 0xd4000000U | (src1Op.range.bstart()&0xff) |
2403                (uint32_t(src0Op.range.bstart()>>6)<<8) |
2404                (uint32_t(src0Op.range.bstart()&63)<<10) |
2405                (uint32_t(gcnInsn.code2)<<16) | (uint32_t(dstReg.bstart()&0xff)<<18));
2406            // VOP3 VINTRP have only one word instruction
2407            wordsNum--;
2408        }
2409    }
2410    if (wordsNum==2)
2411        // second instruction's word
2412        SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
2413                (uint32_t(src2Op.range.bstart())<<18) |
2414                (vop3p ? ((uint32_t(opMods.opselMod>>4)&3)<<27) :
2415                 (uint32_t(modifiers & 3) << 27)) |
2416                /* in VOP3P is also NEG_LO */
2417                ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
2418                ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0) |
2419                ((src2Op.vopMods & VOPOP_NEG) ? (1U<<31) : 0));
2420   
2421    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
2422        return false;
2423    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2424            reinterpret_cast<cxbyte*>(words + wordsNum));
2425   
2426    // update register pool (VGPR and SGPR counting)
2427    if (dstReg && !dstReg.isRegVar())
2428    {
2429        if (dstReg.start>=256)
2430            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2431        else // sgprs
2432        {
2433            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
2434            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
2435        }
2436    }
2437    if (sdstReg && !sdstReg.isRegVar())
2438    {
2439        updateSGPRsNum(gcnRegs.sgprsNum, sdstReg.end-1, arch);
2440        updateRegFlags(gcnRegs.regFlags, sdstReg.start, arch);
2441    }
2442    if (mode2 != GCN_VOP3_VINTRP)
2443    {
2444        // count for SSRC0 and SSRC1 for VOP3A/B encoding (not VINTRP) ???
2445        if (src0Op.range && !src0Op.range.isRegVar() && src0Op.range.start < 256)
2446            updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
2447        if (src1Op.range && !src1Op.range.isRegVar() && src1Op.range.start < 256)
2448            updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
2449    }
2450    if (src2Op.range && !src2Op.range.isRegVar() && src2Op.range.start < 256)
2451        updateRegFlags(gcnRegs.regFlags, src2Op.range.start, arch);
2452    return true;
2453}
2454
2455bool GCNAsmUtils::parseVINTRPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2456                  const char* instrPlace, const char* linePtr, uint16_t arch,
2457                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2458                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
2459{
2460    bool good = true;
2461    RegRange dstReg(0, 0);
2462    RegRange srcReg(0, 0);
2463    if (gcnEncSize==GCNEncSize::BIT64)
2464        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for VINTRP encoding")
2465    if (gcnVOPEnc!=GCNVOPEnc::NORMAL)
2466        ASM_FAIL_BY_ERROR(instrPlace, "DPP and SDWA encoding is illegal for VOP3")
2467   
2468    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2469   
2470    // parse DST (VGPR)
2471    gcnAsm->setCurrentRVU(0);
2472    good &= parseVRegRange(asmr, linePtr, dstReg, 1, GCNFIELD_VINTRP_VDST, true,
2473                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
2474    if (!skipRequiredComma(asmr, linePtr))
2475        return false;
2476   
2477    if ((gcnInsn.mode & GCN_MASK1) == GCN_P0_P10_P20)
2478        good &= parseVINTRP0P10P20(asmr, linePtr, srcReg);
2479    else
2480    {
2481        // regular vector register
2482        gcnAsm->setCurrentRVU(1);
2483        good &= parseVRegRange(asmr, linePtr, srcReg, 1, GCNFIELD_VINTRP_VSRC0, true,
2484                        INSTROP_SYMREGRANGE|INSTROP_READ);
2485    }
2486   
2487    if (!skipRequiredComma(asmr, linePtr))
2488        return false;
2489   
2490    cxbyte attrVal;
2491    good &= parseVINTRPAttr(asmr, linePtr, attrVal);
2492   
2493    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2494        return false;
2495    /* put data (instruction word */
2496    uint32_t word;
2497    SLEV(word, 0xc8000000U | (srcReg.bstart()&0xff) | (uint32_t(attrVal&0xff)<<8) |
2498            (uint32_t(gcnInsn.code1)<<16) | (uint32_t(dstReg.bstart()&0xff)<<18));
2499    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word),
2500            reinterpret_cast<cxbyte*>(&word)+4);
2501    // update register pool (VGPR counting)
2502    if (!dstReg.isRegVar())
2503        updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2504    return true;
2505}
2506
2507bool GCNAsmUtils::parseDSEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2508                  const char* instrPlace, const char* linePtr, uint16_t arch,
2509                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2510                  GCNEncSize gcnEncSize)
2511{
2512    const char* end = asmr.line+asmr.lineSize;
2513    bool good = true;
2514    if (gcnEncSize==GCNEncSize::BIT32)
2515        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for DS encoding")
2516    RegRange dstReg(0, 0);
2517    RegRange addrReg(0, 0);
2518    RegRange data0Reg(0, 0), data1Reg(0, 0);
2519   
2520    bool beforeData = false;
2521    bool vdstUsed = false;
2522   
2523    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2524   
2525    if (((gcnInsn.mode & GCN_ADDR_SRC) != 0 || (gcnInsn.mode & GCN_ONLYDST) != 0) &&
2526            (gcnInsn.mode & GCN_ONLY_SRC) == 0)
2527    {
2528        /* vdst is dst */
2529        cxuint regsNum = (gcnInsn.mode&GCN_REG_DST_64)?2:1;
2530        if ((gcnInsn.mode&GCN_DS_96) != 0)
2531            regsNum = 3;
2532        if ((gcnInsn.mode&GCN_DS_128) != 0 || (gcnInsn.mode&GCN_DST128) != 0)
2533            regsNum = 4;
2534        gcnAsm->setCurrentRVU(0);
2535        good &= parseVRegRange(asmr, linePtr, dstReg, regsNum, GCNFIELD_DS_VDST, true,
2536                    INSTROP_SYMREGRANGE|INSTROP_WRITE);
2537        vdstUsed = beforeData = true;
2538    }
2539   
2540    if ((gcnInsn.mode & GCN_ONLYDST) == 0 && (gcnInsn.mode & GCN_ONLY_SRC) == 0)
2541    {
2542        // parse ADDR as first (VGPR)
2543        if (vdstUsed)
2544            if (!skipRequiredComma(asmr, linePtr))
2545                return false;
2546        gcnAsm->setCurrentRVU(1);
2547        good &= parseVRegRange(asmr, linePtr, addrReg, 1, GCNFIELD_DS_ADDR, true,
2548                    INSTROP_SYMREGRANGE|INSTROP_READ);
2549        beforeData = true;
2550    }
2551   
2552    const uint16_t srcMode = (gcnInsn.mode & GCN_SRCS_MASK);
2553   
2554    if ((gcnInsn.mode & GCN_ONLYDST) == 0 &&
2555        (gcnInsn.mode & (GCN_ADDR_DST|GCN_ADDR_SRC)) != 0 && srcMode != GCN_NOSRC)
2556    {
2557        /* two vdata */
2558        if (beforeData)
2559            if (!skipRequiredComma(asmr, linePtr))
2560                return false;
2561       
2562        cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
2563        if ((gcnInsn.mode&GCN_DS_96) != 0)
2564            regsNum = 3;
2565        if ((gcnInsn.mode&GCN_DS_128) != 0)
2566            regsNum = 4;
2567        // parse VDATA0 (VGPR)
2568        gcnAsm->setCurrentRVU(2);
2569        good &= parseVRegRange(asmr, linePtr, data0Reg, regsNum, GCNFIELD_DS_DATA0, true,
2570                    INSTROP_SYMREGRANGE|INSTROP_READ);
2571        if (srcMode == GCN_2SRCS)
2572        {
2573            // insturction have second source
2574            if (!skipRequiredComma(asmr, linePtr))
2575                return false;
2576            // parse VDATA0 (VGPR)
2577            gcnAsm->setCurrentRVU(3);
2578            good &= parseVRegRange(asmr, linePtr, data1Reg,
2579                       (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, GCNFIELD_DS_DATA1, true,
2580                               INSTROP_SYMREGRANGE|INSTROP_READ);
2581        }
2582    }
2583   
2584    bool haveGds = false;
2585    std::unique_ptr<AsmExpression> offsetExpr, offset2Expr;
2586    char name[10];
2587    uint16_t offset = 0;
2588    cxbyte offset1 = 0, offset2 = 0;
2589    bool haveOffset = false, haveOffset2 = false;
2590    // parse DS modifiers
2591    while (linePtr!=end)
2592    {
2593        skipSpacesToEnd(linePtr, end);
2594        if (linePtr==end)
2595            break;
2596        const char* modPlace = linePtr;
2597        if (!getNameArgS(asmr, 10, name, linePtr, "DS modifier"))
2598        {
2599            good = false;
2600            continue;
2601        }
2602        toLowerString(name);
2603        if (::strcmp(name, "gds")==0)
2604            good &= parseModEnable(asmr, linePtr, haveGds, "gds modifier");
2605        else if ((gcnInsn.mode & GCN_2OFFSETS) == 0) /* single offset */
2606        {
2607            // single offset
2608            if (::strcmp(name, "offset") == 0)
2609            {
2610                if (parseModImm(asmr, linePtr, offset, &offsetExpr, "offset",
2611                            0, WS_UNSIGNED))
2612                {
2613                    // detect multiple occurrences
2614                    if (haveOffset)
2615                        asmr.printWarning(modPlace, "Offset is already defined");
2616                    haveOffset = true;
2617                }
2618                else
2619                    good = false;
2620            }
2621            else
2622                ASM_NOTGOOD_BY_ERROR(modPlace, "Expected 'offset'")
2623        }
2624        else
2625        {
2626            // two offsets (offset0, offset1)
2627            if (::memcmp(name, "offset", 6)==0 &&
2628                (name[6]=='0' || name[6]=='1') && name[7]==0)
2629            {
2630                skipSpacesToEnd(linePtr, end);
2631                if (linePtr!=end && *linePtr==':')
2632                {
2633                    skipCharAndSpacesToEnd(linePtr, end);
2634                    if (name[6]=='0')
2635                    {
2636                        /* offset0 */
2637                        if (parseImm(asmr, linePtr, offset1, &offsetExpr, 0, WS_UNSIGNED))
2638                        {
2639                            // detect multiple occurrences
2640                            if (haveOffset)
2641                                asmr.printWarning(modPlace, "Offset0 is already defined");
2642                            haveOffset = true;
2643                        }
2644                        else
2645                            good = false;
2646                    }
2647                    else
2648                    {
2649                        /* offset1 */
2650                        if (parseImm(asmr, linePtr, offset2, &offset2Expr, 0, WS_UNSIGNED))
2651                        {
2652                            // detect multiple occurrences
2653                            if (haveOffset2)
2654                                asmr.printWarning(modPlace, "Offset1 is already defined");
2655                            haveOffset2 = true;
2656                        }
2657                        else
2658                            good = false;
2659                    }
2660                }
2661                else
2662                    ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before offset")
2663            }
2664            else
2665                ASM_NOTGOOD_BY_ERROR(modPlace,
2666                                "Expected 'offset', 'offset0' or 'offset1'")
2667        }
2668    }
2669   
2670    if ((gcnInsn.mode & GCN_2OFFSETS) != 0)
2671        offset = offset1 | (offset2<<8);
2672   
2673    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2674        return false;
2675   
2676    if ((gcnInsn.mode&GCN_ONLYGDS) != 0 && !haveGds)
2677        ASM_FAIL_BY_ERROR(instrPlace, "Instruction requires GDS modifier")
2678   
2679    // set target expressions for offsets (if needed)
2680    if (offsetExpr!=nullptr)
2681        offsetExpr->setTarget(AsmExprTarget((gcnInsn.mode & GCN_2OFFSETS) ?
2682                    GCNTGT_DSOFFSET8_0 : GCNTGT_DSOFFSET16, asmr.currentSection,
2683                    output.size()));
2684    if (offset2Expr!=nullptr)
2685        offset2Expr->setTarget(AsmExprTarget(GCNTGT_DSOFFSET8_1, asmr.currentSection,
2686                    output.size()));
2687    // put data (two instruction words)
2688    uint32_t words[2];
2689    if ((arch & ARCH_GCN_1_2_4)==0)
2690        SLEV(words[0], 0xd8000000U | uint32_t(offset) | (haveGds ? 0x20000U : 0U) |
2691                (uint32_t(gcnInsn.code1)<<18));
2692    else
2693        SLEV(words[0], 0xd8000000U | uint32_t(offset) | (haveGds ? 0x10000U : 0U) |
2694                (uint32_t(gcnInsn.code1)<<17));
2695    SLEV(words[1], (addrReg.bstart()&0xff) | (uint32_t(data0Reg.bstart()&0xff)<<8) |
2696            (uint32_t(data1Reg.bstart()&0xff)<<16) | (uint32_t(dstReg.bstart()&0xff)<<24));
2697    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2698            reinterpret_cast<cxbyte*>(words + 2));
2699   
2700    offsetExpr.release();
2701    offset2Expr.release();
2702    // update register pool (VGPR counting)
2703    if (dstReg && !dstReg.isRegVar())
2704        updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2705    return true;
2706}
2707
2708// data format names (sorted by names) (for MUBUF/MTBUF)
2709static const std::pair<const char*, uint16_t> mtbufDFMTNamesMap[] =
2710{
2711    { "10_10_10_2", 8 },
2712    { "10_11_11", 6 },
2713    { "11_11_10", 7 },
2714    { "16", 2 },
2715    { "16_16", 5 },
2716    { "16_16_16_16", 12 },
2717    { "2_10_10_10", 9 },
2718    { "32", 4 },
2719    { "32_32", 11 },
2720    { "32_32_32", 13 },
2721    { "32_32_32_32", 14 },
2722    { "8", 1 },
2723    { "8_8", 3 },
2724    { "8_8_8_8", 10 }
2725};
2726
2727// number format names (sorted by names) (for MUBUF/MTBUF)
2728static const std::pair<const char*, cxuint> mtbufNFMTNamesMap[] =
2729{
2730    { "float", 7 },
2731    { "sint", 5 },
2732    { "snorm", 1 },
2733    { "snorm_ogl", 6 },
2734    { "sscaled", 3 },
2735    { "uint", 4 },
2736    { "unorm", 0 },
2737    { "uscaled", 2 }
2738};
2739
2740bool GCNAsmUtils::parseMUBUFEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2741                  const char* instrPlace, const char* linePtr, uint16_t arch,
2742                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2743                  GCNEncSize gcnEncSize)
2744{
2745    const char* end = asmr.line+asmr.lineSize;
2746    bool good = true;
2747    if (gcnEncSize==GCNEncSize::BIT32)
2748        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for MUBUF/MTBUF encoding")
2749    const uint16_t mode1 = (gcnInsn.mode & GCN_MASK1);
2750    RegRange vaddrReg(0, 0);
2751    RegRange vdataReg(0, 0);
2752    GCNOperand soffsetOp{};
2753    RegRange srsrcReg(0, 0);
2754    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
2755    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
2756    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2757   
2758    skipSpacesToEnd(linePtr, end);
2759    const char* vdataPlace = linePtr;
2760    const char* vaddrPlace = nullptr;
2761    bool parsedVaddr = false;
2762    if (mode1 != GCN_ARG_NONE)
2763    {
2764        if (mode1 != GCN_MUBUF_NOVAD)
2765        {
2766            gcnAsm->setCurrentRVU(0);
2767            // parse VDATA (various VGPR number, verified later)
2768            good &= parseVRegRange(asmr, linePtr, vdataReg, 0, GCNFIELD_M_VDATA, true,
2769                        INSTROP_SYMREGRANGE|INSTROP_READ);
2770            if (!skipRequiredComma(asmr, linePtr))
2771                return false;
2772           
2773            skipSpacesToEnd(linePtr, end);
2774            vaddrPlace = linePtr;
2775            gcnAsm->setCurrentRVU(1);
2776            // parse VADDR (1 or 2 VGPR's) (optional)
2777            if (!parseVRegRange(asmr, linePtr, vaddrReg, 0, GCNFIELD_M_VADDR, false,
2778                        INSTROP_SYMREGRANGE|INSTROP_READ))
2779                good = false;
2780            if (vaddrReg) // only if vaddr is
2781            {
2782                parsedVaddr = true;
2783                if (!skipRequiredComma(asmr, linePtr))
2784                    return false;
2785            }
2786            else
2787            {
2788                // if not, default is v0, then parse off
2789                if (linePtr+3<=end && ::strncasecmp(linePtr, "off", 3)==0 &&
2790                    (isSpace(linePtr[3]) || linePtr[3]==','))
2791                {
2792                    linePtr+=3;
2793                    if (!skipRequiredComma(asmr, linePtr))
2794                        return false;
2795                }
2796                vaddrReg = {256, 257};
2797            }
2798        }
2799        // parse SRSREG (4 SGPR's)
2800        gcnAsm->setCurrentRVU(2);
2801        good &= parseSRegRange(asmr, linePtr, srsrcReg, arch, 4, GCNFIELD_M_SRSRC, true,
2802                        INSTROP_SYMREGRANGE|INSTROP_READ);
2803        if (!skipRequiredComma(asmr, linePtr))
2804            return false;
2805        // parse SOFFSET (SGPR or scalar source or constant)
2806        gcnAsm->setCurrentRVU(3);
2807        good &= parseOperand(asmr, linePtr, soffsetOp, nullptr, arch, 1,
2808                 INSTROP_SREGS|INSTROP_SSOURCE|INSTROP_ONLYINLINECONSTS|INSTROP_READ|
2809                 INSTROP_NOLITERALERRORMUBUF, GCNFIELD_M_SOFFSET);
2810    }
2811   
2812    bool haveOffset = false, haveFormat = false;
2813    cxuint dfmt = 1, nfmt = 0;
2814    cxuint offset = 0;
2815    std::unique_ptr<AsmExpression> offsetExpr;
2816    bool haveAddr64 = false, haveTfe = false, haveSlc = false, haveLds = false;
2817    bool haveGlc = false, haveOffen = false, haveIdxen = false;
2818    const char* modName = (gcnInsn.encoding==GCNENC_MTBUF) ?
2819            "MTBUF modifier" : "MUBUF modifier";
2820   
2821    // main loop to parsing MUBUF/MTBUF modifiers
2822    while(linePtr!=end)
2823    {
2824        skipSpacesToEnd(linePtr, end);
2825        if (linePtr==end)
2826            break;
2827        char name[10];
2828        const char* modPlace = linePtr;
2829        if (!getNameArgS(asmr, 10, name, linePtr, modName))
2830        {
2831            good = false;
2832            continue;
2833        }
2834        toLowerString(name);
2835       
2836        if (name[0] == 'o')
2837        {
2838            // offen, offset
2839            if (::strcmp(name+1, "ffen")==0)
2840                good &= parseModEnable(asmr, linePtr, haveOffen, "offen modifier");
2841            else if (::strcmp(name+1, "ffset")==0)
2842            {
2843                // parse offset
2844                if (parseModImm(asmr, linePtr, offset, &offsetExpr, "offset",
2845                                12, WS_UNSIGNED))
2846                {
2847                    if (haveOffset)
2848                        asmr.printWarning(modPlace, "Offset is already defined");
2849                    haveOffset = true;
2850                }
2851                else
2852                    good = false;
2853            }
2854            else
2855                ASM_NOTGOOD_BY_ERROR(modPlace, (gcnInsn.encoding==GCNENC_MUBUF) ? 
2856                    "Unknown MUBUF modifier" : "Unknown MTBUF modifier")
2857        }
2858        else if (gcnInsn.encoding==GCNENC_MTBUF && ::strcmp(name, "format")==0)
2859        {
2860            // parse format
2861            bool modGood = true;
2862            skipSpacesToEnd(linePtr, end);
2863            if (linePtr==end || *linePtr!=':')
2864            {
2865                ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before format")
2866                continue;
2867            }
2868            skipCharAndSpacesToEnd(linePtr, end);
2869           
2870            // parse [DATA_FORMAT:NUMBER_FORMAT]
2871            if (linePtr==end || *linePtr!='[')
2872                ASM_NOTGOOD_BY_ERROR1(modGood = good, modPlace,
2873                                "Expected '[' before format")
2874            if (modGood)
2875            {
2876                skipCharAndSpacesToEnd(linePtr, end);
2877                const char* fmtPlace = linePtr;
2878                char fmtName[30];
2879                bool haveNFMT = false;
2880                if (linePtr != end && *linePtr=='@')
2881                {
2882                    // expression, parse DATA_FORMAT
2883                    linePtr++;
2884                    if (!parseImm(asmr, linePtr, dfmt, nullptr, 4, WS_UNSIGNED))
2885                        modGood = good = false;
2886                }
2887                else if (getMUBUFFmtNameArg(
2888                            asmr, 30, fmtName, linePtr, "data/number format"))
2889                {
2890                    toLowerString(fmtName);
2891                    size_t dfmtNameIndex = (::strncmp(fmtName,
2892                                 "buf_data_format_", 16)==0) ? 16 : 0;
2893                    size_t dfmtIdx = binaryMapFind(mtbufDFMTNamesMap, mtbufDFMTNamesMap+14,
2894                                fmtName+dfmtNameIndex, CStringLess())-mtbufDFMTNamesMap;
2895                    if (dfmtIdx != 14)
2896                        dfmt = mtbufDFMTNamesMap[dfmtIdx].second;
2897                    else
2898                    {
2899                        // nfmt (if not found, then try parse number format)
2900                        size_t nfmtNameIndex = (::strncmp(fmtName,
2901                                 "buf_num_format_", 15)==0) ? 15 : 0;
2902                        size_t nfmtIdx = binaryMapFind(mtbufNFMTNamesMap,
2903                               mtbufNFMTNamesMap+8, fmtName+nfmtNameIndex,
2904                               CStringLess())-mtbufNFMTNamesMap;
2905                        // check if found
2906                        if (nfmtIdx!=8)
2907                        {
2908                            nfmt = mtbufNFMTNamesMap[nfmtIdx].second;
2909                            haveNFMT = true;
2910                        }
2911                        else
2912                            ASM_NOTGOOD_BY_ERROR1(modGood = good, fmtPlace,
2913                                        "Unknown data/number format")
2914                    }
2915                }
2916                else
2917                    modGood = good = false;
2918               
2919                skipSpacesToEnd(linePtr, end);
2920                if (!haveNFMT && linePtr!=end && *linePtr==',')
2921                {
2922                    skipCharAndSpacesToEnd(linePtr, end);
2923                    if (linePtr != end && *linePtr=='@')
2924                    {
2925                        // expression (number format)
2926                        linePtr++;
2927                        if (!parseImm(asmr, linePtr, nfmt, nullptr, 3, WS_UNSIGNED))
2928                            modGood = good = false;
2929                    }
2930                    else
2931                    {
2932                        // parse NUMBER format from name
2933                        fmtPlace = linePtr;
2934                        good &= getEnumeration(asmr, linePtr, "number format",
2935                                8, mtbufNFMTNamesMap, nfmt, "buf_num_format_");
2936                    }
2937                }
2938                skipSpacesToEnd(linePtr, end);
2939                // close format
2940                if (linePtr!=end && *linePtr==']')
2941                    linePtr++;
2942                else
2943                    ASM_NOTGOOD_BY_ERROR(linePtr, "Unterminated format modifier")
2944                if (modGood)
2945                {
2946                    if (haveFormat)
2947                        asmr.printWarning(modPlace, "Format is already defined");
2948                    haveFormat = true;
2949                }
2950            }
2951        }
2952        // other modifiers
2953        else if (!isGCN12 && ::strcmp(name, "addr64")==0)
2954            good &= parseModEnable(asmr, linePtr, haveAddr64, "addr64 modifier");
2955        else if (::strcmp(name, "tfe")==0)
2956            good &= parseModEnable(asmr, linePtr, haveTfe, "tfe modifier");
2957        else if (::strcmp(name, "glc")==0)
2958            good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
2959        else if (::strcmp(name, "slc")==0)
2960            good &= parseModEnable(asmr, linePtr, haveSlc, "slc modifier");
2961        else if (gcnInsn.encoding==GCNENC_MUBUF && ::strcmp(name, "lds")==0)
2962            good &= parseModEnable(asmr, linePtr, haveLds, "lds modifier");
2963        else if (::strcmp(name, "idxen")==0)
2964            good &= parseModEnable(asmr, linePtr, haveIdxen, "idxen modifier");
2965        else
2966            ASM_NOTGOOD_BY_ERROR(modPlace, (gcnInsn.encoding==GCNENC_MUBUF) ? 
2967                    "Unknown MUBUF modifier" : "Unknown MTBUF modifier")
2968    }
2969   
2970    /* checking addr range and vdata range */
2971    bool vdataToRead = false;
2972    bool vdataToWrite = false;
2973    if (vdataReg)
2974    {
2975        vdataToWrite = ((gcnInsn.mode&GCN_MLOAD) != 0 ||
2976                ((gcnInsn.mode&GCN_MATOMIC)!=0 && haveGlc));
2977        vdataToRead = (gcnInsn.mode&GCN_MLOAD)==0 ||
2978                (gcnInsn.mode&GCN_MATOMIC)!=0;
2979        // check register range (number of register) in VDATA
2980        cxuint dregsNum = (((gcnInsn.mode&GCN_DSIZE_MASK)>>GCN_SHIFT2)+1);
2981        if ((gcnInsn.mode & GCN_MUBUF_D16)!=0 && isGCN14)
2982            // 16-bit values packed into half of number of registers
2983            dregsNum = (dregsNum+1)>>1;
2984        dregsNum += (haveTfe);
2985        if (!isXRegRange(vdataReg, dregsNum))
2986        {
2987            char errorMsg[40];
2988            snprintf(errorMsg, 40, "Required %u vector register%s", dregsNum,
2989                     (dregsNum>1) ? "s" : "");
2990            ASM_NOTGOOD_BY_ERROR(vdataPlace, errorMsg)
2991        }
2992    }
2993    if (vaddrReg)
2994    {
2995        if (!parsedVaddr && (haveIdxen || haveOffen || haveAddr64))
2996            // no vaddr in instruction
2997            ASM_NOTGOOD_BY_ERROR(vaddrPlace, "VADDR is required if idxen, offen "
2998                    "or addr64 is enabled")
2999        else
3000        {
3001            const cxuint vaddrSize = ((haveOffen&&haveIdxen) || haveAddr64) ? 2 : 1;
3002            // check register range (number of register) in VADDR
3003            if (!isXRegRange(vaddrReg, vaddrSize))
3004                ASM_NOTGOOD_BY_ERROR(vaddrPlace, (vaddrSize==2) ?
3005                        "Required 2 vector registers" : "Required 1 vector register")
3006        }
3007    }
3008    // fix access for VDATA field
3009    gcnAsm->instrRVUs[0].rwFlags = (vdataToWrite ? ASMRVU_WRITE : 0) |
3010            (vdataToRead ? ASMRVU_READ : 0);
3011    // check fcmpswap
3012    bool vdataDivided = false;
3013    if ((gcnInsn.mode & GCN_MHALFWRITE) != 0 && vdataToWrite && !haveLds &&
3014        gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3015    {
3016        // fix access
3017        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
3018        uint16_t size = rvu.rend-rvu.rstart;
3019        rvu.rend = rvu.rstart + (size>>1);
3020        AsmRegVarUsage& nextRvu = gcnAsm->instrRVUs[4];
3021        nextRvu = rvu;
3022        nextRvu.regField = GCNFIELD_M_VDATAH;
3023        nextRvu.rstart += (size>>1);
3024        nextRvu.rend = rvu.rstart + size;
3025        nextRvu.rwFlags = ASMRVU_READ;
3026        vdataDivided = true;
3027    }
3028    // do not read vaddr if no offen and idxen and no addr64
3029    if (!haveAddr64 && !haveOffen && !haveIdxen)
3030        gcnAsm->instrRVUs[1].regField = ASMFIELD_NONE; // ignore this
3031   
3032    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3033        return false;
3034   
3035    /* checking modifiers conditions */
3036    if (haveAddr64 && (haveOffen || haveIdxen))
3037        ASM_FAIL_BY_ERROR(instrPlace, "Idxen and offen must be zero in 64-bit address mode")
3038    if (haveTfe && haveLds)
3039        ASM_FAIL_BY_ERROR(instrPlace, "Both LDS and TFE is illegal")
3040   
3041    // ignore vdata if LDS
3042    if (haveLds)
3043        gcnAsm->instrRVUs[0].regField = ASMFIELD_NONE;
3044   
3045    if (haveTfe && (vdataDivided ||
3046            gcnAsm->instrRVUs[0].rwFlags!=(ASMRVU_READ|ASMRVU_WRITE)) &&
3047            gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3048    {
3049        // fix for tfe
3050        const cxuint rvuId = (vdataDivided ? 4 : 0);
3051        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[rvuId];
3052        AsmRegVarUsage& lastRvu = gcnAsm->instrRVUs[5];
3053        lastRvu = rvu;
3054        lastRvu.rstart = lastRvu.rend-1;
3055        lastRvu.rwFlags = ASMRVU_READ|ASMRVU_WRITE;
3056        lastRvu.regField = GCNFIELD_M_VDATALAST;
3057        if (lastRvu.regVar==nullptr) // fix for regusage
3058        {
3059            // to save register size for VDATALAST
3060            lastRvu.rstart = gcnAsm->instrRVUs[0].rstart;
3061            lastRvu.rend--;
3062        }
3063        rvu.rend--;
3064    }
3065   
3066    if (offsetExpr!=nullptr)
3067        offsetExpr->setTarget(AsmExprTarget(GCNTGT_MXBUFOFFSET, asmr.currentSection,
3068                    output.size()));
3069   
3070    // put data (instruction words)
3071    uint32_t words[2];
3072    if (gcnInsn.encoding==GCNENC_MUBUF)
3073        SLEV(words[0], 0xe0000000U | offset | (haveOffen ? 0x1000U : 0U) |
3074                (haveIdxen ? 0x2000U : 0U) | (haveGlc ? 0x4000U : 0U) |
3075                ((haveAddr64 && !isGCN12) ? 0x8000U : 0U) | (haveLds ? 0x10000U : 0U) |
3076                ((haveSlc && isGCN12) ? 0x20000U : 0) | (uint32_t(gcnInsn.code1)<<18));
3077    else
3078    {
3079        // MTBUF encoding
3080        uint32_t code = (isGCN12) ? (uint32_t(gcnInsn.code1)<<15) :
3081                (uint32_t(gcnInsn.code1)<<16);
3082        SLEV(words[0], 0xe8000000U | offset | (haveOffen ? 0x1000U : 0U) |
3083                (haveIdxen ? 0x2000U : 0U) | (haveGlc ? 0x4000U : 0U) |
3084                ((haveAddr64 && !isGCN12) ? 0x8000U : 0U) | code |
3085                (uint32_t(dfmt)<<19) | (uint32_t(nfmt)<<23));
3086    }
3087    // second word
3088    SLEV(words[1], (vaddrReg.bstart()&0xff) | (uint32_t(vdataReg.bstart()&0xff)<<8) |
3089            (uint32_t(srsrcReg.bstart()>>2)<<16) |
3090            ((haveSlc && (!isGCN12 || gcnInsn.encoding==GCNENC_MTBUF)) ? (1U<<22) : 0) |
3091            (haveTfe ? (1U<<23) : 0) | (uint32_t(soffsetOp.range.bstart())<<24));
3092   
3093    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3094            reinterpret_cast<cxbyte*>(words + 2));
3095   
3096    offsetExpr.release();
3097    // update register pool (instr loads or save old value) */
3098    if (vdataReg && !vdataReg.isRegVar() && (vdataToWrite || haveTfe) && !haveLds)
3099        updateVGPRsNum(gcnRegs.vgprsNum, vdataReg.end-257);
3100    if (soffsetOp.range && !soffsetOp.range.isRegVar())
3101        updateRegFlags(gcnRegs.regFlags, soffsetOp.range.start, arch);
3102    return true;
3103}
3104
3105bool GCNAsmUtils::parseMIMGEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
3106                  const char* instrPlace, const char* linePtr, uint16_t arch,
3107                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
3108                  GCNEncSize gcnEncSize)
3109{
3110    const char* end = asmr.line+asmr.lineSize;
3111    if (gcnEncSize==GCNEncSize::BIT32)
3112        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for MIMG encoding")
3113    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
3114    bool good = true;
3115    RegRange vaddrReg(0, 0);
3116    RegRange vdataReg(0, 0);
3117    RegRange ssampReg(0, 0);
3118    RegRange srsrcReg(0, 0);
3119    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
3120   
3121    skipSpacesToEnd(linePtr, end);
3122    const char* vdataPlace = linePtr;
3123    gcnAsm->setCurrentRVU(0);
3124    // parse VDATA (various VGPR number, verified later)
3125    good &= parseVRegRange(asmr, linePtr, vdataReg, 0, GCNFIELD_M_VDATA, true,
3126                    INSTROP_SYMREGRANGE|INSTROP_READ);
3127    if (!skipRequiredComma(asmr, linePtr))
3128        return false;
3129   
3130    skipSpacesToEnd(linePtr, end);
3131    const char* vaddrPlace = linePtr;
3132    gcnAsm->setCurrentRVU(1);
3133    // // parse VADDR (various VGPR number, verified later)
3134    good &= parseVRegRange(asmr, linePtr, vaddrReg, 0, GCNFIELD_M_VADDR, true,
3135                    INSTROP_SYMREGRANGE|INSTROP_READ);
3136    cxuint geRegRequired = (gcnInsn.mode&GCN_MIMG_VA_MASK)+1;
3137    cxuint vaddrRegsNum = vaddrReg.end-vaddrReg.start;
3138    cxuint vaddrMaxExtraRegs = (gcnInsn.mode&GCN_MIMG_VADERIV) ? 7 : 3;
3139    if (vaddrRegsNum < geRegRequired || vaddrRegsNum > geRegRequired+vaddrMaxExtraRegs)
3140    {
3141        char buf[60];
3142        snprintf(buf, 60, "Required (%u-%u) vector registers", geRegRequired,
3143                 geRegRequired+vaddrMaxExtraRegs);
3144        ASM_NOTGOOD_BY_ERROR(vaddrPlace, buf)
3145    }
3146   
3147    if (!skipRequiredComma(asmr, linePtr))
3148        return false;
3149    skipSpacesToEnd(linePtr, end);
3150    const char* srsrcPlace = linePtr;
3151    gcnAsm->setCurrentRVU(2);
3152    // parse SRSRC (4 or 8 SGPR's) number of register verified later
3153    good &= parseSRegRange(asmr, linePtr, srsrcReg, arch, 0, GCNFIELD_M_SRSRC, true,
3154                    INSTROP_SYMREGRANGE|INSTROP_READ);
3155   
3156    if ((gcnInsn.mode & GCN_MIMG_SAMPLE) != 0)
3157    {
3158        if (!skipRequiredComma(asmr, linePtr))
3159            return false;
3160        gcnAsm->setCurrentRVU(3);
3161        // parse SSAMP (4 SGPR's)
3162        good &= parseSRegRange(asmr, linePtr, ssampReg, arch, 4, GCNFIELD_MIMG_SSAMP,
3163                               true, INSTROP_SYMREGRANGE|INSTROP_READ);
3164    }
3165   
3166    bool haveTfe = false, haveSlc = false, haveGlc = false;
3167    bool haveDa = false, haveR128 = false, haveLwe = false, haveUnorm = false;
3168    bool haveDMask = false, haveD16 = false, haveA16 = false;
3169    cxbyte dmask = 0x1;
3170    /* modifiers and modifiers */
3171    while(linePtr!=end)
3172    {
3173        skipSpacesToEnd(linePtr, end);
3174        if (linePtr==end)
3175            break;
3176        char name[10];
3177        const char* modPlace = linePtr;
3178        if (!getNameArgS(asmr, 10, name, linePtr, "MIMG modifier"))
3179        {
3180            good = false;
3181            continue;
3182        }
3183        toLowerString(name);
3184       
3185        if (name[0] == 'd')
3186        {
3187            if (name[1]=='a' && name[2]==0)
3188                // DA modifier
3189                good &= parseModEnable(asmr, linePtr, haveDa, "da modifier");
3190            else if ((arch & ARCH_GCN_1_2_4)!=0 && name[1]=='1' &&
3191                name[2]=='6' && name[3]==0)
3192                // D16 modifier
3193                good &= parseModEnable(asmr, linePtr, haveD16, "d16 modifier");
3194            else if (::strcmp(name+1, "mask")==0)
3195            {
3196                // parse dmask
3197                skipSpacesToEnd(linePtr, end);
3198                if (linePtr!=end && *linePtr==':')
3199                {
3200                    /* parse dmask immediate */
3201                    skipCharAndSpacesToEnd(linePtr, end);
3202                    const char* valuePlace = linePtr;
3203                    uint64_t value;
3204                    if (getAbsoluteValueArg(asmr, value, linePtr, true))
3205                    {
3206                        // detect multiple occurrences
3207                        if (haveDMask)
3208                            asmr.printWarning(modPlace, "Dmask is already defined");
3209                        haveDMask = true;
3210                        if (value>0xf)
3211                            asmr.printWarning(valuePlace, "Dmask out of range (0-15)");
3212                        dmask = value&0xf;
3213                        if (dmask == 0)
3214                            ASM_NOTGOOD_BY_ERROR(valuePlace, "Zero in dmask is illegal")
3215                    }
3216                    else
3217                        good = false;
3218                }
3219                else
3220                    ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before dmask")
3221            }
3222            else
3223                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown MIMG modifier")
3224        }
3225        else if (name[0] < 's')
3226        {
3227            // glc, lwe, r128, a16 modifiers
3228            if (::strcmp(name, "glc")==0)
3229                good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
3230            else if (::strcmp(name, "lwe")==0)
3231                good &= parseModEnable(asmr, linePtr, haveLwe, "lwe modifier");
3232            else if (!isGCN14 && ::strcmp(name, "r128")==0)
3233                good &= parseModEnable(asmr, linePtr, haveR128, "r128 modifier");
3234            else if (isGCN14 && ::strcmp(name, "a16")==0)
3235                good &= parseModEnable(asmr, linePtr, haveA16, "a16 modifier");
3236            else
3237                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown MIMG modifier")
3238        }
3239        // other modifiers
3240        else if (::strcmp(name, "tfe")==0)
3241            good &= parseModEnable(asmr, linePtr, haveTfe, "tfe modifier");
3242        else if (::strcmp(name, "slc")==0)
3243            good &= parseModEnable(asmr, linePtr, haveSlc, "slc modifier");
3244        else if (::strcmp(name, "unorm")==0)
3245            good &= parseModEnable(asmr, linePtr, haveUnorm, "unorm modifier");
3246        else
3247            ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown MIMG modifier")
3248    }
3249   
3250    cxuint dregsNum = 4;
3251    // check number of registers in VDATA
3252    if ((gcnInsn.mode & GCN_MIMG_VDATA4) == 0)
3253        dregsNum = ((dmask & 1)?1:0) + ((dmask & 2)?1:0) + ((dmask & 4)?1:0) +
3254                ((dmask & 8)?1:0) + (haveTfe);
3255    if (dregsNum!=0 && !isXRegRange(vdataReg, dregsNum))
3256    {
3257        char errorMsg[40];
3258        snprintf(errorMsg, 40, "Required %u vector register%s", dregsNum,
3259                 (dregsNum>1) ? "s" : "");
3260        ASM_NOTGOOD_BY_ERROR(vdataPlace, errorMsg)
3261    }
3262    // check number of registers in SRSRC
3263    if (!isXRegRange(srsrcReg, (haveR128)?4:8))
3264        ASM_NOTGOOD_BY_ERROR(srsrcPlace, (haveR128) ? "Required 4 scalar registers" :
3265                    "Required 8 scalar registers")
3266   
3267    const bool vdataToWrite = ((gcnInsn.mode&GCN_MLOAD) != 0 ||
3268                ((gcnInsn.mode&GCN_MATOMIC)!=0 && haveGlc));
3269    const bool vdataToRead = ((gcnInsn.mode&GCN_MLOAD) == 0 ||
3270                ((gcnInsn.mode&GCN_MATOMIC)!=0));
3271   
3272    // fix access for VDATA field
3273    gcnAsm->instrRVUs[0].rwFlags = (vdataToWrite ? ASMRVU_WRITE : 0) |
3274            (vdataToRead ? ASMRVU_READ : 0);
3275    // fix alignment
3276    if (gcnAsm->instrRVUs[2].regVar != nullptr)
3277        gcnAsm->instrRVUs[2].align = 4;
3278   
3279    // check fcmpswap
3280    bool vdataDivided = false;
3281    if ((gcnInsn.mode & GCN_MHALFWRITE) != 0 && vdataToWrite &&
3282        gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3283    {
3284        // fix access
3285        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
3286        uint16_t size = rvu.rend-rvu.rstart;
3287        rvu.rend = rvu.rstart + (size>>1);
3288        AsmRegVarUsage& nextRvu = gcnAsm->instrRVUs[4];
3289        nextRvu = rvu;
3290        nextRvu.regField = GCNFIELD_M_VDATAH;
3291        nextRvu.rstart += (size>>1);
3292        nextRvu.rend = rvu.rstart + size;
3293        nextRvu.rwFlags = ASMRVU_READ;
3294        vdataDivided = true;
3295    }
3296   
3297    if (haveTfe && (vdataDivided ||
3298            gcnAsm->instrRVUs[0].rwFlags!=(ASMRVU_READ|ASMRVU_WRITE)) &&
3299       gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3300    {
3301        // fix for tfe
3302        const cxuint rvuId = (vdataDivided ? 4 : 0);
3303        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[rvuId];
3304        AsmRegVarUsage& lastRvu = gcnAsm->instrRVUs[5];
3305        lastRvu = rvu;
3306        lastRvu.rstart = lastRvu.rend-1;
3307        lastRvu.rwFlags = ASMRVU_READ|ASMRVU_WRITE;
3308        lastRvu.regField = GCNFIELD_M_VDATALAST;
3309        if (lastRvu.regVar==nullptr) // fix for regusage
3310        {
3311            // to save register size for VDATALAST
3312            lastRvu.rstart = gcnAsm->instrRVUs[0].rstart;
3313            lastRvu.rend--;
3314        }
3315        rvu.rend--;
3316    }
3317   
3318    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3319        return false;
3320   
3321    /* checking modifiers conditions */
3322    if (!haveUnorm && ((gcnInsn.mode&GCN_MLOAD) == 0 || (gcnInsn.mode&GCN_MATOMIC)!=0))
3323        // unorm is not set for this instruction
3324        ASM_FAIL_BY_ERROR(instrPlace, "Unorm is not set for store or atomic instruction")
3325   
3326    // put instruction words
3327    uint32_t words[2];
3328    SLEV(words[0], 0xf0000000U | (uint32_t(dmask&0xf)<<8) | (haveUnorm ? 0x1000U : 0) |
3329        (haveGlc ? 0x2000U : 0) | (haveDa ? 0x4000U : 0) |
3330        (haveR128|haveA16 ? 0x8000U : 0) |
3331        (haveTfe ? 0x10000U : 0) | (haveLwe ? 0x20000U : 0) |
3332        (uint32_t(gcnInsn.code1)<<18) | (haveSlc ? (1U<<25) : 0));
3333    SLEV(words[1], (vaddrReg.bstart()&0xff) | (uint32_t(vdataReg.bstart()&0xff)<<8) |
3334            (uint32_t(srsrcReg.bstart()>>2)<<16) | (uint32_t(ssampReg.bstart()>>2)<<21) |
3335            (haveD16 ? (1U<<31) : 0));
3336    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3337            reinterpret_cast<cxbyte*>(words + 2));
3338   
3339    // update register pool (instr loads or save old value) */
3340    if (vdataReg && !vdataReg.isRegVar() && (vdataToWrite || haveTfe))
3341        updateVGPRsNum(gcnRegs.vgprsNum, vdataReg.end-257);
3342    return true;
3343}
3344
3345bool GCNAsmUtils::parseEXPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
3346                  const char* instrPlace, const char* linePtr, uint16_t arch,
3347                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
3348                  GCNEncSize gcnEncSize)
3349{
3350    const char* end = asmr.line+asmr.lineSize;
3351    if (gcnEncSize==GCNEncSize::BIT32)
3352        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for EXP encoding")
3353    bool good = true;
3354    cxbyte enMask = 0xf;
3355    cxbyte target = 0;
3356    RegRange vsrcsReg[4];
3357    const char* vsrcPlaces[4];
3358    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
3359   
3360    char name[20];
3361    skipSpacesToEnd(linePtr, end);
3362    const char* targetPlace = linePtr;
3363   
3364    try
3365    {
3366    if (getNameArg(asmr, 20, name, linePtr, "target"))
3367    {
3368        size_t nameSize = linePtr-targetPlace;
3369        const char* nameStart = name;
3370        toLowerString(name);
3371        // parse mrt / mrtz / mrt0 - mrt7
3372        if (name[0]=='m' && name[1]=='r' && name[2]=='t')
3373        {
3374            // parse mrtX target
3375            if (name[3]!='z' || name[4]!=0)
3376            {
3377                nameStart+=3;
3378                target = cstrtobyte(nameStart, name+nameSize);
3379                if (target>=8)
3380                    ASM_NOTGOOD_BY_ERROR(targetPlace, "MRT number out of range (0-7)")
3381            }
3382            else
3383                target = 8; // mrtz
3384        }
3385        // parse pos0 - pos3
3386        else if (name[0]=='p' && name[1]=='o' && name[2]=='s')
3387        {
3388            // parse pos target
3389            nameStart+=3;
3390            cxbyte posNum = cstrtobyte(nameStart, name+nameSize);
3391            if (posNum>=4)
3392                ASM_NOTGOOD_BY_ERROR(targetPlace, "Pos number out of range (0-3)")
3393            else
3394                target = posNum+12;
3395        }
3396        else if (strcmp(name, "null")==0)
3397            target = 9;
3398        // param0 - param 31
3399        else if (memcmp(name, "param", 5)==0)
3400        {
3401            nameStart+=5;
3402            cxbyte posNum = cstrtobyte(nameStart, name+nameSize);
3403            if (posNum>=32)
3404                ASM_NOTGOOD_BY_ERROR(targetPlace, "Param number out of range (0-31)")
3405            else
3406                target = posNum+32;
3407        }
3408        else
3409            ASM_NOTGOOD_BY_ERROR(targetPlace, "Unknown EXP target")
3410    }
3411    else
3412        good = false;
3413    }
3414    catch (const ParseException& ex)
3415    {
3416        // number parsing error
3417        asmr.printError(targetPlace, ex.what());
3418        good = false;
3419    }
3420   
3421    /* parse VSRC0-3 registers */
3422    for (cxuint i = 0; i < 4; i++)
3423    {
3424        if (!skipRequiredComma(asmr, linePtr))
3425            return false;
3426        skipSpacesToEnd(linePtr, end);
3427        vsrcPlaces[i] = linePtr;
3428        // if not 'off', then parse vector register
3429        if (linePtr+2>=end || toLower(linePtr[0])!='o' || toLower(linePtr[1])!='f' ||
3430            toLower(linePtr[2])!='f' || (linePtr+3!=end && isAlnum(linePtr[3])))
3431        {
3432            gcnAsm->setCurrentRVU(i);
3433            good &= parseVRegRange(asmr, linePtr, vsrcsReg[i], 1, GCNFIELD_EXP_VSRC0+i,
3434                        true, INSTROP_SYMREGRANGE|INSTROP_READ);
3435        }
3436        else
3437        {
3438            // if vsrcX is off
3439            enMask &= ~(1U<<i);
3440            vsrcsReg[i] = { 0, 0 };
3441            linePtr += 3;
3442        }
3443    }
3444   
3445    /* EXP modifiers */
3446    bool haveVM = false, haveCompr = false, haveDone = false;
3447    while(linePtr!=end)
3448    {
3449        skipSpacesToEnd(linePtr, end);
3450        if (linePtr==end)
3451            break;
3452        const char* modPlace = linePtr;
3453        if (!getNameArgS(asmr, 10, name, linePtr, "EXP modifier"))
3454        {
3455            good = false;
3456            continue;
3457        }
3458        toLowerString(name);
3459        if (name[0]=='v' && name[1]=='m' && name[2]==0)
3460            good &= parseModEnable(asmr, linePtr, haveVM, "vm modifier");
3461        else if (::strcmp(name, "done")==0)
3462            good &= parseModEnable(asmr, linePtr, haveDone, "done modifier");
3463        else if (::strcmp(name, "compr")==0)
3464            good &= parseModEnable(asmr, linePtr, haveCompr, "compr modifier");
3465        else
3466            ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown EXP modifier")
3467    }
3468   
3469    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3470        return false;
3471   
3472    // checking whether VSRC's is correct in compr mode if enabled
3473    if (haveCompr && !vsrcsReg[0].isRegVar() && !vsrcsReg[1].isRegVar() &&
3474            !vsrcsReg[0].isRegVar() && !vsrcsReg[1].isRegVar())
3475    {
3476        if (vsrcsReg[0].start!=vsrcsReg[1].start && (enMask&3)==3)
3477            // error (vsrc1!=vsrc0)
3478            ASM_FAIL_BY_ERROR(vsrcPlaces[1], "VSRC1 must be equal to VSRC0 in compr mode")
3479        if (vsrcsReg[2].start!=vsrcsReg[3].start && (enMask&12)==12)
3480            // error (vsrc3!=vsrc2)
3481            ASM_FAIL_BY_ERROR(vsrcPlaces[3], "VSRC3 must be equal to VSRC2 in compr mode")
3482        vsrcsReg[1] = vsrcsReg[2];
3483        vsrcsReg[2] = vsrcsReg[3] = { 0, 0 };
3484    }
3485   
3486    // put instruction words
3487    uint32_t words[2];
3488    SLEV(words[0], ((arch&ARCH_GCN_1_2_4) ? 0xc4000000 : 0xf8000000U) | enMask |
3489            (uint32_t(target)<<4) | (haveCompr ? 0x400 : 0) | (haveDone ? 0x800 : 0) |
3490            (haveVM ? 0x1000U : 0));
3491    SLEV(words[1], uint32_t(vsrcsReg[0].bstart()&0xff) |
3492            (uint32_t(vsrcsReg[1].bstart()&0xff)<<8) |
3493            (uint32_t(vsrcsReg[2].bstart()&0xff)<<16) |
3494            (uint32_t(vsrcsReg[3].bstart()&0xff)<<24));
3495   
3496    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3497            reinterpret_cast<cxbyte*>(words + 2));
3498    return true;
3499}
3500
3501bool GCNAsmUtils::parseFLATEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
3502                  const char* instrPlace, const char* linePtr, uint16_t arch,
3503                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
3504                  GCNEncSize gcnEncSize)
3505{
3506    const char* end = asmr.line+asmr.lineSize;
3507    if (gcnEncSize==GCNEncSize::BIT32)
3508        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for FLAT encoding")
3509    const bool isGCN14 = (arch & ARCH_RXVEGA)!=0;
3510    const cxuint flatMode = (gcnInsn.mode & GCN_FLAT_MODEMASK);
3511    bool good = true;
3512    RegRange vaddrReg(0, 0);
3513    RegRange vdstReg(0, 0);
3514    RegRange vdataReg(0, 0);
3515    RegRange saddrReg(0, 0);
3516    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
3517   
3518    skipSpacesToEnd(linePtr, end);
3519    const char* vdstPlace = nullptr;
3520   
3521    bool vaddrOff = false;
3522    const cxuint dregsNum = ((gcnInsn.mode&GCN_DSIZE_MASK)>>GCN_SHIFT2)+1;
3523   
3524    const cxuint addrRegsNum = (flatMode != GCN_FLAT_SCRATCH ?
3525                (flatMode==GCN_FLAT_FLAT ? 2 : 0)  : 1);
3526    const char* addrPlace = nullptr;
3527    if ((gcnInsn.mode & GCN_FLAT_ADST) == 0)
3528    {
3529        // first is destination
3530        vdstPlace = linePtr;
3531       
3532        gcnAsm->setCurrentRVU(0);
3533        good &= parseVRegRange(asmr, linePtr, vdstReg, 0, GCNFIELD_FLAT_VDST, true,
3534                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
3535        if (!skipRequiredComma(asmr, linePtr))
3536            return false;
3537        skipSpacesToEnd(linePtr, end);
3538        addrPlace = linePtr;
3539        if (flatMode == GCN_FLAT_SCRATCH && linePtr+3<=end &&
3540            strncasecmp(linePtr, "off", 3)==0 && (linePtr+3==end || !isAlnum(linePtr[3])))
3541        {
3542            // // if 'off' word
3543            vaddrOff = true;
3544            linePtr+=3;
3545        }
3546        else
3547        {
3548            gcnAsm->setCurrentRVU(1);
3549            // parse VADDR (1 or 2 VGPR's)
3550            good &= parseVRegRange(asmr, linePtr, vaddrReg, addrRegsNum,
3551                    GCNFIELD_FLAT_ADDR, true, INSTROP_SYMREGRANGE|INSTROP_READ);
3552        }
3553    }
3554    else
3555    {
3556        // first is data
3557        skipSpacesToEnd(linePtr, end);
3558        addrPlace = linePtr;
3559        if (flatMode == GCN_FLAT_SCRATCH && linePtr+3<=end &&
3560            strncasecmp(linePtr, "off", 3)==0 && (linePtr+3==end || !isAlnum(linePtr[3])))
3561        {
3562            // if 'off' word
3563            vaddrOff = true;
3564            linePtr+=3;
3565        }
3566        else
3567        {
3568            gcnAsm->setCurrentRVU(1);
3569            // parse VADDR (1 or 2 VGPR's)
3570            good &= parseVRegRange(asmr, linePtr, vaddrReg, addrRegsNum,
3571                        GCNFIELD_FLAT_ADDR, true, INSTROP_SYMREGRANGE|INSTROP_READ);
3572        }
3573        if ((gcnInsn.mode & GCN_FLAT_NODST) == 0)
3574        {
3575            if (!skipRequiredComma(asmr, linePtr))
3576                return false;
3577            skipSpacesToEnd(linePtr, end);
3578            vdstPlace = linePtr;
3579            gcnAsm->setCurrentRVU(0);
3580            // parse VDST (VGPRs, various number of register, verified later)
3581            good &= parseVRegRange(asmr, linePtr, vdstReg, 0, GCNFIELD_FLAT_VDST, true,
3582                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
3583        }
3584    }
3585   
3586    if ((gcnInsn.mode & GCN_FLAT_NODATA) == 0) /* print data */
3587    {
3588        if (!skipRequiredComma(asmr, linePtr))
3589            return false;
3590        gcnAsm->setCurrentRVU(2);
3591        // parse VDATA (VGPRS, 1-4 registers)
3592        good &= parseVRegRange(asmr, linePtr, vdataReg, dregsNum, GCNFIELD_FLAT_DATA,
3593                               true, INSTROP_SYMREGRANGE|INSTROP_READ);
3594    }
3595   
3596    bool saddrOff = false;
3597    if (flatMode != 0)
3598    {
3599        // SADDR
3600        if (!skipRequiredComma(asmr, linePtr))
3601            return false;
3602        skipSpacesToEnd(linePtr, end);
3603        if (flatMode != 0 && linePtr+3<=end && strncasecmp(linePtr, "off", 3)==0 &&
3604            (linePtr+3==end || !isAlnum(linePtr[3])))
3605        {  // if 'off' word
3606            saddrOff = true;
3607            linePtr+=3;
3608        }
3609        else
3610        {
3611            gcnAsm->setCurrentRVU(3);
3612            good &= parseSRegRange(asmr, linePtr, saddrReg, arch,
3613                        (flatMode==GCN_FLAT_SCRATCH ? 1 : 2), GCNFIELD_FLAT_SADDR, true,
3614                        INSTROP_SYMREGRANGE|INSTROP_READ);
3615        }
3616    }
3617   
3618    if (addrRegsNum == 0)
3619    {
3620        // check size of addrRange
3621        // if SADDR then 1 VADDR offset register, otherwise 2 VADDR VGPRs
3622        cxuint reqAddrRegsNum = saddrOff ? 2 : 1;
3623        if (!isXRegRange(vaddrReg, reqAddrRegsNum))
3624        {
3625            char errorMsg[40];
3626            snprintf(errorMsg, 40, "Required %u vector register%s", reqAddrRegsNum,
3627                     (reqAddrRegsNum>1) ? "s" : "");
3628            ASM_NOTGOOD_BY_ERROR(addrPlace, errorMsg)
3629        }
3630    }
3631   
3632    if (flatMode == GCN_FLAT_SCRATCH && !saddrOff && !vaddrOff)
3633        ASM_NOTGOOD_BY_ERROR(instrPlace, "Only one of VADDR and SADDR can be set in "
3634                    "SCRATCH mode")
3635   
3636    if (saddrOff)
3637        saddrReg.start = 0x7f;
3638    if (vaddrOff)
3639        vaddrReg.start = 0x00;
3640   
3641    uint16_t instOffset = 0;
3642    std::unique_ptr<AsmExpression> instOffsetExpr;
3643    bool haveTfe = false, haveSlc = false, haveGlc = false;
3644    bool haveNv = false, haveLds = false, haveInstOffset = false;
3645   
3646    // main loop to parsing FLAT modifiers
3647    while(linePtr!=end)
3648    {
3649        skipSpacesToEnd(linePtr, end);
3650        if (linePtr==end)
3651            break;
3652        char name[20];
3653        const char* modPlace = linePtr;
3654        // get modifier name
3655        if (!getNameArgS(asmr, 20, name, linePtr, "FLAT modifier"))
3656        {
3657            good = false;
3658            continue;
3659        }
3660        // only GCN1.2 modifiers
3661        if (!isGCN14 && ::strcmp(name, "tfe") == 0)
3662            good &= parseModEnable(asmr, linePtr, haveTfe, "tfe modifier");
3663        // only GCN1.4 modifiers
3664        else if (isGCN14 && ::strcmp(name, "nv") == 0)
3665            good &= parseModEnable(asmr, linePtr, haveNv, "nv modifier");
3666        else if (isGCN14 && ::strcmp(name, "lds") == 0)
3667            good &= parseModEnable(asmr, linePtr, haveLds, "lds modifier");
3668        // GCN 1.2/1.4 modifiers
3669        else if (::strcmp(name, "glc") == 0)
3670            good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
3671        else if (::strcmp(name, "slc") == 0)
3672            good &= parseModEnable(asmr, linePtr, haveSlc, "slc modifier");
3673        else if (isGCN14 && ::strcmp(name, "inst_offset")==0)
3674        {
3675            // parse inst_offset, 13-bit with sign, or 12-bit unsigned
3676            if (parseModImm(asmr, linePtr, instOffset, &instOffsetExpr, "inst_offset",
3677                            flatMode!=0 ? 13 : 12, flatMode!=0 ? WS_BOTH : WS_UNSIGNED))
3678            {
3679                if (haveInstOffset)
3680                    asmr.printWarning(modPlace, "InstOffset is already defined");
3681                haveInstOffset = true;
3682            }
3683            else
3684                good = false;
3685        }
3686        else
3687            ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown FLAT modifier")
3688    }
3689    /* check register ranges */
3690    bool dstToWrite = vdstReg && ((gcnInsn.mode & GCN_MATOMIC)==0 || haveGlc);
3691    if (vdstReg)
3692    {
3693        cxuint dstRegsNum = ((gcnInsn.mode & GCN_CMPSWAP)!=0) ? (dregsNum>>1) : dregsNum;
3694        dstRegsNum = (haveTfe) ? dstRegsNum+1:dstRegsNum; // include tfe
3695        // check number of registers for VDST
3696        if (!isXRegRange(vdstReg, dstRegsNum))
3697        {
3698            char errorMsg[40];
3699            snprintf(errorMsg, 40, "Required %u vector register%s", dstRegsNum,
3700                     (dstRegsNum>1) ? "s" : "");
3701            ASM_NOTGOOD_BY_ERROR(vdstPlace, errorMsg)
3702        }
3703       
3704        if (haveTfe && vdstReg && gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
3705        {
3706            // fix for tfe
3707            AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
3708            AsmRegVarUsage& lastRvu = gcnAsm->instrRVUs[3];
3709            lastRvu = rvu;
3710            lastRvu.rstart = lastRvu.rend-1;
3711            lastRvu.rwFlags = ASMRVU_READ|ASMRVU_WRITE;
3712            lastRvu.regField = GCNFIELD_FLAT_VDSTLAST;
3713            if (lastRvu.regVar==nullptr) // fix for regusage
3714            {
3715                // to save register size for VDSTLAST
3716                lastRvu.rstart = rvu.rstart;
3717                lastRvu.rend--;
3718            }
3719            rvu.rend--;
3720        }
3721       
3722        if (!dstToWrite)
3723            gcnAsm->instrRVUs[0].regField = ASMFIELD_NONE;
3724    }
3725   
3726    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
3727        return false;
3728   
3729    if (instOffsetExpr!=nullptr)
3730        instOffsetExpr->setTarget(AsmExprTarget(flatMode!=0 ?
3731                    GCNTGT_INSTOFFSET_S : GCNTGT_INSTOFFSET, asmr.currentSection,
3732                    output.size()));
3733   
3734    // put data (instruction words)
3735    uint32_t words[2];
3736    SLEV(words[0], 0xdc000000U | (haveGlc ? 0x10000 : 0) | (haveSlc ? 0x20000: 0) |
3737            (uint32_t(gcnInsn.code1)<<18) | (haveLds ? 0x2000U : 0) | instOffset |
3738            (uint32_t(flatMode)<<14));
3739    SLEV(words[1], (vaddrReg.bstart()&0xff) | (uint32_t(vdataReg.bstart()&0xff)<<8) |
3740            (haveTfe|haveNv ? (1U<<23) : 0) | (uint32_t(vdstReg.bstart()&0xff)<<24) |
3741            (uint32_t(saddrReg.bstart())<<16));
3742   
3743    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
3744            reinterpret_cast<cxbyte*>(words + 2));
3745   
3746    instOffsetExpr.release();
3747    // update register pool
3748    if (vdstReg && !vdstReg.isRegVar() && (dstToWrite || haveTfe))
3749        updateVGPRsNum(gcnRegs.vgprsNum, vdstReg.end-257);
3750    return true;
3751}
3752
3753};
3754
3755ISAUsageHandler* GCNAssembler::createUsageHandler(std::vector<cxbyte>& content) const
3756{
3757    return new GCNUsageHandler(content, curArchMask);
3758}
3759
3760void GCNAssembler::assemble(const CString& inMnemonic, const char* mnemPlace,
3761            const char* linePtr, const char* lineEnd, std::vector<cxbyte>& output,
3762            ISAUsageHandler* usageHandler)
3763{
3764    CString mnemonic;
3765    size_t inMnemLen = inMnemonic.size();
3766    GCNEncSize gcnEncSize = GCNEncSize::UNKNOWN;
3767    GCNVOPEnc vopEnc = GCNVOPEnc::NORMAL;
3768    // checking encoding suffixes (_e64, _e32,_dpp, _sdwa)
3769    if (inMnemLen>4 && ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e64")==0)
3770    {
3771        gcnEncSize = GCNEncSize::BIT64;
3772        mnemonic = inMnemonic.substr(0, inMnemLen-4);
3773    }
3774    else if (inMnemLen>4 && ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e32")==0)
3775    {
3776        gcnEncSize = GCNEncSize::BIT32;
3777        mnemonic = inMnemonic.substr(0, inMnemLen-4);
3778    }
3779    else if (inMnemLen>6 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
3780        ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_dpp")==0)
3781    {
3782        vopEnc = GCNVOPEnc::DPP;
3783        mnemonic = inMnemonic.substr(0, inMnemLen-4);
3784    }
3785    else if (inMnemLen>7 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
3786        ::strcasecmp(inMnemonic.c_str()+inMnemLen-5, "_sdwa")==0)
3787    {
3788        vopEnc = GCNVOPEnc::SDWA;
3789        mnemonic = inMnemonic.substr(0, inMnemLen-5);
3790    }
3791    else
3792        mnemonic = inMnemonic;
3793   
3794    // find instruction by mnemonic
3795    auto it = binaryFind(gcnInstrSortedTable.begin(), gcnInstrSortedTable.end(),
3796               GCNAsmInstruction{mnemonic.c_str()},
3797               [](const GCNAsmInstruction& instr1, const GCNAsmInstruction& instr2)
3798               { return ::strcmp(instr1.mnemonic, instr2.mnemonic)<0; });
3799   
3800    // find matched entry
3801    if (it != gcnInstrSortedTable.end() && (it->archMask & curArchMask)==0)
3802        // if not match current arch mask
3803        for (++it ;it != gcnInstrSortedTable.end() &&
3804               ::strcmp(it->mnemonic, mnemonic.c_str())==0 &&
3805               (it->archMask & curArchMask)==0; ++it);
3806
3807    if (it == gcnInstrSortedTable.end() || ::strcmp(it->mnemonic, mnemonic.c_str())!=0)
3808    {
3809        // unrecognized mnemonic
3810        printError(mnemPlace, "Unknown instruction");
3811        return;
3812    }
3813   
3814    resetInstrRVUs();
3815    setCurrentRVU(0);
3816    /* decode instruction line */
3817    bool good = false;
3818    switch(it->encoding)
3819    {
3820        case GCNENC_SOPC:
3821            good = GCNAsmUtils::parseSOPCEncoding(assembler, *it, mnemPlace, linePtr,
3822                               curArchMask, output, regs, gcnEncSize);
3823            break;
3824        case GCNENC_SOPP:
3825            good = GCNAsmUtils::parseSOPPEncoding(assembler, *it, mnemPlace, linePtr,
3826                               curArchMask, output, regs, gcnEncSize);
3827            break;
3828        case GCNENC_SOP1:
3829            good = GCNAsmUtils::parseSOP1Encoding(assembler, *it, mnemPlace, linePtr,
3830                               curArchMask, output, regs, gcnEncSize);
3831            break;
3832        case GCNENC_SOP2:
3833            good = GCNAsmUtils::parseSOP2Encoding(assembler, *it, mnemPlace, linePtr,
3834                               curArchMask, output, regs, gcnEncSize);
3835            break;
3836        case GCNENC_SOPK:
3837            good = GCNAsmUtils::parseSOPKEncoding(assembler, *it, mnemPlace, linePtr,
3838                               curArchMask, output, regs, gcnEncSize);
3839            break;
3840        case GCNENC_SMRD:
3841            if (curArchMask & ARCH_GCN_1_2_4)
3842                good = GCNAsmUtils::parseSMEMEncoding(assembler, *it, mnemPlace, linePtr,
3843                               curArchMask, output, regs, gcnEncSize);
3844            else
3845                good = GCNAsmUtils::parseSMRDEncoding(assembler, *it, mnemPlace, linePtr,
3846                               curArchMask, output, regs, gcnEncSize);
3847            break;
3848        case GCNENC_VOPC:
3849            good = GCNAsmUtils::parseVOPCEncoding(assembler, *it, mnemPlace, linePtr,
3850                           curArchMask, output, regs, gcnEncSize, vopEnc);
3851            break;
3852        case GCNENC_VOP1:
3853            good = GCNAsmUtils::parseVOP1Encoding(assembler, *it, mnemPlace, linePtr,
3854                                   curArchMask, output, regs, gcnEncSize, vopEnc);
3855            break;
3856        case GCNENC_VOP2:
3857            good = GCNAsmUtils::parseVOP2Encoding(assembler, *it, mnemPlace, linePtr,
3858                                   curArchMask, output, regs, gcnEncSize, vopEnc);
3859            break;
3860        case GCNENC_VOP3A:
3861        case GCNENC_VOP3B:
3862            good = GCNAsmUtils::parseVOP3Encoding(assembler, *it, mnemPlace, linePtr,
3863                                   curArchMask, output, regs, gcnEncSize, vopEnc);
3864            break;
3865        case GCNENC_VINTRP:
3866            good = GCNAsmUtils::parseVINTRPEncoding(assembler, *it, mnemPlace, linePtr,
3867                           curArchMask, output, regs, gcnEncSize, vopEnc);
3868            break;
3869        case GCNENC_DS:
3870            good = GCNAsmUtils::parseDSEncoding(assembler, *it, mnemPlace, linePtr,
3871                           curArchMask, output, regs, gcnEncSize);
3872            break;
3873        case GCNENC_MUBUF:
3874        case GCNENC_MTBUF:
3875            good = GCNAsmUtils::parseMUBUFEncoding(assembler, *it, mnemPlace, linePtr,
3876                           curArchMask, output, regs, gcnEncSize);
3877            break;
3878        case GCNENC_MIMG:
3879            good = GCNAsmUtils::parseMIMGEncoding(assembler, *it, mnemPlace, linePtr,
3880                           curArchMask, output, regs, gcnEncSize);
3881            break;
3882        case GCNENC_EXP:
3883            good = GCNAsmUtils::parseEXPEncoding(assembler, *it, mnemPlace, linePtr,
3884                           curArchMask, output, regs, gcnEncSize);
3885            break;
3886        case GCNENC_FLAT:
3887            good = GCNAsmUtils::parseFLATEncoding(assembler, *it, mnemPlace, linePtr,
3888                           curArchMask, output, regs, gcnEncSize);
3889            break;
3890        default:
3891            break;
3892    }
3893    // register RegVarUsage in tests, do not apply normal usage
3894    if (good && (assembler.getFlags() & ASM_TESTRUN) != 0)
3895        flushInstrRVUs(usageHandler);
3896}
3897
3898#define GCN_FAIL_BY_ERROR(PLACE, STRING) \
3899    { \
3900        printError(PLACE, STRING); \
3901        return false; \
3902    }
3903
3904// method to resolve expressions in code (in instruction in instruction field)
3905bool GCNAssembler::resolveCode(const AsmSourcePos& sourcePos, cxuint targetSectionId,
3906             cxbyte* sectionData, size_t offset, AsmExprTargetType targetType,
3907             cxuint sectionId, uint64_t value)
3908{
3909    switch(targetType)
3910    {
3911        case GCNTGT_LITIMM:
3912            // literal in instruction
3913            if (sectionId != ASMSECT_ABS)
3914                GCN_FAIL_BY_ERROR(sourcePos,
3915                        "Relative value is illegal in literal expressions")
3916            SULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4), value);
3917            printWarningForRange(32, value, sourcePos);
3918            return true;
3919        case GCNTGT_SOPKSIMM16:
3920            if (sectionId != ASMSECT_ABS)
3921                GCN_FAIL_BY_ERROR(sourcePos,
3922                        "Relative value is illegal in immediate expressions")
3923            SULEV(*reinterpret_cast<uint16_t*>(sectionData+offset), value);
3924            printWarningForRange(16, value, sourcePos);
3925            return true;
3926        case GCNTGT_SOPJMP:
3927        {
3928            if (sectionId != targetSectionId)
3929                // if jump outside current section (.text)
3930                GCN_FAIL_BY_ERROR(sourcePos, "Jump over current section!")
3931            int64_t outOffset = (int64_t(value)-int64_t(offset)-4);
3932            if (outOffset & 3)
3933                GCN_FAIL_BY_ERROR(sourcePos, "Jump is not aligned to word!")
3934            outOffset >>= 2;
3935            if (outOffset > INT16_MAX || outOffset < INT16_MIN)
3936                GCN_FAIL_BY_ERROR(sourcePos, "Jump out of range!")
3937            SULEV(*reinterpret_cast<uint16_t*>(sectionData+offset), outOffset);
3938            uint16_t insnCode = ULEV(*reinterpret_cast<uint16_t*>(sectionData+offset+2));
3939            // add codeflow entry
3940            addCodeFlowEntry(sectionId, { size_t(offset), size_t(value),
3941                    insnCode==0xbf82U ? AsmCodeFlowType::JUMP :
3942                    // CALL from S_CALL_B64
3943                    (((insnCode&0xff80)==0xba80 &&
3944                            (curArchMask&ARCH_RXVEGA)!=0) ? AsmCodeFlowType::CALL :
3945                                AsmCodeFlowType::CJUMP) });
3946            return true;
3947        }
3948        case GCNTGT_SMRDOFFSET:
3949            if (sectionId != ASMSECT_ABS)
3950                GCN_FAIL_BY_ERROR(sourcePos,
3951                            "Relative value is illegal in offset expressions")
3952            sectionData[offset] = value;
3953            printWarningForRange(8, value, sourcePos, WS_UNSIGNED);
3954            return true;
3955        case GCNTGT_DSOFFSET16:
3956            if (sectionId != ASMSECT_ABS)
3957                GCN_FAIL_BY_ERROR(sourcePos,
3958                            "Relative value is illegal in offset expressions")
3959            SULEV(*reinterpret_cast<uint16_t*>(sectionData+offset), value);
3960            printWarningForRange(16, value, sourcePos, WS_UNSIGNED);
3961            return true;
3962        case GCNTGT_DSOFFSET8_0:
3963        case GCNTGT_DSOFFSET8_1:
3964        case GCNTGT_SOPCIMM8:
3965            if (sectionId != ASMSECT_ABS)
3966                GCN_FAIL_BY_ERROR(sourcePos, (targetType != GCNTGT_SOPCIMM8) ?
3967                        "Relative value is illegal in offset expressions" :
3968                        "Relative value is illegal in immediate expressions")
3969            if (targetType==GCNTGT_DSOFFSET8_0)
3970                sectionData[offset] = value;
3971            else
3972                sectionData[offset+1] = value;
3973            printWarningForRange(8, value, sourcePos, WS_UNSIGNED);
3974            return true;
3975        case GCNTGT_MXBUFOFFSET:
3976            if (sectionId != ASMSECT_ABS)
3977                GCN_FAIL_BY_ERROR(sourcePos,
3978                            "Relative value is illegal in offset expressions")
3979            sectionData[offset] = value&0xff;
3980            sectionData[offset+1] = (sectionData[offset+1]&0xf0) | ((value>>8)&0xf);
3981            printWarningForRange(12, value, sourcePos, WS_UNSIGNED);
3982            return true;
3983        case GCNTGT_SMEMOFFSET:
3984        case GCNTGT_SMEMOFFSETVEGA:
3985            if (sectionId != ASMSECT_ABS)
3986                GCN_FAIL_BY_ERROR(sourcePos,
3987                            "Relative value is illegal in offset expressions")
3988            if (targetType==GCNTGT_SMEMOFFSETVEGA)
3989            {
3990                uint32_t oldV = ULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4));
3991                SULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4),
3992                            (oldV & 0xffe00000U) | (value&0x1fffffU));
3993            }
3994            else
3995                SULEV(*reinterpret_cast<uint32_t*>(sectionData+offset+4), value&0xfffffU);
3996            printWarningForRange(targetType==GCNTGT_SMEMOFFSETVEGA ? 21 : 20,
3997                            value, sourcePos,
3998                            targetType==GCNTGT_SMEMOFFSETVEGA ? WS_BOTH : WS_UNSIGNED);
3999            return true;
4000        case GCNTGT_SMEMIMM:
4001            if (sectionId != ASMSECT_ABS)
4002                GCN_FAIL_BY_ERROR(sourcePos,
4003                        "Relative value is illegal in immediate expressions")
4004            sectionData[offset] = (sectionData[offset]&0x3f) | ((value<<6)&0xff);
4005            sectionData[offset+1] = (sectionData[offset+1]&0xe0) | ((value>>2)&0x1f);
4006            printWarningForRange(7, value, sourcePos, WS_UNSIGNED);
4007            return true;
4008        case GCNTGT_INSTOFFSET:
4009            // FLAT unsigned inst_offset
4010            if (sectionId != ASMSECT_ABS)
4011                GCN_FAIL_BY_ERROR(sourcePos,
4012                        "Relative value is illegal in offset expressions")
4013            sectionData[offset] = value;
4014            sectionData[offset+1] = (sectionData[offset+1]&0xf0) | ((value&0xf00)>>8);
4015            printWarningForRange(12, value, sourcePos, WS_UNSIGNED);
4016            return true;
4017        case GCNTGT_INSTOFFSET_S:
4018            // FLAT signed 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]&0xe0) |
4024                    ((value&0x1f00)>>8);
4025            printWarningForRange(13, value, sourcePos, WS_BOTH);
4026            return true;
4027        default:
4028            return false;
4029    }
4030}
4031
4032// check whether name is mnemonic (currently unused anywhere)
4033bool GCNAssembler::checkMnemonic(const CString& inMnemonic) const
4034{
4035    CString mnemonic;
4036    size_t inMnemLen = inMnemonic.size();
4037    // checking for encoding suffixes
4038    if (inMnemLen>4 &&
4039        (::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e64")==0 ||
4040            ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_e32")==0))
4041        mnemonic = inMnemonic.substr(0, inMnemLen-4);
4042    else if (inMnemLen>6 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
4043        ::strcasecmp(inMnemonic.c_str()+inMnemLen-4, "_dpp")==0)
4044        mnemonic = inMnemonic.substr(0, inMnemLen-4);
4045    else if (inMnemLen>7 && toLower(inMnemonic[0])=='v' && inMnemonic[1]=='_' &&
4046        ::strcasecmp(inMnemonic.c_str()+inMnemLen-5, "_sdwa")==0)
4047        mnemonic = inMnemonic.substr(0, inMnemLen-5);
4048    else
4049        mnemonic = inMnemonic;
4050   
4051    return std::binary_search(gcnInstrSortedTable.begin(), gcnInstrSortedTable.end(),
4052               GCNAsmInstruction{mnemonic.c_str()},
4053               [](const GCNAsmInstruction& instr1, const GCNAsmInstruction& instr2)
4054               { return ::strcmp(instr1.mnemonic, instr2.mnemonic)<0; });
4055}
4056
4057void GCNAssembler::setAllocatedRegisters(const cxuint* inRegs, Flags inRegFlags)
4058{
4059    if (inRegs==nullptr)
4060        regs.sgprsNum = regs.vgprsNum = 0;
4061    else // if not null, just copy
4062        std::copy(inRegs, inRegs+2, regTable);
4063    regs.regFlags = inRegFlags;
4064}
4065
4066const cxuint* GCNAssembler::getAllocatedRegisters(size_t& regTypesNum,
4067              Flags& outRegFlags) const
4068{
4069    regTypesNum = 2;
4070    outRegFlags = regs.regFlags;
4071    return regTable;
4072}
4073
4074void GCNAssembler::getMaxRegistersNum(size_t& regTypesNum, cxuint* maxRegs) const
4075{
4076    maxRegs[0] = getGPUMaxRegsNumByArchMask(curArchMask, 0);
4077    maxRegs[1] = getGPUMaxRegsNumByArchMask(curArchMask, 1);
4078    regTypesNum = 2;
4079}
4080
4081void GCNAssembler::getRegisterRanges(size_t& regTypesNum, cxuint* regRanges) const
4082{
4083    regRanges[0] = 0;
4084    regRanges[1] = getGPUMaxRegsNumByArchMask(curArchMask, 0);
4085    regRanges[2] = 256; // vgpr
4086    regRanges[3] = 256+getGPUMaxRegsNumByArchMask(curArchMask, 1);
4087    regTypesNum = 2;
4088}
4089
4090// method that filling code to alignment (used by alignment pseudo-ops on code section)
4091void GCNAssembler::fillAlignment(size_t size, cxbyte* output)
4092{
4093    uint32_t value = LEV(0xbf800000U); // fill with s_nop's
4094    if ((size&3)!=0)
4095    {
4096        // first, we fill zeros
4097        const size_t toAlign4 = 4-(size&3);
4098        ::memset(output, 0, toAlign4);
4099        output += toAlign4;
4100    }
4101    std::fill((uint32_t*)output, ((uint32_t*)output) + (size>>2), value);
4102}
4103
4104bool GCNAssembler::parseRegisterRange(const char*& linePtr, cxuint& regStart,
4105          cxuint& regEnd, const AsmRegVar*& regVar)
4106{
4107    GCNOperand operand;
4108    regVar = nullptr;
4109    if (!GCNAsmUtils::parseOperand(assembler, linePtr, operand, nullptr, curArchMask, 0,
4110                INSTROP_SREGS|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_UNALIGNED,
4111                ASMFIELD_NONE))
4112        return false;
4113    regStart = operand.range.start;
4114    regEnd = operand.range.end;
4115    regVar = operand.range.regVar;
4116    return true;
4117}
4118
4119bool GCNAssembler::relocationIsFit(cxuint bits, AsmExprTargetType tgtType)
4120{
4121    if (bits==32)
4122        return tgtType==GCNTGT_SOPJMP || tgtType==GCNTGT_LITIMM;
4123    return false;
4124}
4125
4126bool GCNAssembler::parseRegisterType(const char*& linePtr, const char* end, cxuint& type)
4127{
4128    skipSpacesToEnd(linePtr, end);
4129    if (linePtr!=end)
4130    {
4131        const char c = toLower(*linePtr);
4132        if (c=='v' || c=='s')
4133        {
4134            type = c=='v' ? REGTYPE_VGPR : REGTYPE_SGPR;
4135            linePtr++;
4136            return true;
4137        }
4138        return false;
4139    }
4140    return false;
4141}
4142
4143static const bool gcnSize11Table[16] =
4144{
4145    false, // GCNENC_SMRD, // 0000
4146    false, // GCNENC_SMRD, // 0001
4147    false, // GCNENC_VINTRP, // 0010
4148    false, // GCNENC_NONE, // 0011 - illegal
4149    true,  // GCNENC_VOP3A, // 0100
4150    false, // GCNENC_NONE, // 0101 - illegal
4151    true,  // GCNENC_DS,   // 0110
4152    true,  // GCNENC_FLAT, // 0111
4153    true,  // GCNENC_MUBUF, // 1000
4154    false, // GCNENC_NONE,  // 1001 - illegal
4155    true,  // GCNENC_MTBUF, // 1010
4156    false, // GCNENC_NONE,  // 1011 - illegal
4157    true,  // GCNENC_MIMG,  // 1100
4158    false, // GCNENC_NONE,  // 1101 - illegal
4159    true,  // GCNENC_EXP,   // 1110
4160    false // GCNENC_NONE   // 1111 - illegal
4161};
4162
4163static const bool gcnSize12Table[16] =
4164{
4165    true,  // GCNENC_SMEM, // 0000
4166    true,  // GCNENC_EXP, // 0001
4167    false, // GCNENC_NONE, // 0010 - illegal
4168    false, // GCNENC_NONE, // 0011 - illegal
4169    true,  // GCNENC_VOP3A, // 0100
4170    false, // GCNENC_VINTRP, // 0101
4171    true,  // GCNENC_DS,   // 0110
4172    true,  // GCNENC_FLAT, // 0111
4173    true,  // GCNENC_MUBUF, // 1000
4174    false, // GCNENC_NONE,  // 1001 - illegal
4175    true,  // GCNENC_MTBUF, // 1010
4176    false, // GCNENC_NONE,  // 1011 - illegal
4177    true,  // GCNENC_MIMG,  // 1100
4178    false, // GCNENC_NONE,  // 1101 - illegal
4179    false, // GCNENC_NONE,  // 1110 - illegal
4180    false // GCNENC_NONE   // 1111 - illegal
4181};
4182
4183// get instruction size, used by register allocation to skip instruction
4184size_t GCNAssembler::getInstructionSize(size_t codeSize, const cxbyte* code) const
4185{
4186    if (codeSize < 4)
4187        return 0; // no instruction
4188    bool isGCN11 = (curArchMask & ARCH_RX2X0)!=0;
4189    bool isGCN12 = (curArchMask & ARCH_GCN_1_2_4)!=0;
4190    const uint32_t insnCode = ULEV(*reinterpret_cast<const uint32_t*>(code));
4191    uint32_t words = 1;
4192    if ((insnCode & 0x80000000U) != 0)
4193    {
4194        if ((insnCode & 0x40000000U) == 0)
4195        {
4196            // SOP???
4197            if  ((insnCode & 0x30000000U) == 0x30000000U)
4198            {
4199                // SOP1/SOPK/SOPC/SOPP
4200                const uint32_t encPart = (insnCode & 0x0f800000U);
4201                if (encPart == 0x0e800000U)
4202                {
4203                    // SOP1
4204                    if ((insnCode&0xff) == 0xff) // literal
4205                        words++;
4206                }
4207                else if (encPart == 0x0f000000U)
4208                {
4209                    // SOPC
4210                    if ((insnCode&0xff) == 0xff ||
4211                        (insnCode&0xff00) == 0xff00) // literal
4212                        words++;
4213                }
4214                else if (encPart != 0x0f800000U)
4215                {
4216                    // SOPK
4217                    const cxuint opcode = (insnCode>>23)&0x1f;
4218                    if ((!isGCN12 && opcode == 21) ||
4219                        (isGCN12 && opcode == 20))
4220                        words++; // additional literal
4221                }
4222            }
4223            else
4224            {
4225                // SOP2
4226                if ((insnCode&0xff) == 0xff || (insnCode&0xff00) == 0xff00)
4227                    words++;  // literal
4228            }
4229        }
4230        else
4231        {
4232            // SMRD and others
4233            const uint32_t encPart = (insnCode&0x3c000000U)>>26;
4234            if ((!isGCN12 && gcnSize11Table[encPart] && (encPart != 7 || isGCN11)) ||
4235                (isGCN12 && gcnSize12Table[encPart]))
4236                words++;
4237        }
4238    }
4239    else
4240    {
4241        // some vector instructions
4242        if ((insnCode & 0x7e000000U) == 0x7c000000U)
4243        {
4244            // VOPC
4245            if ((insnCode&0x1ff) == 0xff || // literal
4246                // SDWA, DDP
4247                (isGCN12 && ((insnCode&0x1ff) == 0xf9 || (insnCode&0x1ff) == 0xfa)))
4248                words++;
4249        }
4250        else if ((insnCode & 0x7e000000U) == 0x7e000000U)
4251        {
4252            // VOP1
4253            if ((insnCode&0x1ff) == 0xff || // literal
4254                // SDWA, DDP
4255                (isGCN12 && ((insnCode&0x1ff) == 0xf9 || (insnCode&0x1ff) == 0xfa)))
4256                words++;
4257        }
4258        else
4259        {
4260            // VOP2
4261            const cxuint opcode = (insnCode >> 25)&0x3f;
4262            if ((!isGCN12 && (opcode == 32 || opcode == 33)) ||
4263                (isGCN12 && (opcode == 23 || opcode == 24 ||
4264                opcode == 36 || opcode == 37))) // V_MADMK and V_MADAK
4265                words++;  // inline 32-bit constant
4266            else if ((insnCode&0x1ff) == 0xff || // literal
4267                // SDWA, DDP
4268                (isGCN12 && ((insnCode&0x1ff) == 0xf9 || (insnCode&0x1ff) == 0xfa)))
4269                words++;  // literal
4270        }
4271    }
4272    return words<<2;
4273}
Note: See TracBrowser for help on using the repository browser.