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

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

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

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