source: CLRX/CLRadeonExtender/trunk/amdasm/GCNAsmEncode1.cpp @ 4810

Last change on this file since 4810 was 4810, checked in by matszpk, 2 months ago

CLRadeonExtender: GCNAsm: Add VOP DPP8 encoding and first testcase for it.

File size: 106.1 KB
Line 
1/*
2 *  CLRadeonExtender - Unofficial OpenCL Radeon Extensions Library
3 *  Copyright (C) 2014-2018 Mateusz Szpakowski
4 *
5 *  This library is free software; you can redistribute it and/or
6 *  modify it under the terms of the GNU Lesser General Public
7 *  License as published by the Free Software Foundation; either
8 *  version 2.1 of the License, or (at your option) any later version.
9 *
10 *  This library is distributed in the hope that it will be useful,
11 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 *  Lesser General Public License for more details.
14 *
15 *  You should have received a copy of the GNU Lesser General Public
16 *  License along with this library; if not, write to the Free Software
17 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 */
19
20#include <CLRX/Config.h>
21#include <cstdio>
22#include <vector>
23#include <memory>
24#include <cstring>
25#include <algorithm>
26#include <CLRX/amdasm/Assembler.h>
27#include <CLRX/utils/Utilities.h>
28#include <CLRX/utils/GPUId.h>
29#include <CLRX/amdasm/GCNDefs.h>
30#include "GCNAsmInternals.h"
31
32namespace CLRX
33{
34
35static const uint32_t constImmFloatLiterals[9] = 
36{
37    0x3f000000, 0xbf000000, 0x3f800000, 0xbf800000,
38    0x40000000, 0xc0000000, 0x40800000, 0xc0800000, 0x3e22f983
39};
40
41// used while converting 32-bit SOPx encoding to 64-bit SOPx encoding
42static void tryPromoteConstImmToLiteral(GCNOperand& src0Op, GPUArchMask arch)
43{
44    if (!src0Op.range.isRegVar() && src0Op.range.start>=128 && src0Op.range.start<=208)
45    {
46        // convert integer const immediates
47        src0Op.value = src0Op.range.start<193? src0Op.range.start-128 :
48                192-src0Op.range.start;
49        src0Op.range.start = 255;
50    }
51    else if (!src0Op.range.isRegVar() &&
52            ((src0Op.range.start>=240 && src0Op.range.start<248) ||
53             ((arch&ARCH_GCN_1_2_4_5)!=0 && src0Op.range.start==248)))
54    {
55        // floating point immediates to literal
56        src0Op.value = constImmFloatLiterals[src0Op.range.start-240];
57        src0Op.range.start = 255;
58    }
59}
60
61// check whether reg range can be equal (regvar and registers)
62static inline bool regRangeCanEqual(const RegRange& r1, const RegRange& r2)
63{
64    return r1.regVar==r2.regVar && r1.start==r2.start;
65}
66
67bool GCNAsmUtils::parseSOP2Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
68                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
69                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
70                  GCNEncSize gcnEncSize)
71{
72    bool good = true;
73    RegRange dstReg(0, 0);
74    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
75   
76    if ((gcnInsn.mode & GCN_MASK1) != GCN_DST_NONE)
77    {
78        // parse SDST (SGPR)
79        gcnAsm->setCurrentRVU(0);
80        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
81                   (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
82                   INSTROP_SYMREGRANGE|INSTROP_WRITE);
83        if (!skipRequiredComma(asmr, linePtr))
84            return false;
85    }
86   
87    std::unique_ptr<AsmExpression> src0Expr, src1Expr;
88    // parse SRC0 (can be SGPR or scalar source)
89    GCNOperand src0Op{};
90    gcnAsm->setCurrentRVU(1);
91    good &= parseOperand(asmr, linePtr, src0Op, &src0Expr, arch,
92             (gcnInsn.mode&GCN_REG_SRC0_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
93                         INSTROP_READ, GCNFIELD_SSRC0);
94    if (!skipRequiredComma(asmr, linePtr))
95        return false;
96    GCNOperand src1Op{};
97    // parse SRC1 (can be SGPR or scalar source)
98    gcnAsm->setCurrentRVU(2);
99    good &= parseOperand(asmr, linePtr, src1Op, &src1Expr, arch,
100             (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
101             (src0Op.range.isVal(255) ? INSTROP_ONLYINLINECONSTS : 0)|INSTROP_READ,
102             GCNFIELD_SSRC1);
103   
104    /// if errors
105    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
106        return false;
107   
108    if (gcnEncSize==GCNEncSize::BIT64)
109    {
110        // try to promote constant immediate to literal
111        tryPromoteConstImmToLiteral(src0Op, arch);
112        tryPromoteConstImmToLiteral(src1Op, arch);
113    }
114    // put data
115    cxuint wordsNum = 1;
116    uint32_t words[2];
117    SLEV(words[0], 0x80000000U | (uint32_t(gcnInsn.code1)<<23) | src0Op.range.bstart() |
118            (src1Op.range.bstart()<<8) | uint32_t(dstReg.bstart())<<16);
119    if (src0Op.range.isVal(255) || src1Op.range.isVal(255))
120    {
121        // put literal value
122        if (src0Expr==nullptr && src1Expr==nullptr)
123            SLEV(words[1], src0Op.range.isVal(255) ? src0Op.value : src1Op.value);
124        else    // zero if unresolved value
125            SLEV(words[1], 0);
126        wordsNum++;
127    }
128    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
129        return false;
130    // set expression targets to resolving later
131    if (src0Expr!=nullptr)
132        src0Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
133                      output.size()));
134    else if (src1Expr!=nullptr)
135        src1Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
136                      output.size()));
137   
138    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
139            reinterpret_cast<cxbyte*>(words + wordsNum));
140    // prevent freeing expressions
141    src0Expr.release();
142    src1Expr.release();
143    // update SGPR counting and VCC usage (regflags)
144    if (dstReg && !dstReg.isRegVar())
145    {
146        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
147        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
148    }
149    if (src0Op.range && !src0Op.range.isRegVar())
150        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
151    if (src1Op.range && !src1Op.range.isRegVar())
152        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
153    return true;
154}
155
156bool GCNAsmUtils::parseSOP1Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
157                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
158                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
159                  GCNEncSize gcnEncSize)
160{
161    bool good = true;
162    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
163    RegRange dstReg(0, 0);
164   
165    if ((gcnInsn.mode & GCN_MASK1) != GCN_DST_NONE)
166    {
167        // parse SDST (SGPR)
168        gcnAsm->setCurrentRVU(0);
169        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
170                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
171                       INSTROP_SYMREGRANGE|INSTROP_WRITE);
172        if ((gcnInsn.mode & GCN_MASK1) != GCN_SRC_NONE)
173            if (!skipRequiredComma(asmr, linePtr))
174                return false;
175    }
176   
177    GCNOperand src0Op{};
178    std::unique_ptr<AsmExpression> src0Expr;
179    if ((gcnInsn.mode & GCN_MASK1) != GCN_SRC_NONE)
180    {
181        // parse SRC0 (can be SGPR or source scalar, constant or literal)
182        gcnAsm->setCurrentRVU(1);
183        good &= parseOperand(asmr, linePtr, src0Op, &src0Expr, arch,
184                 (gcnInsn.mode&GCN_REG_SRC0_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
185                         INSTROP_READ, GCNFIELD_SSRC0);
186    }
187   
188    /// if errors
189    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
190        return false;
191   
192    if (gcnEncSize==GCNEncSize::BIT64)
193        // try to promote constant immediate to literal
194        tryPromoteConstImmToLiteral(src0Op, arch);
195    cxuint wordsNum = 1;
196    uint32_t words[2];
197    // put instruction word
198    SLEV(words[0], 0xbe800000U | (uint32_t(gcnInsn.code1)<<8) | src0Op.range.bstart() |
199            uint32_t(dstReg.bstart())<<16);
200    if (src0Op.range.start==255)
201    {
202        // put literal
203        if (src0Expr==nullptr)
204            SLEV(words[1], src0Op.value);
205        else    // zero if unresolved value
206            SLEV(words[1], 0);
207        wordsNum++;
208    }
209    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
210        return false;
211    // set expression targets
212    if (src0Expr!=nullptr)
213        src0Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
214                      output.size()));
215   
216    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
217            reinterpret_cast<cxbyte*>(words + wordsNum));
218    // prevent freeing expressions
219    src0Expr.release();
220    // update SGPR counting and VCC usage (regflags)
221    if (dstReg && !dstReg.isRegVar())
222    {
223        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
224        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
225    }
226    if (src0Op.range && !src0Op.range.isRegVar())
227        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
228    return true;
229}
230
231// hwreg names sorted by names
232static const std::pair<const char*, cxuint> hwregNamesMap[] =
233{
234    { "gpr_alloc", 5 },
235    { "hw_id", 4 },
236    { "ib_dbg0", 12 },
237    { "ib_dbg1", 13 },
238    { "ib_sts", 7 },
239    { "inst_dw0", 10 },
240    { "inst_dw1", 11 },
241    { "lds_alloc", 6 },
242    { "mode", 1 },
243    { "pc_hi", 9 },
244    { "pc_lo", 8 },
245    { "status", 2 },
246    { "trapsts", 3 }
247};
248
249static const size_t hwregNamesMapSize = sizeof(hwregNamesMap) /
250            sizeof(std::pair<const char*, uint16_t>);
251
252// update SGPR counting and VCC usage (regflags) for GCN 1.4 (VEGA)
253static const std::pair<const char*, cxuint> hwregNamesGCN14Map[] =
254{
255    { "flush_ib", 14 },
256    { "gpr_alloc", 5 },
257    { "hw_id", 4 },
258    { "ib_dbg0", 12 },
259    { "ib_dbg1", 13 },
260    { "ib_sts", 7 },
261    { "inst_dw0", 10 },
262    { "inst_dw1", 11 },
263    { "lds_alloc", 6 },
264    { "mode", 1 },
265    { "pc_hi", 9 },
266    { "pc_lo", 8 },
267    { "sh_mem_bases", 15 },
268    { "sq_shader_tba_hi", 17 },
269    { "sq_shader_tba_lo", 16 },
270    { "sq_shader_tma_hi", 19 },
271    { "sq_shader_tma_lo", 18 },
272    { "status", 2 },
273    { "tba_hi", 17 },
274    { "tba_lo", 16 },
275    { "tma_hi", 19 },
276    { "tma_lo", 18 },
277    { "trapsts", 3 }
278};
279
280static const size_t hwregNamesGCN14MapSize = sizeof(hwregNamesGCN14Map) /
281            sizeof(std::pair<const char*, uint16_t>);
282
283// update SGPR counting and VCC usage (regflags) for GCN 1.4 (VEGA)
284static const std::pair<const char*, cxuint> hwregNamesGCN15Map[] =
285{
286    { "flat_scr_hi", 21 },
287    { "flat_scr_lo", 20 },
288    { "flush_ib", 14 },
289    { "gpr_alloc", 5 },
290    { "hw_id", 4 },
291    { "ib_dbg0", 12 },
292    { "ib_dbg1", 13 },
293    { "ib_sts", 7 },
294    { "inst_dw0", 10 },
295    { "inst_dw1", 11 },
296    { "lds_alloc", 6 },
297    { "mode", 1 },
298    { "pc_hi", 9 },
299    { "pc_lo", 8 },
300    { "pops_packer", 23 },
301    { "sh_mem_bases", 15 },
302    { "sq_shader_tba_hi", 17 },
303    { "sq_shader_tba_lo", 16 },
304    { "sq_shader_tma_hi", 19 },
305    { "sq_shader_tma_lo", 18 },
306    { "status", 2 },
307    { "tba_hi", 17 },
308    { "tba_lo", 16 },
309    { "tma_hi", 19 },
310    { "tma_lo", 18 },
311    { "trapsts", 3 },
312    { "xnack_mask", 22 },
313};
314
315static const size_t hwregNamesGCN15MapSize = sizeof(hwregNamesGCN15Map) /
316            sizeof(std::pair<const char*, uint16_t>);
317
318bool GCNAsmUtils::parseSOPKEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
319                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
320                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
321                  GCNEncSize gcnEncSize)
322{
323    const char* end = asmr.line+asmr.lineSize;
324    bool good = true;
325    RegRange dstReg(0, 0);
326    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
327    const bool isGCN14 = (arch & ARCH_GCN_1_4)!=0;
328    const bool isGCN15 = (arch & ARCH_GCN_1_5)!=0;
329   
330    gcnAsm->setCurrentRVU(0);
331    bool doWrite = (gcnInsn.mode&GCN_MASK1) != GCN_DST_SRC &&
332            ((gcnInsn.mode&GCN_MASK1) != GCN_IMM_REL);
333    if ((gcnInsn.mode & GCN_IMM_DST) == 0 && (gcnInsn.mode&GCN_MASK1) != GCN_DST_NONE)
334    {
335        // parse SDST (SGPR)
336        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
337                   (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
338                   INSTROP_SYMREGRANGE|
339                   (doWrite ? INSTROP_WRITE : INSTROP_READ));
340        if (!skipRequiredComma(asmr, linePtr))
341            return false;
342    }
343   
344    uint16_t imm16 = 0;
345    std::unique_ptr<AsmExpression> imm16Expr;
346   
347    if ((gcnInsn.mode&GCN_MASK1) == GCN_IMM_REL)
348    {
349        // parse relative address
350        uint64_t value = 0;
351        if (!getJumpValueArg(asmr, value, imm16Expr, linePtr))
352            return false;
353        if (imm16Expr==nullptr)
354        {
355            // if resolved at this time
356            int64_t offset = (int64_t(value)-int64_t(output.size())-4);
357            if (offset & 3)
358                ASM_NOTGOOD_BY_ERROR(linePtr, "Jump is not aligned to word!")
359            offset >>= 2;
360            if (offset > INT16_MAX || offset < INT16_MIN)
361                ASM_NOTGOOD_BY_ERROR(linePtr, "Jump out of range")
362            imm16 = offset;
363            // add codeflow entry
364            if (good)
365            {
366                asmr.sections[asmr.currentSection].addCodeFlowEntry({ 
367                    size_t(asmr.currentOutPos), size_t(value),
368                    ((isGCN14 && gcnInsn.code1==21) || (isGCN15 && gcnInsn.code1==22)) ?
369                            AsmCodeFlowType::CALL : AsmCodeFlowType::CJUMP });
370            }
371        }
372    }
373    else if ((gcnInsn.mode&GCN_MASK1) == GCN_IMM_SREG)
374    {
375        // parse hwreg: hwreg(HWREG, bitstart, bitsize)
376        skipSpacesToEnd(linePtr, end);
377        char name[20];
378        const char* funcNamePlace = linePtr;
379        if (!getNameArg(asmr, 20, name, linePtr, "function name", true))
380            return false;
381        toLowerString(name);
382        skipSpacesToEnd(linePtr, end);
383        // if not hwreg
384        if (::strcmp(name, "hwreg")!=0 || linePtr==end || *linePtr!='(')
385            ASM_FAIL_BY_ERROR(funcNamePlace, "Expected hwreg function")
386        ++linePtr;
387        skipSpacesToEnd(linePtr, end);
388        cxuint hwregId = 0;
389        if (linePtr == end || *linePtr!='@')
390        {
391            // parse hwreg by name
392            const char* hwregNamePlace = linePtr;
393            // choose hwReg names map
394            const size_t regMapSize = isGCN15 ? hwregNamesGCN15MapSize :
395                        (isGCN14 ? hwregNamesGCN14MapSize : hwregNamesMapSize);
396            const std::pair<const char*, cxuint>* regMap = isGCN15 ? hwregNamesGCN15Map :
397                        (isGCN14 ? hwregNamesGCN14Map : hwregNamesMap);
398            good &= getEnumeration(asmr, linePtr, "HWRegister",
399                        regMapSize, regMap, hwregId, "hwreg_");
400            if (good && (arch & ARCH_GCN_1_2_4_5) == 0 && hwregId == 13)
401                // if ib_dgb1 in not GCN 1.2
402                ASM_NOTGOOD_BY_ERROR(hwregNamePlace, "Unknown HWRegister")
403        }
404        else
405        {
406            // parametrization (if preceded by '@')
407            linePtr++;
408            good &= parseImm(asmr, linePtr, hwregId, nullptr, 6, WS_UNSIGNED);
409        }
410       
411        if (!skipRequiredComma(asmr, linePtr))
412            return false;
413        uint64_t arg2Value = 0;
414        skipSpacesToEnd(linePtr, end);
415        const char* funcArg2Place = linePtr;
416       
417        // second argument of hwreg
418        if (getAbsoluteValueArg(asmr, arg2Value, linePtr, true))
419        {
420            if (arg2Value >= 32)
421                asmr.printWarning(funcArg2Place, "Second argument out of range (0-31)");
422        }
423        else
424            good = false;
425       
426        if (!skipRequiredComma(asmr, linePtr))
427            return false;
428        uint64_t arg3Value = 0;
429        skipSpacesToEnd(linePtr, end);
430        const char* funcArg3Place = linePtr;
431       
432        // second argument of hwreg
433        if (getAbsoluteValueArg(asmr, arg3Value, linePtr, true))
434        {
435            if (arg3Value >= 33 || arg3Value < 1)
436                asmr.printWarning(funcArg3Place, "Third argument out of range (1-32)");
437        }
438        else
439            good = false;
440       
441        skipSpacesToEnd(linePtr, end);
442        if (linePtr==end || *linePtr!=')')
443            ASM_FAIL_BY_ERROR(linePtr, "Unterminated hwreg function")
444        ++linePtr;
445        imm16 = hwregId | (arg2Value<<6) | ((arg3Value-1)<<11);
446    }
447    else // otherwise we parse expression
448        good &= parseImm(asmr, linePtr, imm16, &imm16Expr);
449   
450    uint32_t imm32 = 0;
451    std::unique_ptr<AsmExpression> imm32Expr;
452    if ((gcnInsn.mode & GCN_IMM_DST) != 0 && (gcnInsn.mode&GCN_MASK1) != GCN_DST_NONE)
453    {
454        // parse SDST as immediate or next source
455        if (!skipRequiredComma(asmr, linePtr))
456            return false;
457        if (gcnInsn.mode & GCN_SOPK_CONST)
458            good &= parseImm(asmr, linePtr, imm32, &imm32Expr);
459        else
460            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
461                   (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SDST, true,
462                   INSTROP_SYMREGRANGE|INSTROP_READ); // new field!
463    }
464   
465    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
466        return false;
467   
468    const cxuint wordsNum = (gcnInsn.mode & GCN_SOPK_CONST) ? 2 : 1;
469    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
470        return false;
471   
472    // put data (instruction words)
473    uint32_t words[2];
474    SLEV(words[0], 0xb0000000U | imm16 | (uint32_t(dstReg.bstart())<<16) |
475                uint32_t(gcnInsn.code1)<<23);
476    if (wordsNum==2)
477        SLEV(words[1], imm32);
478   
479    // setting expresion targets (for immediates)
480    if (imm32Expr!=nullptr)
481        imm32Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
482                           output.size()));
483    if (imm16Expr!=nullptr)
484        imm16Expr->setTarget(AsmExprTarget(((gcnInsn.mode&GCN_MASK1) == GCN_IMM_REL) ?
485                GCNTGT_SOPJMP : GCNTGT_SOPKSIMM16, asmr.currentSection, output.size()));
486   
487    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
488            reinterpret_cast<cxbyte*>(words + wordsNum));
489    /// prevent freeing expression
490    imm32Expr.release();
491    imm16Expr.release();
492    // update SGPR counting and VCC usage (regflags)
493    if (dstReg && !dstReg.isRegVar() && doWrite && (gcnInsn.mode & GCN_IMM_DST)==0)
494        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
495    if (dstReg && !dstReg.isRegVar())
496        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
497    return true;
498}
499
500bool GCNAsmUtils::parseSOPCEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
501                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
502                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
503                  GCNEncSize gcnEncSize)
504{
505    bool good = true;
506    std::unique_ptr<AsmExpression> src0Expr, src1Expr;
507    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
508    GCNOperand src0Op{};
509   
510    // parse SRC0 (can be SGPR, source scalar, literal or constant
511    gcnAsm->setCurrentRVU(0);
512    good &= parseOperand(asmr, linePtr, src0Op, &src0Expr, arch,
513             (gcnInsn.mode&GCN_REG_SRC0_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
514                     INSTROP_READ, GCNFIELD_SSRC0);
515    if (!skipRequiredComma(asmr, linePtr))
516        return false;
517    GCNOperand src1Op{};
518    if ((gcnInsn.mode & GCN_SRC1_IMM) == 0)
519    {
520        // parse SRC1 (can be SGPR, source scalar, literal or constant
521        gcnAsm->setCurrentRVU(1);
522        good &= parseOperand(asmr, linePtr, src1Op, &src1Expr, arch,
523                 (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, INSTROP_SSOURCE|INSTROP_SREGS|
524                 (src0Op.range.start==255 ? INSTROP_ONLYINLINECONSTS : 0)|INSTROP_READ,
525                         GCNFIELD_SSRC1);
526    }
527    else // immediate
528        good &= parseImm(asmr, linePtr, src1Op.range.start, &src1Expr, 8);
529   
530    /// if errors
531    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
532        return false;
533   
534    if (gcnEncSize==GCNEncSize::BIT64)
535    {
536        // try to promote constant immediate to literal
537        tryPromoteConstImmToLiteral(src0Op, arch);
538        tryPromoteConstImmToLiteral(src1Op, arch);
539    }
540    // put data
541    cxuint wordsNum = 1;
542    uint32_t words[2];
543    SLEV(words[0], 0xbf000000U | (uint32_t(gcnInsn.code1)<<16) | src0Op.range.bstart() |
544            (src1Op.range.bstart()<<8));
545    if (src0Op.range.start==255 ||
546        ((gcnInsn.mode & GCN_SRC1_IMM)==0 && src1Op.range.start==255))
547    {
548        // put literal
549        if (src0Expr==nullptr && src1Expr==nullptr)
550            SLEV(words[1], src0Op.range.isVal(255) ? src0Op.value : src1Op.value);
551        else    // zero if unresolved value
552            SLEV(words[1], 0);
553        wordsNum++;
554    }
555    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
556        return false;
557    // set expression targets
558    if (src0Expr!=nullptr)
559        src0Expr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
560                      output.size()));
561    else if (src1Expr!=nullptr)
562        src1Expr->setTarget(AsmExprTarget(
563            ((gcnInsn.mode&GCN_SRC1_IMM)) ? GCNTGT_SOPCIMM8 : GCNTGT_LITIMM,
564            asmr.currentSection, output.size()));
565   
566    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
567            reinterpret_cast<cxbyte*>(words + wordsNum));
568    // prevent freeing expressions
569    src0Expr.release();
570    src1Expr.release();
571    // update SGPR counting and VCC usage (regflags)
572    if (src0Op.range && !src0Op.range.isRegVar())
573        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
574    if (src1Op.range && !src1Op.range.isRegVar())
575        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
576    return true;
577}
578
579// message names sorted by name
580static const std::pair<const char*, uint16_t> sendMessageNamesMap[] =
581{
582    { "gs", 2 },
583    { "gs_done", 3 },
584    { "interrupt", 1 },
585    { "savewave", 4 },
586    { "sysmsg", 15 },
587    { "system", 15 }
588};
589
590static const size_t sendMessageNamesMapSize = sizeof(sendMessageNamesMap) /
591            sizeof(std::pair<const char*, uint16_t>);
592
593// message names sorted by name for GCN1.4 (VEGA)
594static const std::pair<const char*, uint16_t> sendMessageNamesGCN14Map[] =
595{
596    { "early_prim_dealloc", 8 },
597    { "get_doorbell", 10 },
598    { "gs", 2 },
599    { "gs_alloc_req", 9 },
600    { "gs_done", 3 },
601    { "halt_waves", 6 },
602    { "interrupt", 1 },
603    { "ordered_ps_done", 7 },
604    { "savewave", 4 },
605    { "stall_wave_gen", 5 },
606    { "sysmsg", 15 },
607    { "system", 15 }
608};
609
610static const size_t sendMessageNamesGCN14MapSize = sizeof(sendMessageNamesGCN14Map) /
611            sizeof(std::pair<const char*, uint16_t>);
612
613static const char* sendMsgGSOPTable[] =
614{ "nop", "cut", "emit", "emit_cut" };
615
616static const size_t sendMsgGSOPTableSize = sizeof(sendMsgGSOPTable) / sizeof(const char*);
617
618bool GCNAsmUtils::parseSOPPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
619                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
620                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
621                  GCNEncSize gcnEncSize)
622{
623    const char* end = asmr.line+asmr.lineSize;
624    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
625    bool good = true;
626    const bool isGCN14 = (arch & ARCH_GCN_1_4)!=0;
627    const bool isGCN15 = (arch & ARCH_GCN_1_5)!=0;
628    if (gcnEncSize==GCNEncSize::BIT64)
629        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for SOPP encoding")
630   
631    uint16_t imm16 = 0;
632    std::unique_ptr<AsmExpression> imm16Expr;
633    switch (gcnInsn.mode&GCN_MASK1)
634    {
635        case GCN_IMM_REL:
636        {
637            // parse relative address
638            uint64_t value = 0;
639            if (!getJumpValueArg(asmr, value, imm16Expr, linePtr))
640                return false;
641            if (imm16Expr==nullptr)
642            {
643                int64_t offset = (int64_t(value)-int64_t(output.size())-4);
644                if (offset & 3)
645                    ASM_NOTGOOD_BY_ERROR(linePtr, "Jump is not aligned to word!")
646                offset >>= 2;
647                if (offset > INT16_MAX || offset < INT16_MIN)
648                    ASM_NOTGOOD_BY_ERROR(linePtr, "Jump out of range")
649                imm16 = offset;
650                Assembler& asmr = gcnAsm->assembler;
651                // add codeflow entry
652                if (good)
653                    asmr.sections[asmr.currentSection].addCodeFlowEntry({ 
654                        size_t(asmr.currentOutPos), size_t(value),
655                        gcnInsn.code1==2 ? AsmCodeFlowType::JUMP :
656                            AsmCodeFlowType::CJUMP });
657            }
658            break;
659        }
660        case GCN_IMM_LOCKS:
661        {
662            /* parse locks for s_waitcnt */
663            char name[20];
664            bool haveLgkmCnt = false;
665            bool haveExpCnt = false;
666            bool haveVMCnt = false;
667            imm16 = isGCN15 ? 0xff7f : (isGCN14 ? 0xcf7f : 0xf7f);
668            while (true)
669            {
670                skipSpacesToEnd(linePtr, end);
671                const char* funcNamePlace = linePtr;
672                name[0] = 0;
673                // get function name
674                good &= getNameArgS(asmr, 20, name, linePtr, "function name", true);
675                toLowerString(name);
676               
677                cxuint bitPos = 0, bitMask = UINT_MAX;
678                bool goodCnt = true;
679                bool doVMCnt = false;
680                // select bitfield for lock
681                if (::strcmp(name, "vmcnt")==0)
682                {
683                    if (haveVMCnt)
684                        asmr.printWarning(funcNamePlace, "vmcnt was already defined");
685                    bitPos = 0;
686                    bitMask = (isGCN14 || isGCN15) ? 63 : 15;
687                    doVMCnt = haveVMCnt = true;
688                }
689                else if (::strcmp(name, "lgkmcnt")==0)
690                {
691                    if (haveLgkmCnt)
692                        asmr.printWarning(funcNamePlace, "lgkmcnt was already defined");
693                    bitPos = 8;
694                    bitMask = isGCN15 ? 63 : 15;
695                    haveLgkmCnt = true;
696                }
697                else if (::strcmp(name, "expcnt")==0)
698                {
699                    if (haveExpCnt)
700                        asmr.printWarning(funcNamePlace, "expcnt was already defined");
701                    bitPos = 4;
702                    bitMask = 7;
703                    haveExpCnt = true;
704                }
705                else
706                    ASM_NOTGOOD_BY_ERROR1(goodCnt = good, funcNamePlace,
707                                    "Expected vmcnt, lgkmcnt or expcnt")
708               
709                skipSpacesToEnd(linePtr, end);
710                if (linePtr==end || *linePtr!='(')
711                {
712                    if (goodCnt) // only if cnt has been parsed (do not duplicate errors)
713                        asmr.printError(funcNamePlace, "Expected vmcnt, lgkmcnt or expcnt");
714                    return false;
715                }
716                skipCharAndSpacesToEnd(linePtr, end);
717                const char* argPlace = linePtr;
718                uint64_t value;
719                // parse value for lock
720                if (getAbsoluteValueArg(asmr, value, linePtr, true))
721                {
722                    if (value > bitMask)
723                        asmr.printWarning(argPlace, "Value out of range");
724                    if ((!isGCN14 && !isGCN15) || !doVMCnt)
725                        imm16 = (imm16 & ~(bitMask<<bitPos)) | ((value&bitMask)<<bitPos);
726                    else // vmcnt for GFX9
727                        imm16 = (imm16 & 0x3ff0) | ((value&15) | ((value&0x30)<<10));
728                }
729                else
730                    good = false;
731                skipSpacesToEnd(linePtr, end);
732                if (linePtr==end || *linePtr!=')')
733                    ASM_FAIL_BY_ERROR(linePtr, "Unterminated function")
734                // ampersand
735                skipCharAndSpacesToEnd(linePtr, end);
736                if (linePtr==end)
737                    break;
738                if (linePtr[0] == '&')
739                    ++linePtr;
740            }
741           
742            if (gcnInsn.code1==12)
743            {
744                // S_WAICTNT
745                uint16_t lgkmCnt = (imm16>>8) & 15;
746                if ((arch & ARCH_HD7X00) != 0)
747                    lgkmCnt = std::min(uint16_t(7), lgkmCnt);
748                const uint16_t expCnt = (imm16>>4) & 7;
749                const uint16_t vmCnt = ((imm16) & 15) + (isGCN14 ? ((imm16>>10)&0x30) : 0);
750                gcnAsm->waitInstr = { output.size(), { vmCnt, lgkmCnt, expCnt } };
751                gcnAsm->hasWaitInstr = true;
752            }
753            break;
754        }
755        case GCN_IMM_MSGS:
756        {
757            char name[25];
758            const char* funcNamePlace = linePtr;
759            if (!getNameArg(asmr, 25, name, linePtr, "function name", true))
760                return false;
761            toLowerString(name);
762            skipSpacesToEnd(linePtr, end);
763            if (::strcmp(name, "sendmsg")!=0 || linePtr==end || *linePtr!='(')
764                ASM_FAIL_BY_ERROR(funcNamePlace, "Expected sendmsg function")
765            skipCharAndSpacesToEnd(linePtr, end);
766           
767            const char* funcArg1Place = linePtr;
768            size_t sendMessage = 0;
769            if (linePtr == end || *linePtr != '@')
770            {
771                // parse message name
772                if (getNameArg(asmr, 25, name, linePtr, "message name", true))
773                {
774                    toLowerString(name);
775                    const size_t msgNameIndex = (::strncmp(name, "msg_", 4) == 0) ? 4 : 0;
776                    // choose message name table
777                    auto msgMap = (isGCN14 || isGCN15) ? sendMessageNamesGCN14Map :
778                            sendMessageNamesMap;
779                    const size_t msgMapSize = (isGCN14 || isGCN15) ?
780                            sendMessageNamesGCN14MapSize : sendMessageNamesMapSize;
781                    // find message name
782                    size_t index = binaryMapFind(msgMap, msgMap + msgMapSize,
783                            name+msgNameIndex, CStringLess()) - msgMap;
784                    if (index != msgMapSize &&
785                        // save_wave only for >=GCN1.2
786                        (msgMap[index].second!=4 || (arch&ARCH_GCN_1_2_4_5)!=0))
787                        sendMessage = msgMap[index].second;
788                    else
789                        ASM_NOTGOOD_BY_ERROR(funcArg1Place, "Unknown message")
790                }
791                else
792                    good = false;
793            }
794            else
795            {
796                // parametrization
797                linePtr++;
798                good &= parseImm(asmr, linePtr, sendMessage, nullptr, 4, WS_UNSIGNED);
799            }
800           
801            cxuint gsopIndex = 0;
802            cxuint streamId = 0;
803            if (sendMessage == 2 || sendMessage == 3)
804            {
805                if (!skipRequiredComma(asmr, linePtr))
806                    return false;
807                skipSpacesToEnd(linePtr, end);
808               
809                // parse GSOP parameter
810                if (linePtr == end || *linePtr != '@')
811                {
812                    const char* funcArg2Place = linePtr;
813                    if (getNameArg(asmr, 20, name, linePtr, "GSOP", true))
814                    {
815                        toLowerString(name);
816                        // handle gs_op prefix
817                        const size_t gsopNameIndex = (::strncmp(name, "gs_op_", 6) == 0)
818                                    ? 6 : 0;
819                        for (gsopIndex = 0; gsopIndex < 4; gsopIndex++)
820                            if (::strcmp(name+gsopNameIndex,
821                                        sendMsgGSOPTable[gsopIndex])==0)
822                                break;
823                        if (gsopIndex==2 && gsopNameIndex==0)
824                        {
825                            /* 'emit-cut' handling */
826                            if (linePtr+4<=end && ::strncasecmp(linePtr, "-cut", 4)==0 &&
827                                (linePtr==end || (!isAlnum(*linePtr) && *linePtr!='_' &&
828                                *linePtr!='$' && *linePtr!='.')))
829                            {
830                                linePtr+=4;
831                                gsopIndex++;
832                            }
833                        }
834                        if (gsopIndex == sendMsgGSOPTableSize)
835                        {
836                            // not found
837                            gsopIndex = 0;
838                            ASM_NOTGOOD_BY_ERROR(funcArg2Place, "Unknown GSOP")
839                        }
840                    }
841                    else
842                        good = false;
843                }
844                else
845                {
846                    // parametrization
847                    linePtr++;
848                    good &= parseImm(asmr, linePtr, gsopIndex, nullptr, 3, WS_UNSIGNED);
849                }
850               
851                if (gsopIndex!=0)
852                {
853                    if (!skipRequiredComma(asmr, linePtr))
854                        return false;
855                   
856                    uint64_t value;
857                    skipSpacesToEnd(linePtr, end);
858                    const char* func3ArgPlace = linePtr;
859                    // parse STREAMID (third argument of sendmsg)
860                    good &= getAbsoluteValueArg(asmr, value, linePtr, true);
861                    if (value > 3)
862                        asmr.printWarning(func3ArgPlace,
863                                  "StreamId (3rd argument) out of range");
864                    streamId = value&3;
865                }
866            }
867            skipSpacesToEnd(linePtr, end);
868            if (linePtr==end || *linePtr!=')')
869                ASM_FAIL_BY_ERROR(linePtr, "Unterminated sendmsg function")
870            ++linePtr;
871            imm16 = sendMessage | (gsopIndex<<4) | (streamId<<8);
872           
873            gcnAsm->delayedOps[0] = { output.size(), nullptr, uint16_t(0), uint16_t(0),
874                    1, GCNDELOP_SENDMSG, GCNDELOP_NONE, cxbyte(0) };
875            break;
876        }
877        case GCN_IMM_NONE:
878            // if s_endpgm or s_endpgm_saved then add 'end' to code flow entries
879            if (gcnInsn.code1 == 1 || gcnInsn.code1 == 27)
880                asmr.sections[asmr.currentSection].addCodeFlowEntry({ 
881                    size_t(asmr.currentOutPos+4), size_t(0), AsmCodeFlowType::END });
882            break;
883        default:
884            good &= parseImm(asmr, linePtr, imm16, &imm16Expr);
885    }
886    /// if errors
887    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
888        return false;
889   
890    // put data (instruction word)
891    uint32_t word;
892    SLEV(word, 0xbf800000U | imm16 | (uint32_t(gcnInsn.code1)<<16));
893   
894    if (imm16Expr!=nullptr)
895        imm16Expr->setTarget(AsmExprTarget(((gcnInsn.mode&GCN_MASK1) == GCN_IMM_REL) ?
896                GCNTGT_SOPJMP : GCNTGT_SOPKSIMM16, asmr.currentSection, output.size()));
897   
898    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word), 
899            reinterpret_cast<cxbyte*>(&word)+4);
900    /// prevent freeing expression
901    imm16Expr.release();
902    return true;
903}
904
905bool GCNAsmUtils::parseSMRDEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
906                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
907                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
908                  GCNEncSize gcnEncSize)
909{
910    const char* end = asmr.line+asmr.lineSize;
911    bool good = true;
912    if (gcnEncSize==GCNEncSize::BIT64)
913        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for SMRD encoding")
914    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
915   
916    RegRange dstReg(0, 0);
917    RegRange sbaseReg(0, 0);
918    RegRange soffsetReg(0, 0);
919    cxbyte soffsetVal = 0;
920    std::unique_ptr<AsmExpression> soffsetExpr;
921    const GCNInsnMode mode1 = (gcnInsn.mode & GCN_MASK1);
922    if (mode1 == GCN_SMRD_ONLYDST)
923    {
924        // parse SDST (SGPR)
925        gcnAsm->setCurrentRVU(0);
926        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
927                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SMRD_SDST, true,
928                       INSTROP_SYMREGRANGE|INSTROP_WRITE);
929    }
930    else if (mode1 != GCN_ARG_NONE)
931    {
932        const cxuint dregsNum = 1<<((gcnInsn.mode & GCN_DSIZE_MASK)>>GCN_SHIFT2);
933        // parse SDST (SGPR's (1-16 registers))
934        gcnAsm->setCurrentRVU(0);
935        good &= parseSRegRange(asmr, linePtr, dstReg, arch, dregsNum,
936                   GCNFIELD_SMRD_SDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE);
937        if (!skipRequiredComma(asmr, linePtr))
938            return false;
939       
940        // parse SBASE (2 or 4 registers)
941        gcnAsm->setCurrentRVU(1);
942        good &= parseSRegRange(asmr, linePtr, sbaseReg, arch,
943                   (gcnInsn.mode&GCN_SBASE4)?4:2, GCNFIELD_SMRD_SBASE, true,
944                   INSTROP_SYMREGRANGE|INSTROP_READ);
945        if (!skipRequiredComma(asmr, linePtr))
946            return false;
947       
948        skipSpacesToEnd(linePtr, end);
949        if (linePtr==end || *linePtr!='@')
950        {
951            // parse SOFFSET (SGPR)
952            gcnAsm->setCurrentRVU(2);
953            good &= parseSRegRange(asmr, linePtr, soffsetReg, arch, 1,
954                       GCNFIELD_SMRD_SOFFSET, false, INSTROP_SYMREGRANGE|INSTROP_READ);
955        }
956        else // '@' prefix (treat next as expression)
957            skipCharAndSpacesToEnd(linePtr, end);
958       
959        if (!soffsetReg)
960        {
961            // parse immediate
962            soffsetReg.start = 255; // indicate an immediate
963            good &= parseImm(asmr, linePtr, soffsetVal, &soffsetExpr, 0, WS_UNSIGNED);
964        }
965    }
966    /// if errors
967    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
968        return false;
969   
970    if (mode1 != GCN_ARG_NONE)
971        gcnAsm->delayedOps[0] = AsmDelayedOp { output.size(),
972                    gcnAsm->instrRVUs[0].regVar, gcnAsm->instrRVUs[0].rstart,
973                    gcnAsm->instrRVUs[0].rend,
974                    cxbyte(gcnAsm->instrRVUs[0].rend - gcnAsm->instrRVUs[0].rstart),
975                    GCNDELOP_SMEMOP, GCNDELOP_NONE, gcnAsm->instrRVUs[0].rwFlags };
976   
977    if (soffsetExpr!=nullptr)
978        soffsetExpr->setTarget(AsmExprTarget(GCNTGT_SMRDOFFSET, asmr.currentSection,
979                       output.size()));
980   
981    // put data (instruction word)
982    uint32_t word;
983    SLEV(word, 0xc0000000U | (uint32_t(gcnInsn.code1)<<22) |
984            (uint32_t(dstReg.bstart())<<15) |
985            ((sbaseReg.bstart()&~1U)<<8) | ((soffsetReg.isVal(255)) ? 0x100 : 0) |
986            ((soffsetReg.isVal(255)) ? soffsetVal : soffsetReg.bstart()));
987    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word), 
988            reinterpret_cast<cxbyte*>(&word)+4);
989    /// prevent freeing expression
990    soffsetExpr.release();
991   
992    // update SGPR counting and VCC usage (regflags)
993    if (dstReg && !dstReg.isRegVar())
994    {
995        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
996        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
997    }
998    if (!sbaseReg.isRegVar())
999        updateRegFlags(gcnRegs.regFlags, sbaseReg.start, arch);
1000    if (!soffsetReg.isRegVar())
1001        updateRegFlags(gcnRegs.regFlags, soffsetReg.start, arch);
1002    return true;
1003}
1004
1005bool GCNAsmUtils::parseSMEMEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1006                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
1007                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1008                  GCNEncSize gcnEncSize)
1009{
1010    const char* end = asmr.line+asmr.lineSize;
1011    bool good = true;
1012    if (gcnEncSize==GCNEncSize::BIT32)
1013        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for SMEM encoding")
1014   
1015    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1016    RegRange dataReg(0, 0);
1017    RegRange sbaseReg(0, 0);
1018    RegRange soffsetReg(0, 0);
1019    uint32_t soffsetVal = 0;
1020    std::unique_ptr<AsmExpression> soffsetExpr;
1021    std::unique_ptr<AsmExpression> simm7Expr;
1022    const GCNInsnMode mode1 = (gcnInsn.mode & GCN_MASK1);
1023    const bool isGCN14 = (arch & ARCH_GCN_1_4) != 0;
1024    const bool isGCN15 = (arch & ARCH_GCN_1_5) != 0;
1025   
1026    const char* soffsetPlace = nullptr;
1027    AsmSourcePos soffsetPos;
1028   
1029    if (mode1 == GCN_SMRD_ONLYDST)
1030    {
1031        // parse SDST (SGPR)
1032        gcnAsm->setCurrentRVU(0);
1033        good &= parseSRegRange(asmr, linePtr, dataReg, arch,
1034                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_SMRD_SDST, true,
1035                       INSTROP_SYMREGRANGE|INSTROP_WRITE);
1036    }
1037    else if (mode1 != GCN_ARG_NONE)
1038    {
1039        const cxuint dregsNum = 1<<((gcnInsn.mode & GCN_DSIZE_MASK)>>GCN_SHIFT2);
1040        // parse SDST (SGPR's (1-16 registers))
1041        gcnAsm->setCurrentRVU(0);
1042        if ((mode1 & GCN_SMEM_NOSDATA) == 0)
1043        {
1044            if ((mode1 & GCN_SMEM_SDATA_IMM)==0)
1045                good &= parseSRegRange(asmr, linePtr, dataReg, arch, dregsNum,
1046                        GCNFIELD_SMRD_SDST, true, INSTROP_SYMREGRANGE|
1047                        ((gcnInsn.mode & GCN_MLOAD) != 0 ? INSTROP_WRITE : INSTROP_READ));
1048            else
1049                good &= parseImm(asmr, linePtr, dataReg.start, &simm7Expr, 7);
1050            if (!skipRequiredComma(asmr, linePtr))
1051                return false;
1052        }
1053       
1054        // parse SBASE (2 or 4 SGPR's)
1055        gcnAsm->setCurrentRVU(1);
1056        good &= parseSRegRange(asmr, linePtr, sbaseReg, arch,
1057                   (gcnInsn.mode&GCN_SBASE4)?4:2, GCNFIELD_SMRD_SBASE, true,
1058                   INSTROP_SYMREGRANGE|INSTROP_READ);
1059        if (!skipRequiredComma(asmr, linePtr))
1060            return false;
1061       
1062        skipSpacesToEnd(linePtr, end);
1063        if (linePtr==end || *linePtr!='@')
1064        {
1065            // parse SOFFSET (SGPR)
1066            gcnAsm->setCurrentRVU(2);
1067            const char* soffsetPlace = linePtr;
1068            good &= parseSRegRange(asmr, linePtr, soffsetReg, arch, 1,
1069                       GCNFIELD_SMRD_SOFFSET, false, INSTROP_SYMREGRANGE|INSTROP_READ);
1070            if (good && (!isGCN14 && !isGCN15) && (gcnInsn.mode & GCN_MLOAD) == 0 &&
1071                    soffsetReg && !soffsetReg.isVal(124))
1072                // if no M0 register
1073                ASM_NOTGOOD_BY_ERROR(soffsetPlace,
1074                        "Store/Atomic SMEM instructions accepts only M0 register")
1075        }
1076        else // '@' prefix (treat next as expression)
1077            skipCharAndSpacesToEnd(linePtr, end);
1078       
1079        if (!soffsetReg)
1080        {
1081            // parse immediate
1082            soffsetReg.start = 255; // indicate an immediate
1083            skipSpacesToEnd(linePtr, end);
1084            soffsetPlace = linePtr;
1085            soffsetPos = asmr.getSourcePos(soffsetPlace);
1086            good &= parseImm(asmr, linePtr, soffsetVal, &soffsetExpr,
1087                        isGCN14 ? 21 : 20, isGCN14 ? WS_BOTH : WS_UNSIGNED);
1088        }
1089    }
1090    bool haveGlc = false;
1091    bool haveNv = false;
1092    bool haveOffset = false;
1093    bool haveDlc = false;
1094    // parse modifiers
1095    while (linePtr != end)
1096    {
1097        skipSpacesToEnd(linePtr, end);
1098        if (linePtr == end)
1099            break;
1100        const char* modPlace = linePtr;
1101        char name[10];
1102        if (getNameArgS(asmr, 10, name, linePtr, "modifier"))
1103        {
1104            toLowerString(name);
1105            if (::strcmp(name, "glc")==0)
1106                good &= parseModEnable(asmr, linePtr, haveGlc, "glc modifier");
1107            else if (::strcmp(name, "dlc")==0)
1108                good &= parseModEnable(asmr, linePtr, haveDlc, "dlc modifier");
1109            else if ((isGCN14 || isGCN15) && ::strcmp(name, "nv")==0)
1110                good &= parseModEnable(asmr, linePtr, haveNv, "nv modifier");
1111            else if (isGCN14 && ::strcmp(name, "offset")==0)
1112            {
1113                // parse offset and it parameter: offset:XXX
1114                if (parseModImm(asmr, linePtr, soffsetVal, &soffsetExpr, "offset",
1115                        21, WS_BOTH))
1116                {
1117                    if (haveOffset)
1118                        asmr.printWarning(modPlace, "Offset is already defined");
1119                    haveOffset = true;
1120                    if (soffsetReg.isVal(255))
1121                        // illegal second offset
1122                        ASM_NOTGOOD_BY_ERROR(modPlace, "Illegal second offset");
1123                }
1124                else
1125                    good = false;
1126            }
1127            else
1128                ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown SMEM modifier")
1129        }
1130        else
1131            good = false;
1132    }
1133    /// if errors
1134    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1135        return false;
1136   
1137    // set expression target for offsets and immediates
1138    if (soffsetExpr!=nullptr)
1139        soffsetExpr->setTarget(AsmExprTarget(isGCN14 ?
1140                    GCNTGT_SMEMOFFSETVEGA : GCNTGT_SMEMOFFSET,
1141                    asmr.currentSection, output.size()));
1142    if (simm7Expr!=nullptr)
1143        simm7Expr->setTarget(AsmExprTarget(GCNTGT_SMEMIMM, asmr.currentSection,
1144                       output.size()));
1145   
1146    bool dataToRead = false;
1147    bool dataToWrite = false;
1148    if (dataReg)
1149    {
1150        dataToWrite = ((gcnInsn.mode&GCN_MLOAD) != 0 ||
1151                ((gcnInsn.mode&GCN_MATOMIC)!=0 && haveGlc));
1152        dataToRead = (gcnInsn.mode&GCN_MLOAD)==0 || (gcnInsn.mode&GCN_MATOMIC)!=0;
1153    }
1154   
1155    gcnAsm->instrRVUs[0].rwFlags = (dataToRead ? ASMRVU_READ : 0) |
1156            (dataToWrite ? ASMRVU_WRITE : 0);
1157    // check fcmpswap
1158    if ((gcnInsn.mode & GCN_MHALFWRITE) != 0 && dataToWrite &&
1159            gcnAsm->instrRVUs[0].regField != ASMFIELD_NONE)
1160    {
1161        // fix access
1162        AsmRegVarUsage& rvu = gcnAsm->instrRVUs[0];
1163        uint16_t size = rvu.rend-rvu.rstart;
1164        rvu.rend = rvu.rstart + (size>>1);
1165        AsmRegVarUsage& nextRvu = gcnAsm->instrRVUs[3];
1166        nextRvu = rvu;
1167        nextRvu.regField = GCNFIELD_SMRD_SDSTH;
1168        nextRvu.rstart += (size>>1);
1169        nextRvu.rend = rvu.rstart + size;
1170        nextRvu.rwFlags = ASMRVU_READ;
1171        nextRvu.align = 0;
1172    }
1173   
1174    if (mode1 != GCN_ARG_NONE)
1175    {
1176        gcnAsm->delayedOps[0] = AsmDelayedOp { output.size(),
1177                    gcnAsm->instrRVUs[0].regVar, gcnAsm->instrRVUs[0].rstart,
1178                    gcnAsm->instrRVUs[0].rend,
1179                    cxbyte(gcnAsm->instrRVUs[0].rend - gcnAsm->instrRVUs[0].rstart),
1180                    GCNDELOP_SMEMOP, GCNDELOP_NONE, gcnAsm->instrRVUs[0].rwFlags };
1181        if (gcnAsm->instrRVUs[3].regField != ASMFIELD_NONE)
1182            gcnAsm->delayedOps[1] = AsmDelayedOp { output.size(),
1183                    gcnAsm->instrRVUs[3].regVar, gcnAsm->instrRVUs[3].rstart,
1184                    gcnAsm->instrRVUs[3].rend,
1185                    cxbyte(gcnAsm->instrRVUs[0].rend - gcnAsm->instrRVUs[0].rstart),
1186                    GCNDELOP_SMEMOP, GCNDELOP_NONE, gcnAsm->instrRVUs[3].rwFlags };
1187    }
1188   
1189    // put data (2 instruction words)
1190    uint32_t words[2];
1191    const uint32_t encoding = isGCN15 ? 0xf4000000U : 0xc0000000U;
1192    const uint32_t soffsetRegNull = isGCN15 ? 0x7d : 0;
1193    SLEV(words[0], encoding | (uint32_t(gcnInsn.code1)<<18) | (dataReg.bstart()<<6) |
1194            (sbaseReg.bstart()>>1) |
1195            // enable IMM if soffset is immediate or haveOffset with SGPR
1196            ((!isGCN15 && (soffsetReg.isVal(255) || haveOffset)) ? 0x20000 : 0) |
1197            (haveGlc ? 0x10000 : 0) | (haveNv ? 0x8000 : 0) |
1198            (((!isGCN15 && haveOffset) || (isGCN15 && haveDlc)) ? 0x4000 : 0));
1199    SLEV(words[1],
1200            // store IMM OFFSET if offset: or IMM offset instead SGPR
1201            ((isGCN15 || soffsetReg.isVal(255) || haveOffset) ?
1202                                soffsetVal : soffsetReg.bstart()) |
1203            // store SGPR in SOFFSET if have offset and have SGPR offset
1204                (((isGCN15 && !soffsetReg.isVal(255)) ||
1205                        (!isGCN15 && haveOffset && !soffsetReg.isVal(255))) ?
1206                        (soffsetReg.bstart()<<25) : (soffsetRegNull<<25)));
1207   
1208    output.insert(output.end(), reinterpret_cast<cxbyte*>(words), 
1209            reinterpret_cast<cxbyte*>(words+2));
1210    /// prevent freeing expression
1211    soffsetExpr.release();
1212    simm7Expr.release();
1213   
1214    // update SGPR counting and VCC usage (regflags)
1215    if (!dataReg.isRegVar() && dataToWrite)
1216    {
1217        updateSGPRsNum(gcnRegs.sgprsNum, dataReg.end-1, arch);
1218        updateRegFlags(gcnRegs.regFlags, dataReg.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
1227// choose between 64-bit immediate (FP64) and 32-bit immediate
1228static Flags correctOpType(uint32_t regsNum, Flags typeMask)
1229{
1230    return (regsNum==2 && (typeMask==INSTROP_FLOAT || typeMask==INSTROP_INT)) ?
1231        INSTROP_V64BIT : typeMask;
1232}
1233
1234static void encodeVOPWords(uint32_t vop0Word, cxbyte modifiers,
1235        const VOPExtraModifiers& extraMods, const GCNOperand& src0Op,
1236        const GCNOperand& src1Op, uint32_t immValue, GCNInsnMode mode1,
1237        // dstMod - (16bits lower value, 16bit - use dstMod instead std encoding
1238        uint32_t inDstMod, cxuint& wordsNum, uint32_t* words)
1239{
1240    // VOP2 encoding
1241    cxuint src0out = src0Op.range.bstart();
1242    if (extraMods.needSDWA)
1243        src0out = 0xf9;
1244    else if (extraMods.needDPP)
1245        src0out = 0xfa;
1246    else if (extraMods.needDPP8)
1247        src0out = (extraMods.fi ? 0xea : 0xe9);
1248    SLEV(words[0], vop0Word | uint32_t(src0out));
1249    if (extraMods.needSDWA)
1250    {
1251        const uint32_t dstMod = (inDstMod & 0x10000) ? (inDstMod&0xff00) :
1252                    ((uint32_t(extraMods.dstSel)<<8) |
1253                    (uint32_t(extraMods.dstUnused)<<11) |
1254                    ((modifiers & VOP3_CLAMP) ? 0x2000 : 0) |
1255                    (uint32_t(modifiers & 3) << 14));
1256        // if SDWA encoding
1257        SLEV(words[wordsNum++], dstMod | (src0Op.range.bstart()&0xff) |
1258                (uint32_t(extraMods.src0Sel)<<16) |
1259                ((src0Op.vopMods&VOPOP_SEXT) ? (1U<<19) : 0) |
1260                ((src0Op.vopMods&VOPOP_NEG) ? (1U<<20) : 0) |
1261                ((src0Op.vopMods&VOPOP_ABS) ? (1U<<21) : 0) |
1262                (uint32_t(extraMods.src1Sel)<<24) |
1263                ((src1Op.vopMods&VOPOP_SEXT) ? (1U<<27) : 0) |
1264                ((src1Op.vopMods&VOPOP_NEG) ? (1U<<28) : 0) |
1265                ((src1Op.vopMods&VOPOP_ABS) ? (1U<<29) : 0) |
1266                (src0Op.range.isNonVGPR() ? (1U<<23) : 0) |
1267                (src1Op.range.isNonVGPR() ? (1U<<31) : 0));
1268    }
1269    else if (extraMods.needDPP)
1270        // DPP encoding
1271        SLEV(words[wordsNum++], (src0Op.range.bstart()&0xff) | (extraMods.dppCtrl<<8) |
1272                ((modifiers&VOP3_BOUNDCTRL) ? (1U<<19) : 0) |
1273                ((src0Op.vopMods&VOPOP_NEG) ? (1U<<20) : 0) |
1274                ((src0Op.vopMods&VOPOP_ABS) ? (1U<<21) : 0) |
1275                ((src1Op.vopMods&VOPOP_NEG) ? (1U<<22) : 0) |
1276                ((src1Op.vopMods&VOPOP_ABS) ? (1U<<23) : 0) |
1277                (extraMods.fi ? 0x40000U : 0) |
1278                (uint32_t(extraMods.bankMask)<<24) |
1279                (uint32_t(extraMods.rowMask)<<28));
1280    else if (extraMods.needDPP8)
1281        SLEV(words[wordsNum++], (src0Op.range.bstart()&0xff) | (extraMods.dpp8Value<<8));
1282    else if (src0Op.range.isVal(255)) // otherwise we check for immediate/literal value
1283        SLEV(words[wordsNum++], src0Op.value);
1284    else if (src1Op.range.isVal(255))
1285        // literal from SRC1
1286        SLEV(words[wordsNum++], src1Op.value);
1287    else if (mode1 == GCN_ARG1_IMM || mode1 == GCN_ARG2_IMM)
1288        SLEV(words[wordsNum++], immValue);
1289}
1290
1291static void encodeVOP3Words(GPUArchMask arch, const GCNAsmInstruction& gcnInsn,
1292        cxbyte modifiers, VOPOpModifiers opMods, bool haveDstCC,
1293        const RegRange& dstReg, const RegRange& dstCCReg, const RegRange& srcCCReg,
1294        const GCNOperand& src0Op, const GCNOperand& src1Op,
1295        cxuint& wordsNum, uint32_t* words)
1296{
1297    const bool isGCN12 = (arch & ARCH_GCN_1_2_4_5)!=0;
1298    // VOP3 encoding
1299    uint32_t code = (isGCN12) ?
1300            (uint32_t(gcnInsn.code2)<<16) | ((modifiers&VOP3_CLAMP) ? 0x8000 : 0) :
1301            (uint32_t(gcnInsn.code2)<<17) | ((modifiers&VOP3_CLAMP) ? 0x800 : 0);
1302    cxuint encoding = (arch & ARCH_GCN_1_5)!=0 ? 0xd4000000U : 0xd0000000U;
1303    if (haveDstCC) // if VOP3B
1304        SLEV(words[0], encoding | code |
1305            (dstReg.bstart()&0xff) | (uint32_t(dstCCReg.bstart())<<8));
1306    else // if VOP3A
1307        SLEV(words[0], encoding | code | (dstReg.bstart()&0xff) |
1308            ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
1309            ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
1310            ((opMods.opselMod&15) << 11));
1311    // second dword
1312    SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
1313        (uint32_t(srcCCReg.bstart())<<18) | (uint32_t(modifiers & 3) << 27) |
1314        ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
1315        ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0));
1316    wordsNum++;
1317}
1318
1319bool GCNAsmUtils::parseVOP2Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1320                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
1321                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1322                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1323{
1324    const char* end = asmr.line+asmr.lineSize;
1325    bool good = true;
1326    const GCNInsnMode mode1 = (gcnInsn.mode & GCN_MASK1);
1327    const GCNInsnMode mode2 = (gcnInsn.mode & GCN_MASK2);
1328    const bool isGCN12 = (arch & ARCH_GCN_1_2_4_5)!=0;
1329    const bool isGCN14 = (arch & ARCH_GCN_1_4_5)!=0;
1330    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1331   
1332    RegRange dstReg(0, 0);
1333    RegRange dstCCReg(0, 0);
1334    RegRange srcCCReg(0, 0);
1335    gcnAsm->setCurrentRVU(0);
1336    if (mode1 == GCN_DS1_SGPR)
1337        // if SGPRS as destination
1338        good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1339                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_SDST, true,
1340                       INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1341    else
1342    {
1343         // if VGPRS as destination
1344        bool v_mac = ::strncmp(gcnInsn.mnemonic, "v_mac_", 6)==0 ||
1345                // for V_FMAC_F32 (VEGA20)
1346                ::strncmp(gcnInsn.mnemonic, "v_fmac_", 7)==0;
1347        good &= parseVRegRange(asmr, linePtr, dstReg, (gcnInsn.mode&GCN_REG_DST_64)?2:1,
1348                        GCNFIELD_VOP_VDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE|
1349                              (v_mac?INSTROP_READ:0));
1350    }
1351   
1352    const bool haveDstCC = mode1 == GCN_DS2_VCC || mode1 == GCN_DST_VCC;
1353    const bool haveSrcCC = mode1 == GCN_DS2_VCC || mode1 == GCN_SRC2_VCC;
1354    if (haveDstCC) /* VOP3b */
1355    {
1356        if (!skipRequiredComma(asmr, linePtr))
1357            return false;
1358        // parse SDST (in place VCC) (2 SGPR's)
1359        gcnAsm->setCurrentRVU(1);
1360        good &= parseSRegRange(asmr, linePtr, dstCCReg, arch, 2, GCNFIELD_VOP3_SDST1, true,
1361                               INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1362    }
1363   
1364    GCNOperand src0Op{}, src1Op{};
1365    std::unique_ptr<AsmExpression> src0OpExpr, src1OpExpr;
1366    const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1367            (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1368   
1369    const Flags vopOpModFlags = ((haveDstCC && !isGCN12) ?
1370                    INSTROP_VOP3NEG : INSTROP_VOP3MODS);
1371    if (!skipRequiredComma(asmr, linePtr))
1372        return false;
1373    cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1374    // parse SRC0 (can be VGPR, SGPR, scalar source, LDS, literal or constant)
1375    gcnAsm->setCurrentRVU(2);
1376    good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1377            correctOpType(regsNum, literalConstsFlags) | vopOpModFlags |
1378            INSTROP_SGPR_UNALIGNED|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1379            INSTROP_READ, GCNFIELD_VOP_SRC0);
1380   
1381    uint32_t immValue = 0;
1382    std::unique_ptr<AsmExpression> immExpr;
1383    if (mode1 == GCN_ARG1_IMM)
1384    {
1385        // for V_MADMK_FXxx instruction
1386        if (!skipRequiredComma(asmr, linePtr))
1387            return false;
1388        good &= parseLiteralImm(asmr, linePtr, immValue, &immExpr, literalConstsFlags);
1389    }
1390   
1391    if (!skipRequiredComma(asmr, linePtr))
1392        return false;
1393   
1394    bool sgprRegInSrc1 = mode1 == GCN_DS1_SGPR || mode1 == GCN_SRC1_SGPR;
1395    skipSpacesToEnd(linePtr, end);
1396    regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
1397    gcnAsm->setCurrentRVU(3);
1398    // parse SRC1 (can be VGPR, SGPR, scalar source, LDS, literal or constant)
1399    //  except when SGPR for SRC1 when instructions accepts SGPR in this place
1400    good &= parseOperand(asmr, linePtr, src1Op, &src1OpExpr, arch, regsNum,
1401            correctOpType(regsNum, literalConstsFlags) | vopOpModFlags |
1402            (!sgprRegInSrc1 ? INSTROP_VREGS : 0)|INSTROP_SSOURCE|INSTROP_SREGS|
1403            INSTROP_SGPR_UNALIGNED |
1404            (src0Op.range.start==255 ? INSTROP_ONLYINLINECONSTS : 0)|
1405            INSTROP_READ, (!sgprRegInSrc1) ? GCNFIELD_VOP_VSRC1 : GCNFIELD_VOP_SSRC1);
1406   
1407    if (mode1 == GCN_ARG2_IMM)
1408    {
1409        // for V_MADAK_Fxx instruction
1410        if (!skipRequiredComma(asmr, linePtr))
1411            return false;
1412        good &= parseLiteralImm(asmr, linePtr, immValue, &immExpr, literalConstsFlags);
1413    }
1414    else if (haveSrcCC)
1415    {
1416        if (!skipRequiredComma(asmr, linePtr))
1417            return false;
1418        gcnAsm->setCurrentRVU(4);
1419        // parse SSRC (VCC) (2 SGPR's)
1420        good &= parseSRegRange(asmr, linePtr, srcCCReg, arch, 2, GCNFIELD_VOP3_SSRC, true,
1421                       INSTROP_SYMREGRANGE|INSTROP_UNALIGNED|INSTROP_READ);
1422    }
1423   
1424    // modifiers
1425    cxbyte modifiers = 0;
1426    VOPExtraModifiers extraMods{};
1427    VOPOpModifiers opMods{};
1428    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, 3,
1429                    (isGCN12) ? &extraMods : nullptr,
1430                    ((!haveDstCC || isGCN12) ? PARSEVOP_WITHCLAMP : 0)|PARSEVOP_WITHSEXT|
1431                    ((isGCN14 && !haveDstCC) ? PARSEVOP_WITHOPSEL : 0));
1432    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1433        return false;
1434   
1435    // apply VOP modifiers (abs,neg,sext) to operands from modifiers
1436    if (src0Op)
1437        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1438                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1439                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1440    if (src1Op)
1441        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
1442                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
1443                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
1444   
1445    extraMods.needSDWA |= ((src0Op.vopMods | src1Op.vopMods) & VOPOP_SEXT) != 0;
1446    // determine whether VOP3 encoding is needed
1447    bool vop3 = /* src1=sgprs and not (DS1_SGPR|src1_SGPR) */
1448        //((src1Op.range.start<256) ^ sgprRegInSrc1) ||
1449        ((!isGCN14 || !extraMods.needSDWA) &&
1450                (src1Op.range.isNonVGPR() ^ sgprRegInSrc1)) ||
1451        (!isGCN12 && (src0Op.vopMods!=0 || src1Op.vopMods!=0)) ||
1452        (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1453            /* exclude OMOD if RXVEGA and SDWA used */
1454            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0 ||
1455        /* srcCC!=VCC or dstCC!=VCC */
1456        //(haveDstCC && dstCCReg.start!=106) || (haveSrcCC && srcCCReg.start!=106) ||
1457        (haveDstCC && !dstCCReg.isVal(106)) || (haveSrcCC && !srcCCReg.isVal(106)) ||
1458        ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1459   
1460    if ((src0Op.range.isVal(255) || src1Op.range.isVal(255)) &&
1461        (src0Op.range.isSGPR() || src0Op.range.isVal(124) ||
1462         src1Op.range.isSGPR() || src1Op.range.isVal(124)))
1463        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SGPR or M0 is illegal")
1464   
1465    AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1466    if (vop3) // modify fields in reg usage
1467    {
1468        if (rvus[0].regField != ASMFIELD_NONE)
1469            rvus[0].regField = (rvus[0].regField==GCNFIELD_VOP_VDST) ? GCNFIELD_VOP3_VDST :
1470                            GCNFIELD_VOP3_SDST0;
1471        if (rvus[2].regField != ASMFIELD_NONE)
1472            rvus[2].regField = GCNFIELD_VOP3_SRC0;
1473        if (rvus[3].regField != ASMFIELD_NONE)
1474            rvus[3].regField = GCNFIELD_VOP3_SRC1;
1475    }
1476    else
1477    {
1478        // vop2
1479        if (rvus[1].regField != ASMFIELD_NONE)
1480            rvus[1].regField = GCNFIELD_VOP_VCC_SDST1;
1481        if (rvus[4].regField != ASMFIELD_NONE)
1482            rvus[4].regField = GCNFIELD_VOP_VCC_SSRC;
1483    }
1484   
1485    // count number SGPR operands readed by instruction
1486    cxuint sgprsReaded = 0;
1487    if (src0Op.range.isSGPR())
1488        sgprsReaded++;
1489    if (src1Op.range.isSGPR() && !regRangeCanEqual(src0Op.range, src1Op.range))
1490        sgprsReaded++;
1491    if (haveSrcCC)
1492        // check for third operand (SSRC)
1493        if (!regRangeCanEqual(src0Op.range, srcCCReg) &&
1494            !regRangeCanEqual(src1Op.range, srcCCReg))
1495            sgprsReaded++;
1496   
1497    if ((arch & ARCH_GCN_1_5)==0)
1498    {
1499        if (sgprsReaded >= 2)
1500            /* include VCCs (???) */
1501            ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
1502    }
1503    else
1504    {   // NAVI
1505        if (sgprsReaded >= 3)
1506            /* include VCCs (???) */
1507            ASM_FAIL_BY_ERROR(instrPlace, "More than two SGPRs to read in instruction")
1508    }
1509   
1510    const bool needImm = (src0Op.range.start==255 || src1Op.range.start==255 ||
1511             mode1 == GCN_ARG1_IMM || mode1 == GCN_ARG2_IMM);
1512   
1513    bool sextFlags = ((src0Op.vopMods|src1Op.vopMods) & VOPOP_SEXT);
1514    bool absNegFlags = ((src0Op.vopMods|src1Op.vopMods) & (VOPOP_ABS|VOPOP_NEG));
1515    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || extraMods.needDPP8 ||
1516                sextFlags || gcnVOPEnc!=GCNVOPEnc::NORMAL))
1517    {
1518        /* if VOP_SDWA or VOP_DPP is required */
1519        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1520                    gcnVOPEnc, src0Op, extraMods, absNegFlags, instrPlace))
1521            return false;
1522        if (gcnAsm->instrRVUs[2].regField != ASMFIELD_NONE)
1523            gcnAsm->instrRVUs[2].regField = GCNFIELD_DPPSDWA_SRC0;
1524       
1525        if (extraMods.needSDWA && isGCN14)
1526        {
1527            // fix for extra type operand from SDWA
1528            if (rvus[2].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1529                rvus[2].regField = GCNFIELD_DPPSDWA_SSRC0;
1530            if (rvus[3].regField != ASMFIELD_NONE && src1Op.range.isNonVGPR())
1531                rvus[3].regField = GCNFIELD_VOP_SSRC1;
1532        }
1533    }
1534    else if (isGCN12 && ((src0Op.vopMods|src1Op.vopMods) & ~VOPOP_SEXT)!=0 && !sextFlags)
1535        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1536        vop3 = true;
1537   
1538    if (isGCN12 && vop3 && haveDstCC && ((src0Op.vopMods|src1Op.vopMods) & VOPOP_ABS) != 0)
1539        ASM_FAIL_BY_ERROR(instrPlace, "Abs modifier is illegal for VOP3B encoding")
1540    if (vop3 && needImm)
1541        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1542   
1543    if (!checkGCNVOPEncoding(asmr, arch, instrPlace, gcnVOPEnc, gcnInsn.mode, &extraMods))
1544        return false;
1545   
1546    // set target expressions if needed
1547    if (src0OpExpr!=nullptr)
1548        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1549                      output.size()));
1550    if (src1OpExpr!=nullptr)
1551        src1OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1552                      output.size()));
1553    if (immExpr!=nullptr)
1554        immExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1555                      output.size()));
1556   
1557    // put data (instruction words)
1558    cxuint wordsNum = 1;
1559    uint32_t words[2];
1560    if (!vop3)
1561        // VOP2 encoding
1562        encodeVOPWords((uint32_t(gcnInsn.code1)<<25) |
1563                (uint32_t(src1Op.range.bstart()&0xff)<<9) |
1564                (uint32_t(dstReg.bstart()&0xff)<<17),
1565                modifiers, extraMods, src0Op, src1Op, immValue, mode1,
1566                0, wordsNum, words);
1567    else
1568        // VOP3 encoding
1569        encodeVOP3Words(arch, gcnInsn, modifiers, opMods, haveDstCC,
1570                dstReg, dstCCReg, srcCCReg, src0Op, src1Op, wordsNum, words);
1571   
1572    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1573        return false;
1574   
1575    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1576            reinterpret_cast<cxbyte*>(words + wordsNum));
1577    /// prevent freeing expression
1578    src0OpExpr.release();
1579    src1OpExpr.release();
1580    immExpr.release();
1581    // update register pool (VGPR and SGPR counting)
1582    if (!dstReg.isRegVar())
1583    {
1584        if (dstReg.start>=256)
1585            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
1586        else // sgprs
1587        {
1588            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1589            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1590        }
1591    }
1592    // for SRC operands
1593    if (src0Op.range && !src0Op.range.isRegVar())
1594        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1595    if (src1Op.range && !src1Op.range.isRegVar())
1596        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
1597    if (dstCCReg && !dstCCReg.isRegVar())
1598    {
1599        updateSGPRsNum(gcnRegs.sgprsNum, dstCCReg.end-1, arch);
1600        updateRegFlags(gcnRegs.regFlags, dstCCReg.start, arch);
1601    }
1602    if (srcCCReg && !srcCCReg.isRegVar())
1603        updateRegFlags(gcnRegs.regFlags, srcCCReg.start, arch);
1604    return true;
1605}
1606
1607bool GCNAsmUtils::parseVOP1Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1608                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
1609                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1610                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1611{
1612    bool good = true;
1613    const GCNInsnMode mode1 = (gcnInsn.mode & GCN_MASK1);
1614    const GCNInsnMode mode2 = (gcnInsn.mode & GCN_MASK2);
1615    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1616    const bool isGCN14 = (arch & ARCH_GCN_1_4)!=0;
1617   
1618    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1619    RegRange dstReg(0, 0);
1620    GCNOperand src0Op{};
1621    std::unique_ptr<AsmExpression> src0OpExpr;
1622    cxbyte modifiers = 0;
1623    if (mode1 != GCN_VOP_ARG_NONE)
1624    {
1625        gcnAsm->setCurrentRVU(0);
1626        if (mode1 == GCN_DST_SGPR)
1627            // if SGPRS as destination
1628            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1629                           (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_SDST, true,
1630                           INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1631        else // if VGPRS as destination
1632            good &= parseVRegRange(asmr, linePtr, dstReg,
1633                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP_VDST, true,
1634                                  INSTROP_SYMREGRANGE|INSTROP_WRITE);
1635       
1636        const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1637                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1638       
1639        if (!skipRequiredComma(asmr, linePtr))
1640            return false;
1641        cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1642        gcnAsm->setCurrentRVU(1);
1643        // parse SRC0 (can be VGPR, SGPR, source scalar, literal or cosntant, LDS
1644        good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1645                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
1646                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1647                    INSTROP_VOP3MODS|INSTROP_READ, GCNFIELD_VOP_SRC0);
1648    }
1649    // modifiers
1650    VOPExtraModifiers extraMods{};
1651    VOPOpModifiers opMods{};
1652    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods,
1653                  (mode1!=GCN_VOP_ARG_NONE) ? 2 : 0, (isGCN12)?&extraMods:nullptr,
1654                  PARSEVOP_WITHCLAMP|PARSEVOP_WITHSEXT|
1655                  (isGCN14 ? PARSEVOP_WITHOPSEL : 0), (mode1!=GCN_VOP_ARG_NONE) ? 2 : 0);
1656    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1657        return false;
1658   
1659    if (src0Op)
1660        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1661                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1662                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1663   
1664    extraMods.needSDWA |= ((src0Op.vopMods) & VOPOP_SEXT) != 0;
1665    // check whether VOP3 encoding is needed
1666    bool vop3 = ((!isGCN12 && src0Op.vopMods!=0) ||
1667            (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1668            /* exclude OMOD if RXVEGA and SDWA used */
1669            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0) ||
1670            ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1671    if (vop3) // modify fields in reg usage
1672    {
1673        AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1674        if (rvus[0].regField != ASMFIELD_NONE)
1675            rvus[0].regField = (rvus[0].regField==GCNFIELD_VOP_VDST) ?
1676                        GCNFIELD_VOP3_VDST : GCNFIELD_VOP3_SDST0;
1677        if (rvus[1].regField != ASMFIELD_NONE)
1678            rvus[1].regField = GCNFIELD_VOP3_SRC0;
1679    }
1680   
1681    bool sextFlags = (src0Op.vopMods & VOPOP_SEXT);
1682    bool absNegFlags = (src0Op.vopMods & (VOPOP_ABS|VOPOP_NEG));
1683    bool needImm = (src0Op && src0Op.range.isVal(255));
1684    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || extraMods.needDPP8 ||
1685                sextFlags || gcnVOPEnc!=GCNVOPEnc::NORMAL))
1686    {
1687        /* if VOP_SDWA or VOP_DPP is required */
1688        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1689                    gcnVOPEnc, src0Op, extraMods, absNegFlags, instrPlace))
1690            return false;
1691        if (gcnAsm->instrRVUs[1].regField != ASMFIELD_NONE)
1692            gcnAsm->instrRVUs[1].regField = GCNFIELD_DPPSDWA_SRC0;
1693        if (extraMods.needSDWA && isGCN14)
1694        {
1695            // fix for extra type operand from SDWA
1696            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1697            if (rvus[1].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1698                rvus[1].regField = GCNFIELD_DPPSDWA_SSRC0;
1699        }
1700    }
1701    else if (isGCN12 && (src0Op.vopMods & ~VOPOP_SEXT)!=0 && !sextFlags)
1702        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1703        vop3 = true;
1704   
1705    if (vop3 && src0Op.range.isVal(255))
1706        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1707   
1708    if (!checkGCNVOPEncoding(asmr, arch, instrPlace, gcnVOPEnc, gcnInsn.mode, &extraMods))
1709        return false;
1710   
1711    if (src0OpExpr!=nullptr)
1712        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1713                      output.size()));
1714   
1715    // put data (instruction word)
1716    cxuint wordsNum = 1;
1717    uint32_t words[2];
1718    if (!vop3)
1719        // VOP1 encoding
1720        encodeVOPWords(0x7e000000U | (uint32_t(gcnInsn.code1)<<9) |
1721                (uint32_t(dstReg.bstart()&0xff)<<17),
1722                modifiers, extraMods, src0Op, GCNOperand{ { 256, 257 } }, 0, mode1,
1723                0, wordsNum, words);
1724    else
1725        // VOP3 encoding
1726        encodeVOP3Words(arch, gcnInsn, modifiers, opMods, false,
1727                dstReg, RegRange{}, RegRange{}, src0Op, GCNOperand{}, wordsNum, words);
1728   
1729    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1730        return false;
1731   
1732    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1733            reinterpret_cast<cxbyte*>(words + wordsNum));
1734    /// prevent freeing expression
1735    src0OpExpr.release();
1736    // update register pool (VGPR and SGPR counting)
1737    if (dstReg && !dstReg.isRegVar())
1738    {
1739        if (dstReg.start>=256)
1740            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
1741        else // sgprs
1742        {
1743            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1744            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1745        }
1746    }
1747    if (src0Op.range && !src0Op.range.isRegVar())
1748        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1749    return true;
1750}
1751
1752bool GCNAsmUtils::parseVOPCEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1753                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
1754                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1755                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1756{
1757    bool good = true;
1758    const GCNInsnMode mode2 = (gcnInsn.mode & GCN_MASK2);
1759    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1760    const bool isGCN14 = (arch & ARCH_GCN_1_4)!=0;
1761   
1762    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1763    RegRange dstReg(0, 0);
1764    GCNOperand src0Op{};
1765    std::unique_ptr<AsmExpression> src0OpExpr;
1766    GCNOperand src1Op{};
1767    std::unique_ptr<AsmExpression> src1OpExpr;
1768    cxbyte modifiers = 0;
1769   
1770    // parse SDST (2 SGPR's)
1771    gcnAsm->setCurrentRVU(0);
1772    good &= parseSRegRange(asmr, linePtr, dstReg, arch, 2, GCNFIELD_VOP3_SDST0, true,
1773                           INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1774    if (!skipRequiredComma(asmr, linePtr))
1775        return false;
1776   
1777    const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
1778                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
1779    cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
1780    gcnAsm->setCurrentRVU(1);
1781    // parse SRC0 (can VGPR, SGPR, scalar source,literal or constant)
1782    good &= parseOperand(asmr, linePtr, src0Op, &src0OpExpr, arch, regsNum,
1783                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
1784                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
1785                    INSTROP_VOP3MODS|INSTROP_READ, GCNFIELD_VOP_SRC0);
1786   
1787    if (!skipRequiredComma(asmr, linePtr))
1788        return false;
1789    regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
1790    gcnAsm->setCurrentRVU(2);
1791    // parse SRC1 (can VGPR, SGPR, scalar source,literal or constant)
1792    good &= parseOperand(asmr, linePtr, src1Op, &src1OpExpr, arch, regsNum,
1793                correctOpType(regsNum, literalConstsFlags) | INSTROP_VOP3MODS|
1794                INSTROP_SGPR_UNALIGNED|INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|
1795                INSTROP_READ| (src0Op.range.isVal(255) ? INSTROP_ONLYINLINECONSTS : 0),
1796                GCNFIELD_VOP_VSRC1);
1797    // modifiers
1798    VOPExtraModifiers extraMods{};
1799    VOPOpModifiers opMods{};
1800    good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, 3,
1801                (isGCN12)?&extraMods:nullptr, (isGCN14 ? PARSEVOP_NODSTMODS : 0)|
1802                PARSEVOP_WITHCLAMP|PARSEVOP_WITHSEXT|(isGCN14 ? PARSEVOP_WITHOPSEL : 0));
1803    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
1804        return false;
1805   
1806    // set VOP operand modifiers to src operands
1807    if (src0Op)
1808        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
1809                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
1810                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
1811    if (src1Op)
1812        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
1813                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
1814                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
1815   
1816    // determine whether SDWA is needed or VOP3 encoding needed
1817    extraMods.needSDWA |= ((src0Op.vopMods | src1Op.vopMods) & VOPOP_SEXT) != 0;
1818    bool vop3 = //(dstReg.start!=106) || (src1Op.range.start<256) ||
1819        ((!isGCN14 || !extraMods.needSDWA) && !dstReg.isVal(106)) ||
1820        ((!isGCN14 || !extraMods.needSDWA) && src1Op.range.isNonVGPR()) ||
1821        (!isGCN12 && (src0Op.vopMods!=0 || src1Op.vopMods!=0)) ||
1822        (modifiers&~(VOP3_BOUNDCTRL|(extraMods.needSDWA?VOP3_CLAMP:0)|
1823            /* exclude OMOD if RXVEGA and SDWA used */
1824            ((isGCN14 && extraMods.needSDWA) ? 3 : 0)))!=0 ||
1825        ((opMods.opselMod & 15) != 0) || (gcnEncSize==GCNEncSize::BIT64);
1826   
1827    if ((src0Op.range.isVal(255) || src1Op.range.isVal(255)) &&
1828        (src0Op.range.isSGPR() || src0Op.range.isVal(124) ||
1829         src1Op.range.isSGPR() || src1Op.range.isVal(124)))
1830        ASM_FAIL_BY_ERROR(instrPlace, "Literal with SGPR or M0 is illegal")
1831    if ((arch & ARCH_GCN_1_5) == 0 && src0Op.range.isSGPR() && src1Op.range.isSGPR() &&
1832        !regRangeCanEqual(src0Op.range, src1Op.range))
1833        /* include VCCs (???) */
1834        ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
1835   
1836    AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1837    if (vop3)
1838    {
1839        // modify fields in reg usage
1840        if (rvus[1].regField != ASMFIELD_NONE)
1841            rvus[1].regField = GCNFIELD_VOP3_SRC0;
1842        if (rvus[2].regField != ASMFIELD_NONE)
1843            rvus[2].regField = GCNFIELD_VOP3_SRC1;
1844    }
1845    else if (rvus[0].regField != ASMFIELD_NONE)
1846        rvus[0].regField = GCNFIELD_VOP_VCC_SDST0;
1847   
1848    const bool needImm = src0Op.range.start==255 || src1Op.range.start==255;
1849   
1850    bool sextFlags = ((src0Op.vopMods|src1Op.vopMods) & VOPOP_SEXT);
1851    bool absNegFlags = (src0Op.vopMods & (VOPOP_ABS|VOPOP_NEG));
1852    if (isGCN12 && (extraMods.needSDWA || extraMods.needDPP || extraMods.needDPP8 ||
1853                sextFlags || gcnVOPEnc!=GCNVOPEnc::NORMAL))
1854    {
1855        /* if VOP_SDWA or VOP_DPP is required */
1856        if (!checkGCNVOPExtraModifers(asmr, arch, needImm, sextFlags, vop3,
1857                    gcnVOPEnc, src0Op, extraMods, absNegFlags, instrPlace))
1858            return false;
1859        if (gcnAsm->instrRVUs[1].regField != ASMFIELD_NONE)
1860            gcnAsm->instrRVUs[1].regField = GCNFIELD_DPPSDWA_SRC0;
1861       
1862        if (extraMods.needSDWA && isGCN14)
1863        {
1864            // fix for extra type operand from SDWA
1865            AsmRegVarUsage* rvus = gcnAsm->instrRVUs;
1866            if (rvus[0].regField != ASMFIELD_NONE)
1867                rvus[0].regField = GCNFIELD_SDWAB_SDST;
1868            if (rvus[1].regField != ASMFIELD_NONE && src0Op.range.isNonVGPR())
1869                rvus[1].regField = GCNFIELD_DPPSDWA_SSRC0;
1870            if (rvus[2].regField != ASMFIELD_NONE && src1Op.range.isNonVGPR())
1871                rvus[2].regField = GCNFIELD_VOP_SSRC1;
1872        }
1873    }
1874    else if (isGCN12 && ((src0Op.vopMods|src1Op.vopMods) & ~VOPOP_SEXT)!=0 && !sextFlags)
1875        // if all pass we check we promote VOP3 if only operand modifiers expect sext()
1876        vop3 = true;
1877   
1878    if (vop3 && (src0Op.range.isVal(255) || src1Op.range.isVal(255)))
1879        ASM_FAIL_BY_ERROR(instrPlace, "Literal in VOP3 encoding is illegal")
1880   
1881    if (!checkGCNVOPEncoding(asmr, arch, instrPlace, gcnVOPEnc, gcnInsn.mode, &extraMods))
1882        return false;
1883   
1884    if (isGCN14 && extraMods.needSDWA && ((modifiers & VOP3_CLAMP)!=0 || (modifiers&3)!=0))
1885        ASM_FAIL_BY_ERROR(instrPlace, "Modifiers CLAMP and OMOD is illegal in SDWAB")
1886   
1887    if (src0OpExpr!=nullptr)
1888        src0OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1889                      output.size()));
1890    if (src1OpExpr!=nullptr)
1891        src1OpExpr->setTarget(AsmExprTarget(GCNTGT_LITIMM, asmr.currentSection,
1892                      output.size()));
1893   
1894    // put data (instruction words)
1895    cxuint wordsNum = 1;
1896    uint32_t words[2];
1897    if (!vop3)
1898    {
1899        // VOPC encoding
1900        const uint32_t dstMods = (isGCN14 ? 0x10000 : 0) |
1901                ((isGCN14 && !dstReg.isVal(106)) ? ((dstReg.bstart()|0x80)<<8) : 0);
1902       
1903        encodeVOPWords(0x7c000000U | (uint32_t(gcnInsn.code1)<<17) |
1904                (uint32_t(src1Op.range.bstart()&0xff)<<9),
1905                modifiers, extraMods, src0Op, src1Op, 0, 0,
1906                dstMods, wordsNum, words);
1907    }
1908    else
1909        // VOP3 encoding
1910        encodeVOP3Words(arch, gcnInsn, modifiers, opMods, false,
1911                dstReg, RegRange{}, RegRange{}, src0Op, src1Op, wordsNum, words);
1912   
1913    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
1914        return false;
1915    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
1916            reinterpret_cast<cxbyte*>(words + wordsNum));
1917    /// prevent freeing expression
1918    src0OpExpr.release();
1919    src1OpExpr.release();
1920    // update register pool (VGPR and SGPR counting)
1921    if (dstReg && !dstReg.isRegVar())
1922    {
1923        updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
1924        updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
1925    }
1926    if (src0Op.range && !src0Op.range.isRegVar())
1927        updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
1928    if (src1Op.range && !src1Op.range.isRegVar())
1929        updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
1930    return true;
1931}
1932
1933bool GCNAsmUtils::parseVOP3Encoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
1934                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
1935                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
1936                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
1937{
1938    bool good = true;
1939    const GCNInsnMode mode1 = (gcnInsn.mode & GCN_MASK1);
1940    const GCNInsnMode mode2 = (gcnInsn.mode & GCN_MASK2);
1941    const bool isGCN12 = (arch & ARCH_GCN_1_2_4)!=0;
1942    const bool isGCN14 = (arch & ARCH_GCN_1_4)!=0;
1943    const bool vop3p = (gcnInsn.mode & GCN_VOP3_VOP3P) != 0;
1944    if (gcnVOPEnc!=GCNVOPEnc::NORMAL)
1945        ASM_FAIL_BY_ERROR(instrPlace, "DPP and SDWA encoding is illegal for VOP3")
1946   
1947    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
1948    RegRange dstReg(0, 0);
1949    RegRange sdstReg(0, 0);
1950    GCNOperand src0Op{};
1951    GCNOperand src1Op{};
1952    GCNOperand src2Op{};
1953   
1954    const bool vccImplRead = (gcnInsn.mode & GCN_VCC_IMPL_READ)!=0;
1955    const bool vccImplWrite = (gcnInsn.mode & GCN_VCC_IMPL_WRITE)!=0;
1956   
1957    const bool is128Ops = (gcnInsn.mode & 0x7000) == GCN_VOP3_DS2_128;
1958    bool modHigh = false;
1959    cxbyte modifiers = 0;
1960    const Flags vop3Mods = ((gcnInsn.encoding == GCNENC_VOP3B) ?
1961            INSTROP_VOP3NEG : INSTROP_VOP3MODS | INSTROP_NOSEXT) |
1962            (vop3p ? INSTROP_VOP3P : 0);
1963   
1964    // by default OPSEL_HI is [1,1,1] in vop3p instructions
1965    VOPOpModifiers opMods{ 0, 0, 0, cxbyte(vop3p ? 7<<4 : 0) };
1966    cxuint operands = 1;
1967    if (mode1 != GCN_VOP_ARG_NONE)
1968    {
1969        gcnAsm->setCurrentRVU(0);
1970        // parse DST (
1971        if ((gcnInsn.mode&GCN_VOP3_DST_SGPR)==0)
1972            good &= parseVRegRange(asmr, linePtr, dstReg,
1973                       (is128Ops) ? 4 : ((gcnInsn.mode&GCN_REG_DST_64)?2:1),
1974                       GCNFIELD_VOP3_VDST, true, INSTROP_SYMREGRANGE|INSTROP_WRITE);
1975        else // SGPRS as dest
1976            good &= parseSRegRange(asmr, linePtr, dstReg, arch,
1977                       (gcnInsn.mode&GCN_REG_DST_64)?2:1, GCNFIELD_VOP3_SDST0, true,
1978                       INSTROP_SYMREGRANGE|INSTROP_SGPR_UNALIGNED|INSTROP_WRITE);
1979        if (!skipRequiredComma(asmr, linePtr))
1980            return false;
1981       
1982        if (gcnInsn.encoding == GCNENC_VOP3B &&
1983            (mode1 == GCN_DS2_VCC || mode1 == GCN_DST_VCC || mode1 == GCN_DST_VCC_VSRC2 ||
1984             mode1 == GCN_S0EQS12)) /* VOP3b */
1985        {
1986            // SDST (VCC) (2 SGPR's)
1987            gcnAsm->setCurrentRVU(1);
1988            good &= parseSRegRange(asmr, linePtr, sdstReg, arch, 2, GCNFIELD_VOP3_SDST1,
1989                       true, INSTROP_SYMREGRANGE|INSTROP_WRITE|INSTROP_SGPR_UNALIGNED);
1990            if (!skipRequiredComma(asmr, linePtr))
1991                return false;
1992        }
1993        else if (vccImplRead)
1994        {
1995            gcnAsm->setCurrentRVU(1);
1996            gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
1997                        uint16_t(106), uint16_t(107), GCNFIELD_VOP_VCC_IMPL,
1998                        ASMRVU_READ, 0 });
1999        }
2000        else if (vccImplWrite)
2001        {
2002            gcnAsm->setCurrentRVU(1);
2003            gcnAsm->setRegVarUsage({ size_t(asmr.currentOutPos), nullptr,
2004                        uint16_t(106), uint16_t(107), GCNFIELD_VOP_VCC_IMPL,
2005                        ASMRVU_WRITE, 0 });
2006        }
2007       
2008        const Flags literalConstsFlags = (mode2==GCN_FLOATLIT) ? INSTROP_FLOAT :
2009                (mode2==GCN_F16LIT) ? INSTROP_F16 : INSTROP_INT;
2010       
2011        cxuint regsNum;
2012        if (mode2 != GCN_VOP3_VINTRP)
2013        {
2014            // parse SRC0 (can be VGPR, SGPR, scalar source, constant, LDS)
2015            gcnAsm->setCurrentRVU(2);
2016            regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
2017            good &= parseOperand(asmr, linePtr, src0Op, nullptr, arch, regsNum,
2018                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
2019                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_LDS|
2020                    vop3Mods|INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR|INSTROP_READ,
2021                    GCNFIELD_VOP3_SRC0);
2022            operands++;
2023        }
2024       
2025        if (mode2 == GCN_VOP3_VINTRP)
2026        {
2027            // if VINTRP instruction
2028            gcnAsm->setCurrentRVU(3);
2029            if (mode1 != GCN_P0_P10_P20)
2030            {
2031                good &= parseOperand(asmr, linePtr, src1Op, nullptr, arch, 1,
2032                        INSTROP_VREGS|vop3Mods|INSTROP_READ, GCNFIELD_VOP3_SRC1);
2033            }
2034            else /* P0_P10_P20 */
2035                good &= parseVINTRP0P10P20(asmr, linePtr, src1Op.range);
2036           
2037            if (!skipRequiredComma(asmr, linePtr))
2038                return false;
2039           
2040            cxbyte attr;
2041            good &= parseVINTRPAttr(asmr, linePtr, attr);
2042            attr = ((attr&3)<<6) | ((attr&0xfc)>>2);
2043            src0Op.range = { attr, uint16_t(attr+1) };
2044           
2045            if ((gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2)
2046            {
2047                if (!skipRequiredComma(asmr, linePtr))
2048                    return false;
2049                // parse SRC2 (VGPR, SGPR)
2050                gcnAsm->setCurrentRVU(4);
2051                good &= parseOperand(asmr, linePtr, src2Op, nullptr, arch,
2052                    (gcnInsn.mode&GCN_REG_SRC2_64)?2:1, vop3Mods|INSTROP_SGPR_UNALIGNED|
2053                    INSTROP_VREGS|INSTROP_SREGS|INSTROP_READ, GCNFIELD_VOP3_SRC2);
2054            }
2055            // high and vop3
2056            const char* end = asmr.line+asmr.lineSize;
2057            bool haveOpsel = false;
2058            bool haveNeg = false, haveAbs = false;
2059            // own parse VINTRP modifiers with some VOP3 modifiers
2060            while (true)
2061            {
2062                bool alreadyModDefined = false;
2063                skipSpacesToEnd(linePtr, end);
2064                if (linePtr==end)
2065                    break;
2066                char modName[10];
2067                const char* modPlace = linePtr;
2068                if (!getNameArgS(asmr, 10, modName, linePtr, "VINTRP modifier"))
2069                    continue;
2070                if (::strcmp(modName, "high")==0)
2071                    good &= parseModEnable(asmr, linePtr, modHigh, "high modifier");
2072                else if (::strcmp(modName, "vop3")==0)
2073                {
2074                    bool vop3Mod = false;
2075                    good &= parseModEnable(asmr, linePtr, vop3Mod, "vop3 modifier");
2076                    modifiers = (modifiers & ~VOP3_VOP3) | (vop3Mod ? VOP3_VOP3 : 0);
2077                }
2078                else if (parseSingleOMODCLAMP(asmr, linePtr, modPlace, modName, arch,
2079                        modifiers, opMods, 
2080                        (gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2 ? 4 : 3, PARSEVOP_WITHCLAMP, haveAbs, haveNeg,
2081                        alreadyModDefined, good))
2082                {   // do nothing
2083                }
2084                else if (::strcmp(modName, "op_sel")==0)
2085                {
2086                    // op_sel with boolean array
2087                    uint32_t opselVal = 0;
2088                    if (linePtr!=end && *linePtr==':')
2089                    {
2090                        linePtr++;
2091                        if (parseImmWithBoolArray(asmr, linePtr, opselVal, 4, WS_UNSIGNED))
2092                        {
2093                            opMods.opselMod = opselVal;
2094                            if (haveOpsel)
2095                                asmr.printWarning(modPlace, "Opsel is already defined");
2096                            haveOpsel = true;
2097                            opMods.opselMod = opselVal;
2098                        }
2099                    }
2100                    else
2101                        good = false;
2102                }
2103                else
2104                    ASM_NOTGOOD_BY_ERROR(modPlace, "Unknown VINTRP modifier")
2105            }
2106            if (modHigh)
2107            {
2108                src0Op.range.start+=0x100;
2109                src0Op.range.end+=0x100;
2110            }
2111        }
2112        else if (mode1 != GCN_SRC12_NONE)
2113        {
2114            // if encoding have two or three source operands
2115            if (!skipRequiredComma(asmr, linePtr))
2116                return false;
2117            regsNum = (gcnInsn.mode&GCN_REG_SRC1_64)?2:1;
2118            // parse SRC1 (can be VGPR, SGPR, source scalar, constant)
2119            gcnAsm->setCurrentRVU(3);
2120            good &= parseOperand(asmr, linePtr, src1Op, nullptr, arch, regsNum,
2121                    correctOpType(regsNum, literalConstsFlags)|INSTROP_VREGS|
2122                    INSTROP_SGPR_UNALIGNED|INSTROP_SSOURCE|INSTROP_SREGS|vop3Mods|
2123                    INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR|INSTROP_READ,
2124                    GCNFIELD_VOP3_SRC1);
2125            operands++;
2126           
2127            if (mode1 != GCN_SRC2_NONE && mode1 != GCN_DST_VCC)
2128            {
2129                if (!skipRequiredComma(asmr, linePtr))
2130                    return false;
2131                regsNum = (gcnInsn.mode&GCN_REG_SRC2_64)?2:1;
2132                // parse SRC2 (can be VGPR, SGPR, source scalar, constant)
2133                gcnAsm->setCurrentRVU(4);
2134                good &= parseOperand(asmr, linePtr, src2Op, nullptr, arch,
2135                        is128Ops ? 4 : regsNum,
2136                        correctOpType(regsNum, literalConstsFlags)|INSTROP_SGPR_UNALIGNED|
2137                        INSTROP_VREGS|INSTROP_SSOURCE|INSTROP_SREGS|INSTROP_READ|
2138                        vop3Mods|INSTROP_ONLYINLINECONSTS|INSTROP_NOLITERALERROR,
2139                        GCNFIELD_VOP3_SRC2);
2140                operands++;
2141            }
2142        }
2143    }
2144    // modifiers
2145    if (mode2 != GCN_VOP3_VINTRP)
2146        good &= parseVOPModifiers(asmr, linePtr, arch, modifiers, opMods, operands,
2147                    nullptr, ((isGCN12 || gcnInsn.encoding!=GCNENC_VOP3B) ?
2148                            PARSEVOP_WITHCLAMP : 0) |
2149                    ((isGCN14 && gcnInsn.encoding!=GCNENC_VOP3B) ?
2150                            PARSEVOP_WITHOPSEL : 0) | (vop3p ? PARSEVOP_VOP3P : 0), 3);
2151    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2152        return false;
2153   
2154    // apply VOP modifiers (abs,neg,sext) to operands from modifiers
2155    if (src0Op)
2156        src0Op.vopMods |= ((opMods.absMod&1) ? VOPOP_ABS : 0) |
2157                ((opMods.negMod&1) ? VOPOP_NEG : 0) |
2158                ((opMods.sextMod&1) ? VOPOP_SEXT : 0);
2159    if (src1Op)
2160        src1Op.vopMods |= ((opMods.absMod&2) ? VOPOP_ABS : 0) |
2161                ((opMods.negMod&2) ? VOPOP_NEG : 0) |
2162                ((opMods.sextMod&2) ? VOPOP_SEXT : 0);
2163    if (src2Op)
2164        src2Op.vopMods |= ((opMods.absMod&4) ? VOPOP_ABS : 0) |
2165                ((opMods.negMod&4) ? VOPOP_NEG : 0);
2166   
2167    if (mode2 != GCN_VOP3_VINTRP)
2168    {
2169        // count SGPR operands readed by instruction
2170        cxuint numSgprToRead = 0;
2171        //if (src0Op.range.start<108)
2172        if (src0Op.range.isSGPR())
2173            numSgprToRead++;
2174        //if (src1Op && src1Op.range.start<108 &&
2175        if (src1Op && src1Op.range.isSGPR() &&
2176                    !regRangeCanEqual(src0Op.range, src1Op.range))
2177            numSgprToRead++;
2178        //if (src2Op && src2Op.range.start<108 &&
2179        if (src2Op && src2Op.range.isSGPR())
2180        {
2181            if (!regRangeCanEqual(src0Op.range, src2Op.range) &&
2182                !regRangeCanEqual(src1Op.range, src2Op.range))
2183                numSgprToRead++;
2184        }
2185       
2186        if ((arch & ARCH_GCN_1_5)==0 && vccImplRead)
2187        {
2188            if (numSgprToRead >= 1)
2189                /* include VCCs (???) */
2190                ASM_FAIL_BY_ERROR(instrPlace, "No SGPR can be readed in vccImplRead")
2191        }
2192        else if ((arch & ARCH_GCN_1_5)==0 ||
2193            // except vccImplReads for NAVI
2194            vccImplRead)
2195        {
2196            if (numSgprToRead >= 2)
2197                /* include VCCs (???) */
2198                ASM_FAIL_BY_ERROR(instrPlace, "More than one SGPR to read in instruction")
2199        }
2200        else
2201        {   // NAVI
2202            if (numSgprToRead >= 3)
2203                /* include VCCs (???) */
2204                ASM_FAIL_BY_ERROR(instrPlace, "More than two SGPRs to read in instruction")
2205        }
2206    }
2207   
2208    // put data (instruction words)
2209    uint32_t words[2];
2210    cxuint wordsNum = 2;
2211    if (gcnInsn.encoding == GCNENC_VOP3B)
2212    {
2213        // VOP3B encoding
2214        if (!isGCN12)
2215            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<17) |
2216                (dstReg.bstart()&0xff) | (uint32_t(sdstReg.bstart())<<8));
2217        else
2218            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<16) |
2219                (dstReg.bstart()&0xff) | (uint32_t(sdstReg.bstart())<<8) |
2220                ((modifiers&VOP3_CLAMP) ? 0x8000 : 0));
2221    }
2222    else
2223    {
2224        // VOP3A
2225        if (!isGCN12)
2226            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<17) |
2227                (dstReg.bstart()&0xff) | ((modifiers&VOP3_CLAMP) ? 0x800: 0) |
2228                ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2229                ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2230                ((src2Op.vopMods & VOPOP_ABS) ? 0x400 : 0));
2231        else if (mode2 != GCN_VOP3_VINTRP || mode1 == GCN_NEW_OPCODE ||
2232            (gcnInsn.mode & GCN_VOP3_MASK3) == GCN_VINTRP_SRC2 ||
2233            (modifiers & VOP3_VOP3)!=0 || (src0Op.range.bstart()&0x100)!=0/* high */ ||
2234            (modifiers & (VOP3_CLAMP|3)) != 0 || opMods.opselMod != 0 ||
2235            src1Op.vopMods!=0 || src2Op.vopMods!=0)
2236            // new VOP3 for GCN 1.2
2237            SLEV(words[0], 0xd0000000U | (uint32_t(gcnInsn.code1)<<16) |
2238                (dstReg.bstart()&0xff) | ((modifiers&VOP3_CLAMP) ? 0x8000: 0) |
2239                (vop3p ? (uint32_t(opMods.negMod>>4) << 8) /* VOP3P NEG_HI */ :
2240                    ((src0Op.vopMods & VOPOP_ABS) ? 0x100 : 0) |
2241                    ((src1Op.vopMods & VOPOP_ABS) ? 0x200 : 0) |
2242                    ((src2Op.vopMods & VOPOP_ABS) ? 0x400 : 0)) |
2243                (((opMods.opselMod & 64) !=0) ? 0x4000 : 0) |
2244                ((opMods.opselMod&15) << 11));
2245        else // VINTRP
2246        {
2247            SLEV(words[0], 0xd4000000U | (src1Op.range.bstart()&0xff) |
2248                (uint32_t(src0Op.range.bstart()>>6)<<8) |
2249                (uint32_t(src0Op.range.bstart()&63)<<10) |
2250                (uint32_t(gcnInsn.code2)<<16) | (uint32_t(dstReg.bstart()&0xff)<<18));
2251            // VOP3 VINTRP have only one word instruction
2252            wordsNum--;
2253        }
2254    }
2255    if (wordsNum==2)
2256        // second instruction's word
2257        SLEV(words[1], src0Op.range.bstart() | (uint32_t(src1Op.range.bstart())<<9) |
2258                (uint32_t(src2Op.range.bstart())<<18) |
2259                (vop3p ? ((uint32_t(opMods.opselMod>>4)&3)<<27) :
2260                 (uint32_t(modifiers & 3) << 27)) |
2261                /* in VOP3P is also NEG_LO */
2262                ((src0Op.vopMods & VOPOP_NEG) ? (1U<<29) : 0) |
2263                ((src1Op.vopMods & VOPOP_NEG) ? (1U<<30) : 0) |
2264                ((src2Op.vopMods & VOPOP_NEG) ? (1U<<31) : 0));
2265   
2266    if (!checkGCNEncodingSize(asmr, instrPlace, gcnEncSize, wordsNum))
2267        return false;
2268    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2269            reinterpret_cast<cxbyte*>(words + wordsNum));
2270   
2271    // update register pool (VGPR and SGPR counting)
2272    if (dstReg && !dstReg.isRegVar())
2273    {
2274        if (dstReg.start>=256)
2275            updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2276        else // sgprs
2277        {
2278            updateSGPRsNum(gcnRegs.sgprsNum, dstReg.end-1, arch);
2279            updateRegFlags(gcnRegs.regFlags, dstReg.start, arch);
2280        }
2281    }
2282    if (sdstReg && !sdstReg.isRegVar())
2283    {
2284        updateSGPRsNum(gcnRegs.sgprsNum, sdstReg.end-1, arch);
2285        updateRegFlags(gcnRegs.regFlags, sdstReg.start, arch);
2286    }
2287    if (mode2 != GCN_VOP3_VINTRP)
2288    {
2289        // count for SSRC0 and SSRC1 for VOP3A/B encoding (not VINTRP) ???
2290        if (src0Op.range && !src0Op.range.isRegVar() && src0Op.range.start < 256)
2291            updateRegFlags(gcnRegs.regFlags, src0Op.range.start, arch);
2292        if (src1Op.range && !src1Op.range.isRegVar() && src1Op.range.start < 256)
2293            updateRegFlags(gcnRegs.regFlags, src1Op.range.start, arch);
2294    }
2295    if (src2Op.range && !src2Op.range.isRegVar() && src2Op.range.start < 256)
2296        updateRegFlags(gcnRegs.regFlags, src2Op.range.start, arch);
2297    return true;
2298}
2299
2300bool GCNAsmUtils::parseVINTRPEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2301                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
2302                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2303                  GCNEncSize gcnEncSize, GCNVOPEnc gcnVOPEnc)
2304{
2305    bool good = true;
2306    RegRange dstReg(0, 0);
2307    RegRange srcReg(0, 0);
2308    if (gcnEncSize==GCNEncSize::BIT64)
2309        ASM_FAIL_BY_ERROR(instrPlace, "Only 32-bit size for VINTRP encoding")
2310    if (gcnVOPEnc!=GCNVOPEnc::NORMAL)
2311        ASM_FAIL_BY_ERROR(instrPlace, "DPP and SDWA encoding is illegal for VOP3")
2312   
2313    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2314   
2315    // parse DST (VGPR)
2316    gcnAsm->setCurrentRVU(0);
2317    good &= parseVRegRange(asmr, linePtr, dstReg, 1, GCNFIELD_VINTRP_VDST, true,
2318                        INSTROP_SYMREGRANGE|INSTROP_WRITE);
2319    if (!skipRequiredComma(asmr, linePtr))
2320        return false;
2321   
2322    if ((gcnInsn.mode & GCN_MASK1) == GCN_P0_P10_P20)
2323        good &= parseVINTRP0P10P20(asmr, linePtr, srcReg);
2324    else
2325    {
2326        // regular vector register
2327        gcnAsm->setCurrentRVU(1);
2328        good &= parseVRegRange(asmr, linePtr, srcReg, 1, GCNFIELD_VINTRP_VSRC0, true,
2329                        INSTROP_SYMREGRANGE|INSTROP_READ);
2330    }
2331   
2332    if (!skipRequiredComma(asmr, linePtr))
2333        return false;
2334   
2335    cxbyte attrVal;
2336    good &= parseVINTRPAttr(asmr, linePtr, attrVal);
2337   
2338    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2339        return false;
2340    /* put data (instruction word */
2341    uint32_t word;
2342    SLEV(word, 0xc8000000U | (srcReg.bstart()&0xff) | (uint32_t(attrVal&0xff)<<8) |
2343            (uint32_t(gcnInsn.code1)<<16) | (uint32_t(dstReg.bstart()&0xff)<<18));
2344    output.insert(output.end(), reinterpret_cast<cxbyte*>(&word),
2345            reinterpret_cast<cxbyte*>(&word)+4);
2346    // update register pool (VGPR counting)
2347    if (!dstReg.isRegVar())
2348        updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2349    return true;
2350}
2351
2352bool GCNAsmUtils::parseDSEncoding(Assembler& asmr, const GCNAsmInstruction& gcnInsn,
2353                  const char* instrPlace, const char* linePtr, GPUArchMask arch,
2354                  std::vector<cxbyte>& output, GCNAssembler::Regs& gcnRegs,
2355                  GCNEncSize gcnEncSize)
2356{
2357    const char* end = asmr.line+asmr.lineSize;
2358    bool good = true;
2359    if (gcnEncSize==GCNEncSize::BIT32)
2360        ASM_FAIL_BY_ERROR(instrPlace, "Only 64-bit size for DS encoding")
2361    RegRange dstReg(0, 0);
2362    RegRange addrReg(0, 0);
2363    RegRange data0Reg(0, 0), data1Reg(0, 0);
2364   
2365    bool beforeData = false;
2366    bool vdstUsed = false;
2367    cxuint delayRVU = UINT_MAX;
2368    cxuint destDelayRVU = UINT_MAX;
2369    bool secondDelay = false;
2370   
2371    GCNAssembler* gcnAsm = static_cast<GCNAssembler*>(asmr.isaAssembler);
2372   
2373    if (((gcnInsn.mode & GCN_ADDR_SRC) != 0 || (gcnInsn.mode & GCN_ONLYDST) != 0) &&
2374            (gcnInsn.mode & GCN_ONLY_SRC) == 0)
2375    {
2376        /* vdst is dst */
2377        cxuint regsNum = (gcnInsn.mode&GCN_REG_DST_64)?2:1;
2378        if ((gcnInsn.mode&GCN_DS_96) != 0)
2379            regsNum = 3;
2380        if ((gcnInsn.mode&GCN_DS_128) != 0 || (gcnInsn.mode&GCN_DST128) != 0)
2381            regsNum = 4;
2382        gcnAsm->setCurrentRVU(0);
2383        good &= parseVRegRange(asmr, linePtr, dstReg, regsNum, GCNFIELD_DS_VDST, true,
2384                    INSTROP_SYMREGRANGE|INSTROP_WRITE);
2385        vdstUsed = beforeData = true;
2386        destDelayRVU = 0;
2387    }
2388   
2389    if ((gcnInsn.mode & GCN_ONLYDST) == 0 && (gcnInsn.mode & GCN_ONLY_SRC) == 0)
2390    {
2391        // parse ADDR as first (VGPR)
2392        if (vdstUsed)
2393            if (!skipRequiredComma(asmr, linePtr))
2394                return false;
2395        gcnAsm->setCurrentRVU(1);
2396        good &= parseVRegRange(asmr, linePtr, addrReg, 1, GCNFIELD_DS_ADDR, true,
2397                    INSTROP_SYMREGRANGE|INSTROP_READ);
2398        beforeData = true;
2399    }
2400   
2401    const uint16_t srcMode = (gcnInsn.mode & GCN_SRCS_MASK);
2402   
2403    if ((gcnInsn.mode & GCN_ONLYDST) == 0 &&
2404        (gcnInsn.mode & (GCN_ADDR_DST|GCN_ADDR_SRC)) != 0 && srcMode != GCN_NOSRC)
2405    {
2406        /* two vdata */
2407        if (beforeData)
2408            if (!skipRequiredComma(asmr, linePtr))
2409                return false;
2410       
2411        cxuint regsNum = (gcnInsn.mode&GCN_REG_SRC0_64)?2:1;
2412        if ((gcnInsn.mode&GCN_DS_96) != 0)
2413            regsNum = 3;
2414        if ((gcnInsn.mode&GCN_DS_128) != 0)
2415            regsNum = 4;
2416        // parse VDATA0 (VGPR)
2417        gcnAsm->setCurrentRVU(2);
2418        good &= parseVRegRange(asmr, linePtr, data0Reg, regsNum, GCNFIELD_DS_DATA0, true,
2419                    INSTROP_SYMREGRANGE|INSTROP_READ);
2420        if (srcMode == GCN_2SRCS)
2421        {
2422            // instruction have second source
2423            if (!skipRequiredComma(asmr, linePtr))
2424                return false;
2425            // parse VDATA0 (VGPR)
2426            gcnAsm->setCurrentRVU(3);
2427            good &= parseVRegRange(asmr, linePtr, data1Reg,
2428                       (gcnInsn.mode&GCN_REG_SRC1_64)?2:1, GCNFIELD_DS_DATA1, true,
2429                               INSTROP_SYMREGRANGE|INSTROP_READ);
2430            secondDelay = true;
2431        }
2432        delayRVU = 2;
2433    }
2434   
2435    bool haveGds = false;
2436    std::unique_ptr<AsmExpression> offsetExpr, offset2Expr;
2437    char name[10];
2438    uint16_t offset = 0;
2439    cxbyte offset1 = 0, offset2 = 0;
2440    bool haveOffset = false, haveOffset2 = false;
2441    // parse DS modifiers
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            // single offset
2459            if (::strcmp(name, "offset") == 0)
2460            {
2461                if (parseModImm(asmr, linePtr, offset, &offsetExpr, "offset",
2462                            0, WS_UNSIGNED))
2463                {
2464                    // detect multiple occurrences
2465                    if (haveOffset)
2466                        asmr.printWarning(modPlace, "Offset is already defined");
2467                    haveOffset = true;
2468                }
2469                else
2470                    good = false;
2471            }
2472            else
2473                ASM_NOTGOOD_BY_ERROR(modPlace, "Expected 'offset'")
2474        }
2475        else
2476        {
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                    {
2487                        /* offset0 */
2488                        if (parseImm(asmr, linePtr, offset1, &offsetExpr, 0, WS_UNSIGNED))
2489                        {
2490                            // detect multiple occurrences
2491                            if (haveOffset)
2492                                asmr.printWarning(modPlace, "Offset0 is already defined");
2493                            haveOffset = true;
2494                        }
2495                        else
2496                            good = false;
2497                    }
2498                    else
2499                    {
2500                        /* offset1 */
2501                        if (parseImm(asmr, linePtr, offset2, &offset2Expr, 0, WS_UNSIGNED))
2502                        {
2503                            // detect multiple occurrences
2504                            if (haveOffset2)
2505                                asmr.printWarning(modPlace, "Offset1 is already defined");
2506                            haveOffset2 = true;
2507                        }
2508                        else
2509                            good = false;
2510                    }
2511                }
2512                else
2513                    ASM_NOTGOOD_BY_ERROR(linePtr, "Expected ':' before offset")
2514            }
2515            else
2516                ASM_NOTGOOD_BY_ERROR(modPlace,
2517                                "Expected 'offset', 'offset0' or 'offset1'")
2518        }
2519    }
2520   
2521    if ((gcnInsn.mode & GCN_2OFFSETS) != 0)
2522        offset = offset1 | (offset2<<8);
2523   
2524    if (!good || !checkGarbagesAtEnd(asmr, linePtr))
2525        return false;
2526   
2527    // register Delayed results
2528    if (destDelayRVU != UINT_MAX)
2529        gcnAsm->delayedOps[0] = { output.size(), gcnAsm->instrRVUs[destDelayRVU].regVar,
2530                    gcnAsm->instrRVUs[destDelayRVU].rstart,
2531                    gcnAsm->instrRVUs[destDelayRVU].rend,
2532                    1, haveGds ? GCNDELOP_GDSOP : GCNDELOP_LDSOP, GCNDELOP_NONE,
2533                    gcnAsm->instrRVUs[destDelayRVU].rwFlags };
2534   
2535    if (delayRVU != UINT_MAX)
2536    {
2537        gcnAsm->delayedOps[1] = { output.size(), gcnAsm->instrRVUs[delayRVU].regVar,
2538                    gcnAsm->instrRVUs[delayRVU].rstart, gcnAsm->instrRVUs[delayRVU].rend,
2539                    1, haveGds ? GCNDELOP_GDSOP : GCNDELOP_LDSOP,
2540                    haveGds ? GCNDELOP_EXPORT : GCNDELOP_NONE,
2541                    gcnAsm->instrRVUs[delayRVU].rwFlags,
2542                    cxbyte(haveGds ? ASMRVU_READ : 0) };
2543        if (secondDelay)
2544            gcnAsm->delayedOps[2] = { output.size(),
2545                    gcnAsm->instrRVUs[delayRVU+1].regVar,
2546                    gcnAsm->instrRVUs[delayRVU+1].rstart,
2547                    gcnAsm->instrRVUs[delayRVU+1].rend,
2548                    1, haveGds ? GCNDELOP_GDSOP : GCNDELOP_LDSOP,
2549                    haveGds ? GCNDELOP_EXPORT : GCNDELOP_NONE,
2550                    gcnAsm->instrRVUs[delayRVU+1].rwFlags,
2551                    cxbyte(haveGds ? ASMRVU_READ : 0) };
2552    }
2553    if ((gcnInsn.mode & GCN_SRC_ADDR2) != 0)
2554        // register for DS_*_SRC2_* instructions
2555        gcnAsm->delayedOps[0] = { output.size(), nullptr, uint16_t(0), uint16_t(0),
2556                1, haveGds ? GCNDELOP_GDSOP : GCNDELOP_LDSOP,
2557                haveGds ? GCNDELOP_EXPORT : GCNDELOP_NONE, cxbyte(0) };
2558   
2559    if ((gcnInsn.mode&GCN_ONLYGDS) != 0 && !haveGds)
2560        ASM_FAIL_BY_ERROR(instrPlace, "Instruction requires GDS modifier")
2561   
2562    // set target expressions for offsets (if needed)
2563    if (offsetExpr!=nullptr)
2564        offsetExpr->setTarget(AsmExprTarget((gcnInsn.mode & GCN_2OFFSETS) ?
2565                    GCNTGT_DSOFFSET8_0 : GCNTGT_DSOFFSET16, asmr.currentSection,
2566                    output.size()));
2567    if (offset2Expr!=nullptr)
2568        offset2Expr->setTarget(AsmExprTarget(GCNTGT_DSOFFSET8_1, asmr.currentSection,
2569                    output.size()));
2570    // put data (two instruction words)
2571    uint32_t words[2];
2572    if ((arch & ARCH_GCN_1_2_4)==0)
2573        SLEV(words[0], 0xd8000000U | uint32_t(offset) | (haveGds ? 0x20000U : 0U) |
2574                (uint32_t(gcnInsn.code1)<<18));
2575    else
2576        SLEV(words[0], 0xd8000000U | uint32_t(offset) | (haveGds ? 0x10000U : 0U) |
2577                (uint32_t(gcnInsn.code1)<<17));
2578    SLEV(words[1], (addrReg.bstart()&0xff) | (uint32_t(data0Reg.bstart()&0xff)<<8) |
2579            (uint32_t(data1Reg.bstart()&0xff)<<16) | (uint32_t(dstReg.bstart()&0xff)<<24));
2580    output.insert(output.end(), reinterpret_cast<cxbyte*>(words),
2581            reinterpret_cast<cxbyte*>(words + 2));
2582   
2583    offsetExpr.release();
2584    offset2Expr.release();
2585    // update register pool (VGPR counting)
2586    if (dstReg && !dstReg.isRegVar())
2587        updateVGPRsNum(gcnRegs.vgprsNum, dstReg.end-257);
2588    return true;
2589}
2590
2591};
Note: See TracBrowser for help on using the repository browser.