This commit is contained in:
Fuad Ismail 2025-07-30 15:56:22 +02:00 committed by GitHub
commit 03848046a8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 430 additions and 79 deletions

View File

@ -4354,7 +4354,7 @@ HRESULT CordbNativeCode::EnumerateVariableHomes(ICorDebugVariableHomeEnum **ppEn
int CordbNativeCode::GetCallInstructionLength(BYTE *ip, ULONG32 count)
{
#if defined(TARGET_ARM)
#if defined(TARGET_ARM) || defined(TARGET_RISCV64)
if (Is32BitInstruction(*(WORD*)ip))
return 4;
else
@ -4726,8 +4726,6 @@ int CordbNativeCode::GetCallInstructionLength(BYTE *ip, ULONG32 count)
_ASSERTE(!"Invalid opcode!");
return -1;
#elif defined(TARGET_RISCV64)
return MAX_INSTRUCTION_LENGTH;
#else
#error Platform not implemented
#endif

View File

@ -70,7 +70,7 @@ void NativeWalker::Decode()
LOG((LF_CORDB, LL_INFO100000, "RiscV64Walker::Decode instruction at %p, opcode: %x\n", m_ip, opcode));
// TODO after "C" Standard Extension support implemented, add C.J, C.JAL, C.JR, C.JALR, C.BEQZ, C.BNEZ
// TODO-RISCV64-RVC: after "C" Standard Extension support implemented, add C.J, C.JAL, C.JR, C.JALR, C.BEQZ, C.BNEZ
if ((opcode & 0x7f) == 0x6f) // JAL
{

View File

@ -470,7 +470,7 @@ RtlpGetFunctionEndAddress (
FunctionLength = *(PTR_ULONG64)(ImageBase + FunctionLength) & 0x3ffff;
}
return FunctionEntry->BeginAddress + 4 * FunctionLength;
return FunctionEntry->BeginAddress + 2 * FunctionLength;
}
#define RUNTIME_FUNCTION__BeginAddress(FunctionEntry) ((FunctionEntry)->BeginAddress)

View File

@ -803,18 +803,18 @@ struct RISCV64GcInfoEncoding {
// GC Pointers are 8-bytes aligned
static inline constexpr int32_t NORMALIZE_STACK_SLOT (int32_t x) { return ((x)>>3); }
static inline constexpr int32_t DENORMALIZE_STACK_SLOT (int32_t x) { return ((x)<<3); }
// All Instructions are 4 bytes long
static inline constexpr uint32_t NORMALIZE_CODE_LENGTH (uint32_t x) { return ((x)>>2); }
static inline constexpr uint32_t DENORMALIZE_CODE_LENGTH (uint32_t x) { return ((x)<<2); }
// All Instructions are 2/4 bytes long
static inline constexpr uint32_t NORMALIZE_CODE_LENGTH (uint32_t x) { return ((x)>>1); }
static inline constexpr uint32_t DENORMALIZE_CODE_LENGTH (uint32_t x) { return ((x)<<1); }
// Encode Frame pointer X8 as zero, sp/x2 as 1
static inline constexpr uint32_t NORMALIZE_STACK_BASE_REGISTER (uint32_t x) { return ((x) == 8 ? 0u : 1u); }
static inline constexpr uint32_t DENORMALIZE_STACK_BASE_REGISTER (uint32_t x) { return ((x) == 0 ? 8u : 2u); }
static inline constexpr uint32_t NORMALIZE_SIZE_OF_STACK_AREA (uint32_t x) { return ((x)>>3); }
static inline constexpr uint32_t DENORMALIZE_SIZE_OF_STACK_AREA (uint32_t x) { return ((x)<<3); }
static const bool CODE_OFFSETS_NEED_NORMALIZATION = true;
// Instructions are 4 bytes long
static inline constexpr uint32_t NORMALIZE_CODE_OFFSET (uint32_t x) { return ((x)>>2); }
static inline constexpr uint32_t DENORMALIZE_CODE_OFFSET (uint32_t x) { return ((x)<<2); }
// Instructions are 2/4 bytes long
static inline constexpr uint32_t NORMALIZE_CODE_OFFSET (uint32_t x) { return ((x)>>1); }
static inline constexpr uint32_t DENORMALIZE_CODE_OFFSET (uint32_t x) { return ((x)<<1); }
static const int PSP_SYM_STACK_SLOT_ENCBASE = 6;
static const int GENERICS_INST_CONTEXT_STACK_SLOT_ENCBASE = 6;

View File

@ -101,6 +101,15 @@ inline ResultType ThumbCodeToDataPointer(SourceType pCode)
#endif // TARGET_ARM
#ifdef TARGET_RISCV64
inline bool Is32BitInstruction(WORD opcode)
{
return (opcode & 0b11) == 0b11;
}
#endif
// Convert from a PCODE to the corresponding PINSTR. On many architectures this will be the identity function;
// on ARM, this will mask off the THUMB bit.
inline TADDR PCODEToPINSTR(PCODE pc)

View File

@ -786,6 +786,7 @@ void CodeGen::genZeroInitFrameUsingBlockInit(int untrLclHi, int untrLclLo, regNu
GetEmitter()->emitIns_R_R_R(INS_add, EA_PTRSIZE, rEndAddr, rEndAddr, rAddr);
}
// TODO-RISCV64-RVC: Remove hardcoded branch offset here
GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding);
GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding + REGSIZE_BYTES);
GetEmitter()->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, rAddr, padding + 2 * REGSIZE_BYTES);
@ -1006,10 +1007,11 @@ void CodeGen::genCodeForIncSaturate(GenTree* tree)
regNumber operandReg = genConsumeReg(operand);
emitAttr attr = emitActualTypeSize(tree);
BasicBlock* skip = genCreateTempLabel();
GetEmitter()->emitIns_R_R_I(INS_addi, attr, targetReg, operandReg, 1);
// bne targetReg, zero, 2 * 4
GetEmitter()->emitIns_R_R_I(INS_bne, attr, targetReg, REG_R0, 8);
GetEmitter()->emitIns_J_cond_la(INS_bne, skip, targetReg, REG_R0);
GetEmitter()->emitIns_R_R_I(INS_xori, attr, targetReg, targetReg, -1);
genDefineTempLabel(skip);
genProduceReg(tree);
}
@ -1533,6 +1535,8 @@ void CodeGen::genLclHeap(GenTree* tree)
// and localloc size is a multiple of STACK_ALIGN.
// Loop:
BasicBlock* loop = genCreateTempLabel();
genDefineTempLabel(loop);
emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, REG_SPBASE, REG_SPBASE, -16);
emit->emitIns_R_R_I(INS_sd, EA_PTRSIZE, REG_R0, REG_SPBASE, 8);
@ -1546,7 +1550,7 @@ void CodeGen::genLclHeap(GenTree* tree)
emit->emitIns_R_R_I(INS_addi, emitActualTypeSize(type), regCnt, regCnt, -16);
// goto Loop
emit->emitIns_R_R_I(INS_bne, EA_PTRSIZE, regCnt, REG_R0, -4 << 2);
emit->emitIns_J_cond_la(INS_bne, loop, regCnt, REG_R0);
lastTouchDelta = 0;
}
@ -1591,8 +1595,10 @@ void CodeGen::genLclHeap(GenTree* tree)
emit->emitIns_R_R_R(INS_sub, EA_PTRSIZE, regCnt, REG_SPBASE, regCnt);
// Overflow, set regCnt to lowest possible value
emit->emitIns_R_R_I(INS_beq, EA_PTRSIZE, tempReg, REG_R0, 2 << 2);
BasicBlock* skip = genCreateTempLabel();
emit->emitIns_J_cond_la(INS_beq, skip, tempReg, REG_R0);
emit->emitIns_R_R(INS_mov, EA_PTRSIZE, regCnt, REG_R0);
genDefineTempLabel(skip);
regNumber rPageSize = internalRegisters.GetSingle(tree);
@ -1603,10 +1609,12 @@ void CodeGen::genLclHeap(GenTree* tree)
emit->emitIns_R_R(INS_mov, EA_PTRSIZE, tempReg, REG_SPBASE);
// tickle the page - this triggers a page fault when on the guard page
BasicBlock* loop = genCreateTempLabel();
genDefineTempLabel(loop);
emit->emitIns_R_R_I(INS_lw, EA_4BYTE, REG_R0, tempReg, 0);
emit->emitIns_R_R_R(INS_sub, EA_4BYTE, tempReg, tempReg, rPageSize);
emit->emitIns_R_R_I(INS_bgeu, EA_PTRSIZE, tempReg, regCnt, -2 << 2);
emit->emitIns_J_cond_la(INS_bgeu, loop, tempReg, regCnt);
// lastTouchDelta is dynamic, and can be up to a page. So if we have outgoing arg space,
// we're going to assume the worst and probe.
@ -6517,6 +6525,7 @@ void CodeGen::genJumpToThrowHlpBlk_la(
imm = 3 << 2;
}
// TODO-RISCV64-RVC: Remove hardcoded branch offset here
emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, reg2, imm);
}
else
@ -6529,12 +6538,14 @@ void CodeGen::genJumpToThrowHlpBlk_la(
params.ireg = REG_DEFAULT_HELPER_CALL_TARGET;
if (compiler->opts.compReloc)
{
// TODO-RISCV64-RVC: Remove hardcoded branch offset here
ssize_t imm = (3 + 1) << 2;
emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, reg2, imm);
emit->emitIns_R_AI(INS_jal, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)pAddr);
}
else
{
// TODO-RISCV64-RVC: Remove hardcoded branch offset here
ssize_t imm = 9 << 2;
emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, reg2, imm);
// TODO-RISCV64-CQ: In the future we may consider using emitter::emitLoadImmediate instead,

View File

@ -141,10 +141,19 @@ bool emitter::emitInsWritesToLclVarStackLoc(instrDesc* id)
emitter::MajorOpcode emitter::GetMajorOpcode(code_t code)
{
assert((code & 0b11) == 0b11); // 16-bit instructions unsupported
code_t opcode = (code >> 2) & 0b11111;
assert((opcode & 0b111) != 0b111); // 48-bit and larger instructions unsupported
return (MajorOpcode)opcode;
if ((code & 0b11) == 0b11)
{
code_t opcode = (code >> 2) & 0b11111;
assert((opcode & 0b111) != 0b111); // 48-bit and larger instructions unsupported
return (MajorOpcode)opcode;
}
else
{
code_t op = code & 0b11;
code_t funct3 = (code >> 13) & 0b111;
code_t idx = 32 + (op << 3) + funct3;
return (MajorOpcode)idx;
}
}
inline bool emitter::emitInsMayWriteToGCReg(instruction ins)
@ -755,6 +764,7 @@ void emitter::emitIns_R_R_I(
code |= ((imm >> 5) & 0x3f) << 25;
code |= ((imm >> 12) & 0x1) << 31;
// TODO-RISCV64: Move jump logic to emitIns_J
// TODO-RISC64-RVC: Remove this once all branches uses emitIns_J
id->idAddr()->iiaSetInstrCount(static_cast<int>(imm / sizeof(code_t)));
}
else if (ins == INS_csrrs || ins == INS_csrrw || ins == INS_csrrc)
@ -821,6 +831,11 @@ void emitter::emitIns_R_I_I(
void emitter::emitIns_R_R_R(
instruction ins, emitAttr attr, regNumber reg1, regNumber reg2, regNumber reg3, insOpts opt) /* = INS_OPTS_NONE */
{
if (tryEmitCompressedIns_R_R_R(ins, attr, reg1, reg2, reg3, opt))
{
return;
}
code_t code = emitInsCode(ins);
if ((INS_add <= ins && ins <= INS_and) || (INS_mul <= ins && ins <= INS_remuw) ||
@ -977,6 +992,173 @@ void emitter::emitIns_R_R_R(
appendToCurIG(id);
}
bool emitter::tryEmitCompressedIns_R_R_R(
instruction ins, emitAttr attr, regNumber rd, regNumber rs1, regNumber rs2, insOpts opt)
{
// TODO-RISCV64-RVC: Disable this early return once compresed instructions are allowed in prolog / epilog
if (emitComp->compGeneratingProlog || emitComp->compGeneratingEpilog)
{
return false;
}
instruction compressedIns = tryGetCompressedIns_R_R_R(ins, attr, rd, rs1, rs2, opt);
if (compressedIns == INS_none)
{
return false;
}
code_t code;
switch (compressedIns)
{
case INS_c_mv:
code = insEncodeCRTypeInstr(compressedIns, rd, rs2);
break;
case INS_c_add:
code = insEncodeCRTypeInstr(compressedIns, rd, rs2);
break;
case INS_c_and:
case INS_c_or:
case INS_c_xor:
case INS_c_sub:
case INS_c_addw:
case INS_c_subw:
{
unsigned rdRvc = tryGetRvcRegisterNumber(rd);
unsigned rs2Rvc = tryGetRvcRegisterNumber(rs2);
assert((rdRvc != -1) && (rs2Rvc != -1));
code = insEncodeCATypeInstr(compressedIns, rdRvc, rs2Rvc);
break;
}
default:
return false;
};
instrDesc* id = emitNewInstr(attr);
id->idIns(ins);
id->idReg1(rd);
id->idReg2(rs1);
id->idReg3(rs2);
id->idAddr()->iiaSetInstrEncode(code);
id->idCodeSize(2);
appendToCurIG(id);
return true;
}
instruction emitter::tryGetCompressedIns_R_R_R(
instruction ins, emitAttr attr, regNumber rd, regNumber rs1, regNumber rs2, insOpts opt)
{
switch (ins)
{
case INS_add:
{
if ((rs1 == REG_R0) && (rd != REG_R0) && (rs2 != REG_R0))
{
return INS_c_mv;
}
else if ((rd == rs1) && (rd != REG_R0) && (rs2 != REG_R0))
{
return INS_c_add;
}
break;
}
case INS_and:
case INS_or:
case INS_xor:
case INS_sub:
case INS_addw:
case INS_subw:
{
unsigned rdRvc = tryGetRvcRegisterNumber(rd);
unsigned rs2Rvc = tryGetRvcRegisterNumber(rs2);
if ((rd == rs1) && (rdRvc != -1) && (rs2Rvc != -1))
{
return getCompressedArithmeticIns(ins);
}
break;
}
default:
break;
};
return INS_none;
}
unsigned emitter::tryGetRvcRegisterNumber(regNumber reg)
{
switch (reg)
{
case REG_FP:
return 0;
case REG_S1:
return 1;
case REG_A0:
return 2;
case REG_A1:
return 3;
case REG_A2:
return 4;
case REG_A3:
return 5;
case REG_A4:
return 6;
case REG_A5:
return 7;
default:
return -1;
}
}
regNumber emitter::getRegNumberFromRvcReg(unsigned rvcReg)
{
assert((rvcReg >> 3) == 0);
switch (rvcReg)
{
case 0:
return REG_FP;
case 1:
return REG_S1;
case 2:
return REG_A0;
case 3:
return REG_A1;
case 4:
return REG_A2;
case 5:
return REG_A3;
case 6:
return REG_A4;
case 7:
return REG_A5;
default:
unreached();
}
}
instruction emitter::getCompressedArithmeticIns(instruction ins)
{
assert((ins == INS_and) || (ins == INS_or) || (ins == INS_xor) || (ins == INS_sub) || (ins == INS_addw) ||
(ins == INS_subw));
switch (ins)
{
case INS_and:
return INS_c_and;
case INS_or:
return INS_c_or;
case INS_xor:
return INS_c_xor;
case INS_sub:
return INS_c_sub;
case INS_addw:
return INS_c_addw;
case INS_subw:
return INS_c_subw;
default:
unreached();
}
}
/*****************************************************************************
*
* Add an instruction referencing three registers and a constant.
@ -2284,7 +2466,7 @@ AGAIN:
#endif // DEBUG_EMIT
assert(jmpDist >= 0);
assert(!(jmpDist & 0x3));
assert(!(jmpDist & 0x1));
if (!(isLinkingEnd & 0x2) && (extra > 0) &&
(jmp->idInsOpt() == INS_OPTS_J || jmp->idInsOpt() == INS_OPTS_J_cond))
@ -2371,15 +2553,17 @@ AGAIN:
/*****************************************************************************
*
* Emit a 32-bit RISCV64 instruction
* Emit a 16/32-bit RISCV64 instruction
*/
unsigned emitter::emitOutput_Instr(BYTE* dst, code_t code) const
{
assert(dst != nullptr);
static_assert(sizeof(code_t) == 4, "code_t must be 4 bytes");
memcpy(dst + writeableOffset, &code, sizeof(code));
return sizeof(code_t);
unsigned codeSize = ((code & 0b11) == 0b11) ? 4 : 2;
assert((codeSize == 4) || ((code >> 16) == 0));
memcpy(dst + writeableOffset, &code, codeSize);
return codeSize;
}
static inline void assertCodeLength(size_t code, uint8_t size)
@ -2555,6 +2739,52 @@ static inline void assertCodeLength(size_t code, uint8_t size)
(imm20HiBit << 31);
}
/*****************************************************************************
*
* Emit a 16-bit RISCV64C CR-Type instruction
*
* Note: Instruction types as per RISC-V Spec, Chapter "Compressed Instruction Formats"
* CR Format:
* 15-------------12-11-----------------7-6------------------2-1-----0
* | funct4 | rd/rs1 | rs2 | op |
* -------------------------------------------------------------------
*/
/*static*/ emitter::code_t emitter::insEncodeCRTypeInstr(instruction ins, unsigned rdRs1, unsigned rs2)
{
assert((INS_c_mv <= ins) && (ins <= INS_c_add));
code_t insCode = emitInsCode(ins);
assertCodeLength(insCode, 16);
assertCodeLength(rdRs1, 5);
assertCodeLength(rs2, 5);
return insCode | (rs2 << 2) | (rdRs1 << 7);
}
/*****************************************************************************
*
* Emit a 16-bit RISCV64C CA-Type instruction
*
* Note: Instruction types as per RISC-V Spec, Chapter "Compressed Instruction Formats"
* CA Format:
* 15-----------------------10-9----------7-6------5-4---------2-1---0
* | funct6 | rd'/rs1' | funct2 | rs2' | op |
* -------------------------------------------------------------------
*/
/*static*/ emitter::code_t emitter::insEncodeCATypeInstr(instruction ins, unsigned rdRs1Rvc, unsigned rs2Rvc)
{
assert((INS_c_and <= ins) && (ins <= INS_c_subw));
code_t insCode = emitInsCode(ins);
assertCodeLength(insCode, 16);
assertCodeLength(rdRs1Rvc, 3);
assertCodeLength(rs2Rvc, 3);
return insCode | (rs2Rvc << 2) | (rdRs1Rvc << 7);
}
static constexpr unsigned kInstructionOpcodeMask = 0x7f;
static constexpr unsigned kInstructionFunct3Mask = 0x7000;
static constexpr unsigned kInstructionFunct5Mask = 0xf8000000;
@ -3138,7 +3368,7 @@ BYTE* emitter::emitOutputInstr_OptsRc(BYTE* dst, const instrDesc* id, instructio
assert(reg1 != REG_ZERO);
assert(id->idCodeSize() == 2 * sizeof(code_t));
const ssize_t immediate = (emitConsBlock - dst) + offset;
assert((immediate > 0) && ((immediate & 0x03) == 0));
assert((immediate > 0) && ((immediate & 0x01) == 0));
assert(isValidSimm32(immediate));
const regNumber tempReg = isFloatReg(reg1) ? codeGen->rsGetRsvdReg() : reg1;
@ -3158,7 +3388,7 @@ BYTE* emitter::emitOutputInstr_OptsRl(BYTE* dst, instrDesc* id, instruction* ins
*ins = INS_auipc;
const ssize_t immediate = (emitCodeBlock - dst) + igOffs;
assert((immediate & 0x03) == 0);
assert((immediate & 0x01) == 0);
assert(isValidSimm32(immediate));
dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, UpperNBitsOfWordSignExtend<20>(immediate));
dst += emitOutput_ITypeInstr(dst, INS_addi, reg1, reg1, LowerNBitsOfWord<12>(immediate));
@ -3168,7 +3398,7 @@ BYTE* emitter::emitOutputInstr_OptsRl(BYTE* dst, instrDesc* id, instruction* ins
BYTE* emitter::emitOutputInstr_OptsJalr(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins)
{
const ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, jmp) - 4;
assert((immediate & 0x03) == 0);
assert((immediate & 0x01) == 0);
*ins = jmp->idIns();
if (jmp->idInsIs(INS_jal, INS_j)) // far jump
@ -3211,7 +3441,7 @@ BYTE* emitter::emitOutputInstr_OptsJCond(BYTE* dst, instrDesc* id, const insGrou
BYTE* emitter::emitOutputInstr_OptsJ(BYTE* dst, instrDesc* id, const insGroup* ig, instruction* ins)
{
const ssize_t immediate = emitOutputInstrJumpDistance(dst, ig, static_cast<instrDescJmp*>(id));
assert((immediate & 0x03) == 0);
assert((immediate & 0x01) == 0);
*ins = id->idIns();
@ -4723,6 +4953,63 @@ void emitter::emitDispInsName(
}
return;
}
case MajorOpcode::JrJalrMvAdd:
{
unsigned funct4 = (code >> 12) & 0xf;
unsigned rdRs1 = (code >> 7) & 0x1f;
unsigned rs2 = (code >> 2) & 0x1f;
// TODO-RISCV64-RVC: Introduce a switch in jitconfigvalues.h to show c.* prefix.
if (funct4 == 0b1001 && rdRs1 != REG_R0 && rs2 != REG_R0)
{
printf("add %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else if (funct4 == 0b1000 && rdRs1 != REG_R0 && rs2 != REG_R0)
{
printf("mv %s, %s\n", RegNames[rdRs1], RegNames[rs2]);
}
else
{
return emitDispIllegalInstruction(code);
}
return;
}
case MajorOpcode::MiscAlu:
{
unsigned funct6 = (code >> 10) & 0x3f;
unsigned funct2 = (code >> 5) & 0x3;
unsigned rdRs1 = getRegNumberFromRvcReg(((code >> 7) & 0x7));
unsigned rs2 = getRegNumberFromRvcReg(((code >> 2) & 0x7));
// TODO-RISCV64-RVC: Introduce a switch in jitconfigvalues.h to show c.* prefix.
if (funct6 == 0b100011 && funct2 == 0b00)
{
printf("sub %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else if (funct6 == 0b100011 && funct2 == 0b01)
{
printf("xor %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else if (funct6 == 0b100011 && funct2 == 0b10)
{
printf("or %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else if (funct6 == 0b100011 && funct2 == 0b11)
{
printf("and %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else if (funct6 == 0b100111 && funct2 == 0b00)
{
printf("subw %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else if (funct6 == 0b100111 && funct2 == 0b01)
{
printf("addw %s, %s, %s\n", RegNames[rdRs1], RegNames[rdRs1], RegNames[rs2]);
}
else
{
emitDispIllegalInstruction(code);
}
return;
}
default:
NO_WAY("illegal ins within emitDisInsName!");
}
@ -4754,10 +5041,14 @@ void emitter::emitDispIns(
unsigned instrSize;
for (size_t i = 0; i < sz; instr += instrSize, i += instrSize, offset += instrSize)
{
// TODO-RISCV64: support different size instructions
instrSize = sizeof(code_t);
code_t instruction;
memcpy(&instruction, instr, instrSize);
if ((instruction & 0b11) != 0b11)
{
instruction &= 0xffff;
instrSize = 2;
}
#ifdef DEBUG
if (emitComp->verbose && i != 0)
{
@ -5470,7 +5761,7 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
result.insMemoryAccessKind = PERFSCORE_MEMORY_NONE;
unsigned codeSize = id->idCodeSize();
assert((codeSize >= 4) && (codeSize % sizeof(code_t) == 0));
assert((codeSize >= 2) && ((codeSize % 2) == 0));
// Some instructions like jumps or loads may have not-yet-known simple auxilliary instructions (lui, addi, slli,
// etc) for building immediates, assume cost of one each.
@ -5499,6 +5790,8 @@ emitter::insExecutionCharacteristics emitter::getInsExecutionCharacteristics(ins
case MajorOpcode::Op:
case MajorOpcode::Op32:
case MajorOpcode::JrJalrMvAdd:
case MajorOpcode::MiscAlu:
if (id->idInsIs(INS_mul, INS_mulh, INS_mulhu, INS_mulhsu, INS_mulw))
{
result.insLatency = PERFSCORE_LATENCY_3C;

View File

@ -3,7 +3,7 @@
#if defined(TARGET_RISCV64)
// The RISCV64 instructions are all 32 bits in size.
// The RISCV64 instructions are all 32 / 16 bits in size.
// we use an unsigned int to hold the encoded instructions.
// This typedef defines the type that we use to hold encoded instructions.
//
@ -62,6 +62,17 @@ bool emitInsIsLoad(instruction ins);
bool emitInsIsStore(instruction ins);
bool emitInsIsLoadOrStore(instruction ins);
// RVC emitters
bool tryEmitCompressedIns_R_R_R(
instruction ins, emitAttr attr, regNumber rd, regNumber rs1, regNumber rs2, insOpts opt);
// RVC helpers
instruction tryGetCompressedIns_R_R_R(
instruction ins, emitAttr attr, regNumber rd, regNumber rs1, regNumber rs2, insOpts opt);
unsigned tryGetRvcRegisterNumber(regNumber reg);
instruction getCompressedArithmeticIns(instruction ins);
regNumber getRegNumberFromRvcReg(unsigned rvcReg);
void emitDispInsName(
code_t code, const BYTE* addr, bool doffs, unsigned insOffset, const instrDesc* id, const insGroup* ig);
void emitDispInsInstrNum(const instrDesc* id) const;
@ -77,7 +88,7 @@ static emitter::code_t emitInsCode(instruction ins /*, insFormat fmt*/);
// Generate code for a load or store operation and handle the case of contained GT_LEA op1 with [base + offset]
void emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataReg, GenTreeIndir* indir);
// Emit the 32-bit RISCV64 instruction 'code' into the 'dst' buffer
// Emit the 16/32-bit RISCV64 instruction 'code' into the 'dst' buffer
unsigned emitOutput_Instr(BYTE* dst, code_t code) const;
ssize_t emitOutputInstrJumpDistance(const BYTE* src, const insGroup* ig, instrDescJmp* jmp);
@ -100,6 +111,9 @@ static code_t insEncodeUTypeInstr(unsigned opcode, unsigned rd, unsigned imm20);
static code_t insEncodeBTypeInstr(unsigned opcode, unsigned funct3, unsigned rs1, unsigned rs2, unsigned imm13);
static code_t insEncodeJTypeInstr(unsigned opcode, unsigned rd, unsigned imm21);
static code_t insEncodeCRTypeInstr(instruction ins, unsigned rdRs1, unsigned rs2);
static code_t insEncodeCATypeInstr(instruction ins, unsigned rdRs1Rvc, unsigned rs2Rvc);
#ifdef DEBUG
static void emitOutput_RTypeInstr_SanityCheck(instruction ins, regNumber rd, regNumber rs1, regNumber rs2);
static void emitOutput_ITypeInstr_SanityCheck(
@ -135,11 +149,13 @@ static unsigned TrimSignedToImm13(ssize_t imm13);
static unsigned TrimSignedToImm20(ssize_t imm20);
static unsigned TrimSignedToImm21(ssize_t imm21);
// Major opcode of a 32-bit instruction as per "The RISC-V Instruction Set Manual", Chapter "RV32/64G Instruction Set
// Listings", Table "RISC-V base opcode map"
// Major opcode of 32-bit & 16-bit instructions as per "The RISC-V Instruction Set Manual", chapter "RV32/64G
// Instruction Set Listings", table "RISC-V base opcode map" and chapter "RVC Instruction Set Listings", table "RVC
// opcode map instructions"
enum class MajorOpcode
{
// clang-format off
// inst[1:0] = 11
// inst[4:2] 000, 001, 010, 011, 100, 101, 110, 111 (>32Bit)
/* inst[6:5] */
/* 00 */ Load, LoadFp, Custom0, MiscMem, OpImm, Auipc, OpImm32, Encoding48Bit1,
@ -147,6 +163,14 @@ enum class MajorOpcode
/* 11 */ MAdd, MSub, NmSub, NmAdd, OpFp, OpV, Custom2Rv128, Encoding48Bit2,
/* 11 */ Branch, Jalr, Reserved, Jal, System, OpVe, Custom3Rv128, Encoding80Bit,
// clang-format on
// clang-format off
// inst[15:13] 000, 001, 010, 011, 100, 101, 110, 111
/* inst[1:0] */
/* 00 */ Addi4Spn, Fld, Lw, Ld, Reserved2, Fsd, Sw, Sd,
/* 01 */ Addi, Addiw, Li, LuiAddi16Sp, MiscAlu, J, Beqz, Bnez,
/* 10 */ Slli, FldSp, LwSp, Ldsp, JrJalrMvAdd, FsdSp, SwSp, SdSp,
// clang-format on
};
//------------------------------------------------------------------------

View File

@ -307,6 +307,16 @@ INST(sh2add_uw, "sh2add_uw", 0, 0x2000403b)
INST(sh3add_uw, "sh3add_uw", 0, 0x2000603b)
INST(slli_uw, "slli_uw", 0, 0x0800101b)
// RVC
INST(c_mv, "c.mv", 0, 0x00008002)
INST(c_add, "c.add", 0, 0x00009002)
INST(c_and, "c.and", 0, 0x00008c61)
INST(c_or, "c.or", 0, 0x00008c41)
INST(c_xor, "c.xor", 0, 0x00008c21)
INST(c_sub, "c.sub", 0, 0x00008c01)
INST(c_addw, "c.addw", 0, 0x00009c21)
INST(c_subw, "c.subw", 0, 0x00009c01)
// clang-format on
/*****************************************************************************/
#undef INST

View File

@ -56,7 +56,7 @@
#define MIN_ARG_AREA_FOR_CALL 0 // Minimum required outgoing argument space for a call.
#define CODE_ALIGN 4 // code alignment requirement
#define CODE_ALIGN 2 // code alignment requirement
#define STACK_ALIGN 16 // stack alignment requirement
#define FIRST_INT_CALLEE_SAVED REG_S1

View File

@ -48,7 +48,7 @@ const unsigned MAX_PROLOG_SIZE_BYTES = 200;
const unsigned MAX_EPILOG_SIZE_BYTES = 200;
#define UWC_END 0xE4 // "end" unwind code
#define UWC_END_C 0xE5 // "end_c" unwind code
#define UW_MAX_FRAGMENT_SIZE_BYTES (1U << 20)
#define UW_MAX_FRAGMENT_SIZE_BYTES (1U << 19)
#define UW_MAX_CODE_WORDS_COUNT 31
#define UW_MAX_EPILOG_START_INDEX 0x3FFU

View File

@ -347,9 +347,9 @@ void DumpUnwindInfo(Compiler* comp,
printf(" X bit : %u\n", XBit);
printf(" Vers : %u\n", Vers);
printf(" Function Length : %u (0x%05x) Actual length = %u (0x%06x)\n", functionLength, functionLength,
functionLength * 4, functionLength * 4);
functionLength * 2, functionLength * 2);
assert(functionLength * 4 == endOffset - startOffset);
assert(functionLength * 2 == endOffset - startOffset);
if (codeWords == 0 && epilogCount == 0)
{
@ -392,7 +392,7 @@ void DumpUnwindInfo(Compiler* comp,
// of the current funclet, not the offset from the beginning of the main function.
// To help find it when looking through JitDump output, also show the offset from
// the beginning of the main function.
DWORD epilogStartOffsetFromMainFunctionBegin = epilogStartOffset * 4 + startOffset;
DWORD epilogStartOffsetFromMainFunctionBegin = epilogStartOffset * 2 + startOffset;
assert(res == 0);
@ -400,7 +400,7 @@ void DumpUnwindInfo(Compiler* comp,
printf(" Epilog Start Offset : %u (0x%05x) Actual offset = %u (0x%06x) Offset from main "
"function begin = %u (0x%06x)\n",
comp->dspOffset(epilogStartOffset), comp->dspOffset(epilogStartOffset),
comp->dspOffset(epilogStartOffset * 4), comp->dspOffset(epilogStartOffset * 4),
comp->dspOffset(epilogStartOffset * 2), comp->dspOffset(epilogStartOffset * 2),
comp->dspOffset(epilogStartOffsetFromMainFunctionBegin),
comp->dspOffset(epilogStartOffsetFromMainFunctionBegin));
printf(" Epilog Start Index : %u (0x%02x)\n", epilogStartIndex, epilogStartIndex);
@ -1556,8 +1556,8 @@ void UnwindFragmentInfo::Finalize(UNATIVE_OFFSET functionLength)
// Compute the header
noway_assert((functionLength & 3) == 0);
DWORD headerFunctionLength = functionLength / 4;
noway_assert((functionLength & 1) == 0);
DWORD headerFunctionLength = functionLength / 2;
DWORD headerVers = 0; // Version of the unwind info is zero. No other version number is currently defined.
DWORD headerXBit = 0; // We never generate "exception data", but the VM might add some.
@ -1644,9 +1644,9 @@ void UnwindFragmentInfo::Finalize(UNATIVE_OFFSET functionLength)
// NOT the offset from the beginning of the main function.
DWORD headerEpilogStartOffset = pEpi->GetStartOffset() - GetStartOffset();
noway_assert((headerEpilogStartOffset & 3) == 0);
headerEpilogStartOffset /= 4; // The unwind data stores the actual offset divided by 4 (since the low 2 bits
// of the actual offset is always zero)
noway_assert((headerEpilogStartOffset & 1) == 0);
headerEpilogStartOffset /= 2; // The unwind data stores the actual offset divided by 2 (since the low bit of
// the actual offset is always zero)
DWORD headerEpilogStartIndex = pEpi->GetStartIndex();

View File

@ -177,10 +177,10 @@ namespace Internal.TypeSystem
switch (Architecture)
{
case TargetArchitecture.ARM:
case TargetArchitecture.RiscV64:
return 2;
case TargetArchitecture.ARM64:
case TargetArchitecture.LoongArch64:
case TargetArchitecture.RiscV64:
return 4;
default:
return 1;

View File

@ -847,9 +847,7 @@ namespace ILCompiler.ObjectWriter
{
EM_ARM => 0x05000000u, // For ARM32 claim conformance with the EABI specification
EM_LOONGARCH => 0x43u, // For LoongArch ELF psABI specify the ABI version (1) and modifiers (64-bit GPRs, 64-bit FPRs)
// TODO: update once RISC-V runtime supports "C" extension (compressed instructions)
// it should be 0x0005u EF_RISCV_RVC (0x0001) | EF_RISCV_FLOAT_ABI_DOUBLE (0x0006)
EM_RISCV => 0x0004u, // EF_RISCV_FLOAT_ABI_DOUBLE (double precision floating-point ABI).
EM_RISCV => 0x0005u, // EF_RISCV_RVC (RVC ABI) | EF_RISCV_FLOAT_ABI_DOUBLE (double precision floating-point ABI).
_ => 0u
},
};

View File

@ -180,10 +180,10 @@ namespace ILCompiler.Reflection.ReadyToRun
switch (_target)
{
case Machine.ArmThumb2:
case Machine.RiscV64:
return (x << 1);
case Machine.Arm64:
case Machine.LoongArch64:
case Machine.RiscV64:
return (x << 2);
}
return x;
@ -194,10 +194,10 @@ namespace ILCompiler.Reflection.ReadyToRun
switch (_target)
{
case Machine.ArmThumb2:
case Machine.RiscV64:
return (x >> 1);
case Machine.Arm64:
case Machine.LoongArch64:
case Machine.RiscV64:
return (x >> 2);
}
return x;

View File

@ -30,13 +30,13 @@ namespace ILCompiler.Reflection.ReadyToRun.RiscV64
// of the current funclet, not the offset from the beginning of the main function.
// To help find it when looking through JitDump output, also show the offset from
// the beginning of the main function.
EpilogStartOffsetFromMainFunctionBegin = EpilogStartOffset * 4 + startOffset;
EpilogStartOffsetFromMainFunctionBegin = EpilogStartOffset * 2 + startOffset;
}
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($" Epilog Start Offset: 0x{EpilogStartOffset:X5} Actual offset = 0x{EpilogStartOffset * 4:X5} Offset from main function begin = 0x{EpilogStartOffsetFromMainFunctionBegin:X6}");
sb.AppendLine($" Epilog Start Offset: 0x{EpilogStartOffset:X5} Actual offset = 0x{EpilogStartOffset * 2:X5} Offset from main function begin = 0x{EpilogStartOffsetFromMainFunctionBegin:X6}");
sb.AppendLine($" Condition: {Condition} (0x{Condition:X})" + ((Condition == 0xE) ? " (always)" : ""));
sb.Append($" Epilog Start Index: {EpilogStartIndex} (0x{EpilogStartIndex:X})");
return sb.ToString();
@ -85,7 +85,7 @@ namespace ILCompiler.Reflection.ReadyToRun.RiscV64
EBit = ExtractBits(dw, 21, 1);
XBit = ExtractBits(dw, 20, 1);
Vers = ExtractBits(dw, 18, 2);
FunctionLength = ExtractBits(dw, 0, 18) * 4;
FunctionLength = ExtractBits(dw, 0, 18) * 2;
if (CodeWords == 0 && EpilogCount == 0)
{

View File

@ -192,9 +192,8 @@ namespace R2RDump
// Instructions are dumped as 4-byte hexadecimal integers
Machine.LoongArch64 => 4 * 2 + 1,
// Instructions are dumped as 4-byte hexadecimal integers
// TODO: update once RISC-V runtime supports "C" extension (compressed instructions)
Machine.RiscV64 => 4 * 2 + 1,
// Instructions are either 2 or 4 bytes long
Machine.RiscV64 => 4 * 3,
_ => throw new NotImplementedException()
};
@ -270,8 +269,7 @@ namespace R2RDump
}
else
{
// TODO: update once RISC-V runtime supports "C" extension (compressed instructions)
if (_reader.Machine is Machine.Arm64 or Machine.LoongArch64 or Machine.RiscV64)
if (_reader.Machine is Machine.Arm64 or Machine.LoongArch64)
{
// Replace " hh hh hh hh " byte dump with " hhhhhhhh ".
// CoreDisTools should be fixed to dump bytes this way for ARM64.
@ -360,7 +358,7 @@ namespace R2RDump
break;
case Machine.RiscV64:
ProbeRiscV64Quirks(rtf, imageOffset, rtfOffset, ref fixedTranslatedLine);
// TODO-RISCV64-RVC: Add back ProbeRiscV64Quirks here once it's modified to support compressed instructions.
break;
default:
@ -1222,6 +1220,7 @@ namespace R2RDump
/// <param name="instruction">Textual representation of the instruction</param>
private void ProbeRiscV64Quirks(RuntimeFunction rtf, int imageOffset, int rtfOffset, ref string instruction)
{
// TODO-RISCV64-RVC: Modify this method to detect patterns in forward traversal. See ProbeArm64Quirks implementation.
const int InstructionSize = 4;
uint instr = BitConverter.ToUInt32(_reader.Image, imageOffset + rtfOffset);
@ -1233,11 +1232,11 @@ namespace R2RDump
addi
ld
jalr
auipc
ld
jalr
auipc
addi
ld

View File

@ -25,6 +25,8 @@
#define MEMORY_READ_DWORD(params, addr) (*dac_cast<PTR_DWORD>(addr))
#define MEMORY_READ_QWORD(params, addr) (*dac_cast<PTR_UINT64>(addr))
#define WORDS_TO_HALFWORDS(value) ((value) << 1)
typedef struct _RISCV64_UNWIND_PARAMS
{
PT_KNONVOLATILE_CONTEXT_POINTERS ContextPointers;
@ -127,7 +129,7 @@ Arguments:
Return Value:
The size of the scope described by the unwind codes, in halfword units.
The size of the scope described by the unwind codes, in word units.
--*/
@ -148,6 +150,9 @@ Return Value:
break;
}
// TODO-RISCV64-RVC: Once we support RVC code in prolog / epilog,
// need to create table for mapping CurCode to OpSize.
// See arm/unwinder.cpp.
UnwindCodePtr += UnwindCodeSizeTable[Opcode];
ScopeSize++;
}
@ -386,7 +391,7 @@ Return Value:
ULONG ScopeNum;
ULONG ScopeSize;
ULONG ScopeStart;
DWORD64 SkipWords;
DWORD64 SkipHalfWords;
NTSTATUS Status;
ULONG_PTR UnwindCodePtr;
ULONG_PTR UnwindCodesEndPtr;
@ -428,7 +433,7 @@ Return Value:
}
FunctionLength = HeaderWord & 0x3ffff;
OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 4;
OffsetInFunction = (ControlPcRva - FunctionEntry->BeginAddress) / 2;
//
// Determine the number of epilog scope records and the maximum number
@ -467,7 +472,7 @@ Return Value:
UnwindCodePtr = UnwindDataPtr + 4 * EpilogScopeCount;
UnwindCodesEndPtr = UnwindCodePtr + 4 * UnwindWords;
SkipWords = 0;
SkipHalfWords = 0;
//
// If we're near the start of the function, and this function has a prolog,
@ -483,11 +488,11 @@ Return Value:
// in the body of the function.
//
if (OffsetInFunction < 4 * UnwindWords) {
ScopeSize = RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams);
if (OffsetInFunction < WORDS_TO_HALFWORDS(4 * UnwindWords)) {
ScopeSize = WORDS_TO_HALFWORDS(RtlpComputeScopeSize(UnwindCodePtr, UnwindCodesEndPtr, FALSE, UnwindParams));
if (OffsetInFunction < ScopeSize) {
SkipWords = ScopeSize - OffsetInFunction;
SkipHalfWords = ScopeSize - OffsetInFunction;
ExceptionHandler = NULL;
ExceptionHandlerData = NULL;
goto ExecuteCodes;
@ -511,13 +516,13 @@ Return Value:
//
if ((HeaderWord & (1 << 21)) != 0) {
if (OffsetInFunction + (4 * UnwindWords - UnwindIndex) >= FunctionLength) {
ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams);
if (OffsetInFunction + WORDS_TO_HALFWORDS(4 * UnwindWords - UnwindIndex) >= FunctionLength) {
ScopeSize = WORDS_TO_HALFWORDS(RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams));
ScopeStart = FunctionLength - ScopeSize;
if (OffsetInFunction >= ScopeStart) {
UnwindCodePtr += UnwindIndex;
SkipWords = OffsetInFunction - ScopeStart;
SkipHalfWords = OffsetInFunction - ScopeStart;
ExceptionHandler = NULL;
ExceptionHandlerData = NULL;
}
@ -547,13 +552,13 @@ Return Value:
}
UnwindIndex = HeaderWord >> 22;
if (OffsetInFunction < ScopeStart + (4 * UnwindWords - UnwindIndex)) {
ScopeSize = RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams);
if (OffsetInFunction < ScopeStart + WORDS_TO_HALFWORDS(4 * UnwindWords - UnwindIndex)) {
ScopeSize = WORDS_TO_HALFWORDS(RtlpComputeScopeSize(UnwindCodePtr + UnwindIndex, UnwindCodesEndPtr, TRUE, UnwindParams));
if (OffsetInFunction < ScopeStart + ScopeSize) {
UnwindCodePtr += UnwindIndex;
SkipWords = OffsetInFunction - ScopeStart;
SkipHalfWords = OffsetInFunction - ScopeStart;
ExceptionHandler = NULL;
ExceptionHandlerData = NULL;
break;
@ -569,13 +574,16 @@ ExecuteCodes:
// to skip.
//
while (UnwindCodePtr < UnwindCodesEndPtr && SkipWords > 0) {
while (UnwindCodePtr < UnwindCodesEndPtr && SkipHalfWords > 0) {
CurCode = MEMORY_READ_BYTE(UnwindParams, UnwindCodePtr);
if (OPCODE_IS_END(CurCode)) {
break;
}
UnwindCodePtr += UnwindCodeSizeTable[CurCode];
SkipWords--;
// TODO-RISCV64-RVC: Once we support RVC code in prolog / epilog,
// need to create table for mapping CurCode to OpSize.
// See arm/unwinder.cpp.
SkipHalfWords -= 2;
}
//

View File

@ -159,6 +159,7 @@ void RiscV64SingleStepper::Apply(T_CONTEXT *pCtx)
m_originalPc = pCtx->Pc;
// By default assume the next PC is right after the current instruction.
// TODO-RISCV64-RVC: change this after "C" Standard Extension support implemented
m_targetPc = m_originalPc + sizeof(uint32_t);
m_fEmulate = false;
@ -263,7 +264,7 @@ bool RiscV64SingleStepper::Fixup(T_CONTEXT *pCtx, DWORD dwExceptionCode)
LOG((LF_CORDB, LL_INFO100000, "RiscV64SingleStepper::Fixup emulated breakpoint\n"));
pCtx->Pc = m_originalPc;
_ASSERTE((pCtx->Pc & 0x3) == 0); // TODO change this after "C" Standard Extension support implemented
_ASSERTE((pCtx->Pc & 0x3) == 0); // TODO-RISCV64-RVC: change this after "C" Standard Extension support implemented
return false;
}
}
@ -352,7 +353,7 @@ bool RiscV64SingleStepper::TryEmulate(T_CONTEXT *pCtx, uint32_t opcode, bool exe
// instruction address).
bool fEmulated = false;
// TODO after "C" Standard Extension support implemented, add C.J, C.JAL, C.JR, C.JALR, C.BEQZ, C.BNEZ
// TODO-RISCV64-RVC: after "C" Standard Extension support implemented, add C.J, C.JAL, C.JR, C.JALR, C.BEQZ, C.BNEZ
if ((opcode & 0x7f) == 0x17) // AUIPC
{