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

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

CLRadeonExtender: GCNAsm: Add parametrization to modifiers in SMEM encoding.

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