1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
4 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
5 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
9 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
18 #if defined(TARGET_RISCV64)
20 /*****************************************************************************/
21 /*****************************************************************************/
27 /*****************************************************************************/
29 const instruction emitJumpKindInstructions[] = {
32 #define JMP_SMALL(en, rev, ins) INS_##ins,
36 const emitJumpKind emitReverseJumpKinds[] = {
39 #define JMP_SMALL(en, rev, ins) EJ_##rev,
43 /*****************************************************************************
44 * Look up the instruction for a jump kind
47 /*static*/ instruction emitter::emitJumpKindToIns(emitJumpKind jumpKind)
49 assert((unsigned)jumpKind < ArrLen(emitJumpKindInstructions));
50 return emitJumpKindInstructions[jumpKind];
53 /*****************************************************************************
54 * Reverse the conditional jump
57 /*static*/ emitJumpKind emitter::emitReverseJumpKind(emitJumpKind jumpKind)
59 assert(jumpKind < EJ_COUNT);
60 return emitReverseJumpKinds[jumpKind];
63 /*****************************************************************************
65 * Return the allocated size (in bytes) of the given instruction descriptor.
68 size_t emitter::emitSizeOfInsDsc(instrDesc* id) const
70 if (emitIsSmallInsDsc(id))
71 return SMALL_IDSC_SIZE;
73 insOpts insOp = id->idInsOpt();
80 return sizeof(instrDescJmp);
83 if (id->idIsLargeCall())
85 /* Must be a "fat" call descriptor */
86 return sizeof(instrDescCGCA);
90 assert(!id->idIsLargeDsp());
91 assert(!id->idIsLargeCns());
92 return sizeof(instrDesc);
100 return sizeof(instrDesc);
102 NO_WAY("unexpected instruction descriptor format");
107 bool emitter::emitInsWritesToLclVarStackLoc(instrDesc* id)
109 if (!id->idIsLclVar())
112 instruction ins = id->idIns();
114 // This list is related to the list of instructions used to store local vars in emitIns_S_R().
115 // We don't accept writing to float local vars.
134 /*static*/ const BYTE CodeGenInterface::instInfo[] =
136 #define INST(id, nm, info, e1) info,
141 inline bool emitter::emitInsMayWriteToGCReg(instruction ins)
143 assert(ins != INS_invalid);
144 return (ins <= INS_remuw) && (ins >= INS_mov) && !(ins >= INS_jal && ins <= INS_bgeu && ins != INS_jalr) &&
145 (CodeGenInterface::instInfo[ins] & ST) == 0
150 //------------------------------------------------------------------------
151 // emitInsLoad: Returns true if the instruction is some kind of load instruction.
153 bool emitter::emitInsIsLoad(instruction ins)
155 // We have pseudo ins like lea which are not included in emitInsLdStTab.
156 if (ins < ArrLen(CodeGenInterface::instInfo))
157 return (CodeGenInterface::instInfo[ins] & LD) != 0;
162 //------------------------------------------------------------------------
163 // emitInsIsStore: Returns true if the instruction is some kind of store instruction.
165 bool emitter::emitInsIsStore(instruction ins)
167 // We have pseudo ins like lea which are not included in emitInsLdStTab.
168 if (ins < ArrLen(CodeGenInterface::instInfo))
169 return (CodeGenInterface::instInfo[ins] & ST) != 0;
174 //-------------------------------------------------------------------------
175 // emitInsIsLoadOrStore: Returns true if the instruction is some kind of load/store instruction.
177 bool emitter::emitInsIsLoadOrStore(instruction ins)
179 // We have pseudo ins like lea which are not included in emitInsLdStTab.
180 if (ins < ArrLen(CodeGenInterface::instInfo))
181 return (CodeGenInterface::instInfo[ins] & (LD | ST)) != 0;
186 /*****************************************************************************
188 * Returns the specific encoding of the given CPU instruction.
191 inline emitter::code_t emitter::emitInsCode(instruction ins /*, insFormat fmt*/)
193 code_t code = BAD_CODE;
196 const static code_t insCode[] =
198 #define INST(id, nm, info, e1) e1,
205 assert((code != BAD_CODE));
210 /****************************************************************************
212 * Add an instruction with no operands.
215 void emitter::emitIns(instruction ins)
217 instrDesc* id = emitNewInstr(EA_8BYTE);
220 id->idAddr()->iiaSetInstrEncode(emitInsCode(ins));
226 /*****************************************************************************
227 * emitter::emitIns_S_R(), emitter::emitIns_S_R_R() and emitter::emitIns_R_S():
229 * Add an Load/Store instruction(s): base+offset and base-addr-computing if needed.
230 * For referencing a stack-based local variable and a register
233 void emitter::emitIns_S_R(instruction ins, emitAttr attr, regNumber reg1, int varx, int offs)
235 emitIns_S_R_R(ins, attr, reg1, REG_NA, varx, offs);
238 void emitter::emitIns_S_R_R(instruction ins, emitAttr attr, regNumber reg1, regNumber tmpReg, int varx, int offs)
242 assert(tmpReg != codeGen->rsGetRsvdReg());
243 assert(reg1 != codeGen->rsGetRsvdReg());
245 emitAttr size = EA_SIZE(attr);
259 NYI_RISCV64("illegal ins within emitIns_S_R_R!");
262 } // end switch (ins)
265 /* Figure out the variable's frame position */
269 base = emitComp->lvaFrameAddress(varx, &FPbased);
271 regNumber regBase = FPbased ? REG_FPBASE : REG_SPBASE;
274 if (tmpReg == REG_NA)
285 assert(reg2 != REG_NA && reg2 != codeGen->rsGetRsvdReg());
287 if (!isValidSimm12(imm))
289 // If immediate does not fit to store immediate 12 bits, construct necessary value in rsRsvdReg()
290 // and keep tmpReg hint value unchanged.
291 assert(isValidSimm20((imm + 0x800) >> 12));
293 emitIns_R_I(INS_lui, EA_PTRSIZE, codeGen->rsGetRsvdReg(), (imm + 0x800) >> 12);
294 emitIns_R_R_R(INS_add, EA_PTRSIZE, codeGen->rsGetRsvdReg(), codeGen->rsGetRsvdReg(), reg2);
297 reg2 = codeGen->rsGetRsvdReg();
300 instrDesc* id = emitNewInstr(attr);
308 assert(isGeneralRegister(reg2));
309 code_t code = emitInsCode(ins);
310 code |= (code_t)(reg1 & 0x1f) << 20;
311 code |= (code_t)reg2 << 15;
312 code |= (((imm >> 5) & 0x7f) << 25) | ((imm & 0x1f) << 7);
314 id->idAddr()->iiaSetInstrEncode(code);
315 id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
323 * Special notes for `offs`, please see the comment for `emitter::emitIns_S_R`.
325 void emitter::emitIns_R_S(instruction ins, emitAttr attr, regNumber reg1, int varx, int offs)
329 emitAttr size = EA_SIZE(attr);
350 assert(size == EA_8BYTE);
354 NYI_RISCV64("illegal ins within emitIns_R_S!");
357 } // end switch (ins)
360 /* Figure out the variable's frame position */
364 base = emitComp->lvaFrameAddress(varx, &FPbased);
365 imm = offs < 0 ? -offs - 8 : base + offs;
367 regNumber reg2 = FPbased ? REG_FPBASE : REG_SPBASE;
369 offs = offs < 0 ? -offs - 8 : offs;
371 reg1 = (regNumber)((char)reg1 & 0x1f);
373 if ((-2048 <= imm) && (imm < 2048))
379 code = emitInsCode(ins);
380 code |= (code_t)reg1 << 7;
381 code |= (code_t)reg2 << 15;
382 code |= (imm & 0xfff) << 20;
388 assert(isValidSimm20((imm + 0x800) >> 12));
389 emitIns_R_I(INS_lui, EA_PTRSIZE, codeGen->rsGetRsvdReg(), (imm + 0x800) >> 12);
390 ssize_t imm2 = imm & 0xfff;
391 emitIns_R_R_I(INS_addi, EA_PTRSIZE, codeGen->rsGetRsvdReg(), codeGen->rsGetRsvdReg(), imm2);
394 code = emitInsCode(ins);
395 code |= (code_t)reg1 << 7;
396 code |= (code_t)reg2 << 15;
397 code |= (code_t)codeGen->rsGetRsvdReg() << 20;
401 assert(isValidSimm20((imm + 0x800) >> 12));
402 emitIns_R_I(INS_lui, EA_PTRSIZE, codeGen->rsGetRsvdReg(), (imm + 0x800) >> 12);
404 emitIns_R_R_R(INS_add, EA_PTRSIZE, codeGen->rsGetRsvdReg(), codeGen->rsGetRsvdReg(), reg2);
406 ssize_t imm2 = imm & 0xfff;
407 code = emitInsCode(ins);
408 code |= (code_t)reg1 << 7;
409 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
410 code |= (code_t)(imm2 & 0xfff) << 20;
414 instrDesc* id = emitNewInstr(attr);
420 id->idAddr()->iiaSetInstrEncode(code);
421 id->idAddr()->iiaLclVar.initLclVarAddr(varx, offs);
428 /*****************************************************************************
430 * Add an instruction with a single immediate value.
433 void emitter::emitIns_I(instruction ins, emitAttr attr, ssize_t imm)
435 code_t code = emitInsCode(ins);
440 code |= ((imm & 0xff) << 20);
443 assert(imm >= -1048576 && imm < 1048576);
444 code |= ((imm >> 12) & 0xff) << 12;
445 code |= ((imm >> 11) & 0x1) << 20;
446 code |= ((imm >> 1) & 0x3ff) << 21;
447 code |= ((imm >> 20) & 0x1) << 31;
450 NO_WAY("illegal ins within emitIns_I!");
453 instrDesc* id = emitNewInstr(attr);
456 id->idAddr()->iiaSetInstrEncode(code);
462 void emitter::emitIns_I_I(instruction ins, emitAttr attr, ssize_t cc, ssize_t offs)
464 NYI_RISCV64("emitIns_I_I-----unimplemented/unused on RISCV64 yet----");
467 /*****************************************************************************
469 * Add an instruction referencing a register and a constant.
472 void emitter::emitIns_R_I(instruction ins, emitAttr attr, regNumber reg, ssize_t imm, insOpts opt /* = INS_OPTS_NONE */)
474 code_t code = emitInsCode(ins);
480 assert(reg != REG_R0);
481 assert(isGeneralRegister(reg));
482 assert((((size_t)imm) >> 20) == 0);
485 code |= (imm & 0xfffff) << 12;
488 assert(isGeneralRegisterOrR0(reg));
489 assert(isValidSimm21(imm));
492 code |= ((imm >> 12) & 0xff) << 12;
493 code |= ((imm >> 11) & 0x1) << 20;
494 code |= ((imm >> 1) & 0x3ff) << 21;
495 code |= ((imm >> 20) & 0x1) << 31;
498 NO_WAY("illegal ins within emitIns_R_I!");
500 } // end switch (ins)
502 instrDesc* id = emitNewInstr(attr);
506 id->idAddr()->iiaSetInstrEncode(code);
512 //------------------------------------------------------------------------
513 // emitIns_Mov: Emits a move instruction
516 // ins -- The instruction being emitted
517 // attr -- The emit attribute
518 // dstReg -- The destination register
519 // srcReg -- The source register
520 // canSkip -- true if the move can be elided when dstReg == srcReg, otherwise false
521 // insOpts -- The instruction options
523 void emitter::emitIns_Mov(
524 instruction ins, emitAttr attr, regNumber dstReg, regNumber srcReg, bool canSkip, insOpts opt /* = INS_OPTS_NONE */)
526 if (!canSkip || (dstReg != srcReg))
528 if ((EA_4BYTE == attr) && (INS_mov == ins))
530 assert(isGeneralRegisterOrR0(srcReg));
531 assert(isGeneralRegisterOrR0(dstReg));
532 emitIns_R_R_I(INS_addiw, attr, dstReg, srcReg, 0);
534 else if (INS_fsgnj_s == ins || INS_fsgnj_d == ins)
536 assert(isFloatReg(srcReg));
537 assert(isFloatReg(dstReg));
538 emitIns_R_R_R(ins, attr, dstReg, srcReg, srcReg);
540 else if (genIsValidFloatReg(srcReg) || genIsValidFloatReg(dstReg))
542 emitIns_R_R(ins, attr, dstReg, srcReg);
546 assert(isGeneralRegisterOrR0(srcReg));
547 assert(isGeneralRegisterOrR0(dstReg));
548 emitIns_R_R_I(INS_addi, attr, dstReg, srcReg, 0);
553 void emitter::emitIns_Mov(emitAttr attr, regNumber dstReg, regNumber srcReg, bool canSkip)
555 if (!canSkip || dstReg != srcReg)
557 assert(attr == EA_4BYTE || attr == EA_PTRSIZE);
558 if (isGeneralRegisterOrR0(dstReg) && isGeneralRegisterOrR0(srcReg))
560 emitIns_R_R_I(attr == EA_4BYTE ? INS_addiw : INS_addi, attr, dstReg, srcReg, 0);
562 else if (isGeneralRegisterOrR0(dstReg) && genIsValidFloatReg(srcReg))
564 emitIns_R_R(attr == EA_4BYTE ? INS_fmv_x_w : INS_fmv_x_d, attr, dstReg, srcReg);
566 else if (genIsValidFloatReg(dstReg) && isGeneralRegisterOrR0(srcReg))
568 emitIns_R_R(attr == EA_4BYTE ? INS_fmv_w_x : INS_fmv_d_x, attr, dstReg, srcReg);
570 else if (genIsValidFloatReg(dstReg) && genIsValidFloatReg(srcReg))
572 emitIns_R_R_R(attr == EA_4BYTE ? INS_fsgnj_s : INS_fsgnj_d, attr, dstReg, srcReg, srcReg);
576 assert(!"Invalid registers in emitIns_Mov()\n");
581 /*****************************************************************************
583 * Add an instruction referencing two registers
586 void emitter::emitIns_R_R(
587 instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, insOpts opt /* = INS_OPTS_NONE */)
589 code_t code = emitInsCode(ins);
593 assert(isGeneralRegisterOrR0(reg1));
594 assert(isGeneralRegisterOrR0(reg2));
598 else if (INS_fmv_x_d == ins || INS_fmv_x_w == ins || INS_fclass_s == ins || INS_fclass_d == ins)
600 assert(isGeneralRegisterOrR0(reg1));
601 assert(isFloatReg(reg2));
603 code |= (reg2 & 0x1f) << 15;
605 else if (INS_fcvt_w_s == ins || INS_fcvt_wu_s == ins || INS_fcvt_w_d == ins || INS_fcvt_wu_d == ins ||
606 INS_fcvt_l_s == ins || INS_fcvt_lu_s == ins || INS_fcvt_l_d == ins || INS_fcvt_lu_d == ins)
608 assert(isGeneralRegisterOrR0(reg1));
609 assert(isFloatReg(reg2));
611 code |= (reg2 & 0x1f) << 15;
614 else if (INS_fmv_w_x == ins || INS_fmv_d_x == ins)
616 assert(isFloatReg(reg1));
617 assert(isGeneralRegisterOrR0(reg2));
618 code |= (reg1 & 0x1f) << 7;
621 else if (INS_fcvt_s_w == ins || INS_fcvt_s_wu == ins || INS_fcvt_d_w == ins || INS_fcvt_d_wu == ins ||
622 INS_fcvt_s_l == ins || INS_fcvt_s_lu == ins || INS_fcvt_d_l == ins || INS_fcvt_d_lu == ins)
624 assert(isFloatReg(reg1));
625 assert(isGeneralRegisterOrR0(reg2));
626 code |= (reg1 & 0x1f) << 7;
630 else if (INS_fcvt_s_d == ins || INS_fcvt_d_s == ins)
632 assert(isFloatReg(reg1));
633 assert(isFloatReg(reg2));
634 code |= (reg1 & 0x1f) << 7;
635 code |= (reg2 & 0x1f) << 15;
640 NYI_RISCV64("illegal ins within emitIns_R_R!");
643 instrDesc* id = emitNewInstr(attr);
648 id->idAddr()->iiaSetInstrEncode(code);
654 /*****************************************************************************
656 * Add an instruction referencing two registers and a constant.
659 void emitter::emitIns_R_R_I(
660 instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, ssize_t imm, insOpts opt /* = INS_OPTS_NONE */)
662 code_t code = emitInsCode(ins);
663 instrDesc* id = emitNewInstr(attr);
665 if ((INS_addi <= ins && INS_srai >= ins) || (INS_addiw <= ins && INS_sraiw >= ins) ||
666 (INS_lb <= ins && INS_lhu >= ins) || INS_ld == ins || INS_lw == ins || INS_jalr == ins || INS_fld == ins ||
669 assert(isGeneralRegister(reg2));
670 code |= (reg1 & 0x1f) << 7; // rd
671 code |= reg2 << 15; // rs1
672 code |= imm << 20; // imm
674 else if (INS_sd == ins || INS_sw == ins || INS_sh == ins || INS_sb == ins || INS_fsw == ins || INS_fsd == ins)
676 assert(isGeneralRegister(reg2));
677 code |= (reg1 & 0x1f) << 20; // rs2
678 code |= reg2 << 15; // rs1
679 code |= (((imm >> 5) & 0x7f) << 25) | ((imm & 0x1f) << 7); // imm
681 else if (INS_beq <= ins && INS_bgeu >= ins)
683 assert(isGeneralRegister(reg1));
684 assert(isGeneralRegister(reg2));
685 assert(isValidSimm13(imm));
689 code |= ((imm >> 11) & 0x1) << 7;
690 code |= ((imm >> 1) & 0xf) << 8;
691 code |= ((imm >> 5) & 0x3f) << 25;
692 code |= ((imm >> 12) & 0x1) << 31;
693 // TODO-RISCV64: Move jump logic to emitIns_J
694 id->idAddr()->iiaSetInstrCount(imm / sizeof(code_t));
696 else if (ins == INS_csrrs || ins == INS_csrrw || ins == INS_csrrc)
698 assert(isGeneralRegisterOrR0(reg1));
699 assert(isGeneralRegisterOrR0(reg2));
700 assert(isValidUimm12(imm));
707 NYI_RISCV64("illegal ins within emitIns_R_R_I!");
713 id->idAddr()->iiaSetInstrEncode(code);
719 /*****************************************************************************
721 * Add an instruction referencing register and two constants.
724 void emitter::emitIns_R_I_I(
725 instruction ins, emitAttr attr, regNumber reg1, ssize_t imm1, ssize_t imm2, insOpts opt) /* = INS_OPTS_NONE */
727 code_t code = emitInsCode(ins);
729 if (INS_csrrwi <= ins && ins <= INS_csrrci)
731 assert(isGeneralRegisterOrR0(reg1));
732 assert(isValidUimm5(imm1));
733 assert(isValidUimm12(imm2));
740 NYI_RISCV64("illegal ins within emitIns_R_I_I!");
742 instrDesc* id = emitNewInstr(attr);
746 id->idAddr()->iiaSetInstrEncode(code);
752 /*****************************************************************************
754 * Add an instruction referencing three registers.
757 void emitter::emitIns_R_R_R(
758 instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, insOpts opt) /* = INS_OPTS_NONE */
760 code_t code = emitInsCode(ins);
762 if ((INS_add <= ins && ins <= INS_and) || (INS_mul <= ins && ins <= INS_remuw) ||
763 (INS_addw <= ins && ins <= INS_sraw) || (INS_fadd_s <= ins && ins <= INS_fmax_s) ||
764 (INS_fadd_d <= ins && ins <= INS_fmax_d) || (INS_feq_s <= ins && ins <= INS_fle_s) ||
765 (INS_feq_d <= ins && ins <= INS_fle_d) || (INS_lr_w <= ins && ins <= INS_amomaxu_d))
856 NYI_RISCV64("illegal ins within emitIns_R_R_R!");
860 // Src/data register for load reserved should be empty
861 assert((ins != INS_lr_w && ins != INS_lr_d) || reg3 == REG_R0);
863 code |= ((reg1 & 0x1f) << 7);
864 code |= ((reg2 & 0x1f) << 15);
865 code |= ((reg3 & 0x1f) << 20);
866 if ((INS_fadd_s <= ins && INS_fsqrt_s >= ins) || (INS_fadd_d <= ins && INS_fsqrt_d >= ins))
870 else if (INS_lr_w <= ins && ins <= INS_amomaxu_d)
872 // For now all atomics are seq. consistent as Interlocked.* APIs don't expose acquire/release ordering
878 NYI_RISCV64("illegal ins within emitIns_R_R_R!");
881 instrDesc* id = emitNewInstr(attr);
887 id->idAddr()->iiaSetInstrEncode(code);
893 /*****************************************************************************
895 * Add an instruction referencing three registers and a constant.
898 void emitter::emitIns_R_R_R_I(instruction ins,
904 insOpts opt /* = INS_OPTS_NONE */,
905 emitAttr attrReg2 /* = EA_UNKNOWN */)
907 NYI_RISCV64("emitIns_R_R_R_I-----unimplemented/unused on RISCV64 yet----");
910 /*****************************************************************************
912 * Add an instruction referencing two registers and two constants.
915 void emitter::emitIns_R_R_I_I(
916 instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, int imm1, int imm2, insOpts opt)
918 NYI_RISCV64("emitIns_R_R_I_I-----unimplemented/unused on RISCV64 yet----");
921 /*****************************************************************************
923 * Add an instruction referencing four registers.
926 void emitter::emitIns_R_R_R_R(
927 instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, regNumber reg4)
929 NYI_RISCV64("emitIns_R_R_R_R-----unimplemented/unused on RISCV64 yet----");
932 /*****************************************************************************
934 * Add an instruction with a register + static member operands.
935 * Constant is stored into JIT data which is adjacent to code.
938 void emitter::emitIns_R_C(
939 instruction ins, emitAttr attr, regNumber reg, regNumber addrReg, CORINFO_FIELD_HANDLE fldHnd, int offs)
942 assert(instrDesc::fitsInSmallCns(offs)); // can optimize.
943 instrDesc* id = emitNewInstr(attr);
946 assert(reg != REG_R0); // for special. reg Must not be R0.
947 id->idReg1(reg); // destination register that will get the constant value.
949 id->idSmallCns(offs); // usually is 0.
950 id->idInsOpt(INS_OPTS_RC);
951 if (emitComp->opts.compReloc)
953 id->idSetIsDspReloc();
959 if (EA_IS_GCREF(attr))
961 /* A special value indicates a GCref pointer value */
962 id->idGCref(GCT_GCREF);
963 id->idOpSize(EA_PTRSIZE);
965 else if (EA_IS_BYREF(attr))
967 /* A special value indicates a Byref pointer value */
968 id->idGCref(GCT_BYREF);
969 id->idOpSize(EA_PTRSIZE);
972 // TODO-RISCV64: this maybe deleted.
973 id->idSetIsBound(); // We won't patch address since we will know the exact distance
974 // once JIT code and data are allocated together.
976 assert(addrReg == REG_NA); // NOTE: for RISV64, not support addrReg != REG_NA.
978 id->idAddr()->iiaFieldHnd = fldHnd;
983 void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs)
985 NYI_RISCV64("emitIns_R_AR-----unimplemented/unused on RISCV64 yet----");
988 // This computes address from the immediate which is relocatable.
989 void emitter::emitIns_R_AI(instruction ins,
992 ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags))
994 assert(EA_IS_RELOC(attr)); // EA_PTR_DSP_RELOC
995 assert(ins == INS_jal); // for special.
996 assert(isGeneralRegister(reg));
998 // INS_OPTS_RELOC: placeholders. 2-ins:
999 // case:EA_HANDLE_CNS_RELOC
1000 // auipc reg, off-hi-20bits
1001 // addi reg, reg, off-lo-12bits
1002 // case:EA_PTR_DSP_RELOC
1003 // auipc reg, off-hi-20bits
1004 // ld reg, reg, off-lo-12bits
1006 instrDesc* id = emitNewInstr(attr);
1009 assert(reg != REG_R0); // for special. reg Must not be R0.
1010 id->idReg1(reg); // destination register that will get the constant value.
1012 id->idInsOpt(INS_OPTS_RELOC);
1014 if (EA_IS_GCREF(attr))
1016 /* A special value indicates a GCref pointer value */
1017 id->idGCref(GCT_GCREF);
1018 id->idOpSize(EA_PTRSIZE);
1020 else if (EA_IS_BYREF(attr))
1022 /* A special value indicates a Byref pointer value */
1023 id->idGCref(GCT_BYREF);
1024 id->idOpSize(EA_PTRSIZE);
1027 id->idAddr()->iiaAddr = (BYTE*)addr;
1033 /*****************************************************************************
1035 * Record that a jump instruction uses the short encoding
1038 void emitter::emitSetShortJump(instrDescJmp* id)
1040 // TODO-RISCV64: maybe delete it on future.
1041 NYI_RISCV64("emitSetShortJump-----unimplemented/unused on RISCV64 yet----");
1044 /*****************************************************************************
1046 * Add a label instruction.
1049 void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg)
1051 assert(dst->bbFlags & BBF_HAS_LABEL);
1053 // if for reloc! 4-ins:
1054 // auipc reg, offset-hi20
1055 // addi reg, reg, offset-lo12
1058 // lui tmp, dst-hi-20bits
1059 // addi tmp, tmp, dst-lo-12bits
1060 // lui reg, 0xff << 12
1061 // slli reg, reg, 32
1062 // add reg, tmp, reg
1064 instrDesc* id = emitNewInstr(attr);
1067 id->idInsOpt(INS_OPTS_RL);
1068 id->idAddr()->iiaBBlabel = dst;
1070 if (emitComp->opts.compReloc)
1072 id->idSetIsDspReloc();
1080 if (EA_IS_GCREF(attr))
1082 /* A special value indicates a GCref pointer value */
1083 id->idGCref(GCT_GCREF);
1084 id->idOpSize(EA_PTRSIZE);
1086 else if (EA_IS_BYREF(attr))
1088 /* A special value indicates a Byref pointer value */
1089 id->idGCref(GCT_BYREF);
1090 id->idOpSize(EA_PTRSIZE);
1094 // Mark the catch return
1095 if (emitComp->compCurBB->bbJumpKind == BBJ_EHCATCHRET)
1097 id->idDebugOnlyInfo()->idCatchRet = true;
1104 void emitter::emitIns_J_R(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg)
1106 NYI_RISCV64("emitIns_J_R-----unimplemented/unused on RISCV64 yet----");
1109 void emitter::emitIns_J(instruction ins, BasicBlock* dst, int instrCount)
1111 assert(dst != nullptr);
1113 // INS_OPTS_J: placeholders. 1-ins: if the dst outof-range will be replaced by INS_OPTS_JALR.
1114 // jal/j/jalr/bnez/beqz/beq/bne/blt/bge/bltu/bgeu dst
1116 assert(dst->bbFlags & BBF_HAS_LABEL);
1118 instrDescJmp* id = emitNewInstrJmp();
1119 assert((INS_jal <= ins) && (ins <= INS_bgeu));
1121 id->idReg1((regNumber)(instrCount & 0x1f));
1122 id->idReg2((regNumber)((instrCount >> 5) & 0x1f));
1124 id->idInsOpt(INS_OPTS_J);
1125 emitCounts_INS_OPTS_J++;
1126 id->idAddr()->iiaBBlabel = dst;
1128 if (emitComp->opts.compReloc)
1130 id->idSetIsDspReloc();
1133 id->idjShort = false;
1135 // TODO-RISCV64: maybe deleted this.
1136 id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst);
1138 if (emitComp->opts.compLongAddress) // Force long branches
1139 id->idjKeepLong = 1;
1142 /* Record the jump's IG and offset within it */
1143 id->idjIG = emitCurIG;
1144 id->idjOffs = emitCurIGsize;
1146 /* Append this jump to this IG's jump list */
1147 id->idjNext = emitCurIGjmpList;
1148 emitCurIGjmpList = id;
1159 void emitter::emitIns_J_cond_la(instruction ins, BasicBlock* dst, regNumber reg1, regNumber reg2)
1162 // Now the emitIns_J_cond_la() is only the short condition branch.
1163 // There is no long condition branch for RISCV64 so far.
1164 // For RISCV64 , the long condition branch is like this:
1165 // ---> branch_condition condition_target; //here is the condition branch, short branch is enough.
1166 // ---> jump jump_target; (this supporting the long jump.)
1167 // condition_target:
1173 // INS_OPTS_J_cond: placeholders. 1-ins.
1174 // ins reg1, reg2, dst
1176 assert(dst != nullptr);
1177 assert(dst->bbFlags & BBF_HAS_LABEL);
1179 instrDescJmp* id = emitNewInstrJmp();
1184 id->idjShort = false;
1186 id->idInsOpt(INS_OPTS_J_cond);
1187 id->idAddr()->iiaBBlabel = dst;
1189 id->idjKeepLong = emitComp->fgInDifferentRegions(emitComp->compCurBB, dst);
1191 if (emitComp->opts.compLongAddress) // Force long branches
1192 id->idjKeepLong = 1;
1195 /* Record the jump's IG and offset within it */
1196 id->idjIG = emitCurIG;
1197 id->idjOffs = emitCurIGsize;
1199 /* Append this jump to this IG's jump list */
1200 id->idjNext = emitCurIGjmpList;
1201 emitCurIGjmpList = id;
1212 /*****************************************************************************
1214 * Emits load of 64-bit constant to register.
1217 void emitter::emitLoadImmediate(emitAttr size, regNumber reg, ssize_t imm)
1219 // In the worst case a sequence of 8 instructions will be used:
1220 // LUI + ADDIW + SLLI + ADDI + SLLI + ADDI + SLLI + ADDI
1222 // First 2 instructions (LUI + ADDIW) load up to 31 bit. And followed
1223 // sequence of (SLLI + ADDI) is used for loading remaining part by batches
1224 // of up to 11 bits.
1226 // Note that LUI, ADDI/W use sign extension so that's why we have to work
1227 // with 31 and 11 bit batches there.
1229 assert(!EA_IS_RELOC(size));
1230 assert(isGeneralRegister(reg));
1232 if (isValidSimm12(imm))
1234 emitIns_R_R_I(INS_addi, size, reg, REG_R0, imm & 0xFFF);
1238 // TODO-RISCV64: maybe optimized via emitDataConst(), check #86790
1240 UINT32 msb = BitOperations::BitScanReverse((uint64_t)imm);
1244 high31 = (imm >> (msb - 30)) & 0x7FffFFff;
1248 high31 = imm & 0x7FffFFff;
1251 // Since ADDIW use sign extension fo immediate
1252 // we have to adjust higher 19 bit loaded by LUI
1253 // for case when low part is bigger than 0x800.
1254 UINT32 high19 = (high31 + 0x800) >> 12;
1256 emitIns_R_I(INS_lui, size, reg, high19);
1257 emitIns_R_R_I(INS_addiw, size, reg, reg, high31 & 0xFFF);
1259 // And load remaining part part by batches of 11 bits size.
1260 INT32 remainingShift = msb - 30;
1261 while (remainingShift > 0)
1263 UINT32 shift = remainingShift >= 11 ? 11 : remainingShift % 11;
1264 emitIns_R_R_I(INS_slli, size, reg, reg, shift);
1266 UINT32 mask = 0x7ff >> (11 - shift);
1267 ssize_t low11 = (imm >> (remainingShift - shift)) & mask;
1268 emitIns_R_R_I(INS_addi, size, reg, reg, low11);
1269 remainingShift = remainingShift - shift;
1273 /*****************************************************************************
1275 * Add a call instruction (direct or indirect).
1276 * argSize<0 means that the caller will pop the arguments
1278 * The other arguments are interpreted depending on callType as shown:
1279 * Unless otherwise specified, ireg,xreg,xmul,disp should have default values.
1281 * EC_FUNC_TOKEN : addr is the method address
1283 * If callType is one of these emitCallTypes, addr has to be NULL.
1284 * EC_INDIR_R : "call ireg".
1288 void emitter::emitIns_Call(EmitCallType callType,
1289 CORINFO_METHOD_HANDLE methHnd,
1290 INDEBUG_LDISASM_COMMA(CORINFO_SIG_INFO* sigInfo) // used to report call sites to the EE
1293 emitAttr retSize MULTIREG_HAS_SECOND_GC_RET_ONLY_ARG(emitAttr secondRetSize),
1294 VARSET_VALARG_TP ptrVars,
1295 regMaskTP gcrefRegs,
1296 regMaskTP byrefRegs,
1297 const DebugInfo& di /* = DebugInfo() */,
1298 regNumber ireg /* = REG_NA */,
1299 regNumber xreg /* = REG_NA */,
1300 unsigned xmul /* = 0 */,
1301 ssize_t disp /* = 0 */,
1302 bool isJump /* = false */)
1304 /* Sanity check the arguments depending on callType */
1306 assert(callType < EC_COUNT);
1307 assert((callType != EC_FUNC_TOKEN) || (ireg == REG_NA && xreg == REG_NA && xmul == 0 && disp == 0));
1308 assert(callType < EC_INDIR_R || addr == NULL);
1309 assert(callType != EC_INDIR_R || (ireg < REG_COUNT && xreg == REG_NA && xmul == 0 && disp == 0));
1311 // RISCV64 never uses these
1312 assert(xreg == REG_NA && xmul == 0 && disp == 0);
1314 // Our stack level should be always greater than the bytes of arguments we push. Just
1316 assert((unsigned)abs(argSize) <= codeGen->genStackLevel);
1318 // Trim out any callee-trashed registers from the live set.
1319 regMaskTP savedSet = emitGetGCRegsSavedOrModified(methHnd);
1320 gcrefRegs &= savedSet;
1321 byrefRegs &= savedSet;
1324 if (EMIT_GC_VERBOSE)
1326 printf("Call: GCvars=%s ", VarSetOps::ToString(emitComp, ptrVars));
1327 dumpConvertedVarSet(emitComp, ptrVars);
1328 printf(", gcrefRegs=");
1329 printRegMaskInt(gcrefRegs);
1330 emitDispRegSet(gcrefRegs);
1331 printf(", byrefRegs=");
1332 printRegMaskInt(byrefRegs);
1333 emitDispRegSet(byrefRegs);
1338 /* Managed RetVal: emit sequence point for the call */
1339 if (emitComp->opts.compDbgInfo && di.GetLocation().IsValid())
1341 codeGen->genIPmappingAdd(IPmappingDscKind::Normal, di, false);
1345 We need to allocate the appropriate instruction descriptor based
1346 on whether this is a direct/indirect call, and whether we need to
1347 record an updated set of live GC variables.
1351 assert(argSize % REGSIZE_BYTES == 0);
1352 int argCnt = (int)(argSize / (int)REGSIZE_BYTES);
1354 if (callType >= EC_INDIR_R)
1356 /* Indirect call, virtual calls */
1358 assert(callType == EC_INDIR_R);
1360 id = emitNewInstrCallInd(argCnt, disp, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
1364 /* Helper/static/nonvirtual/function calls (direct or through handle),
1365 and calls to an absolute addr. */
1367 assert(callType == EC_FUNC_TOKEN);
1369 id = emitNewInstrCallDir(argCnt, ptrVars, gcrefRegs, byrefRegs, retSize, secondRetSize);
1372 /* Update the emitter's live GC ref sets */
1374 VarSetOps::Assign(emitComp, emitThisGCrefVars, ptrVars);
1375 emitThisGCrefRegs = gcrefRegs;
1376 emitThisByrefRegs = byrefRegs;
1378 id->idSetIsNoGC(emitNoGChelper(methHnd));
1380 /* Set the instruction - special case jumping a function */
1383 ins = INS_jalr; // jalr
1386 id->idInsOpt(INS_OPTS_C);
1387 // TODO-RISCV64: maybe optimize.
1389 // INS_OPTS_C: placeholders. 1/2/4-ins:
1390 // if (callType == EC_INDIR_R)
1391 // jalr REG_R0/REG_RA, ireg, 0 <---- 1-ins
1392 // else if (callType == EC_FUNC_TOKEN || callType == EC_FUNC_ADDR)
1394 // //pc + offset_38bits # only when reloc.
1395 // auipc t2, addr-hi20
1396 // jalr r0/1, t2, addr-lo12
1399 // lui t2, dst_offset_lo32-hi
1400 // ori t2, t2, dst_offset_lo32-lo
1401 // lui t2, dst_offset_hi32-lo
1402 // jalr REG_R0/REG_RA, t2, 0
1404 /* Record the address: method, indirection, or funcptr */
1405 if (callType == EC_INDIR_R)
1407 /* This is an indirect call (either a virtual call or func ptr call) */
1408 // assert(callType == EC_INDIR_R);
1410 id->idSetIsCallRegPtr();
1412 regNumber reg_jalr = isJump ? REG_R0 : REG_RA;
1413 id->idReg4(reg_jalr);
1414 id->idReg3(ireg); // NOTE: for EC_INDIR_R, using idReg3.
1415 assert(xreg == REG_NA);
1421 /* This is a simple direct call: "call helper/method/addr" */
1423 assert(callType == EC_FUNC_TOKEN);
1424 assert(addr != NULL);
1426 addr = (void*)(((size_t)addr) + (isJump ? 0 : 1)); // NOTE: low-bit0 is used for jirl ra/r0,rd,0
1427 id->idAddr()->iiaAddr = (BYTE*)addr;
1429 if (emitComp->opts.compReloc)
1431 id->idSetIsDspReloc();
1441 if (EMIT_GC_VERBOSE)
1443 if (id->idIsLargeCall())
1445 printf("[%02u] Rec call GC vars = %s\n", id->idDebugOnlyInfo()->idNum,
1446 VarSetOps::ToString(emitComp, ((instrDescCGCA*)id)->idcGCvars));
1450 id->idDebugOnlyInfo()->idMemCookie = (size_t)methHnd; // method token
1451 id->idDebugOnlyInfo()->idCallSig = sigInfo;
1455 if (addr != nullptr)
1457 codeGen->getDisAssembler().disSetMethod((size_t)addr, methHnd);
1459 #endif // LATE_DISASM
1464 /*****************************************************************************
1466 * Output a call instruction.
1469 unsigned emitter::emitOutputCall(insGroup* ig, BYTE* dst, instrDesc* id, code_t code)
1471 unsigned char callInstrSize = sizeof(code_t); // 4 bytes
1472 regMaskTP gcrefRegs;
1473 regMaskTP byrefRegs;
1475 VARSET_TP GCvars(VarSetOps::UninitVal());
1477 // Is this a "fat" call descriptor?
1478 if (id->idIsLargeCall())
1480 instrDescCGCA* idCall = (instrDescCGCA*)id;
1481 gcrefRegs = idCall->idcGcrefRegs;
1482 byrefRegs = idCall->idcByrefRegs;
1483 VarSetOps::Assign(emitComp, GCvars, idCall->idcGCvars);
1487 assert(!id->idIsLargeDsp());
1488 assert(!id->idIsLargeCns());
1490 gcrefRegs = emitDecodeCallGCregs(id);
1492 VarSetOps::AssignNoCopy(emitComp, GCvars, VarSetOps::MakeEmpty(emitComp));
1495 /* We update the GC info before the call as the variables cannot be
1496 used by the call. Killing variables before the call helps with
1497 boundary conditions if the call is CORINFO_HELP_THROW - see bug 50029.
1498 If we ever track aliased variables (which could be used by the
1499 call), we would have to keep them alive past the call. */
1501 emitUpdateLiveGCvars(GCvars, dst);
1504 // Output any delta in GC variable info, corresponding to the before-call GC var updates done above.
1505 if (EMIT_GC_VERBOSE || emitComp->opts.disasmWithGC)
1507 emitDispGCVarDelta(); // define in emit.cpp
1511 assert(id->idIns() == INS_jalr);
1512 if (id->idIsCallRegPtr())
1514 code = emitInsCode(id->idIns());
1515 code |= (code_t)id->idReg4() << 7;
1516 code |= (code_t)id->idReg3() << 15;
1517 // the offset default is 0;
1518 emitOutput_Instr(dst, code);
1520 else if (id->idIsReloc())
1522 // pc + offset_32bits
1524 // auipc t2, addr-hi20
1525 // jalr r0/1,t2,addr-lo12
1527 emitOutput_Instr(dst, 0x00000397);
1529 size_t addr = (size_t)(id->idAddr()->iiaAddr); // get addr.
1531 int reg2 = ((int)addr & 1) + 10;
1534 assert(isValidSimm32(addr - (ssize_t)dst));
1535 assert((addr & 1) == 0);
1538 emitGCregDeadUpd(REG_DEFAULT_HELPER_CALL_TARGET, dst);
1541 code = emitInsCode(INS_auipc);
1542 assert((code | (REG_DEFAULT_HELPER_CALL_TARGET << 7)) == 0x00000397);
1543 assert((int)REG_DEFAULT_HELPER_CALL_TARGET == 7);
1544 code = emitInsCode(INS_jalr);
1545 assert(code == 0x00000067);
1547 emitOutput_Instr(dst, 0x00000067 | (REG_DEFAULT_HELPER_CALL_TARGET << 15) | reg2 << 7);
1549 emitRecordRelocation(dst - 4, (BYTE*)addr, IMAGE_REL_RISCV64_JALR);
1553 // lui t2, dst_offset_hi32-hi
1554 // addi t2, t2, dst_offset_hi32-lo
1556 // addi t2, t2, dst_offset_low32-hi
1558 // addi t2, t2, dst_offset_low32-md
1562 ssize_t imm = (ssize_t)(id->idAddr()->iiaAddr);
1563 assert((imm >> 32) <= 0xff);
1565 int reg2 = (int)(imm & 1);
1568 UINT32 high = imm >> 32;
1569 code = emitInsCode(INS_lui);
1570 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1571 code |= ((code_t)((high + 0x800) >> 12) & 0xfffff) << 12;
1572 emitOutput_Instr(dst, code);
1575 emitGCregDeadUpd(REG_DEFAULT_HELPER_CALL_TARGET, dst);
1577 code = emitInsCode(INS_addi);
1578 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1579 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1580 code |= (code_t)(high & 0xfff) << 20;
1581 emitOutput_Instr(dst, code);
1584 code = emitInsCode(INS_slli);
1585 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1586 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1587 code |= (code_t)(11 << 20);
1588 emitOutput_Instr(dst, code);
1591 UINT32 low = imm & 0xffffffff;
1593 code = emitInsCode(INS_addi);
1594 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1595 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1596 code |= ((low >> 21) & 0x7ff) << 20;
1597 emitOutput_Instr(dst, code);
1600 code = emitInsCode(INS_slli);
1601 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1602 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1603 code |= (code_t)(11 << 20);
1604 emitOutput_Instr(dst, code);
1607 code = emitInsCode(INS_addi);
1608 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1609 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1610 code |= ((low >> 10) & 0x7ff) << 20;
1611 emitOutput_Instr(dst, code);
1614 code = emitInsCode(INS_slli);
1615 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1616 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1617 code |= (code_t)(10 << 20);
1618 emitOutput_Instr(dst, code);
1621 code = emitInsCode(INS_jalr);
1622 code |= (code_t)reg2 << 7;
1623 code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1624 code |= (low & 0x3ff) << 20;
1625 // the offset default is 0;
1626 emitOutput_Instr(dst, code);
1631 // If the method returns a GC ref, mark INTRET (A0) appropriately.
1632 if (id->idGCref() == GCT_GCREF)
1634 gcrefRegs |= RBM_INTRET;
1636 else if (id->idGCref() == GCT_BYREF)
1638 byrefRegs |= RBM_INTRET;
1641 // If is a multi-register return method is called, mark INTRET_1 (A1) appropriately
1642 if (id->idIsLargeCall())
1644 instrDescCGCA* idCall = (instrDescCGCA*)id;
1645 if (idCall->idSecondGCref() == GCT_GCREF)
1647 gcrefRegs |= RBM_INTRET_1;
1649 else if (idCall->idSecondGCref() == GCT_BYREF)
1651 byrefRegs |= RBM_INTRET_1;
1655 // If the GC register set has changed, report the new set.
1656 if (gcrefRegs != emitThisGCrefRegs)
1658 emitUpdateLiveGCregs(GCT_GCREF, gcrefRegs, dst);
1660 // If the Byref register set has changed, report the new set.
1661 if (byrefRegs != emitThisByrefRegs)
1663 emitUpdateLiveGCregs(GCT_BYREF, byrefRegs, dst);
1666 // Some helper calls may be marked as not requiring GC info to be recorded.
1667 if (!id->idIsNoGC())
1669 // On RISCV64, as on AMD64 and LOONGARCH64, we don't change the stack pointer to push/pop args.
1670 // So we're not really doing a "stack pop" here (note that "args" is 0), but we use this mechanism
1671 // to record the call for GC info purposes. (It might be best to use an alternate call,
1672 // and protect "emitStackPop" under the EMIT_TRACK_STACK_DEPTH preprocessor variable.)
1673 emitStackPop(dst, /*isCall*/ true, callInstrSize, /*args*/ 0);
1675 // Do we need to record a call location for GC purposes?
1677 if (!emitFullGCinfo)
1679 emitRecordGCcall(dst, callInstrSize);
1682 if (id->idIsCallRegPtr())
1684 callInstrSize = 1 << 2;
1688 callInstrSize = id->idIsReloc() ? (2 << 2) : (8 << 2); // INS_OPTS_C: 2/9-ins.
1691 return callInstrSize;
1694 void emitter::emitJumpDistBind()
1697 if (emitComp->verbose)
1699 printf("*************** In emitJumpDistBind()\n");
1701 if (EMIT_INSTLIST_VERBOSE)
1703 printf("\nInstruction list before the jump distance binding:\n\n");
1704 emitDispIGlist(true);
1709 auto printJmpInfo = [this](const instrDescJmp* jmp, const insGroup* jmpIG, NATIVE_OFFSET extra,
1710 UNATIVE_OFFSET srcInstrOffs, UNATIVE_OFFSET srcEncodingOffs, UNATIVE_OFFSET dstOffs,
1711 NATIVE_OFFSET jmpDist, const char* direction) {
1712 assert(jmp->idDebugOnlyInfo() != nullptr);
1713 if (jmp->idDebugOnlyInfo()->idNum == (unsigned)INTERESTING_JUMP_NUM || INTERESTING_JUMP_NUM == 0)
1715 const char* dirId = (strcmp(direction, "fwd") == 0) ? "[1]" : "[2]";
1716 if (INTERESTING_JUMP_NUM == 0)
1718 printf("%s Jump %u:\n", dirId, jmp->idDebugOnlyInfo()->idNum);
1720 printf("%s Jump block is at %08X\n", dirId, jmpIG->igOffs);
1721 printf("%s Jump reloffset is %04X\n", dirId, jmp->idjOffs);
1722 printf("%s Jump source is at %08X\n", dirId, srcEncodingOffs);
1723 printf("%s Label block is at %08X\n", dirId, dstOffs);
1724 printf("%s Jump dist. is %04X\n", dirId, jmpDist);
1727 printf("%s Dist excess [S] = %d \n", dirId, extra);
1732 printf("Estimate of %s jump [%08X/%03u]: %04X -> %04X = %04X\n", direction, dspPtr(jmp),
1733 jmp->idDebugOnlyInfo()->idNum, srcInstrOffs, dstOffs, jmpDist);
1740 UNATIVE_OFFSET adjIG;
1741 UNATIVE_OFFSET adjSJ;
1744 insGroup* prologIG = emitPrologIG;
1748 // bit0 of isLinkingEnd: indicating whether updating the instrDescJmp's size with the type INS_OPTS_J;
1749 // bit1 of isLinkingEnd: indicating not needed updating the size while emitTotalCodeSize <= 0xfff or had
1751 unsigned int isLinkingEnd = emitTotalCodeSize <= 0xfff ? 2 : 0;
1753 UNATIVE_OFFSET ssz = 0; // relative small jump's delay-slot.
1754 // small jump max. neg distance
1755 NATIVE_OFFSET nsd = B_DIST_SMALL_MAX_NEG;
1756 // small jump max. pos distance
1757 NATIVE_OFFSET maxPlaceholderSize =
1758 emitCounts_INS_OPTS_J * (6 << 2); // the max placeholder sizeof(INS_OPTS_JALR) - sizeof(INS_OPTS_J)
1759 NATIVE_OFFSET psd = B_DIST_SMALL_MAX_POS - maxPlaceholderSize;
1761 /*****************************************************************************/
1762 /* If the default small encoding is not enough, we start again here. */
1763 /*****************************************************************************/
1772 insGroup* lastIG = nullptr;
1773 instrDescJmp* lastSJ = nullptr;
1780 for (jmp = emitJumpList; jmp; jmp = jmp->idjNext)
1785 UNATIVE_OFFSET jsz; // size of the jump instruction in bytes
1787 NATIVE_OFFSET extra; // How far beyond the short jump range is this jump offset?
1788 UNATIVE_OFFSET srcInstrOffs; // offset of the source instruction of the jump
1789 UNATIVE_OFFSET srcEncodingOffs; // offset of the source used by the instruction set to calculate the relative
1790 // offset of the jump
1791 UNATIVE_OFFSET dstOffs;
1792 NATIVE_OFFSET jmpDist; // the relative jump distance, as it will be encoded
1794 /* Make sure the jumps are properly ordered */
1797 assert(lastSJ == nullptr || lastIG != jmp->idjIG || lastSJ->idjOffs < (jmp->idjOffs + adjSJ));
1798 lastSJ = (lastIG == jmp->idjIG) ? jmp : nullptr;
1800 assert(lastIG == nullptr || lastIG->igNum <= jmp->idjIG->igNum || jmp->idjIG == prologIG ||
1801 emitNxtIGnum > unsigned(0xFFFF)); // igNum might overflow
1802 lastIG = jmp->idjIG;
1805 /* Get hold of the current jump size */
1807 jsz = jmp->idCodeSize();
1809 /* Get the group the jump is in */
1813 /* Are we in a group different from the previous jump? */
1817 /* Were there any jumps before this one? */
1821 /* Adjust the offsets of the intervening blocks */
1825 lstIG = lstIG->igNext;
1830 printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs,
1831 lstIG->igOffs + adjIG);
1834 lstIG->igOffs += adjIG;
1835 assert(IsCodeAligned(lstIG->igOffs));
1836 } while (lstIG != jmpIG);
1839 /* We've got the first jump in a new group */
1844 /* Apply any local size adjustment to the jump's relative offset */
1845 jmp->idjOffs += adjSJ;
1847 // If this is a jump via register, the instruction size does not change, so we are done.
1848 CLANG_FORMAT_COMMENT_ANCHOR;
1850 /* Have we bound this jump's target already? */
1852 if (jmp->idIsBound())
1854 /* Does the jump already have the smallest size? */
1858 // We should not be jumping/branching across funclets/functions
1859 emitCheckFuncletBranch(jmp, jmpIG);
1864 tgtIG = jmp->idAddr()->iiaIGlabel;
1868 /* First time we've seen this label, convert its target */
1869 CLANG_FORMAT_COMMENT_ANCHOR;
1871 tgtIG = (insGroup*)emitCodeGetCookie(jmp->idAddr()->iiaBBlabel);
1878 printf(" to %s\n", emitLabelString(tgtIG));
1882 printf("-- ERROR, no emitter cookie for " FMT_BB "; it is probably missing BBF_HAS_LABEL.\n",
1883 jmp->idAddr()->iiaBBlabel->bbNum);
1889 /* Record the bound target */
1891 jmp->idAddr()->iiaIGlabel = tgtIG;
1892 jmp->idSetIsBound();
1895 // We should not be jumping/branching across funclets/functions
1896 emitCheckFuncletBranch(jmp, jmpIG);
1899 In the following distance calculations, if we're not actually
1900 scheduling the code (i.e. reordering instructions), we can
1901 use the actual offset of the jump (rather than the beg/end of
1902 the instruction group) since the jump will not be moved around
1903 and thus its offset is accurate.
1905 First we need to figure out whether this jump is a forward or
1906 backward one; to do this we simply look at the ordinals of the
1907 group that contains the jump and the target.
1910 srcInstrOffs = jmpIG->igOffs + jmp->idjOffs;
1912 /* Note that the destination is always the beginning of an IG, so no need for an offset inside it */
1913 dstOffs = tgtIG->igOffs;
1915 srcEncodingOffs = srcInstrOffs + ssz; // Encoding offset of relative offset for small branch
1917 if (jmpIG->igNum < tgtIG->igNum)
1921 /* Adjust the target offset by the current delta. This is a worst-case estimate, as jumps between
1922 here and the target could be shortened, causing the actual distance to shrink.
1927 /* Compute the distance estimate */
1929 jmpDist = dstOffs - srcEncodingOffs;
1931 /* How much beyond the max. short distance does the jump go? */
1933 extra = jmpDist - psd;
1936 printJmpInfo(jmp, jmpIG, extra, srcInstrOffs, srcEncodingOffs, dstOffs, jmpDist, "fwd");
1937 #endif // DEBUG_EMIT
1939 assert(jmpDist >= 0); // Forward jump
1940 assert(!(jmpDist & 0x3));
1942 if (!(isLinkingEnd & 0x2) && (extra > 0) &&
1943 (jmp->idInsOpt() == INS_OPTS_J || jmp->idInsOpt() == INS_OPTS_J_cond))
1945 // transform forward INS_OPTS_J/INS_OPTS_J_cond jump when jmpDist exceed the maximum short distance
1946 instruction ins = jmp->idIns();
1947 assert((INS_jal <= ins) && (ins <= INS_bgeu));
1949 if (ins > INS_jalr ||
1950 (ins < INS_jalr && ins > INS_j)) // jal < beqz < bnez < jalr < beq/bne/blt/bltu/bge/bgeu
1952 if (isValidSimm13(jmpDist + maxPlaceholderSize))
1956 else if (isValidSimm21(jmpDist + maxPlaceholderSize))
1958 // convert to opposite branch and jal
1963 // convert to opposite branch and jalr
1967 else if (ins == INS_jal || ins == INS_j)
1969 if (isValidSimm21(jmpDist + maxPlaceholderSize))
1981 assert(ins == INS_jalr);
1982 assert((jmpDist + maxPlaceholderSize) < 0x800);
1986 jmp->idInsOpt(INS_OPTS_JALR);
1987 jmp->idCodeSize(jmp->idCodeSize() + extra);
1988 jmpIG->igSize += (unsigned short)extra; // the placeholder sizeof(INS_OPTS_JALR) - sizeof(INS_OPTS_J).
1989 adjSJ += (UNATIVE_OFFSET)extra;
1990 adjIG += (UNATIVE_OFFSET)extra;
1991 emitTotalCodeSize += (UNATIVE_OFFSET)extra;
1992 jmpIG->igFlags |= IGF_UPD_ISZ;
1993 isLinkingEnd |= 0x1;
2001 /* Compute the distance estimate */
2003 jmpDist = srcEncodingOffs - dstOffs;
2005 /* How much beyond the max. short distance does the jump go? */
2007 extra = jmpDist + nsd;
2010 printJmpInfo(jmp, jmpIG, extra, srcInstrOffs, srcEncodingOffs, dstOffs, jmpDist, "bwd");
2011 #endif // DEBUG_EMIT
2013 assert(jmpDist >= 0); // Backward jump
2014 assert(!(jmpDist & 0x3));
2016 if (!(isLinkingEnd & 0x2) && (extra > 0) &&
2017 (jmp->idInsOpt() == INS_OPTS_J || jmp->idInsOpt() == INS_OPTS_J_cond))
2019 // transform backward INS_OPTS_J/INS_OPTS_J_cond jump when jmpDist exceed the maximum short distance
2020 instruction ins = jmp->idIns();
2021 assert((INS_jal <= ins) && (ins <= INS_bgeu));
2023 if (ins > INS_jalr ||
2024 (ins < INS_jalr && ins > INS_j)) // jal < beqz < bnez < jalr < beq/bne/blt/bltu/bge/bgeu
2026 if (isValidSimm13(jmpDist + maxPlaceholderSize))
2030 else if (isValidSimm21(jmpDist + maxPlaceholderSize))
2032 // convert to opposite branch and jal
2037 // convert to opposite branch and jalr
2041 else if (ins == INS_jal || ins == INS_j)
2043 if (isValidSimm21(jmpDist + maxPlaceholderSize))
2055 assert(ins == INS_jalr);
2056 assert((jmpDist + maxPlaceholderSize) < 0x800);
2060 jmp->idInsOpt(INS_OPTS_JALR);
2061 jmp->idCodeSize(jmp->idCodeSize() + extra);
2062 jmpIG->igSize += (unsigned short)extra; // the placeholder sizeof(INS_OPTS_JALR) - sizeof(INS_OPTS_J).
2063 adjSJ += (UNATIVE_OFFSET)extra;
2064 adjIG += (UNATIVE_OFFSET)extra;
2065 emitTotalCodeSize += (UNATIVE_OFFSET)extra;
2066 jmpIG->igFlags |= IGF_UPD_ISZ;
2067 isLinkingEnd |= 0x1;
2071 } // end for each jump
2073 if ((isLinkingEnd & 0x3) < 0x2)
2075 // indicating the instrDescJmp's size of the type INS_OPTS_J had updated
2076 // after the first round and should iterate again to update.
2079 // Adjust offsets of any remaining blocks.
2082 lstIG = lstIG->igNext;
2090 printf("Adjusted offset of " FMT_BB " from %04X to %04X\n", lstIG->igNum, lstIG->igOffs,
2091 lstIG->igOffs + adjIG);
2095 lstIG->igOffs += adjIG;
2097 assert(IsCodeAligned(lstIG->igOffs));
2103 if (EMIT_INSTLIST_VERBOSE)
2105 printf("\nLabels list after the jump distance binding:\n\n");
2106 emitDispIGlist(false);
2113 /*****************************************************************************
2115 * Emit a 32-bit RISCV64 instruction
2118 unsigned emitter::emitOutput_Instr(BYTE* dst, code_t code)
2120 assert(sizeof(code_t) == 4);
2121 memcpy(dst + writeableOffset, &code, sizeof(code_t));
2122 return sizeof(code_t);
2125 void emitter::emitOutputInstrJumpDistanceHelper(const insGroup* ig,
2127 UNATIVE_OFFSET& dstOffs,
2128 const BYTE*& dstAddr) const
2130 // TODO-RISCV64-BUG: Currently the iiaEncodedInstrCount is not set by the riscv impl making distinguishing the jump
2131 // to label and an instruction-count based jumps impossible
2132 if (jmp->idAddr()->iiaHasInstrCount())
2134 assert(ig != nullptr);
2135 int instrCount = jmp->idAddr()->iiaGetInstrCount();
2136 unsigned insNum = emitFindInsNum(ig, jmp);
2139 // Backward branches using instruction count must be within the same instruction group.
2140 assert(insNum + 1 >= static_cast<unsigned>(-instrCount));
2142 dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount);
2143 dstAddr = emitOffsetToPtr(dstOffs);
2146 dstOffs = jmp->idAddr()->iiaIGlabel->igOffs;
2147 dstAddr = emitOffsetToPtr(dstOffs);
2150 ssize_t emitter::emitOutputInstrJumpDistance(const BYTE* dst, const BYTE* src, const insGroup* ig, instrDescJmp* jmp)
2152 UNATIVE_OFFSET srcOffs = emitCurCodeOffs(src);
2153 const BYTE* srcAddr = emitOffsetToPtr(srcOffs);
2155 assert(!jmp->idAddr()->iiaIsJitDataOffset()); // not used by riscv64 impl
2157 UNATIVE_OFFSET dstOffs{};
2158 const BYTE* dstAddr = nullptr;
2159 emitOutputInstrJumpDistanceHelper(ig, jmp, dstOffs, dstAddr);
2161 ssize_t distVal = static_cast<ssize_t>(dstAddr - srcAddr);
2163 if (dstOffs > srcOffs)
2165 // This is a forward jump
2167 emitFwdJumps = true;
2169 // The target offset will be closer by at least 'emitOffsAdj', but only if this
2170 // jump doesn't cross the hot-cold boundary.
2171 if (!emitJumpCrossHotColdBoundary(srcOffs, dstOffs))
2173 distVal -= emitOffsAdj;
2174 dstOffs -= emitOffsAdj;
2176 jmp->idjOffs = dstOffs;
2177 if (jmp->idjOffs != dstOffs)
2179 IMPL_LIMITATION("Method is too large");
2185 /*****************************************************************************
2187 * Append the machine code corresponding to the given instruction descriptor
2188 * to the code block at '*dp'; the base of the code block is 'bp', and 'ig'
2189 * is the instruction group that contains the instruction. Updates '*dp' to
2190 * point past the generated code, and returns the size of the instruction
2191 * descriptor in bytes.
2194 size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp)
2196 BYTE* const dst = *dp;
2197 BYTE* dstRW = *dp + writeableOffset;
2198 BYTE* dstRW2 = dstRW + 4; // addr for updating gc info if needed.
2199 const BYTE* const odstRW = dstRW;
2200 const BYTE* const odst = *dp;
2203 size_t sz; // = emitSizeOfInsDsc(id);
2205 assert(REG_NA == (int)REG_NA);
2207 insOpts insOp = id->idInsOpt();
2211 case INS_OPTS_RELOC:
2213 regNumber reg1 = id->idReg1();
2215 *(code_t*)dstRW = 0x00000017 | (code_t)(reg1 << 7);
2220 code = emitInsCode(INS_auipc);
2221 assert(code == 0x00000017);
2222 code = emitInsCode(INS_addi);
2223 assert(code == 0x00000013);
2224 code = emitInsCode(INS_ld);
2225 assert(code == 0x00003003);
2228 if (id->idIsCnsReloc())
2231 *(code_t*)dstRW = 0x00000013 | (code_t)(reg1 << 7) | (code_t)(reg1 << 15);
2235 assert(id->idIsDspReloc());
2237 *(code_t*)dstRW = 0x00003003 | (code_t)(reg1 << 7) | (code_t)(reg1 << 15);
2242 emitRecordRelocation(dstRW - 8 - writeableOffset, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC);
2244 sz = sizeof(instrDesc);
2249 ssize_t imm = (ssize_t)(id->idAddr()->iiaAddr);
2250 regNumber reg1 = id->idReg1();
2252 switch (id->idCodeSize())
2257 { // special for INT64_MAX or UINT32_MAX;
2258 code = emitInsCode(INS_addi);
2259 code |= (code_t)reg1 << 7;
2260 code |= (code_t)REG_R0 << 15;
2261 code |= 0xfff << 10;
2263 *(code_t*)dstRW = code;
2266 ssize_t ui6 = (imm == INT64_MAX) ? 1 : 32;
2267 code = emitInsCode(INS_srli);
2268 code |= ((code_t)(reg1 << 7) | ((code_t)(reg1 << 15)) | (ui6 << 20));
2269 *(code_t*)dstRW = code;
2273 code = emitInsCode(INS_lui);
2274 code |= (code_t)(reg1 << 7);
2275 code |= ((code_t)((imm + 0x800) >> 12) & 0xfffff) << 12;
2277 *(code_t*)dstRW = code;
2280 code = emitInsCode(INS_addi);
2281 code |= (code_t)reg1 << 7;
2282 code |= (code_t)reg1 << 15;
2283 code |= (code_t)(imm & 0xfff) << 20;
2284 *(code_t*)dstRW = code;
2290 ssize_t high = (imm >> 32) & 0xffffffff;
2291 code = emitInsCode(INS_lui);
2292 code |= (code_t)reg1 << 7;
2293 code |= ((code_t)((high + 0x800) >> 12) & 0xfffff) << 12;
2295 *(code_t*)dstRW = code;
2298 code = emitInsCode(INS_addi);
2299 code |= (code_t)reg1 << 7;
2300 code |= (code_t)reg1 << 15;
2301 code |= (code_t)(high & 0xfff) << 20;
2302 *(code_t*)dstRW = code;
2305 ssize_t low = imm & 0xffffffff;
2307 code = emitInsCode(INS_slli);
2308 code |= (code_t)reg1 << 7;
2309 code |= (code_t)reg1 << 15;
2310 code |= (code_t)11 << 20;
2311 *(code_t*)dstRW = code;
2314 code = emitInsCode(INS_addi);
2315 code |= (code_t)reg1 << 7;
2316 code |= (code_t)reg1 << 15;
2317 code |= (code_t)((low >> 21) & 0x7ff) << 20;
2318 *(code_t*)dstRW = code;
2321 code = emitInsCode(INS_slli);
2322 code |= (code_t)reg1 << 7;
2323 code |= (code_t)reg1 << 15;
2324 code |= (code_t)11 << 20;
2325 *(code_t*)dstRW = code;
2328 code = emitInsCode(INS_addi);
2329 code |= (code_t)reg1 << 7;
2330 code |= (code_t)reg1 << 15;
2331 code |= (code_t)((low >> 10) & 0x7ff) << 20;
2332 *(code_t*)dstRW = code;
2335 code = emitInsCode(INS_slli);
2336 code |= (code_t)reg1 << 7;
2337 code |= (code_t)reg1 << 15;
2338 code |= (code_t)10 << 20;
2339 *(code_t*)dstRW = code;
2342 code = emitInsCode(INS_addi);
2343 code |= (code_t)reg1 << 7;
2344 code |= (code_t)reg1 << 15;
2345 code |= (code_t)((low)&0x3ff) << 20;
2346 *(code_t*)dstRW = code;
2357 sz = sizeof(instrDesc);
2362 // Reference to JIT data
2363 assert(id->idAddr()->iiaIsJitDataOffset());
2364 assert(id->idGCref() == GCT_NONE);
2366 int doff = id->idAddr()->iiaGetJitDataOffset();
2369 ssize_t imm = emitGetInsSC(id);
2370 assert((imm >= 0) && (imm < 0x4000)); // 0x4000 is arbitrary, currently 'imm' is always 0.
2372 unsigned dataOffs = (unsigned)(doff + imm);
2374 assert(dataOffs < emitDataSize());
2377 regNumber reg1 = id->idReg1();
2379 if (id->idIsReloc())
2381 // get the addr-offset of the data.
2382 imm = (ssize_t)emitConsBlock - (ssize_t)(dstRW - writeableOffset) + dataOffs;
2386 doff = (int)(imm & 0xfff);
2387 assert(isValidSimm20((imm + 0x800) >> 12));
2390 code = emitInsCode(INS_auipc);
2391 assert(code == 0x00000017);
2393 code = 0x00000017 | (codeGen->rsGetRsvdReg() << 7);
2394 *(code_t*)dstRW = code | ((code_t)((imm + 0x800) & 0xfffff000));
2399 assert(isGeneralRegister(reg1));
2402 code = emitInsCode(INS_addi);
2403 assert(code == 0x00000013);
2405 code = 0x00000013 | (codeGen->rsGetRsvdReg() << 15);
2406 *(code_t*)dstRW = code | ((code_t)reg1 << 7) | (((code_t)doff & 0xfff) << 20);
2410 code = emitInsCode(ins);
2411 code |= (code_t)(reg1 & 0x1f) << 7;
2412 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2413 code |= (code_t)(doff & 0xfff) << 20;
2414 *(code_t*)dstRW = code;
2420 // get the addr of the data.
2421 imm = (ssize_t)emitConsBlock + dataOffs;
2423 code = emitInsCode(INS_lui);
2426 assert((imm >> 40) == 0);
2430 UINT32 high = imm >> 11;
2432 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2433 code |= (code_t)(((high + 0x800) >> 12) << 12);
2434 *(code_t*)dstRW = code;
2437 code = emitInsCode(INS_addi);
2438 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2439 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2440 code |= (code_t)(high & 0xFFF) << 20;
2441 *(code_t*)dstRW = code;
2444 code = emitInsCode(INS_slli);
2445 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2446 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2447 code |= (code_t)11 << 20;
2448 *(code_t*)dstRW = code;
2452 code = emitInsCode(INS_addi);
2453 code |= (code_t)reg1 << 7;
2454 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2455 code |= (code_t)doff << 20;
2456 *(code_t*)dstRW = code;
2461 assert((imm >> 40) == 0);
2464 UINT32 high = imm >> 11;
2466 code |= (code_t)(codeGen->rsGetRsvdReg() << 7);
2467 code |= (code_t)(((high + 0x800) >> 12) << 12);
2468 *(code_t*)dstRW = code;
2471 code = emitInsCode(INS_addi);
2472 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2473 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2474 code |= (code_t)(high & 0xFFF) << 20;
2475 *(code_t*)dstRW = code;
2478 code = emitInsCode(INS_slli);
2479 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2480 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2481 code |= (code_t)11 << 20;
2482 *(code_t*)dstRW = code;
2485 code = emitInsCode(ins);
2486 code |= (code_t)(reg1 & 0x1f) << 7;
2487 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2488 code |= (code_t)doff << 20;
2489 *(code_t*)dstRW = code;
2494 sz = sizeof(instrDesc);
2500 insGroup* tgtIG = (insGroup*)emitCodeGetCookie(id->idAddr()->iiaBBlabel);
2501 id->idAddr()->iiaIGlabel = tgtIG;
2503 regNumber reg1 = id->idReg1();
2504 assert(isGeneralRegister(reg1));
2506 if (id->idIsReloc())
2508 ssize_t imm = (ssize_t)tgtIG->igOffs;
2509 imm = (ssize_t)emitCodeBlock + imm - (ssize_t)(dstRW - writeableOffset);
2510 assert((imm & 3) == 0);
2512 int doff = (int)(imm & 0xfff);
2513 assert(isValidSimm20((imm + 0x800) >> 12));
2516 *(code_t*)dstRW = code | (code_t)reg1 << 7 | ((imm + 0x800) & 0xfffff000);
2519 code = emitInsCode(INS_auipc);
2520 assert(code == 0x00000017);
2521 code = emitInsCode(INS_addi);
2522 assert(code == 0x00000013);
2525 *(code_t*)dstRW = 0x00000013 | ((code_t)reg1 << 7) | ((code_t)reg1 << 15) | ((doff & 0xfff) << 20);
2529 ssize_t imm = (ssize_t)tgtIG->igOffs + (ssize_t)emitCodeBlock;
2530 assert((imm >> (32 + 20)) == 0);
2532 code = emitInsCode(INS_lui);
2533 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2534 code |= ((code_t)((imm + 0x800) >> 12) & 0xfffff) << 12;
2536 *(code_t*)dstRW = code;
2539 code = emitInsCode(INS_addi);
2540 code |= (code_t)codeGen->rsGetRsvdReg() << 7;
2541 code |= (code_t)codeGen->rsGetRsvdReg() << 15;
2542 code |= (code_t)(imm & 0xfff) << 20;
2543 *(code_t*)dstRW = code;
2546 code = emitInsCode(INS_addi);
2547 code |= (code_t)reg1 << 7;
2548 code |= (((imm + 0x80000800) >> 32) & 0xfff) << 20;
2549 *(code_t*)dstRW = code;
2552 code = emitInsCode(INS_slli);
2553 code |= (code_t)reg1 << 7;
2554 code |= (code_t)reg1 << 15;
2555 code |= (code_t)32 << 20;
2556 *(code_t*)dstRW = code;
2560 code = emitInsCode(INS_add);
2561 code |= (code_t)reg1 << 7;
2562 code |= (code_t)reg1 << 15;
2563 code |= (code_t)codeGen->rsGetRsvdReg() << 20;
2564 *(code_t*)dstRW = code;
2569 sz = sizeof(instrDesc);
2574 instrDescJmp* jmp = (instrDescJmp*)id;
2576 regNumber reg1 = id->idReg1();
2578 ssize_t imm = emitOutputInstrJumpDistance(dstRW, dst, ig, jmp);
2581 assert((imm & 0x3) == 0);
2584 assert(jmp->idCodeSize() > 4); // The original INS_OPTS_JALR: not used by now!!!
2585 switch (jmp->idCodeSize())
2589 assert((INS_blt <= ins && ins <= INS_bgeu) || (INS_beq == ins) || (INS_bne == ins) ||
2590 (INS_bnez == ins) || (INS_beqz == ins));
2591 assert(isValidSimm21(imm));
2592 assert((emitInsCode(INS_bne) & 0xefff) == emitInsCode(INS_beq));
2593 assert((emitInsCode(INS_bge) & 0xefff) == emitInsCode(INS_blt));
2594 assert((emitInsCode(INS_bgeu) & 0xefff) == emitInsCode(INS_bltu));
2596 regNumber reg2 = REG_R0;
2597 if (INS_beqz != ins && INS_bnez != ins)
2598 reg2 = id->idReg2();
2599 code = emitInsCode(ins) ^ 0x1000;
2600 code |= (code_t)reg1 << 15; /* rj */
2601 code |= (code_t)reg2 << 20; /* rd */
2603 *(code_t*)dstRW = code;
2606 code = emitInsCode(INS_jal);
2607 code |= ((imm >> 12) & 0xff) << 12;
2608 code |= ((imm >> 11) & 0x1) << 20;
2609 code |= ((imm >> 1) & 0x3ff) << 21;
2610 code |= ((imm >> 20) & 0x1) << 31;
2612 *(code_t*)dstRW = code;
2618 assert(ins == INS_j || ins == INS_jal);
2619 // Make target address with offset, then jump (JALR) with the target address
2621 regNumber tmpReg1 = REG_RA;
2622 ssize_t high = ((imm + 0x80000000) >> 32) & 0xffffffff;
2623 code = emitInsCode(INS_lui);
2624 code |= (code_t)tmpReg1 << 7;
2625 code |= ((code_t)((high + 0x800) >> 12) & 0xfffff) << 12;
2627 *(code_t*)dstRW = code;
2630 code = emitInsCode(INS_addi);
2631 code |= (code_t)tmpReg1 << 7;
2632 code |= (code_t)tmpReg1 << 15;
2633 code |= (code_t)(high & 0xfff) << 20;
2634 *(code_t*)dstRW = code;
2637 code = emitInsCode(INS_slli);
2638 code |= (code_t)tmpReg1 << 7;
2639 code |= (code_t)tmpReg1 << 15;
2640 code |= (code_t)32 << 20;
2641 *(code_t*)dstRW = code;
2644 regNumber tmpReg2 = codeGen->rsGetRsvdReg();
2645 ssize_t low = imm & 0xffffffff;
2646 code = emitInsCode(INS_auipc);
2647 code |= (code_t)tmpReg2 << 7;
2648 code |= ((code_t)((low + 0x800) >> 12) & 0xfffff) << 12;
2650 *(code_t*)dstRW = code;
2653 code = emitInsCode(INS_add);
2654 code |= (code_t)tmpReg2 << 7;
2655 code |= (code_t)tmpReg1 << 15;
2656 code |= (code_t)tmpReg2 << 20;
2657 *(code_t*)dstRW = code;
2660 code = emitInsCode(INS_jalr);
2661 code |= (code_t)REG_RA << 7; // use REG_RA for returning
2662 code |= (code_t)tmpReg2 << 15;
2663 code |= (code_t)(low & 0xfff) << 20;
2664 *(code_t*)dstRW = code;
2670 assert((INS_blt <= ins && ins <= INS_bgeu) || (INS_beq == ins) || (INS_bne == ins) ||
2671 (INS_bnez == ins) || (INS_beqz == ins));
2672 assert((emitInsCode(INS_bne) & 0xefff) == emitInsCode(INS_beq));
2673 assert((emitInsCode(INS_bge) & 0xefff) == emitInsCode(INS_blt));
2674 assert((emitInsCode(INS_bgeu) & 0xefff) == emitInsCode(INS_bltu));
2676 regNumber reg2 = REG_R0;
2677 if (INS_beqz != ins && INS_bnez != ins)
2678 reg2 = id->idReg2();
2679 code = emitInsCode(ins) ^ 0x1000;
2680 code |= (code_t)reg1 << 15; /* rj */
2681 code |= (code_t)reg2 << 20; /* rd */
2683 *(code_t*)dstRW = code;
2686 // Make target address with offset, then jump (JALR) with the target address
2688 regNumber tmpReg1 = REG_RA;
2689 ssize_t high = ((imm + 0x80000000) >> 32) & 0xffffffff;
2690 code = emitInsCode(INS_lui);
2691 code |= (code_t)tmpReg1 << 7;
2692 code |= ((code_t)((high + 0x800) >> 12) & 0xfffff) << 12;
2694 *(code_t*)dstRW = code;
2697 code = emitInsCode(INS_addi);
2698 code |= (code_t)tmpReg1 << 7;
2699 code |= (code_t)tmpReg1 << 15;
2700 code |= (code_t)(high & 0xfff) << 20;
2701 *(code_t*)dstRW = code;
2704 code = emitInsCode(INS_slli);
2705 code |= (code_t)tmpReg1 << 7;
2706 code |= (code_t)tmpReg1 << 15;
2707 code |= (code_t)32 << 20;
2708 *(code_t*)dstRW = code;
2711 regNumber tmpReg2 = codeGen->rsGetRsvdReg();
2712 ssize_t low = imm & 0xffffffff;
2713 code = emitInsCode(INS_auipc);
2714 code |= (code_t)tmpReg2 << 7;
2715 code |= ((code_t)((low + 0x800) >> 12) & 0xfffff) << 12;
2717 *(code_t*)dstRW = code;
2720 code = emitInsCode(INS_add);
2721 code |= (code_t)tmpReg2 << 7;
2722 code |= (code_t)tmpReg1 << 15;
2723 code |= (code_t)tmpReg2 << 20;
2724 *(code_t*)dstRW = code;
2727 code = emitInsCode(INS_jalr);
2728 code |= (code_t)REG_RA << 7; // use REG_RA for returning
2729 code |= (code_t)tmpReg2 << 15;
2730 code |= (code_t)(low & 0xfff) << 20;
2731 *(code_t*)dstRW = code;
2742 sz = sizeof(instrDescJmp);
2745 case INS_OPTS_J_cond:
2747 ssize_t imm = emitOutputInstrJumpDistance(dstRW, dst, ig, static_cast<instrDescJmp*>(id));
2748 assert(isValidSimm13(imm));
2752 code = emitInsCode(ins);
2753 code |= ((code_t)id->idReg1()) << 15;
2754 code |= ((code_t)id->idReg2()) << 20;
2755 code |= ((imm >> 11) & 0x1) << 7;
2756 code |= ((imm >> 1) & 0xf) << 8;
2757 code |= ((imm >> 5) & 0x3f) << 25;
2758 code |= ((imm >> 12) & 0x1) << 31;
2759 *(code_t*)dstRW = code;
2762 sz = sizeof(instrDescJmp);
2766 // jal/j/jalr/bnez/beqz/beq/bne/blt/bge/bltu/bgeu dstRW-relative.
2768 ssize_t imm = emitOutputInstrJumpDistance(dstRW, dst, ig, static_cast<instrDescJmp*>(id));
2769 assert((imm & 3) == 0);
2772 code = emitInsCode(ins);
2775 assert(isValidSimm21(imm));
2776 code |= ((imm >> 12) & 0xff) << 12;
2777 code |= ((imm >> 11) & 0x1) << 20;
2778 code |= ((imm >> 1) & 0x3ff) << 21;
2779 code |= ((imm >> 20) & 0x1) << 31;
2780 code |= REG_RA << 7;
2782 else if (ins == INS_j)
2784 assert(isValidSimm21(imm));
2785 code |= ((imm >> 12) & 0xff) << 12;
2786 code |= ((imm >> 11) & 0x1) << 20;
2787 code |= ((imm >> 1) & 0x3ff) << 21;
2788 code |= ((imm >> 20) & 0x1) << 31;
2790 else if (ins == INS_jalr)
2792 assert(isValidSimm12(imm));
2793 code |= ((code_t)(imm & 0xfff) << 20);
2794 code |= ((code_t)id->idReg1()) << 7;
2795 code |= ((code_t)id->idReg2()) << 15;
2797 else if (ins == INS_bnez || ins == INS_beqz)
2799 assert(isValidSimm13(imm));
2800 code |= (code_t)id->idReg1() << 15;
2801 code |= ((imm >> 11) & 0x1) << 7;
2802 code |= ((imm >> 1) & 0xf) << 8;
2803 code |= ((imm >> 5) & 0x3f) << 25;
2804 code |= ((imm >> 12) & 0x1) << 31;
2806 else if ((INS_beq <= ins) && (ins <= INS_bgeu))
2808 assert(isValidSimm13(imm));
2809 code |= ((code_t)id->idReg1()) << 15;
2810 code |= ((code_t)id->idReg2()) << 20;
2811 code |= ((imm >> 11) & 0x1) << 7;
2812 code |= ((imm >> 1) & 0xf) << 8;
2813 code |= ((imm >> 5) & 0x3f) << 25;
2814 code |= ((imm >> 12) & 0x1) << 31;
2821 *(code_t*)dstRW = code;
2824 sz = sizeof(instrDescJmp);
2828 if (id->idIsLargeCall())
2830 /* Must be a "fat" call descriptor */
2831 sz = sizeof(instrDescCGCA);
2835 assert(!id->idIsLargeDsp());
2836 assert(!id->idIsLargeCns());
2837 sz = sizeof(instrDesc);
2839 dstRW += emitOutputCall(ig, *dp, id, 0);
2845 // case INS_OPTS_NONE:
2847 *(code_t*)dstRW = id->idAddr()->iiaGetInstrEncode();
2850 sz = emitSizeOfInsDsc(id);
2854 // Determine if any registers now hold GC refs, or whether a register that was overwritten held a GC ref.
2855 // We assume here that "id->idGCref()" is not GC_NONE only if the instruction described by "id" writes a
2856 // GC ref to register "id->idReg1()". (It may, apparently, also not be GC_NONE in other cases, such as
2857 // for stores, but we ignore those cases here.)
2858 if (emitInsMayWriteToGCReg(ins)) // True if "id->idIns()" writes to a register than can hold GC ref.
2860 // We assume that "idReg1" is the primary destination register for all instructions
2861 if (id->idGCref() != GCT_NONE)
2863 emitGCregLiveUpd(id->idGCref(), id->idReg1(), dstRW2 - writeableOffset);
2867 emitGCregDeadUpd(id->idReg1(), dstRW2 - writeableOffset);
2871 // Now we determine if the instruction has written to a (local variable) stack location, and either written a GC
2872 // ref or overwritten one.
2873 if (emitInsWritesToLclVarStackLoc(id) /*|| emitInsWritesToLclVarStackLocPair(id)*/)
2875 int varNum = id->idAddr()->iiaLclVar.lvaVarNum();
2876 unsigned ofs = AlignDown(id->idAddr()->iiaLclVar.lvaOffset(), TARGET_POINTER_SIZE);
2878 int adr = emitComp->lvaFrameAddress(varNum, &FPbased);
2879 if (id->idGCref() != GCT_NONE)
2881 emitGCvarLiveUpd(adr + ofs, varNum, id->idGCref(), dstRW2 - writeableOffset DEBUG_ARG(varNum));
2885 // If the type of the local is a gc ref type, update the liveness.
2889 // "Regular" (non-spill-temp) local.
2890 vt = var_types(emitComp->lvaTable[varNum].lvType);
2894 TempDsc* tmpDsc = codeGen->regSet.tmpFindNum(varNum);
2895 vt = tmpDsc->tdTempType();
2897 if (vt == TYP_REF || vt == TYP_BYREF)
2898 emitGCvarDeadUpd(adr + ofs, dstRW2 - writeableOffset DEBUG_ARG(varNum));
2900 // if (emitInsWritesToLclVarStackLocPair(id))
2902 // unsigned ofs2 = ofs + TARGET_POINTER_SIZE;
2903 // if (id->idGCrefReg2() != GCT_NONE)
2905 // emitGCvarLiveUpd(adr + ofs2, varNum, id->idGCrefReg2(), *dp);
2909 // // If the type of the local is a gc ref type, update the liveness.
2913 // // "Regular" (non-spill-temp) local.
2914 // vt = var_types(emitComp->lvaTable[varNum].lvType);
2918 // TempDsc* tmpDsc = codeGen->regSet.tmpFindNum(varNum);
2919 // vt = tmpDsc->tdTempType();
2921 // if (vt == TYP_REF || vt == TYP_BYREF)
2922 // emitGCvarDeadUpd(adr + ofs2, *dp);
2928 /* Make sure we set the instruction descriptor size correctly */
2930 if (emitComp->opts.disAsm || emitComp->verbose)
2933 bool dspOffs = emitComp->opts.dspGCtbls;
2934 #else // !DUMP_GC_TABLES
2935 bool dspOffs = !emitComp->opts.disDiffable;
2936 #endif // !DUMP_GC_TABLES
2937 emitDispIns(id, false, dspOffs, true, emitCurCodeOffs(odst), *dp, (dstRW - odstRW), ig);
2940 if (emitComp->compDebugBreak)
2942 // For example, set JitBreakEmitOutputInstr=a6 will break when this method is called for
2943 // emitting instruction a6, (i.e. IN00a6 in jitdump).
2944 if ((unsigned)JitConfig.JitBreakEmitOutputInstr() == id->idDebugOnlyInfo()->idNum)
2946 assert(!"JitBreakEmitOutputInstr reached");
2950 if (emitComp->opts.disAsm)
2952 emitDispIns(id, false, false, true, emitCurCodeOffs(odst), *dp, (dstRW - odstRW), ig);
2956 /* All instructions are expected to generate code */
2958 assert(*dp != (dstRW - writeableOffset));
2960 *dp = dstRW - writeableOffset;
2965 bool emitter::emitDispBranchInstrType(unsigned opcode2) const
2993 void emitter::emitDispBranchOffset(const instrDesc* id, const insGroup* ig) const
2995 int instrCount = id->idAddr()->iiaGetInstrCount();
2998 printf("pc%+d instructions", instrCount);
3001 unsigned insNum = emitFindInsNum(ig, id);
3003 if (ig->igInsCnt < insNum + 1 + instrCount)
3005 // TODO-RISCV64-BUG: This should be a labeled offset but does not contain an iiaIGlabel
3006 printf("pc%+d instructions", instrCount);
3010 UNATIVE_OFFSET srcOffs = ig->igOffs + emitFindOffset(ig, insNum + 1);
3011 UNATIVE_OFFSET dstOffs = ig->igOffs + emitFindOffset(ig, insNum + 1 + instrCount);
3012 ssize_t relOffs = static_cast<ssize_t>(emitOffsetToPtr(dstOffs) - emitOffsetToPtr(srcOffs));
3013 printf("pc%+d (%d instructions)", static_cast<int>(relOffs), instrCount);
3016 void emitter::emitDispBranchLabel(const instrDesc* id) const
3018 if (id->idIsBound())
3020 return emitPrintLabel(id->idAddr()->iiaIGlabel);
3022 printf("L_M%03u_", FMT_BB, emitComp->compMethodID, id->idAddr()->iiaBBlabel->bbNum);
3025 bool emitter::emitDispBranch(unsigned opcode2,
3026 const char* register1Name,
3027 const char* register2Name,
3028 const instrDesc* id,
3029 const insGroup* ig) const
3031 if (!emitDispBranchInstrType(opcode2))
3035 printf(" %s, %s, ", register1Name, register2Name);
3036 assert(id != nullptr);
3037 if (id->idAddr()->iiaHasInstrCount())
3039 // Branch is jumping to some non-labeled offset
3040 emitDispBranchOffset(id, ig);
3044 // Branch is jumping to the labeled offset
3045 emitDispBranchLabel(id);
3051 void emitter::emitDispIllegalInstruction(code_t instructionCode)
3053 printf("RISCV64 illegal instruction: 0x%08X\n", instructionCode);
3056 /*****************************************************************************/
3057 /*****************************************************************************/
3060 static const char* const RegNames[] =
3062 #define REGDEF(name, rnum, mask, sname) sname,
3063 #include "register.h"
3067 //----------------------------------------------------------------------------------------
3068 // Disassemble the given instruction.
3069 // The `emitter::emitDispInsName` is focused on the most important for debugging.
3070 // So it implemented as far as simply and independently which is very useful for
3071 // porting easily to the release mode.
3074 // code - The instruction's encoding.
3075 // addr - The address of the code.
3076 // doffs - Flag informing whether the instruction's offset should be displayed.
3077 // insOffset - The instruction's offset.
3078 // id - The instrDesc of the code if needed.
3079 // ig - The insGroup of the code if needed
3082 // The length of the instruction's name include aligned space is 15.
3085 void emitter::emitDispInsName(
3086 code_t code, const BYTE* addr, bool doffs, unsigned insOffset, const instrDesc* id, const insGroup* ig)
3088 const BYTE* insAdr = addr - writeableOffset;
3090 unsigned int opcode = code & 0x7f;
3091 assert((opcode & 0x3) == 0x3);
3093 emitDispInsAddr(insAdr);
3094 emitDispInsOffs(insOffset, doffs);
3102 const char* rd = RegNames[(code >> 7) & 0x1f];
3103 int imm20 = (code >> 12) & 0xfffff;
3104 if (imm20 & 0x80000)
3106 imm20 |= 0xfff00000;
3108 printf("lui %s, %d\n", rd, imm20);
3113 const char* rd = RegNames[(code >> 7) & 0x1f];
3114 int imm20 = (code >> 12) & 0xfffff;
3115 if (imm20 & 0x80000)
3117 imm20 |= 0xfff00000;
3119 printf("auipc %s, %d\n", rd, imm20);
3124 unsigned int opcode2 = (code >> 12) & 0x7;
3125 const char* rd = RegNames[(code >> 7) & 0x1f];
3126 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3127 int imm12 = (((int)code) >> 20); // & 0xfff;
3128 // if (imm12 & 0x800)
3130 // imm12 |= 0xfffff000;
3135 printf("addi %s, %s, %d\n", rd, rs1, imm12);
3138 printf("slli %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6 BITS for SHAMT in RISCV64
3141 printf("slti %s, %s, %d\n", rd, rs1, imm12);
3144 printf("sltiu %s, %s, %d\n", rd, rs1, imm12);
3147 printf("xori %s, %s, 0x%x\n", rd, rs1, imm12);
3149 case 0x5: // SRLI & SRAI
3150 if (((code >> 30) & 0x1) == 0)
3152 printf("srli %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6BITS for SHAMT in RISCV64
3156 printf("srai %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6BITS for SHAMT in RISCV64
3160 printf("ori %s, %s, 0x%x\n", rd, rs1, imm12 & 0xfff);
3163 printf("andi %s, %s, 0x%x\n", rd, rs1, imm12 & 0xfff);
3166 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3172 unsigned int opcode2 = (code >> 12) & 0x7;
3173 const char* rd = RegNames[(code >> 7) & 0x1f];
3174 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3175 int imm12 = (((int)code) >> 20); // & 0xfff;
3176 // if (imm12 & 0x800)
3178 // imm12 |= 0xfffff000;
3183 printf("addiw %s, %s, %d\n", rd, rs1, imm12);
3186 printf("slliw %s, %s, %d\n", rd, rs1, imm12 & 0x3f); // 6 BITS for SHAMT in RISCV64
3188 case 0x5: // SRLIW & SRAIW
3189 if (((code >> 30) & 0x1) == 0)
3191 printf("srliw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5BITS for SHAMT in RISCV64
3195 printf("sraiw %s, %s, %d\n", rd, rs1, imm12 & 0x1f); // 5BITS for SHAMT in RISCV64
3199 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3205 unsigned int opcode2 = (code >> 25) & 0x3;
3206 unsigned int opcode3 = (code >> 12) & 0x7;
3207 const char* rd = RegNames[(code >> 7) & 0x1f];
3208 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3209 const char* rs2 = RegNames[(code >> 20) & 0x1f];
3214 case 0x0: // ADD & SUB
3215 if (((code >> 30) & 0x1) == 0)
3217 printf("add %s, %s, %s\n", rd, rs1, rs2);
3221 printf("sub %s, %s, %s\n", rd, rs1, rs2);
3225 printf("sll %s, %s, %s\n", rd, rs1, rs2);
3228 printf("slt %s, %s, %s\n", rd, rs1, rs2);
3231 printf("sltu %s, %s, %s\n", rd, rs1, rs2);
3234 printf("xor %s, %s, %s\n", rd, rs1, rs2);
3236 case 0x5: // SRL & SRA
3237 if (((code >> 30) & 0x1) == 0)
3239 printf("srl %s, %s, %s\n", rd, rs1, rs2);
3243 printf("sra %s, %s, %s\n", rd, rs1, rs2);
3247 printf("or %s, %s, %s\n", rd, rs1, rs2);
3250 printf("and %s, %s, %s\n", rd, rs1, rs2);
3253 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3257 else if (opcode2 == 0x1)
3262 printf("mul %s, %s, %s\n", rd, rs1, rs2);
3265 printf("mulh %s, %s, %s\n", rd, rs1, rs2);
3268 printf("mulhsu %s, %s, %s\n", rd, rs1, rs2);
3271 printf("mulhu %s, %s, %s\n", rd, rs1, rs2);
3274 printf("div %s, %s, %s\n", rd, rs1, rs2);
3277 printf("divu %s, %s, %s\n", rd, rs1, rs2);
3280 printf("rem %s, %s, %s\n", rd, rs1, rs2);
3283 printf("remu %s, %s, %s\n", rd, rs1, rs2);
3286 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3292 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3298 unsigned int opcode2 = (code >> 25) & 0x3;
3299 unsigned int opcode3 = (code >> 12) & 0x7;
3300 const char* rd = RegNames[(code >> 7) & 0x1f];
3301 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3302 const char* rs2 = RegNames[(code >> 20) & 0x1f];
3308 case 0x0: // ADDW & SUBW
3309 if (((code >> 30) & 0x1) == 0)
3311 printf("addw %s, %s, %s\n", rd, rs1, rs2);
3315 printf("subw %s, %s, %s\n", rd, rs1, rs2);
3319 printf("sllw %s, %s, %s\n", rd, rs1, rs2);
3321 case 0x5: // SRLW & SRAW
3322 if (((code >> 30) & 0x1) == 0)
3324 printf("srlw %s, %s, %s\n", rd, rs1, rs2);
3328 printf("sraw %s, %s, %s\n", rd, rs1, rs2);
3332 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3336 else if (opcode2 == 1)
3341 printf("mulw %s, %s, %s\n", rd, rs1, rs2);
3344 printf("divw %s, %s, %s\n", rd, rs1, rs2);
3347 printf("divuw %s, %s, %s\n", rd, rs1, rs2);
3350 printf("remw %s, %s, %s\n", rd, rs1, rs2);
3353 printf("remuw %s, %s, %s\n", rd, rs1, rs2);
3356 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3362 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3368 unsigned int opcode2 = (code >> 12) & 0x7;
3369 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3370 const char* rs2 = RegNames[(code >> 20) & 0x1f];
3371 int offset = (((code >> 25) & 0x7f) << 5) | ((code >> 7) & 0x1f);
3374 offset |= 0xfffff000;
3380 printf("sb %s, %d(%s)\n", rs2, offset, rs1);
3383 printf("sh %s, %d(%s)\n", rs2, offset, rs1);
3386 printf("sw %s, %d(%s)\n", rs2, offset, rs1);
3389 printf("sd %s, %d(%s)\n", rs2, offset, rs1);
3392 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3396 case 0x63: // BRANCH
3398 unsigned int opcode2 = (code >> 12) & 0x7;
3399 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3400 const char* rs2 = RegNames[(code >> 20) & 0x1f];
3401 // int offset = (((code >> 31) & 0x1) << 12) | (((code >> 7) & 0x1) << 11) | (((code >> 25) & 0x3f) << 5) |
3402 // (((code >> 8) & 0xf) << 1);
3403 // if (offset & 0x800)
3405 // offset |= 0xfffff000;
3407 if (!emitDispBranch(opcode2, rs1, rs2, id, ig))
3409 emitDispIllegalInstruction(code);
3415 unsigned int opcode2 = (code >> 12) & 0x7;
3416 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3417 const char* rd = RegNames[(code >> 7) & 0x1f];
3418 int offset = ((code >> 20) & 0xfff);
3421 offset |= 0xfffff000;
3427 printf("lb %s, %d(%s)\n", rd, offset, rs1);
3430 printf("lh %s, %d(%s)\n", rd, offset, rs1);
3433 printf("lw %s, %d(%s)\n", rd, offset, rs1);
3436 printf("ld %s, %d(%s)\n", rd, offset, rs1);
3439 printf("lbu %s, %d(%s)\n", rd, offset, rs1);
3442 printf("lhu %s, %d(%s)\n", rd, offset, rs1);
3445 printf("lwu %s, %d(%s)\n", rd, offset, rs1);
3448 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3454 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3455 const char* rd = RegNames[(code >> 7) & 0x1f];
3456 int offset = ((code >> 20) & 0xfff);
3459 offset |= 0xfffff000;
3461 printf("jalr %s, %d(%s)", rd, offset, rs1);
3462 CORINFO_METHOD_HANDLE handle = (CORINFO_METHOD_HANDLE)id->idDebugOnlyInfo()->idMemCookie;
3463 // Target for ret call is unclear, e.g.:
3468 const char* methodName = emitComp->eeGetMethodFullName(handle);
3469 printf("\t\t// %s", methodName);
3477 const char* rd = RegNames[(code >> 7) & 0x1f];
3478 int offset = (((code >> 31) & 0x1) << 20) | (((code >> 12) & 0xff) << 12) | (((code >> 20) & 0x1) << 11) |
3479 (((code >> 21) & 0x3ff) << 1);
3480 if (offset & 0x80000)
3482 offset |= 0xfff00000;
3484 printf("jal %s, %d", rd, offset);
3485 CORINFO_METHOD_HANDLE handle = (CORINFO_METHOD_HANDLE)id->idDebugOnlyInfo()->idMemCookie;
3488 const char* methodName = emitComp->eeGetMethodFullName(handle);
3489 printf("\t\t// %s", methodName);
3497 int pred = ((code) >> 24) & 0xf;
3498 int succ = ((code) >> 20) & 0xf;
3499 printf("fence %d, %d\n", pred, succ);
3504 unsigned int opcode2 = (code >> 12) & 0x7;
3507 const char* rd = RegNames[(code >> 7) & 0x1f];
3508 int csrtype = (code >> 20);
3511 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3515 printf("csrrw %s, %d, %s\n", rd, csrtype, rs1);
3518 printf("csrrs %s, %d, %s\n", rd, csrtype, rs1);
3521 printf("csrrc %s, %d, %s\n", rd, csrtype, rs1);
3524 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3530 unsigned imm5 = ((code >> 15) & 0x1f);
3534 printf("csrrwi %s, %d, %d\n", rd, csrtype, imm5);
3537 printf("csrrsi %s, %d, %d\n", rd, csrtype, imm5);
3540 printf("csrrci %s, %d, %d\n", rd, csrtype, imm5);
3543 printf("RISCV64 illegal instruction: 0x%08X\n", code);
3549 if (code == emitInsCode(INS_ebreak))
3555 NYI_RISCV64("illegal ins within emitDisInsName!");
3561 unsigned int opcode2 = (code >> 25) & 0x7f;
3562 unsigned int opcode3 = (code >> 20) & 0x1f;
3563 unsigned int opcode4 = (code >> 12) & 0x7;
3564 const char* fd = RegNames[((code >> 7) & 0x1f) | 0x20];
3565 const char* fs1 = RegNames[((code >> 15) & 0x1f) | 0x20];
3566 const char* fs2 = RegNames[((code >> 20) & 0x1f) | 0x20];
3568 const char* xd = RegNames[(code >> 7) & 0x1f];
3569 const char* xs1 = RegNames[(code >> 15) & 0x1f];
3570 const char* xs2 = RegNames[(code >> 20) & 0x1f];
3574 case 0x00: // FADD.S
3575 printf("fadd.s %s, %s, %s\n", fd, fs1, fs2);
3577 case 0x04: // FSUB.S
3578 printf("fsub.s %s, %s, %s\n", fd, fs1, fs2);
3580 case 0x08: // FMUL.S
3581 printf("fmul.s %s, %s, %s\n", fd, fs1, fs2);
3583 case 0x0C: // FDIV.S
3584 printf("fdiv.s %s, %s, %s\n", fd, fs1, fs2);
3586 case 0x2C: // FSQRT.S
3587 printf("fsqrt.s %s, %s\n", fd, fs1);
3589 case 0x10: // FSGNJ.S & FSGNJN.S & FSGNJX.S
3590 if (opcode4 == 0) // FSGNJ.S
3592 printf("fsgnj.s %s, %s, %s\n", fd, fs1, fs2);
3594 else if (opcode4 == 1) // FSGNJN.S
3596 printf("fsgnjn.s %s, %s, %s\n", fd, fs1, fs2);
3598 else if (opcode4 == 2) // FSGNJX.S
3600 printf("fsgnjx.s %s, %s, %s\n", fd, fs1, fs2);
3604 NYI_RISCV64("illegal ins within emitDisInsName!");
3607 case 0x14: // FMIN.S & FMAX.S
3608 if (opcode4 == 0) // FMIN.S
3610 printf("fmin.s %s, %s, %s\n", fd, fs1, fs2);
3612 else if (opcode4 == 1) // FMAX.S
3614 printf("fmax.s %s, %s, %s\n", fd, fs1, fs2);
3618 NYI_RISCV64("illegal ins within emitDisInsName!");
3621 case 0x60: // FCVT.W.S & FCVT.WU.S & FCVT.L.S & FCVT.LU.S
3622 if (opcode3 == 0) // FCVT.W.S
3624 printf("fcvt.w.s %s, %s\n", xd, fs1);
3626 else if (opcode3 == 1) // FCVT.WU.S
3628 printf("fcvt.wu.s %s, %s\n", xd, fs1);
3630 else if (opcode3 == 2) // FCVT.L.S
3632 printf("fcvt.l.s %s, %s\n", xd, fs1);
3634 else if (opcode3 == 3) // FCVT.LU.S
3636 printf("fcvt.lu.s %s, %s\n", xd, fs1);
3640 NYI_RISCV64("illegal ins within emitDisInsName!");
3643 case 0x70: // FMV.X.W & FCLASS.S
3644 if (opcode4 == 0) // FMV.X.W
3646 printf("fmv.x.w %s, %s\n", xd, fs1);
3648 else if (opcode4 == 1) // FCLASS.S
3650 printf("fclass.s %s, %s\n", xd, fs1);
3654 NYI_RISCV64("illegal ins within emitDisInsName!");
3657 case 0x50: // FLE.S & FLT.S & FEQ.S
3658 if (opcode4 == 0) // FLE.S
3660 printf("fle.s %s, %s, %s\n", xd, fs1, fs2);
3662 else if (opcode4 == 1) // FLT.S
3664 printf("flt.s %s, %s, %s\n", xd, fs1, fs2);
3666 else if (opcode4 == 2) // FEQ.S
3668 printf("feq.s %s, %s, %s\n", xd, fs1, fs2);
3672 NYI_RISCV64("illegal ins within emitDisInsName!");
3675 case 0x68: // FCVT.S.W & FCVT.S.WU & FCVT.S.L & FCVT.S.LU
3676 if (opcode3 == 0) // FCVT.S.W
3678 printf("fcvt.s.w %s, %s\n", fd, xs1);
3680 else if (opcode3 == 1) // FCVT.S.WU
3682 printf("fcvt.s.wu %s, %s\n", fd, xs1);
3684 else if (opcode3 == 2) // FCVT.S.L
3686 printf("fcvt.s.l %s, %s\n", fd, xs1);
3688 else if (opcode3 == 3) // FCVT.S.LU
3690 printf("fcvt.s.lu %s, %s\n", fd, xs1);
3695 NYI_RISCV64("illegal ins within emitDisInsName!");
3698 case 0x78: // FMV.W.X
3699 printf("fmv.w.x %s, %s\n", fd, xs1);
3702 printf("fadd.d %s, %s, %s\n", fd, fs1, fs2);
3705 printf("fsub.d %s, %s, %s\n", fd, fs1, fs2);
3708 printf("fmul.d %s, %s, %s\n", fd, fs1, fs2);
3711 printf("fdiv.d %s, %s, %s\n", fd, fs1, fs2);
3713 case 0x2d: // FSQRT.D
3714 printf("fsqrt.d %s, %s\n", fd, fs1);
3716 case 0x11: // FSGNJ.D & FSGNJN.D & FSGNJX.D
3717 if (opcode4 == 0) // FSGNJ.D
3719 printf("fsgnj.d %s, %s, %s\n", fd, fs1, fs2);
3721 else if (opcode4 == 1) // FSGNJN.D
3723 printf("fsgnjn.d %s, %s, %s\n", fd, fs1, fs2);
3725 else if (opcode4 == 2) // FSGNJX.D
3727 printf("fsgnjx.d %s, %s, %s\n", fd, fs1, fs2);
3731 NYI_RISCV64("illegal ins within emitDisInsName!");
3734 case 0x15: // FMIN.D & FMAX.D
3735 if (opcode4 == 0) // FMIN.D
3737 printf("fmin.d %s, %s, %s\n", fd, fs1, fs2);
3739 else if (opcode4 == 1) // FMAX.D
3741 printf("fmax.d %s, %s, %s\n", fd, fs1, fs2);
3745 NYI_RISCV64("illegal ins within emitDisInsName!");
3748 case 0x20: // FCVT.S.D
3749 if (opcode3 == 1) // FCVT.S.D
3751 printf("fcvt.s.d %s, %s\n", fd, fs1);
3755 NYI_RISCV64("illegal ins within emitDisInsName!");
3758 case 0x21: // FCVT.D.S
3759 if (opcode3 == 0) // FCVT.D.S
3761 printf("fcvt.d.s %s, %s\n", fd, fs1);
3765 NYI_RISCV64("illegal ins within emitDisInsName!");
3768 case 0x51: // FLE.D & FLT.D & FEQ.D
3769 if (opcode4 == 0) // FLE.D
3771 printf("fle.d %s, %s, %s\n", xd, fs1, fs2);
3773 else if (opcode4 == 1) // FLT.D
3775 printf("flt.d %s, %s, %s\n", xd, fs1, fs2);
3777 else if (opcode4 == 2) // FEQ.D
3779 printf("feq.d %s, %s, %s\n", xd, fs1, fs2);
3783 NYI_RISCV64("illegal ins within emitDisInsName!");
3786 case 0x61: // FCVT.W.D & FCVT.WU.D & FCVT.L.D & FCVT.LU.D
3788 if (opcode3 == 0) // FCVT.W.D
3790 printf("fcvt.w.d %s, %s\n", xd, fs1);
3792 else if (opcode3 == 1) // FCVT.WU.D
3794 printf("fcvt.wu.d %s, %s\n", xd, fs1);
3796 else if (opcode3 == 2) // FCVT.L.D
3798 printf("fcvt.l.d %s, %s\n", xd, fs1);
3800 else if (opcode3 == 3) // FCVT.LU.D
3802 printf("fcvt.lu.d %s, %s\n", xd, fs1);
3806 NYI_RISCV64("illegal ins within emitDisInsName!");
3809 case 0x69: // FCVT.D.W & FCVT.D.WU & FCVT.D.L & FCVT.D.LU
3810 if (opcode3 == 0) // FCVT.D.W
3812 printf("fcvt.d.w %s, %s\n", fd, xs1);
3814 else if (opcode3 == 1) // FCVT.D.WU
3816 printf("fcvt.d.wu %s, %s\n", fd, xs1);
3818 else if (opcode3 == 2)
3820 printf("fcvt.d.l %s, %s\n", fd, xs1);
3822 else if (opcode3 == 3)
3824 printf("fcvt.d.lu %s, %s\n", fd, xs1);
3829 NYI_RISCV64("illegal ins within emitDisInsName!");
3833 case 0x71: // FMV.X.D & FCLASS.D
3834 if (opcode4 == 0) // FMV.X.D
3836 printf("fmv.x.d %s, %s\n", xd, fs1);
3838 else if (opcode4 == 1) // FCLASS.D
3840 printf("fclass.d %s, %s\n", xd, fs1);
3844 NYI_RISCV64("illegal ins within emitDisInsName!");
3847 case 0x79: // FMV.D.X
3848 assert(opcode4 == 0);
3849 printf("fmv.d.x %s, %s\n", fd, xs1);
3852 NYI_RISCV64("illegal ins within emitDisInsName!");
3859 unsigned int opcode2 = (code >> 12) & 0x7;
3861 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3862 const char* rs2 = RegNames[((code >> 20) & 0x1f) | 0x20];
3863 int offset = (((code >> 25) & 0x7f) << 5) | ((code >> 7) & 0x1f);
3866 offset |= 0xfffff000;
3868 if (opcode2 == 2) // FSW
3870 printf("fsw %s, %d(%s)\n", rs2, offset, rs1);
3872 else if (opcode2 == 3) // FSD
3874 printf("fsd %s, %d(%s)\n", rs2, offset, rs1);
3878 NYI_RISCV64("illegal ins within emitDisInsName!");
3884 unsigned int opcode2 = (code >> 12) & 0x7;
3885 const char* rs1 = RegNames[(code >> 15) & 0x1f];
3886 const char* rd = RegNames[((code >> 7) & 0x1f) | 0x20];
3887 int offset = ((code >> 20) & 0xfff);
3890 offset |= 0xfffff000;
3892 if (opcode2 == 2) // FLW
3894 printf("flw %s, %d(%s)\n", rd, offset, rs1);
3896 else if (opcode2 == 3) // FLD
3898 printf("fld %s, %d(%s)\n", rd, offset, rs1);
3902 NYI_RISCV64("illegal ins within emitDisInsName!");
3906 case 0x2f: // AMO - atomic memory operation
3908 bool hasDataReg = true;
3910 switch (code >> 27) // funct5
3947 assert(!"Illegal funct5 within atomic memory operation, emitDisInsName");
3952 switch ((code >> 12) & 0x7) // funct3: width
3961 assert(!"Illegal width tag within atomic memory operation, emitDisInsName");
3965 const char* aq = code & (1 << 25) ? "aq" : "";
3966 const char* rl = code & (1 << 26) ? "rl" : "";
3968 int len = printf("%s.%c.%s%s", name, width, aq, rl);
3973 static const int INS_LEN = 14;
3974 assert(len <= INS_LEN);
3976 const char* dest = RegNames[(code >> 7) & 0x1f];
3977 const char* addr = RegNames[(code >> 15) & 0x1f];
3979 int dataReg = (code >> 20) & 0x1f;
3982 const char* data = RegNames[dataReg];
3983 printf("%*s %s, %s, (%s)\n", INS_LEN - len, "", dest, data, addr);
3987 assert(dataReg == REG_R0);
3988 printf("%*s %s, (%s)\n", INS_LEN - len, "", dest, addr);
3993 NO_WAY("illegal ins within emitDisInsName!");
3996 NO_WAY("illegal ins within emitDisInsName!");
3999 /*****************************************************************************
4001 * Display (optionally) the instruction encoding in hex
4004 void emitter::emitDispInsHex(instrDesc* id, BYTE* code, size_t sz)
4006 if (!emitComp->opts.disCodeBytes)
4011 // We do not display the instruction hex if we want diff-able disassembly
4012 if (!emitComp->opts.disDiffable)
4016 printf(" %08X ", (*((code_t*)code)));
4026 void emitter::emitDispInsInstrNum(const instrDesc* id) const
4029 if (!emitComp->verbose)
4032 printf("IN%04x: ", id->idDebugOnlyInfo()->idNum);
4036 void emitter::emitDispIns(
4037 instrDesc* id, bool isNew, bool doffs, bool asmfm, unsigned offset, BYTE* pCode, size_t sz, insGroup* ig)
4039 if (pCode == nullptr)
4042 emitDispInsInstrNum(id);
4044 const BYTE* instr = pCode + writeableOffset;
4046 for (size_t i = 0; i < sz; instr += instrSize, i += instrSize, offset += instrSize)
4048 // TODO-RISCV64: support different size instructions
4049 instrSize = sizeof(code_t);
4051 memcpy(&instruction, instr, instrSize);
4052 emitDispInsName(instruction, instr, doffs, offset, id, ig);
4058 /*****************************************************************************
4060 * Display a stack frame reference.
4063 void emitter::emitDispFrameRef(int varx, int disp, int offs, bool asmfm)
4065 NYI_RISCV64("emitDispFrameRef-----unimplemented/unused on RISCV64 yet----");
4070 // Generate code for a load or store operation with a potentially complex addressing mode
4071 // This method handles the case of a GT_IND with contained GT_LEA op1 of the x86 form [base + index*sccale + offset]
4073 void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataReg, GenTreeIndir* indir)
4075 GenTree* addr = indir->Addr();
4077 if (addr->isContained())
4079 assert(addr->OperIs(GT_CLS_VAR_ADDR, GT_LCL_ADDR, GT_LEA));
4084 if (addr->OperGet() == GT_LEA)
4086 offset = addr->AsAddrMode()->Offset();
4087 if (addr->AsAddrMode()->gtScale > 0)
4089 assert(isPow2(addr->AsAddrMode()->gtScale));
4090 BitScanForward(&lsl, addr->AsAddrMode()->gtScale);
4094 GenTree* memBase = indir->Base();
4095 emitAttr addType = varTypeIsGC(memBase) ? EA_BYREF : EA_PTRSIZE;
4097 if (indir->HasIndex())
4099 GenTree* index = indir->Index();
4103 regNumber tmpReg = indir->GetSingleTempReg();
4105 if (isValidSimm12(offset))
4109 // Generate code to set tmpReg = base + index*scale
4110 emitIns_R_R_I(INS_slli, addType, tmpReg, index->GetRegNum(), lsl);
4111 emitIns_R_R_R(INS_add, addType, tmpReg, memBase->GetRegNum(), tmpReg);
4115 // Generate code to set tmpReg = base + index
4116 emitIns_R_R_R(INS_add, addType, tmpReg, memBase->GetRegNum(), index->GetRegNum());
4119 noway_assert(emitInsIsLoad(ins) || (tmpReg != dataReg));
4121 // Then load/store dataReg from/to [tmpReg + offset]
4122 emitIns_R_R_I(ins, attr, dataReg, tmpReg, offset);
4124 else // large offset
4126 // First load/store tmpReg with the large offset constant
4127 emitLoadImmediate(EA_PTRSIZE, tmpReg,
4128 offset); // codeGen->instGen_Set_Reg_To_Imm(EA_PTRSIZE, tmpReg, offset);
4129 // Then add the base register
4131 emitIns_R_R_R(INS_add, addType, tmpReg, tmpReg, memBase->GetRegNum());
4133 noway_assert(emitInsIsLoad(ins) || (tmpReg != dataReg));
4134 noway_assert(tmpReg != index->GetRegNum());
4136 regNumber scaleReg = indir->GetSingleTempReg();
4137 // Then load/store dataReg from/to [tmpReg + index*scale]
4138 emitIns_R_R_I(INS_slli, addType, scaleReg, index->GetRegNum(), lsl);
4139 emitIns_R_R_R(INS_add, addType, tmpReg, tmpReg, scaleReg);
4140 emitIns_R_R_I(ins, attr, dataReg, tmpReg, 0);
4143 else // (offset == 0)
4145 // Then load/store dataReg from/to [memBase + index]
4146 switch (EA_SIZE(emitTypeSize(indir->TypeGet())))
4149 assert(((ins <= INS_lhu) && (ins >= INS_lb)) || ins == INS_lwu || ins == INS_ld ||
4150 ((ins <= INS_sw) && (ins >= INS_sb)) || ins == INS_sd);
4151 if (ins <= INS_lhu || ins == INS_lwu || ins == INS_ld)
4153 if (varTypeIsUnsigned(indir->TypeGet()))
4162 assert(((ins <= INS_lhu) && (ins >= INS_lb)) || ins == INS_lwu || ins == INS_ld ||
4163 ((ins <= INS_sw) && (ins >= INS_sb)) || ins == INS_sd);
4164 if (ins <= INS_lhu || ins == INS_lwu || ins == INS_ld)
4166 if (varTypeIsUnsigned(indir->TypeGet()))
4175 assert(((ins <= INS_lhu) && (ins >= INS_lb)) || ins == INS_lwu || ins == INS_ld ||
4176 ((ins <= INS_sw) && (ins >= INS_sb)) || ins == INS_sd || ins == INS_fsw ||
4178 assert(INS_fsw > INS_sd);
4179 if (ins <= INS_lhu || ins == INS_lwu || ins == INS_ld)
4181 if (varTypeIsUnsigned(indir->TypeGet()))
4186 else if (ins != INS_flw && ins != INS_fsw)
4190 assert(((ins <= INS_lhu) && (ins >= INS_lb)) || ins == INS_lwu || ins == INS_ld ||
4191 ((ins <= INS_sw) && (ins >= INS_sb)) || ins == INS_sd || ins == INS_fld ||
4193 assert(INS_fsd > INS_sd);
4194 if (ins <= INS_lhu || ins == INS_lwu || ins == INS_ld)
4198 else if (ins != INS_fld && ins != INS_fsd)
4202 NO_WAY("illegal ins within emitInsLoadStoreOp!");
4207 // Then load/store dataReg from/to [memBase + index*scale]
4208 emitIns_R_R_I(INS_slli, emitActualTypeSize(index->TypeGet()), codeGen->rsGetRsvdReg(),
4209 index->GetRegNum(), lsl);
4210 emitIns_R_R_R(INS_add, addType, codeGen->rsGetRsvdReg(), memBase->GetRegNum(),
4211 codeGen->rsGetRsvdReg());
4212 emitIns_R_R_I(ins, attr, dataReg, codeGen->rsGetRsvdReg(), 0);
4216 emitIns_R_R_R(INS_add, addType, codeGen->rsGetRsvdReg(), memBase->GetRegNum(), index->GetRegNum());
4217 emitIns_R_R_I(ins, attr, dataReg, codeGen->rsGetRsvdReg(), 0);
4221 else // no Index register
4223 if (addr->OperGet() == GT_CLS_VAR_ADDR)
4225 // Get a temp integer register to compute long address.
4226 regNumber addrReg = indir->GetSingleTempReg();
4228 emitIns_R_C(ins, attr, dataReg, addrReg, addr->AsClsVar()->gtClsVarHnd, 0);
4230 else if (addr->OperIs(GT_LCL_ADDR))
4232 GenTreeLclVarCommon* varNode = addr->AsLclVarCommon();
4233 unsigned lclNum = varNode->GetLclNum();
4234 unsigned offset = varNode->GetLclOffs();
4235 if (emitInsIsStore(ins))
4237 emitIns_S_R(ins, attr, dataReg, lclNum, offset);
4241 emitIns_R_S(ins, attr, dataReg, lclNum, offset);
4244 else if (isValidSimm12(offset))
4246 // Then load/store dataReg from/to [memBase + offset]
4247 emitIns_R_R_I(ins, attr, dataReg, memBase->GetRegNum(), offset);
4251 // We require a tmpReg to hold the offset
4252 regNumber tmpReg = indir->GetSingleTempReg();
4254 // First load/store tmpReg with the large offset constant
4255 emitLoadImmediate(EA_PTRSIZE, tmpReg, offset);
4256 // codeGen->instGen_Set_Reg_To_Imm(EA_PTRSIZE, tmpReg, offset);
4258 // Then load/store dataReg from/to [memBase + tmpReg]
4259 emitIns_R_R_R(INS_add, addType, tmpReg, memBase->GetRegNum(), tmpReg);
4260 emitIns_R_R_I(ins, attr, dataReg, tmpReg, 0);
4264 else // addr is not contained, so we evaluate it into a register
4267 if (addr->OperIs(GT_LCL_ADDR))
4269 // If the local var is a gcref or byref, the local var better be untracked, because we have
4270 // no logic here to track local variable lifetime changes, like we do in the contained case
4271 // above. E.g., for a `st a0,[a1]` for byref `a1` to local `V01`, we won't store the local
4272 // `V01` and so the emitter can't update the GC lifetime for `V01` if this is a variable birth.
4273 LclVarDsc* varDsc = emitComp->lvaGetDesc(addr->AsLclVarCommon());
4274 assert(!varDsc->lvTracked);
4278 // Then load/store dataReg from/to [addrReg]
4279 emitIns_R_R_I(ins, attr, dataReg, addr->GetRegNum(), 0);
4283 // The callee must call genConsumeReg() for any non-contained srcs
4284 // and genProduceReg() for any non-contained dsts.
4286 regNumber emitter::emitInsBinary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src)
4288 NYI_RISCV64("emitInsBinary-----unimplemented/unused on RISCV64 yet----");
4292 // The callee must call genConsumeReg() for any non-contained srcs
4293 // and genProduceReg() for any non-contained dsts.
4294 regNumber emitter::emitInsTernary(instruction ins, emitAttr attr, GenTree* dst, GenTree* src1, GenTree* src2)
4296 // dst can only be a reg
4297 assert(!dst->isContained());
4299 // find immed (if any) - it cannot be a dst
4300 // Only one src can be an int.
4301 GenTreeIntConCommon* intConst = nullptr;
4302 GenTree* nonIntReg = nullptr;
4304 const bool needCheckOv = dst->gtOverflowEx();
4306 if (varTypeIsFloating(dst))
4308 // src1 can only be a reg
4309 assert(!src1->isContained());
4310 // src2 can only be a reg
4311 assert(!src2->isContained());
4313 else // not floating point
4315 // src2 can be immed or reg
4316 assert(!src2->isContained() || src2->isContainedIntOrIImmed());
4318 // Check src2 first as we can always allow it to be a contained immediate
4319 if (src2->isContainedIntOrIImmed())
4321 intConst = src2->AsIntConCommon();
4324 // Only for commutative operations do we check src1 and allow it to be a contained immediate
4325 else if (dst->OperIsCommutative())
4327 // src1 can be immed or reg
4328 assert(!src1->isContained() || src1->isContainedIntOrIImmed());
4330 // Check src1 and allow it to be a contained immediate
4331 if (src1->isContainedIntOrIImmed())
4333 assert(!src2->isContainedIntOrIImmed());
4334 intConst = src1->AsIntConCommon();
4340 // src1 can only be a reg
4341 assert(!src1->isContained());
4350 assert(attr == EA_8BYTE);
4352 else if (ins == INS_addw) // || ins == INS_add
4354 assert(attr == EA_4BYTE);
4356 else if (ins == INS_addi)
4358 assert(intConst != nullptr);
4360 else if (ins == INS_addiw)
4362 assert(intConst != nullptr);
4364 else if (ins == INS_sub)
4366 assert(attr == EA_8BYTE);
4368 else if (ins == INS_subw)
4370 assert(attr == EA_4BYTE);
4372 else if ((ins == INS_mul) || (ins == INS_mulh) || (ins == INS_mulhu))
4374 assert(attr == EA_8BYTE);
4375 // NOTE: overflow format doesn't support an int constant operand directly.
4376 assert(intConst == nullptr);
4378 else if (ins == INS_mulw)
4380 assert(attr == EA_4BYTE);
4381 // NOTE: overflow format doesn't support an int constant operand directly.
4382 assert(intConst == nullptr);
4386 printf("RISCV64-Invalid ins for overflow check: %s\n", codeGen->genInsName(ins));
4387 assert(!"Invalid ins for overflow check");
4392 regNumber dstReg = dst->GetRegNum();
4393 regNumber src1Reg = src1->GetRegNum();
4394 regNumber src2Reg = src2->GetRegNum();
4396 if (intConst != nullptr)
4398 ssize_t imm = intConst->IconValue();
4399 assert(isValidSimm12(imm));
4403 assert(attr == EA_8BYTE);
4404 assert(imm != -2048);
4408 else if (ins == INS_subw)
4410 assert(attr == EA_4BYTE);
4411 assert(imm != -2048);
4416 assert(ins == INS_addi || ins == INS_addiw || ins == INS_andi || ins == INS_ori || ins == INS_xori);
4418 regNumber tempReg = needCheckOv ? dst->ExtractTempReg() : REG_NA;
4422 emitIns_R_R(INS_mov, attr, tempReg, nonIntReg->GetRegNum());
4425 emitIns_R_R_I(ins, attr, dstReg, nonIntReg->GetRegNum(), imm);
4429 // At this point andi/ori/xori are excluded by previous checks
4430 assert(ins == INS_addi || ins == INS_addiw);
4433 if ((dst->gtFlags & GTF_UNSIGNED) != 0)
4435 codeGen->genJumpToThrowHlpBlk_la(SCK_OVERFLOW, INS_bltu, dstReg, nullptr, tempReg);
4441 // B > 0 and C > 0, if A < B, goto overflow
4442 BasicBlock* tmpLabel = codeGen->genCreateTempLabel();
4443 emitIns_J_cond_la(INS_bge, tmpLabel, REG_R0, tempReg);
4444 emitIns_R_R_I(INS_slti, EA_PTRSIZE, tempReg, dstReg, imm);
4446 codeGen->genJumpToThrowHlpBlk_la(SCK_OVERFLOW, INS_bne, tempReg);
4448 codeGen->genDefineTempLabel(tmpLabel);
4452 // B < 0 and C < 0, if A > B, goto overflow
4453 BasicBlock* tmpLabel = codeGen->genCreateTempLabel();
4454 emitIns_J_cond_la(INS_bge, tmpLabel, tempReg, REG_R0);
4455 emitIns_R_R_I(INS_addi, attr, tempReg, REG_R0, imm);
4457 codeGen->genJumpToThrowHlpBlk_la(SCK_OVERFLOW, INS_blt, tempReg, nullptr, dstReg);
4459 codeGen->genDefineTempLabel(tmpLabel);
4464 else if (varTypeIsFloating(dst))
4466 emitIns_R_R_R(ins, attr, dstReg, src1Reg, src2Reg);
4470 regNumber tempReg = needCheckOv ? dst->ExtractTempReg() : REG_NA;
4472 switch (dst->OperGet())
4476 if (!needCheckOv && !(dst->gtFlags & GTF_UNSIGNED))
4478 emitIns_R_R_R(ins, attr, dstReg, src1Reg, src2Reg);
4484 assert(tempReg != dstReg);
4485 assert(tempReg != src1Reg);
4486 assert(tempReg != src2Reg);
4488 assert(REG_RA != dstReg);
4489 assert(REG_RA != src1Reg);
4490 assert(REG_RA != src2Reg);
4492 if ((dst->gtFlags & GTF_UNSIGNED) != 0)
4494 if (attr == EA_4BYTE)
4496 emitIns_R_R_I(INS_slli, EA_8BYTE, tempReg, src1Reg, 32);
4497 emitIns_R_R_I(INS_slli, EA_8BYTE, REG_RA, src2Reg, 32);
4498 emitIns_R_R_R(INS_mulhu, EA_8BYTE, tempReg, tempReg, REG_RA);
4499 emitIns_R_R_I(INS_srai, attr, tempReg, tempReg, 32);
4503 emitIns_R_R_R(INS_mulhu, attr, tempReg, src1Reg, src2Reg);
4508 if (attr == EA_4BYTE)
4510 emitIns_R_R_R(INS_mul, EA_8BYTE, tempReg, src1Reg, src2Reg);
4511 emitIns_R_R_I(INS_srai, attr, tempReg, tempReg, 32);
4515 emitIns_R_R_R(INS_mulh, attr, tempReg, src1Reg, src2Reg);
4520 // n * n bytes will store n bytes result
4521 emitIns_R_R_R(ins, attr, dstReg, src1Reg, src2Reg);
4523 if ((dst->gtFlags & GTF_UNSIGNED) != 0)
4525 if (attr == EA_4BYTE)
4527 emitIns_R_R_I(INS_slli, EA_8BYTE, dstReg, dstReg, 32);
4528 emitIns_R_R_I(INS_srli, EA_8BYTE, dstReg, dstReg, 32);
4534 assert(tempReg != dstReg);
4535 assert(tempReg != src1Reg);
4536 assert(tempReg != src2Reg);
4538 if ((dst->gtFlags & GTF_UNSIGNED) != 0)
4540 codeGen->genJumpToThrowHlpBlk_la(SCK_OVERFLOW, INS_bne, tempReg);
4544 regNumber tempReg2 = dst->ExtractTempReg();
4545 assert(tempReg2 != dstReg);
4546 assert(tempReg2 != src1Reg);
4547 assert(tempReg2 != src2Reg);
4548 size_t imm = (EA_SIZE(attr) == EA_8BYTE) ? 63 : 31;
4549 emitIns_R_R_I(EA_SIZE(attr) == EA_8BYTE ? INS_srai : INS_sraiw, attr, tempReg2, dstReg,
4551 codeGen->genJumpToThrowHlpBlk_la(SCK_OVERFLOW, INS_bne, tempReg, nullptr, tempReg2);
4563 emitIns_R_R_R(ins, attr, dstReg, src1Reg, src2Reg);
4565 // TODO-RISCV64-CQ: here sign-extend dst when deal with 32bit data is too conservative.
4566 if (EA_SIZE(attr) == EA_4BYTE)
4567 emitIns_R_R_I(INS_slliw, attr, dstReg, dstReg, 0);
4574 regNumber regOp1 = src1Reg;
4575 regNumber regOp2 = src2Reg;
4576 regNumber saveOperReg1 = REG_NA;
4577 regNumber saveOperReg2 = REG_NA;
4579 if ((dst->gtFlags & GTF_UNSIGNED) && (attr == EA_8BYTE))
4581 if (src1->gtType == TYP_INT)
4583 emitIns_R_R_I(INS_slli, EA_8BYTE, regOp1, regOp1, 32);
4584 emitIns_R_R_I(INS_srli, EA_8BYTE, regOp1, regOp1, 32);
4586 if (src2->gtType == TYP_INT)
4588 emitIns_R_R_I(INS_slli, EA_8BYTE, regOp2, regOp2, 32);
4589 emitIns_R_R_I(INS_srli, EA_8BYTE, regOp2, regOp2, 32);
4595 assert(!varTypeIsFloating(dst));
4597 assert(tempReg != dstReg);
4599 if (dstReg == regOp1)
4601 assert(tempReg != regOp1);
4602 assert(REG_RA != regOp1);
4603 saveOperReg1 = tempReg;
4604 saveOperReg2 = regOp2;
4605 emitIns_R_R_I(INS_addi, attr, tempReg, regOp1, 0);
4607 else if (dstReg == regOp2)
4609 assert(tempReg != regOp2);
4610 assert(REG_RA != regOp2);
4611 saveOperReg1 = regOp1;
4612 saveOperReg2 = tempReg;
4613 emitIns_R_R_I(INS_addi, attr, tempReg, regOp2, 0);
4617 saveOperReg1 = regOp1;
4618 saveOperReg2 = regOp2;
4622 emitIns_R_R_R(ins, attr, dstReg, regOp1, regOp2);
4631 bool isAdd = (dst->OperGet() == GT_ADD);
4632 if ((dst->gtFlags & GTF_UNSIGNED) != 0)
4634 // if A < B, goto overflow
4638 tempReg2 = saveOperReg1;
4642 tempReg1 = saveOperReg1;
4643 tempReg2 = saveOperReg2;
4645 codeGen->genJumpToThrowHlpBlk_la(SCK_OVERFLOW, INS_bltu, tempReg1, nullptr, tempReg2);
4650 tempReg2 = dst->ExtractTempReg();
4651 assert(tempReg1 != tempReg2);
4652 assert(tempReg1 != saveOperReg1);
4653 assert(tempReg2 != saveOperReg2);
4655 ssize_t ui6 = (attr == EA_4BYTE) ? 31 : 63;
4656 emitIns_R_R_I(INS_srli, attr, tempReg1, isAdd ? saveOperReg1 : dstReg, ui6);
4657 emitIns_R_R_I(INS_srli, attr, tempReg2, saveOperReg2, ui6);
4659 emitIns_R_R_R(INS_xor, attr, tempReg1, tempReg1, tempReg2);
4660 if (attr == EA_4BYTE)
4663 emitIns_R_R_I(INS_andi, attr, tempReg1, tempReg1, imm);
4664 emitIns_R_R_I(INS_andi, attr, tempReg2, tempReg2, imm);
4666 // if (B > 0 && C < 0) || (B < 0 && C > 0), skip overflow
4667 BasicBlock* tmpLabel = codeGen->genCreateTempLabel();
4668 BasicBlock* tmpLabel2 = codeGen->genCreateTempLabel();
4669 BasicBlock* tmpLabel3 = codeGen->genCreateTempLabel();
4671 emitIns_J_cond_la(INS_bne, tmpLabel, tempReg1, REG_R0);
4673 emitIns_J_cond_la(INS_bne, tmpLabel3, tempReg2, REG_R0);
4675 // B > 0 and C > 0, if A < B, goto overflow
4676 emitIns_J_cond_la(INS_bge, tmpLabel, isAdd ? dstReg : saveOperReg1,
4677 isAdd ? saveOperReg1 : saveOperReg2);
4679 codeGen->genDefineTempLabel(tmpLabel2);
4681 codeGen->genJumpToThrowHlpBlk(EJ_jmp, SCK_OVERFLOW);
4683 codeGen->genDefineTempLabel(tmpLabel3);
4685 // B < 0 and C < 0, if A > B, goto overflow
4686 emitIns_J_cond_la(INS_blt, tmpLabel2, isAdd ? saveOperReg1 : saveOperReg2,
4687 isAdd ? dstReg : saveOperReg1);
4689 codeGen->genDefineTempLabel(tmpLabel);
4696 NO_WAY("unexpected instruction within emitInsTernary!");
4703 unsigned emitter::get_curTotalCodeSize()
4705 return emitTotalCodeSize;
4708 #if defined(DEBUG) || defined(LATE_DISASM)
4710 //----------------------------------------------------------------------------------------
4711 // getInsExecutionCharacteristics:
4712 // Returns the current instruction execution characteristics
4715 // id - The current instruction descriptor to be evaluated
4718 // A struct containing the current instruction execution characteristics
4721 // The instruction latencies and throughput values returned by this function
4722 // are NOT accurate and just a function feature.
4723 emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(instrDesc* id)
4725 insExecutionCharacteristics result;
4727 // TODO-RISCV64: support this function.
4728 result.insThroughput = PERFSCORE_THROUGHPUT_ZERO;
4729 result.insLatency = PERFSCORE_LATENCY_ZERO;
4730 result.insMemoryAccessKind = PERFSCORE_MEMORY_NONE;
4735 #endif // defined(DEBUG) || defined(LATE_DISASM)
4738 //------------------------------------------------------------------------
4739 // emitRegName: Returns a general-purpose register name or SIMD and floating-point scalar register name.
4741 // TODO-RISCV64: supporting SIMD.
4743 // reg - A general-purpose register orfloating-point register.
4744 // size - unused parameter.
4745 // varName - unused parameter.
4748 // A string that represents a general-purpose register name or floating-point scalar register name.
4750 const char* emitter::emitRegName(regNumber reg, emitAttr size, bool varName) const
4752 assert(reg < REG_COUNT);
4754 const char* rn = nullptr;
4757 assert(rn != nullptr);
4763 //------------------------------------------------------------------------
4764 // IsMovInstruction: Determines whether a give instruction is a move instruction
4767 // ins -- The instruction being checked
4769 bool emitter::IsMovInstruction(instruction ins)
4788 #endif // defined(TARGET_RISCV64)