From 77fdbb8d48233aa228bc9c854363a329f8fbba79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 18 Aug 2025 17:02:42 +0200 Subject: [PATCH 01/39] Synthesize handles for function addresses with auipc+ld --- src/coreclr/jit/codegenriscv64.cpp | 13 +++--- src/coreclr/jit/emitriscv64.cpp | 72 +++++++++++++++++------------- src/coreclr/jit/emitriscv64.h | 2 +- src/coreclr/jit/instr.h | 2 +- 4 files changed, 49 insertions(+), 40 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index cf68d74d659543..9192e1086e166d 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -907,7 +907,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, if (EA_IS_RELOC(size)) { assert(genIsValidIntReg(reg)); - GetEmitter()->emitIns_R_AI(INS_jal, size, reg, imm); + GetEmitter()->emitIns_R_AI(INS_addi, size, reg, imm); } else { @@ -2746,7 +2746,7 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) if (compiler->opts.compReloc) { - GetEmitter()->emitIns_R_AI(INS_jal, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)helperFunction.addr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)helperFunction.addr); } else { @@ -3643,8 +3643,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, if (compiler->opts.compReloc) { - // TODO-RISCV64: here the jal is special flag rather than a real instruction. - GetEmitter()->emitIns_R_AI(INS_jal, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr); } else { @@ -4455,7 +4454,7 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) { if (compiler->opts.compReloc) { - emit->emitIns_R_AI(INS_jal, EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); } else { @@ -4499,7 +4498,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) // GetEmitter()->emitIns_R_R_I(INS_ld_d, EA_PTRSIZE, regGSConst, regGSConst, 0); if (compiler->opts.compReloc) { - GetEmitter()->emitIns_R_AI(INS_jal, EA_PTR_DSP_RELOC, regGSConst, + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); } else @@ -6547,7 +6546,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( // 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); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)pAddr); } else { diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index edb5d94ccee070..5ba11fc692aea3 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -94,7 +94,7 @@ size_t emitter::emitSizeOfInsDsc(instrDesc* id) const case INS_OPTS_RC: case INS_OPTS_RL: - case INS_OPTS_RELOC: + case INS_OPTS_PCREL: case INS_OPTS_NONE: return sizeof(instrDesc); case INS_OPTS_I: @@ -1242,22 +1242,16 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu NYI_RISCV64("emitIns_R_AR-----unimplemented/unused on RISCV64 yet----"); } -// This computes address from the immediate which is relocatable. +// This computes address from the immediate which is PC-relative. void emitter::emitIns_R_AI(instruction ins, emitAttr attr, regNumber reg, ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { - assert(EA_IS_RELOC(attr)); // EA_PTR_DSP_RELOC - assert(ins == INS_jal); // for special. assert(isGeneralRegister(reg)); - // INS_OPTS_RELOC: placeholders. 2-ins: - // case:EA_HANDLE_CNS_RELOC - // auipc reg, off-hi-20bits - // addi reg, reg, off-lo-12bits - // case:EA_PTR_DSP_RELOC + // 2-ins: // auipc reg, off-hi-20bits - // ld reg, reg, off-lo-12bits + // ins reg, reg, off-lo-12bits instrDesc* id = emitNewInstr(attr); @@ -1265,24 +1259,28 @@ void emitter::emitIns_R_AI(instruction ins, assert(reg != REG_R0); // for special. reg Must not be R0. id->idReg1(reg); // destination register that will get the constant value. - id->idInsOpt(INS_OPTS_RELOC); + id->idInsOpt(INS_OPTS_PCREL); + id->idOpSize(EA_SIZE(attr)); if (EA_IS_GCREF(attr)) { /* A special value indicates a GCref pointer value */ id->idGCref(GCT_GCREF); - id->idOpSize(EA_PTRSIZE); } else if (EA_IS_BYREF(attr)) { /* A special value indicates a Byref pointer value */ id->idGCref(GCT_BYREF); - id->idOpSize(EA_PTRSIZE); } id->idAddr()->iiaAddr = (BYTE*)addr; id->idCodeSize(8); +#ifdef DEBUG + id->idDebugOnlyInfo()->idMemCookie = addr; + id->idDebugOnlyInfo()->idFlags = gtFlags; +#endif + appendToCurIG(id); } @@ -3200,26 +3198,31 @@ static ssize_t UpperWordOfDoubleWordDoubleSignExtend(ssize_t doubleWord) return static_cast(LowerNBitsOfWord<21>(imm21)); } -BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruction* ins) +BYTE* emitter::emitOutputInstr_OptsPcRel(BYTE* dst, const instrDesc* id, instruction* ins) { BYTE* const dstBase = dst; const regNumber reg1 = id->idReg1(); + assert(isGeneralRegister(reg1)); - dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, 0); - - if (id->idIsCnsReloc()) - { - *ins = INS_addi; - } - else + unsigned hi20 = 0; + unsigned lo12 = 0; + if (!id->idIsReloc()) { - assert(id->idIsDspReloc()); - *ins = INS_ld; + UNATIVE_OFFSET srcOffs = emitCurCodeOffs(dst); + const BYTE* srcAddr = emitOffsetToPtr(srcOffs); + ssize_t distance = (ssize_t)(id->idAddr()->iiaAddr - srcAddr); + assert(isValidSimm32(distance)); + hi20 = UpperNBitsOfWordSignExtend<20>(distance); + lo12 = LowerNBitsOfWord<12>(distance); } - dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, 0); + *ins = id->idIns(); + assert(*ins == INS_addi || emitInsIsLoad(*ins)); + dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, hi20); + dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, lo12); - emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); + if (id->idIsReloc()) + emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); return dst; } @@ -3428,8 +3431,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) switch (insOp) { - case INS_OPTS_RELOC: - dst = emitOutputInstr_OptsReloc(dst, id, &ins); + case INS_OPTS_PCREL: + dst = emitOutputInstr_OptsPcRel(dst, id, &ins); sz = sizeof(instrDesc); break; case INS_OPTS_RC: @@ -5148,11 +5151,18 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR assert(memBase == indir->Addr()); ssize_t cns = addr->AsIntCon()->IconValue(); - ssize_t off = (cns << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended - cns -= off; + if (addr->IsIconHandle(GTF_ICON_FTN_ADDR)) + { + emitIns_R_AI(ins, attr, dataReg, (size_t)cns, cns, addr->GetIconHandleFlag()); + } + else + { + ssize_t off = (cns << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended + cns -= off; - emitLoadImmediate(EA_PTRSIZE, codeGen->rsGetRsvdReg(), cns); - emitIns_R_R_I(ins, attr, dataReg, codeGen->rsGetRsvdReg(), off); + emitLoadImmediate(EA_PTRSIZE, codeGen->rsGetRsvdReg(), cns); + emitIns_R_R_I(ins, attr, dataReg, codeGen->rsGetRsvdReg(), off); + } } else if (isValidSimm12(offset)) { diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 616802ed478eea..e32205a7d63037 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -135,7 +135,7 @@ unsigned emitOutput_BTypeInstr_InvertComparation( BYTE* dst, instruction ins, regNumber rs1, regNumber rs2, unsigned imm13) const; unsigned emitOutput_JTypeInstr(BYTE* dst, instruction ins, regNumber rd, unsigned imm21) const; -BYTE* emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruction* ins); +BYTE* emitOutputInstr_OptsPcRel(BYTE* dst, const instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsRc(BYTE* dst, const instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsRl(BYTE* dst, instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsJalr(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins); diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index 15de2fd08f77bd..30241bd4e44591 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -558,7 +558,7 @@ enum insOpts : unsigned INS_OPTS_J_cond, // see ::emitIns_J_cond_la(). INS_OPTS_I, // see ::emitLoadImmediate(). INS_OPTS_C, // see ::emitIns_Call(). - INS_OPTS_RELOC, // see ::emitIns_R_AI(). + INS_OPTS_PCREL, // see ::emitIns_R_AI(). }; enum insBarrier : unsigned From 60ec4a930ecde73a257d04c1079503b3e77708f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 29 Aug 2025 12:42:57 +0200 Subject: [PATCH 02/39] Use auipc+ld/addi for addresses also in non-compReloc --- src/coreclr/jit/codegenriscv64.cpp | 114 +++++------------------------ src/coreclr/jit/emitriscv64.cpp | 2 +- 2 files changed, 21 insertions(+), 95 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 9192e1086e166d..8ed0aa6d27ae6e 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -899,15 +899,14 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, { emitter* emit = GetEmitter(); - if (!compiler->opts.compReloc) - { - size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs. - } - + assert(genIsValidIntReg(reg)); if (EA_IS_RELOC(size)) { - assert(genIsValidIntReg(reg)); - GetEmitter()->emitIns_R_AI(INS_addi, size, reg, imm); + if (!compiler->opts.compReloc) + { + size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs. + } + emit->emitIns_R_AI(INS_addi, size, reg, imm); } else { @@ -2744,16 +2743,8 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) params.callType = EC_INDIR_R; params.ireg = REG_DEFAULT_HELPER_CALL_TARGET; - if (compiler->opts.compReloc) - { - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)helperFunction.addr); - } - else - { - // TODO-RISCV64: maybe optimize further. - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, params.ireg, (ssize_t)helperFunction.addr); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, params.ireg, params.ireg, 0); - } + emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; + GetEmitter()->emitIns_R_AI(INS_ld, attr, params.ireg, (ssize_t)helperFunction.addr); regSet.verifyRegUsed(params.ireg); } @@ -3584,23 +3575,6 @@ int CodeGenInterface::genCallerSPtoInitialSPdelta() const return callerSPtoSPdelta; } -// Produce generic and unoptimized code for loading constant to register and dereferencing it -// at the end -static void emitLoadConstAtAddr(emitter* emit, regNumber dstRegister, ssize_t imm) -{ - ssize_t high = imm >> 32; - emit->emitIns_R_I(INS_lui, EA_PTRSIZE, dstRegister, (high + 0x800) >> 12); - emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, dstRegister, dstRegister, (high & 0xfff)); - - ssize_t low = imm & 0xffffffff; - emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstRegister, dstRegister, 11); - emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, dstRegister, dstRegister, ((low >> 21) & 0x7ff)); - - emit->emitIns_R_R_I(INS_slli, EA_PTRSIZE, dstRegister, dstRegister, 11); - emit->emitIns_R_R_I(INS_addi, EA_PTRSIZE, dstRegister, dstRegister, ((low >> 10) & 0x7ff)); - emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, dstRegister, dstRegister, (low & 0x3ff)); -} - /***************************************************************************** * Emit a call to a helper function. */ @@ -3641,14 +3615,8 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, // assert that all registers in callTargetMask are in the callKillSet noway_assert((callTargetMask & killSet) == callTargetMask); - if (compiler->opts.compReloc) - { - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr); - } - else - { - emitLoadConstAtAddr(GetEmitter(), callTargetReg, (ssize_t)pAddr); - } + emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; + GetEmitter()->emitIns_R_AI(INS_ld, attr, callTargetReg, (ssize_t)pAddr); regSet.verifyRegUsed(callTargetReg); params.callType = EC_INDIR_R; @@ -4452,15 +4420,9 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) } else { - if (compiler->opts.compReloc) - { - emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); - } - else - { - emit->emitLoadImmediate(EA_PTRSIZE, initReg, ((size_t)compiler->gsGlobalSecurityCookieAddr)); - emit->emitIns_R_R_I(INS_ld, EA_PTRSIZE, initReg, initReg, 0); - } + emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; + emit->emitIns_R_AI(INS_ld, attr, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + regSet.verifyRegUsed(initReg); emit->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); } @@ -4494,33 +4456,8 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) else { // AOT case - GS cookie constant needs to be accessed through an indirection. - // instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); - // GetEmitter()->emitIns_R_R_I(INS_ld_d, EA_PTRSIZE, regGSConst, regGSConst, 0); - if (compiler->opts.compReloc) - { - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, regGSConst, - (ssize_t)compiler->gsGlobalSecurityCookieAddr); - } - else - { - // TODO-RISCV64: maybe optimize furtherk! - UINT32 high = ((ssize_t)compiler->gsGlobalSecurityCookieAddr) >> 32; - if (((high + 0x800) >> 12) != 0) - { - GetEmitter()->emitIns_R_I(INS_lui, EA_PTRSIZE, regGSConst, ((int32_t)(high + 0x800)) >> 12); - } - if ((high & 0xFFF) != 0) - { - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, regGSConst, REG_R0, (high & 0xfff)); - } - UINT32 low = ((ssize_t)compiler->gsGlobalSecurityCookieAddr) & 0xffffffff; - GetEmitter()->emitIns_R_R_I(INS_slli, EA_PTRSIZE, regGSConst, regGSConst, 11); - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, regGSConst, regGSConst, (low >> 21) & 0x7FF); - GetEmitter()->emitIns_R_R_I(INS_slli, EA_PTRSIZE, regGSConst, regGSConst, 11); - GetEmitter()->emitIns_R_R_I(INS_addi, EA_PTRSIZE, regGSConst, regGSConst, (low >> 10) & 0x7FF); - GetEmitter()->emitIns_R_R_I(INS_slli, EA_PTRSIZE, regGSConst, regGSConst, 10); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, regGSConst, regGSConst, low & 0x3FF); - } + emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; + GetEmitter()->emitIns_R_AI(INS_ld, attr, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); regSet.verifyRegUsed(regGSConst); } // Load this method's GS value from the stack frame @@ -6541,22 +6478,11 @@ void CodeGen::genJumpToThrowHlpBlk_la( params.callType = EC_INDIR_R; 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_ld, 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, - // which is less straightforward but offers slightly better codegen. - emitLoadConstAtAddr(GetEmitter(), params.ireg, (ssize_t)pAddr); - } + // 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); + emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; + emit->emitIns_R_AI(INS_ld, attr, params.ireg, (ssize_t)pAddr); regSet.verifyRegUsed(params.ireg); } diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 5ba11fc692aea3..c1780df7c16c60 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1742,7 +1742,7 @@ void emitter::emitLoadImmediate(emitAttr size, regNumber reg, ssize_t imm) appendToCurIG(id); } - else if (size == EA_PTRSIZE) + else if (EA_SIZE(size) == EA_PTRSIZE) { assert(!emitComp->compGeneratingProlog && !emitComp->compGeneratingEpilog); auto constAddr = emitDataConst(&originalImm, sizeof(long), sizeof(long), TYP_LONG); From 848ac99a5bf02893328eb9dcfabef9cfcf6147df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 29 Aug 2025 17:49:43 +0200 Subject: [PATCH 03/39] Always record relocation --- src/coreclr/inc/utilcode.h | 4 +-- src/coreclr/jit/codegenriscv64.cpp | 24 ++++++------- src/coreclr/jit/emitriscv64.cpp | 36 +++++++------------ src/coreclr/jit/emitriscv64.h | 2 +- src/coreclr/jit/instr.h | 2 +- .../superpmi/superpmi-shared/spmiutil.cpp | 23 ++++++------ src/coreclr/utilcode/util.cpp | 23 ++++++------ 7 files changed, 52 insertions(+), 62 deletions(-) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index cabd2e61d07e86..f324840dbc2a4c 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3360,12 +3360,12 @@ void PutLoongArch64PC12(UINT32 * pCode, INT64 imm); void PutLoongArch64JIR(UINT32 * pCode, INT64 imm); //***************************************************************************** -// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr) +// Extract the PC-Relative offset from auipc + I-type adder (addi/load/jalr) //***************************************************************************** INT64 GetRiscV64AuipcItype(UINT32 * pCode); //***************************************************************************** -// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr) +// Deposit the PC-Relative offset into auipc + I-type adder (addi/load/jalr) //***************************************************************************** void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset); diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 8ed0aa6d27ae6e..c773ac96fd6e95 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -902,10 +902,6 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, assert(genIsValidIntReg(reg)); if (EA_IS_RELOC(size)) { - if (!compiler->opts.compReloc) - { - size = EA_SIZE(size); // Strip any Reloc flags from size if we aren't doing relocs. - } emit->emitIns_R_AI(INS_addi, size, reg, imm); } else @@ -2426,6 +2422,11 @@ instruction CodeGen::genGetInsForOper(GenTree* treeNode) break; default: + + char message[256]; + _snprintf_s(message, ArrLen(message), _TRUNCATE, "Unhandled oper in genGetInsForOper() - float: %s", + GenTree::OpName(oper)); + NYIRAW(message); NO_WAY("Unhandled oper in genGetInsForOper() - float"); break; } @@ -2743,8 +2744,7 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) params.callType = EC_INDIR_R; params.ireg = REG_DEFAULT_HELPER_CALL_TARGET; - emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; - GetEmitter()->emitIns_R_AI(INS_ld, attr, params.ireg, (ssize_t)helperFunction.addr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)helperFunction.addr); regSet.verifyRegUsed(params.ireg); } @@ -3615,8 +3615,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, // assert that all registers in callTargetMask are in the callKillSet noway_assert((callTargetMask & killSet) == callTargetMask); - emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; - GetEmitter()->emitIns_R_AI(INS_ld, attr, callTargetReg, (ssize_t)pAddr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr); regSet.verifyRegUsed(callTargetReg); params.callType = EC_INDIR_R; @@ -4420,8 +4419,7 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) } else { - emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; - emit->emitIns_R_AI(INS_ld, attr, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); regSet.verifyRegUsed(initReg); emit->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); @@ -4456,8 +4454,7 @@ void CodeGen::genEmitGSCookieCheck(bool pushReg) else { // AOT case - GS cookie constant needs to be accessed through an indirection. - emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; - GetEmitter()->emitIns_R_AI(INS_ld, attr, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); regSet.verifyRegUsed(regGSConst); } // Load this method's GS value from the stack frame @@ -6481,8 +6478,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( // 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); - emitAttr attr = compiler->opts.compReloc ? EA_PTR_DSP_RELOC : EA_PTRSIZE; - emit->emitIns_R_AI(INS_ld, attr, params.ireg, (ssize_t)pAddr); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)pAddr); regSet.verifyRegUsed(params.ireg); } diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index c1780df7c16c60..2a53a9f789a313 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -94,7 +94,7 @@ size_t emitter::emitSizeOfInsDsc(instrDesc* id) const case INS_OPTS_RC: case INS_OPTS_RL: - case INS_OPTS_PCREL: + case INS_OPTS_RELOC: case INS_OPTS_NONE: return sizeof(instrDesc); case INS_OPTS_I: @@ -1242,12 +1242,14 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu NYI_RISCV64("emitIns_R_AR-----unimplemented/unused on RISCV64 yet----"); } -// This computes address from the immediate which is PC-relative. +// This computes address from the immediate which is relocatable. void emitter::emitIns_R_AI(instruction ins, emitAttr attr, regNumber reg, ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { + assert(EA_IS_RELOC(attr)); + assert(ins == INS_addi || emitInsIsLoad(ins)); assert(isGeneralRegister(reg)); // 2-ins: // auipc reg, off-hi-20bits @@ -1259,7 +1261,7 @@ void emitter::emitIns_R_AI(instruction ins, assert(reg != REG_R0); // for special. reg Must not be R0. id->idReg1(reg); // destination register that will get the constant value. - id->idInsOpt(INS_OPTS_PCREL); + id->idInsOpt(INS_OPTS_RELOC); id->idOpSize(EA_SIZE(attr)); if (EA_IS_GCREF(attr)) @@ -3198,32 +3200,18 @@ static ssize_t UpperWordOfDoubleWordDoubleSignExtend(ssize_t doubleWord) return static_cast(LowerNBitsOfWord<21>(imm21)); } -BYTE* emitter::emitOutputInstr_OptsPcRel(BYTE* dst, const instrDesc* id, instruction* ins) +BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruction* ins) { BYTE* const dstBase = dst; const regNumber reg1 = id->idReg1(); assert(isGeneralRegister(reg1)); - unsigned hi20 = 0; - unsigned lo12 = 0; - if (!id->idIsReloc()) - { - UNATIVE_OFFSET srcOffs = emitCurCodeOffs(dst); - const BYTE* srcAddr = emitOffsetToPtr(srcOffs); - ssize_t distance = (ssize_t)(id->idAddr()->iiaAddr - srcAddr); - assert(isValidSimm32(distance)); - hi20 = UpperNBitsOfWordSignExtend<20>(distance); - lo12 = LowerNBitsOfWord<12>(distance); - } - *ins = id->idIns(); assert(*ins == INS_addi || emitInsIsLoad(*ins)); - dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, hi20); - dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, lo12); - - if (id->idIsReloc()) - emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); + dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, 0); + dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, 0); + emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); return dst; } @@ -3431,8 +3419,8 @@ size_t emitter::emitOutputInstr(insGroup* ig, instrDesc* id, BYTE** dp) switch (insOp) { - case INS_OPTS_PCREL: - dst = emitOutputInstr_OptsPcRel(dst, id, &ins); + case INS_OPTS_RELOC: + dst = emitOutputInstr_OptsReloc(dst, id, &ins); sz = sizeof(instrDesc); break; case INS_OPTS_RC: @@ -5153,7 +5141,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR if (addr->IsIconHandle(GTF_ICON_FTN_ADDR)) { - emitIns_R_AI(ins, attr, dataReg, (size_t)cns, cns, addr->GetIconHandleFlag()); + emitIns_R_AI(ins, EA_PTR_DSP_RELOC, dataReg, (size_t)cns, cns, addr->GetIconHandleFlag()); } else { diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index e32205a7d63037..616802ed478eea 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -135,7 +135,7 @@ unsigned emitOutput_BTypeInstr_InvertComparation( BYTE* dst, instruction ins, regNumber rs1, regNumber rs2, unsigned imm13) const; unsigned emitOutput_JTypeInstr(BYTE* dst, instruction ins, regNumber rd, unsigned imm21) const; -BYTE* emitOutputInstr_OptsPcRel(BYTE* dst, const instrDesc* id, instruction* ins); +BYTE* emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsRc(BYTE* dst, const instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsRl(BYTE* dst, instrDesc* id, instruction* ins); BYTE* emitOutputInstr_OptsJalr(BYTE* dst, instrDescJmp* jmp, const insGroup* ig, instruction* ins); diff --git a/src/coreclr/jit/instr.h b/src/coreclr/jit/instr.h index 30241bd4e44591..15de2fd08f77bd 100644 --- a/src/coreclr/jit/instr.h +++ b/src/coreclr/jit/instr.h @@ -558,7 +558,7 @@ enum insOpts : unsigned INS_OPTS_J_cond, // see ::emitIns_J_cond_la(). INS_OPTS_I, // see ::emitLoadImmediate(). INS_OPTS_C, // see ::emitIns_Call(). - INS_OPTS_PCREL, // see ::emitIns_R_AI(). + INS_OPTS_RELOC, // see ::emitIns_R_AI(). }; enum insBarrier : unsigned diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index 2b17d0ac8352ad..2b48a3444f4816 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -478,30 +478,33 @@ void PutArm32MovtConstant(UINT32* p, unsigned con) } //***************************************************************************** -// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr) +// Extract the PC-Relative offset from auipc + I-type adder (addi/load/jalr) //***************************************************************************** INT64 GetRiscV64AuipcItype(UINT32 * pCode) { enum { - OpcodeAuipc = 0x00000017, - OpcodeAddi = 0x00000013, - OpcodeLd = 0x00003003, - OpcodeJalr = 0x00000067, - OpcodeUTypeMask = 0x0000007F, - OpcodeITypeMask = 0x0000307F, + OpcodeAuipc = 0x17, + OpcodeAddi = 0x13, + OpcodeLoad = 0x03, + OpcodeJalr = 0x67, + OpcodeMask = 0x7F, + + Funct3AddiJalr = 0x0000, + Funct3Mask = 0x7000, }; UINT32 auipc = pCode[0]; - _ASSERTE((auipc & OpcodeUTypeMask) == OpcodeAuipc); + _ASSERTE((auipc & OpcodeMask) == OpcodeAuipc); int auipcRegDest = (auipc >> 7) & 0x1F; _ASSERTE(auipcRegDest != 0); INT64 hi20 = (INT32(auipc) >> 12) << 12; UINT32 iType = pCode[1]; - UINT32 opcode = iType & OpcodeITypeMask; - _ASSERTE(opcode == OpcodeAddi || opcode == OpcodeLd || opcode == OpcodeJalr); + UINT32 opcode = iType & OpcodeMask; + UINT32 funct3 = iType & Funct3Mask; + _ASSERTE(opcode == OpcodeLoad || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); int iTypeRegSrc = (iType >> 15) & 0x1F; _ASSERTE(auipcRegDest == iTypeRegSrc); diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index e6c2bf271f46c3..0397e6239a05a4 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2334,30 +2334,33 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38) //***************************************************************************** -// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr) +// Extract the PC-Relative offset from auipc + I-type adder (addi/load/jalr) //***************************************************************************** INT64 GetRiscV64AuipcItype(UINT32 * pCode) { enum { - OpcodeAuipc = 0x00000017, - OpcodeAddi = 0x00000013, - OpcodeLd = 0x00003003, - OpcodeJalr = 0x00000067, - OpcodeUTypeMask = 0x0000007F, - OpcodeITypeMask = 0x0000307F, + OpcodeAuipc = 0x17, + OpcodeAddi = 0x13, + OpcodeLoad = 0x03, + OpcodeJalr = 0x67, + OpcodeMask = 0x7F, + + Funct3AddiJalr = 0x0000, + Funct3Mask = 0x7000, }; UINT32 auipc = pCode[0]; - _ASSERTE((auipc & OpcodeUTypeMask) == OpcodeAuipc); + _ASSERTE((auipc & OpcodeMask) == OpcodeAuipc); int auipcRegDest = (auipc >> 7) & 0x1F; _ASSERTE(auipcRegDest != 0); INT64 hi20 = (INT32(auipc) >> 12) << 12; UINT32 iType = pCode[1]; - UINT32 opcode = iType & OpcodeITypeMask; - _ASSERTE(opcode == OpcodeAddi || opcode == OpcodeLd || opcode == OpcodeJalr); + UINT32 opcode = iType & OpcodeMask; + UINT32 funct3 = iType & Funct3Mask; + _ASSERTE(opcode == OpcodeLoad || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); int iTypeRegSrc = (iType >> 15) & 0x1F; _ASSERTE(auipcRegDest == iTypeRegSrc); From 65014e25c41f761e325cb9b98839f4bac6868e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 1 Sep 2025 15:30:46 +0200 Subject: [PATCH 04/39] clear gc info when address is built in register --- src/coreclr/jit/emitriscv64.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 2a53a9f789a313..b520180e759050 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -3209,6 +3209,7 @@ BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruc *ins = id->idIns(); assert(*ins == INS_addi || emitInsIsLoad(*ins)); dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, 0); + emitGCregDeadUpd(reg1, dst); dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, 0); emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); From b43e7900bda77a00d6d96fb25412abfcc9cd67e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 1 Sep 2025 15:40:52 +0200 Subject: [PATCH 05/39] fix reloc type --- src/coreclr/jit/emitriscv64.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index b520180e759050..885d926cd7a74b 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -5142,7 +5142,8 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR if (addr->IsIconHandle(GTF_ICON_FTN_ADDR)) { - emitIns_R_AI(ins, EA_PTR_DSP_RELOC, dataReg, (size_t)cns, cns, addr->GetIconHandleFlag()); + attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG); + emitIns_R_AI(ins, attr, dataReg, (size_t)cns, cns, addr->GetIconHandleFlag()); } else { From bd0e79cc0470c7eae53ef67028e7c567c9fb9846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 1 Sep 2025 15:58:08 +0200 Subject: [PATCH 06/39] cleanup: emitAlllocInstr sets idGCref and idOpSize based on attr --- src/coreclr/jit/emitriscv64.cpp | 39 --------------------------------- 1 file changed, 39 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 885d926cd7a74b..8c9fe4b5bc134a 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1213,19 +1213,6 @@ void emitter::emitIns_R_C( id->idInsOpt(INS_OPTS_RC); id->idCodeSize(2 * sizeof(code_t)); // auipc + load/addi - if (EA_IS_GCREF(attr)) - { - /* A special value indicates a GCref pointer value */ - id->idGCref(GCT_GCREF); - id->idOpSize(EA_PTRSIZE); - } - else if (EA_IS_BYREF(attr)) - { - /* A special value indicates a Byref pointer value */ - id->idGCref(GCT_BYREF); - id->idOpSize(EA_PTRSIZE); - } - // TODO-RISCV64: this maybe deleted. id->idSetIsBound(); // We won't patch address since we will know the exact distance // once JIT code and data are allocated together. @@ -1262,19 +1249,6 @@ void emitter::emitIns_R_AI(instruction ins, id->idReg1(reg); // destination register that will get the constant value. id->idInsOpt(INS_OPTS_RELOC); - - id->idOpSize(EA_SIZE(attr)); - if (EA_IS_GCREF(attr)) - { - /* A special value indicates a GCref pointer value */ - id->idGCref(GCT_GCREF); - } - else if (EA_IS_BYREF(attr)) - { - /* A special value indicates a Byref pointer value */ - id->idGCref(GCT_BYREF); - } - id->idAddr()->iiaAddr = (BYTE*)addr; id->idCodeSize(8); @@ -1322,19 +1296,6 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu id->idCodeSize(2 * sizeof(code_t)); id->idReg1(reg); - if (EA_IS_GCREF(attr)) - { - /* A special value indicates a GCref pointer value */ - id->idGCref(GCT_GCREF); - id->idOpSize(EA_PTRSIZE); - } - else if (EA_IS_BYREF(attr)) - { - /* A special value indicates a Byref pointer value */ - id->idGCref(GCT_BYREF); - id->idOpSize(EA_PTRSIZE); - } - #ifdef DEBUG // Mark the catch return if (emitComp->compCurBB->KindIs(BBJ_EHCATCHRET)) From 1f016da245e0abf9e5d5c903b3f6fbee338dc45e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 1 Sep 2025 17:41:57 +0200 Subject: [PATCH 07/39] remove redundant assert --- src/coreclr/jit/codegenriscv64.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index c773ac96fd6e95..b5b8e29e7dc839 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2422,11 +2422,6 @@ instruction CodeGen::genGetInsForOper(GenTree* treeNode) break; default: - - char message[256]; - _snprintf_s(message, ArrLen(message), _TRUNCATE, "Unhandled oper in genGetInsForOper() - float: %s", - GenTree::OpName(oper)); - NYIRAW(message); NO_WAY("Unhandled oper in genGetInsForOper() - float"); break; } From 80bca29c9c1fe1abf858b253f56a5f395a011732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 1 Sep 2025 17:45:21 +0200 Subject: [PATCH 08/39] remove unused debug fields --- src/coreclr/jit/emitriscv64.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 8c9fe4b5bc134a..7ae2db18ae897f 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1252,11 +1252,6 @@ void emitter::emitIns_R_AI(instruction ins, id->idAddr()->iiaAddr = (BYTE*)addr; id->idCodeSize(8); -#ifdef DEBUG - id->idDebugOnlyInfo()->idMemCookie = addr; - id->idDebugOnlyInfo()->idFlags = gtFlags; -#endif - appendToCurIG(id); } From 289740afba58c835ec8e2c9d562c3bd193f0904f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 24 Oct 2025 09:52:45 +0200 Subject: [PATCH 09/39] Estimate if data is in range, handle also stores --- src/coreclr/inc/utilcode.h | 8 ++--- src/coreclr/jit/codegenriscv64.cpp | 15 +++++---- src/coreclr/jit/compiler.cpp | 2 +- src/coreclr/jit/compiler.h | 2 +- src/coreclr/jit/emitriscv64.cpp | 43 +++++++++++++----------- src/coreclr/jit/emitriscv64.h | 3 +- src/coreclr/jit/gentree.cpp | 2 +- src/coreclr/jit/gentree.h | 2 +- src/coreclr/utilcode/util.cpp | 45 ++++++++++++++++---------- src/coreclr/vm/jitinterface.cpp | 52 ++++++++++++++++++------------ src/coreclr/vm/jitinterface.h | 14 ++++---- 11 files changed, 110 insertions(+), 78 deletions(-) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index bc7995ca2b0623..82f463b8d41cbc 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3353,14 +3353,14 @@ void PutLoongArch64PC12(UINT32 * pCode, INT64 imm); void PutLoongArch64JIR(UINT32 * pCode, INT64 imm); //***************************************************************************** -// Extract the PC-Relative offset from auipc + I-type adder (addi/load/jalr) +// Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcItype(UINT32 * pCode); +INT64 GetRiscV64AuipcCombo(UINT32 * pCode); //***************************************************************************** -// Deposit the PC-Relative offset into auipc + I-type adder (addi/load/jalr) +// Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 offsetLo12, INT32 offsetHi20); //***************************************************************************** // Returns whether the offset fits into bl instruction diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index ddc81ab1b4870f..351f653435d885 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -902,7 +902,7 @@ void CodeGen::instGen_Set_Reg_To_Imm(emitAttr size, assert(genIsValidIntReg(reg)); if (EA_IS_RELOC(size)) { - emit->emitIns_R_AI(INS_addi, size, reg, imm); + emit->emitIns_R_AI(INS_addi, size, reg, reg, imm); } else { @@ -2739,7 +2739,7 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) params.callType = EC_INDIR_R; params.ireg = REG_DEFAULT_HELPER_CALL_TARGET; - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)helperFunction.addr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, params.ireg, (ssize_t)helperFunction.addr); regSet.verifyRegUsed(params.ireg); } @@ -3386,7 +3386,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, void* pAddr = helperFunction.addr; // This is call to a runtime helper. - // lui reg, pAddr #NOTE: this maybe multi-instructions. + // li reg, pAddr #NOTE: this maybe multi-instructions. // ld reg, reg // jalr reg @@ -3402,7 +3402,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, // assert that all registers in callTargetMask are in the callKillSet noway_assert((callTargetMask & killSet) == callTargetMask); - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, callTargetReg, (ssize_t)pAddr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, callTargetReg, callTargetReg, (ssize_t)pAddr); regSet.verifyRegUsed(callTargetReg); params.callType = EC_INDIR_R; @@ -4206,7 +4206,7 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) } else { - emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, initReg, initReg, (ssize_t)compiler->gsGlobalSecurityCookieAddr); regSet.verifyRegUsed(initReg); emit->emitIns_S_R(INS_sd, EA_PTRSIZE, initReg, compiler->lvaGSSecurityCookie, 0); @@ -4245,7 +4245,8 @@ void CodeGen::genEmitGSCookieCheck(bool tailCall) else { // AOT case - GS cookie constant needs to be accessed through an indirection. - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, regGSConst, (ssize_t)compiler->gsGlobalSecurityCookieAddr); + GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, regGSConst, regGSConst, + (ssize_t)compiler->gsGlobalSecurityCookieAddr); regSet.verifyRegUsed(regGSConst); } // Load this method's GS value from the stack frame @@ -6270,7 +6271,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( // 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_ld, EA_PTR_DSP_RELOC, params.ireg, (ssize_t)pAddr); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, params.ireg, (ssize_t)pAddr); regSet.verifyRegUsed(params.ireg); } diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index 9be6adeca4bf2f..73dfcbdfc1bab2 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -2953,7 +2953,7 @@ void Compiler::compInitOptions(JitFlags* jitFlags) #ifdef DEBUG enableFakeSplitting = JitConfig.JitFakeProcedureSplitting(); -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_RISCV64) // Whether encoding of absolute addr as PC-rel offset is enabled opts.compEnablePCRelAddr = (JitConfig.EnablePCRelAddr() != 0); #endif diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e7f3dae76f4102..123cb54b36f594 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10130,7 +10130,7 @@ class Compiler bool compReloc; // Generate relocs for pointers in code, true for all AOT codegen #ifdef DEBUG -#if defined(TARGET_XARCH) +#if defined(TARGET_XARCH) || defined(TARGET_RISCV64) bool compEnablePCRelAddr; // Whether absolute addr be encoded as PC-rel offset by RyuJIT where possible #endif #endif // DEBUG diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 525beae23e6df2..2b16f97ef169d3 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1232,21 +1232,23 @@ void emitter::emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNu // This computes address from the immediate which is relocatable. void emitter::emitIns_R_AI(instruction ins, emitAttr attr, - regNumber reg, + regNumber dataReg, + regNumber addrReg, ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { assert(EA_IS_RELOC(attr)); - assert(ins == INS_addi || emitInsIsLoad(ins)); - assert(isGeneralRegister(reg)); + assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); + assert(emitInsIsStore(ins) || (dataReg == addrReg && dataReg != REG_ZERO)); + assert(isGeneralRegisterOrR0(dataReg) && isGeneralRegister(addrReg)); // 2-ins: - // auipc reg, off-hi-20bits - // ins reg, reg, off-lo-12bits + // auipc addrReg, off-hi-20bits + // ins dataReg, addrReg, off-lo-12bits instrDesc* id = emitNewInstr(attr); id->idIns(ins); - assert(reg != REG_R0); // for special. reg Must not be R0. - id->idReg1(reg); // destination register that will get the constant value. + id->idReg1(dataReg); + id->idReg2(addrReg); id->idInsOpt(INS_OPTS_RELOC); id->idAddr()->iiaAddr = (BYTE*)addr; @@ -3158,15 +3160,19 @@ static ssize_t UpperWordOfDoubleWordDoubleSignExtend(ssize_t doubleWord) BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruction* ins) { - BYTE* const dstBase = dst; - const regNumber reg1 = id->idReg1(); - assert(isGeneralRegister(reg1)); + BYTE* const dstBase = dst; + + regNumber dataReg = id->idReg1(); + regNumber addrReg = id->idReg2(); + assert(isGeneralRegisterOrR0(dataReg) && isGeneralRegister(addrReg)); *ins = id->idIns(); - assert(*ins == INS_addi || emitInsIsLoad(*ins)); - dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, 0); - emitGCregDeadUpd(reg1, dst); - dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, 0); + assert(*ins == INS_addi || emitInsIsLoadOrStore(*ins)); + assert(emitInsIsStore(*ins) || (dataReg == addrReg && dataReg != REG_ZERO)); + dst += emitOutput_UTypeInstr(dst, INS_auipc, addrReg, 0); + emitGCregDeadUpd(addrReg, dst); + dst += emitInsIsStore(*ins) ? emitOutput_STypeInstr(dst, *ins, addrReg, dataReg, 0) + : emitOutput_ITypeInstr(dst, *ins, dataReg, addrReg, 0); emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); return dst; @@ -5096,18 +5102,19 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR assert(memBase == indir->Addr()); ssize_t cns = addr->AsIntCon()->IconValue(); - if (addr->IsIconHandle(GTF_ICON_FTN_ADDR)) + regNumber addrReg = emitInsIsStore(ins) ? codeGen->rsGetRsvdReg() : dataReg; + if (addr->AsIntCon()->FitsInAddrBase(emitComp)) { attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG); - emitIns_R_AI(ins, attr, dataReg, (size_t)cns, cns, addr->GetIconHandleFlag()); + emitIns_R_AI(ins, attr, dataReg, addrReg, (size_t)cns, cns, addr->GetIconHandleFlag()); } else { ssize_t off = (cns << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended cns -= off; - emitLoadImmediate(EA_PTRSIZE, codeGen->rsGetRsvdReg(), cns); - emitIns_R_R_I(ins, attr, dataReg, codeGen->rsGetRsvdReg(), off); + emitLoadImmediate(EA_PTRSIZE, addrReg, cns); + emitIns_R_R_I(ins, attr, dataReg, addrReg, off); } } else if (isValidSimm12(offset)) diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index 616802ed478eea..bf69dd68a4ae0e 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -351,7 +351,8 @@ void emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, void emitIns_R_AI(instruction ins, emitAttr attr, - regNumber reg, + regNumber dataReg, + regNumber addrReg, ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY)); unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id); diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index dbdb7c7485108d..d9ce40148039e1 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -18216,7 +18216,7 @@ bool GenTreeIntConCommon::ImmedValCanBeFolded(Compiler* comp, genTreeOps op) return !ImmedValNeedsReloc(comp) || (op == GT_EQ) || (op == GT_NE); } -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) // Returns true if this absolute address fits within the base of an addr mode. // On Amd64 this effectively means, whether an absolute indirect address can // be encoded as 32-bit offset relative to IP or zero. diff --git a/src/coreclr/jit/gentree.h b/src/coreclr/jit/gentree.h index b76a27879a4a7e..04899ebc610979 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3242,7 +3242,7 @@ struct GenTreeIntConCommon : public GenTree bool ImmedValNeedsReloc(Compiler* comp); bool ImmedValCanBeFolded(Compiler* comp, genTreeOps op); -#ifdef TARGET_XARCH +#if defined(TARGET_XARCH) || defined(TARGET_RISCV64) bool FitsInAddrBase(Compiler* comp); bool AddrNeedsReloc(Compiler* comp); #endif diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 0397e6239a05a4..16aba467970880 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2334,15 +2334,16 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38) //***************************************************************************** -// Extract the PC-Relative offset from auipc + I-type adder (addi/load/jalr) +// Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcItype(UINT32 * pCode) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode) { enum { OpcodeAuipc = 0x17, OpcodeAddi = 0x13, OpcodeLoad = 0x03, + OpcodeStore = 0x23, OpcodeJalr = 0x67, OpcodeMask = 0x7F, @@ -2357,31 +2358,41 @@ INT64 GetRiscV64AuipcItype(UINT32 * pCode) INT64 hi20 = (INT32(auipc) >> 12) << 12; - UINT32 iType = pCode[1]; - UINT32 opcode = iType & OpcodeMask; - UINT32 funct3 = iType & Funct3Mask; - _ASSERTE(opcode == OpcodeLoad || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); - int iTypeRegSrc = (iType >> 15) & 0x1F; - _ASSERTE(auipcRegDest == iTypeRegSrc); + UINT32 instr = pCode[1]; + UINT32 opcode = instr & OpcodeMask; + UINT32 funct3 = instr & Funct3Mask; + _ASSERTE(opcode == OpcodeLoad || opcode == OpcodeStore || + ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); + int addrReg = (instr >> 15) & 0x1F; + _ASSERTE(auipcRegDest == addrReg); - INT64 lo12 = INT32(iType) >> 20; + INT64 lo12 = (INT32(instr) >> 25) << 5; // top 7 bits are in the same spot + int bottomBitsPos = (opcode == OpcodeStore) ? 7 : 20; + lo12 |= (instr >> bottomBitsPos) & 0x1F; return hi20 + lo12; } + //***************************************************************************** -// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr) +// Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) { - INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended - INT32 hi20 = INT32(offset - lo12); - _ASSERTE(INT64(hi20) + INT64(lo12) == offset); + enum + { + OpcodeStore = 0x23, + OpcodeMask = 0x7F, + }; + _ASSERTE((lo12 >> 11) == 0 || (lo12 >> 11) == -1); + _ASSERTE((hi20 & 0xfff) == 0); - _ASSERTE(GetRiscV64AuipcItype(pCode) == 0); + _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); pCode[0] |= hi20; - pCode[1] |= lo12 << 20; - _ASSERTE(GetRiscV64AuipcItype(pCode) == offset); + int bottomBitsPos = ((pCode[1] & OpcodeMask) == OpcodeStore) ? 7 : 20; + pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot + pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; + _ASSERTE(GetRiscV64AuipcCombo(pCode) == INT64(hi20) + INT64(lo12)); } //====================================================================== diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index ce70de8e40b8af..97fa9be20a03fd 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12019,7 +12019,19 @@ void CEEJitInfo::recordRelocation(void * location, _ASSERTE(addlDelta == 0); INT64 offset = (INT64)target - (INT64)location; - PutRiscV64AuipcItype((UINT32 *)locationRW, offset); + + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); + INT32 hi20 = INT32(offset - lo12); + + if (INT64(hi20) + INT64(lo12) == offset) + { + PutRiscV64AuipcCombo((UINT32 *)locationRW, lo12, hi20); + } + else + { + // TODO: emit a stub if it's a jump + m_fJumpStubOverflow = TRUE; + } } break; @@ -12067,13 +12079,13 @@ WORD CEEJitInfo::getRelocTypeHint(void * target) MODE_PREEMPTIVE; } CONTRACTL_END; -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) if (m_fAllowRel32) { if (ExecutableAllocator::IsPreferredExecutableRange(target)) return IMAGE_REL_BASED_REL32; } -#endif // TARGET_AMD64 +#endif // TARGET_AMD64 || // No hints return (WORD)-1; @@ -13265,22 +13277,22 @@ static TADDR UnsafeJitFunctionWorker( class JumpStubOverflowCheck final { -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) static BOOL g_fAllowRel32; -#endif // TARGET_AMD64 +#endif // TARGET_AMD64 || TARGET_RISCV64 BOOL _fForceJumpStubOverflow; BOOL _fAllowRel32; size_t _reserveForJumpStubs; -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) public: JumpStubOverflowCheck() { STANDARD_VM_CONTRACT; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) _fForceJumpStubOverflow = FALSE; #ifdef _DEBUG // Always exercise the overflow codepath with force relocs @@ -13289,12 +13301,12 @@ class JumpStubOverflowCheck final #endif _fAllowRel32 = FALSE; -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) _fAllowRel32 = (g_fAllowRel32 | _fForceJumpStubOverflow) && g_pConfig->JitEnableOptionalRelocs(); -#endif // TARGET_AMD64 +#endif // TARGET_AMD64 || TARGET_RISCV64 _reserveForJumpStubs = 0; -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) } ~JumpStubOverflowCheck() = default; @@ -13303,8 +13315,8 @@ class JumpStubOverflowCheck final { STANDARD_VM_CONTRACT; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) if (_fForceJumpStubOverflow) jitInfo.SetJumpStubOverflow(_fAllowRel32); jitInfo.SetAllowRel32(_fAllowRel32); @@ -13313,24 +13325,24 @@ class JumpStubOverflowCheck final jitInfo.SetJumpStubOverflow(_fForceJumpStubOverflow); #endif jitInfo.SetReserveForJumpStubs(_reserveForJumpStubs); -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) } bool Detected(CEEJitInfo& jitInfo, EEJitManager* jitMgr) { STANDARD_VM_CONTRACT; -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) if (jitInfo.IsJumpStubOverflow()) { // Try again with fAllowRel32 == FALSE. -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) // Disallow rel32 relocs in future. g_fAllowRel32 = FALSE; _fAllowRel32 = FALSE; -#endif // TARGET_AMD64 +#endif // TARGET_AMD64 || TARGET_RISCV64 #if defined(TARGET_ARM64) _fForceJumpStubOverflow = FALSE; #endif // TARGET_ARM64 @@ -13338,16 +13350,16 @@ class JumpStubOverflowCheck final _reserveForJumpStubs = jitInfo.GetReserveForJumpStubs(); return true; } -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) // No overflow detected return false; } }; -#if defined(TARGET_AMD64) +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) BOOL JumpStubOverflowCheck::g_fAllowRel32 = TRUE; -#endif // TARGET_AMD64 +#endif // TARGET_AMD64 || TARGET_RISCV64 static void LogJitMethodBegin(MethodDesc* ftn) { diff --git a/src/coreclr/vm/jitinterface.h b/src/coreclr/vm/jitinterface.h index c5011bcee2c46a..b2ae3b2193545a 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -738,7 +738,7 @@ class CEEJitInfo final : public CEECodeGenInfo #endif // FEATURE_EH_FUNCLETS } -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) void SetAllowRel32(BOOL fAllowRel32) { LIMITED_METHOD_CONTRACT; @@ -746,7 +746,7 @@ class CEEJitInfo final : public CEECodeGenInfo } #endif -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) void SetJumpStubOverflow(BOOL fJumpStubOverflow) { LIMITED_METHOD_CONTRACT; @@ -798,7 +798,7 @@ class CEEJitInfo final : public CEECodeGenInfo LIMITED_METHOD_CONTRACT; return 0; } -#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) +#endif // defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) #ifdef FEATURE_ON_STACK_REPLACEMENT // Called by the runtime to supply patchpoint information to the jit. @@ -824,10 +824,10 @@ class CEEJitInfo final : public CEECodeGenInfo m_totalUnwindInfos(0), m_usedUnwindInfos(0) #endif -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) , m_fAllowRel32(FALSE) #endif -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) , m_fJumpStubOverflow(FALSE), m_reserveForJumpStubs(0) #endif @@ -919,10 +919,10 @@ protected : ULONG m_usedUnwindInfos; #endif -#ifdef TARGET_AMD64 +#if defined(TARGET_AMD64) || defined(TARGET_RISCV64) BOOL m_fAllowRel32; // Use 32-bit PC relative address modes #endif -#if defined(TARGET_AMD64) || defined(TARGET_ARM64) +#if defined(TARGET_AMD64) || defined(TARGET_ARM64) || defined(TARGET_RISCV64) BOOL m_fJumpStubOverflow; // Overflow while trying to alocate jump stub slot within PC relative branch region // The code will need to be regenerated (with m_fRel32Allowed == FALSE for AMD64). size_t m_reserveForJumpStubs; // Space to reserve for jump stubs when allocating code From 41cbc5aa3c38396535cd45804ec6591cf604df9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 24 Oct 2025 11:04:07 +0200 Subject: [PATCH 10/39] Handle floating data --- src/coreclr/jit/emitriscv64.cpp | 8 +++----- src/coreclr/utilcode/util.cpp | 10 +++++++--- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 2b16f97ef169d3..37dec653d560a5 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1238,8 +1238,8 @@ void emitter::emitIns_R_AI(instruction ins, { assert(EA_IS_RELOC(attr)); assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); - assert(emitInsIsStore(ins) || (dataReg == addrReg && dataReg != REG_ZERO)); - assert(isGeneralRegisterOrR0(dataReg) && isGeneralRegister(addrReg)); + assert(emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == addrReg && dataReg != REG_ZERO)); + assert(isGeneralRegister(addrReg)); // 2-ins: // auipc addrReg, off-hi-20bits // ins dataReg, addrReg, off-lo-12bits @@ -3164,11 +3164,9 @@ BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruc regNumber dataReg = id->idReg1(); regNumber addrReg = id->idReg2(); - assert(isGeneralRegisterOrR0(dataReg) && isGeneralRegister(addrReg)); *ins = id->idIns(); assert(*ins == INS_addi || emitInsIsLoadOrStore(*ins)); - assert(emitInsIsStore(*ins) || (dataReg == addrReg && dataReg != REG_ZERO)); dst += emitOutput_UTypeInstr(dst, INS_auipc, addrReg, 0); emitGCregDeadUpd(addrReg, dst); dst += emitInsIsStore(*ins) ? emitOutput_STypeInstr(dst, *ins, addrReg, dataReg, 0) @@ -5102,7 +5100,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR assert(memBase == indir->Addr()); ssize_t cns = addr->AsIntCon()->IconValue(); - regNumber addrReg = emitInsIsStore(ins) ? codeGen->rsGetRsvdReg() : dataReg; + regNumber addrReg = (emitInsIsStore(ins) || isFloatReg(dataReg)) ? codeGen->rsGetRsvdReg() : dataReg; if (addr->AsIntCon()->FitsInAddrBase(emitComp)) { attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG); diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index 16aba467970880..a5a80281d15d98 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2344,6 +2344,8 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) OpcodeAddi = 0x13, OpcodeLoad = 0x03, OpcodeStore = 0x23, + OpcodeLoadFp = 0x07, + OpcodeStoreFp = 0x27, OpcodeJalr = 0x67, OpcodeMask = 0x7F, @@ -2361,13 +2363,13 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) UINT32 instr = pCode[1]; UINT32 opcode = instr & OpcodeMask; UINT32 funct3 = instr & Funct3Mask; - _ASSERTE(opcode == OpcodeLoad || opcode == OpcodeStore || + _ASSERTE(opcode == OpcodeLoad || opcode == OpcodeStore || opcode == OpcodeLoadFp || opcode == OpcodeStoreFp || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); int addrReg = (instr >> 15) & 0x1F; _ASSERTE(auipcRegDest == addrReg); INT64 lo12 = (INT32(instr) >> 25) << 5; // top 7 bits are in the same spot - int bottomBitsPos = (opcode == OpcodeStore) ? 7 : 20; + int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; lo12 |= (instr >> bottomBitsPos) & 0x1F; return hi20 + lo12; @@ -2382,6 +2384,7 @@ void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) enum { OpcodeStore = 0x23, + OpcodeStoreFp = 0x27, OpcodeMask = 0x7F, }; _ASSERTE((lo12 >> 11) == 0 || (lo12 >> 11) == -1); @@ -2389,7 +2392,8 @@ void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); pCode[0] |= hi20; - int bottomBitsPos = ((pCode[1] & OpcodeMask) == OpcodeStore) ? 7 : 20; + UINT32 opcode = pCode[1] & OpcodeMask; + int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; _ASSERTE(GetRiscV64AuipcCombo(pCode) == INT64(hi20) + INT64(lo12)); From 5f86c9d5af05421218a85d9c463e4105365a0a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 24 Oct 2025 12:20:20 +0200 Subject: [PATCH 11/39] spmi --- .../superpmi-shared/compileresult.cpp | 5 +- .../superpmi/superpmi-shared/spmiutil.cpp | 48 ++++++++++++------- .../tools/superpmi/superpmi-shared/spmiutil.h | 4 +- 3 files changed, 37 insertions(+), 20 deletions(-) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp index e878cd59d82ff3..b0b999976d455d 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp @@ -920,7 +920,10 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b // Similar to x64's IMAGE_REL_BASED_REL32 handling we // will handle this by also hardcoding the bottom bits // of the target into the instruction. - PutRiscV64AuipcItype((UINT32*)address, (INT32)tmp.target); + INT64 offset = (INT64)tmp.target; + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); + INT32 hi20 = INT32(offset - lo12); + PutRiscV64AuipcCombo((UINT32*)address, lo12, hi20); } wasRelocHandled = true; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index 2b48a3444f4816..c3483b549a306c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -478,15 +478,18 @@ void PutArm32MovtConstant(UINT32* p, unsigned con) } //***************************************************************************** -// Extract the PC-Relative offset from auipc + I-type adder (addi/load/jalr) +// Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcItype(UINT32 * pCode) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode) { enum { OpcodeAuipc = 0x17, OpcodeAddi = 0x13, OpcodeLoad = 0x03, + OpcodeStore = 0x23, + OpcodeLoadFp = 0x07, + OpcodeStoreFp = 0x27, OpcodeJalr = 0x67, OpcodeMask = 0x7F, @@ -501,31 +504,42 @@ INT64 GetRiscV64AuipcItype(UINT32 * pCode) INT64 hi20 = (INT32(auipc) >> 12) << 12; - UINT32 iType = pCode[1]; - UINT32 opcode = iType & OpcodeMask; - UINT32 funct3 = iType & Funct3Mask; - _ASSERTE(opcode == OpcodeLoad || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); - int iTypeRegSrc = (iType >> 15) & 0x1F; - _ASSERTE(auipcRegDest == iTypeRegSrc); + UINT32 instr = pCode[1]; + UINT32 opcode = instr & OpcodeMask; + UINT32 funct3 = instr & Funct3Mask; + _ASSERTE(opcode == OpcodeLoad || opcode == OpcodeStore || opcode == OpcodeLoadFp || opcode == OpcodeStoreFp || + ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); + int addrReg = (instr >> 15) & 0x1F; + _ASSERTE(auipcRegDest == addrReg); - INT64 lo12 = INT32(iType) >> 20; + INT64 lo12 = (INT32(instr) >> 25) << 5; // top 7 bits are in the same spot + int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + lo12 |= (instr >> bottomBitsPos) & 0x1F; return hi20 + lo12; } //***************************************************************************** -// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr) +// Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) { - INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended - INT32 hi20 = INT32(offset - lo12); - _ASSERTE(INT64(hi20) + INT64(lo12) == offset); + enum + { + OpcodeStore = 0x23, + OpcodeStoreFp = 0x27, + OpcodeMask = 0x7F, + }; + _ASSERTE((lo12 >> 11) == 0 || (lo12 >> 11) == -1); + _ASSERTE((hi20 & 0xfff) == 0); - _ASSERTE(GetRiscV64AuipcItype(pCode) == 0); + _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); pCode[0] |= hi20; - pCode[1] |= lo12 << 20; - _ASSERTE(GetRiscV64AuipcItype(pCode) == offset); + UINT32 opcode = pCode[1] & OpcodeMask; + int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot + pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; + _ASSERTE(GetRiscV64AuipcCombo(pCode) == INT64(hi20) + INT64(lo12)); } template diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index 72e3b2b0f93a50..d0874043158044 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h @@ -97,8 +97,8 @@ bool Is32BitThumb2Instruction(UINT16* p); UINT32 ExtractArm32MovImm(UINT32 instr); void PutArm32MovtConstant(UINT32* p, unsigned con); -INT64 GetRiscV64AuipcItype(UINT32 * pCode); -void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset); +INT64 GetRiscV64AuipcCombo(UINT32 * pCode); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20); template inline constexpr unsigned ArrLen(T (&)[size]) From b857350d46d15832118ac5666ddcb8da22a99349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 24 Oct 2025 13:02:02 +0200 Subject: [PATCH 12/39] fix comment --- src/coreclr/vm/jitinterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 97fa9be20a03fd..8f13be071f852c 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12085,7 +12085,7 @@ WORD CEEJitInfo::getRelocTypeHint(void * target) if (ExecutableAllocator::IsPreferredExecutableRange(target)) return IMAGE_REL_BASED_REL32; } -#endif // TARGET_AMD64 || +#endif // TARGET_AMD64 || TARGET_RISCV64 // No hints return (WORD)-1; From 9fa6ef2fe982a0c5127dc4fe67ce0fed3cf9718d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 27 Oct 2025 09:32:34 +0100 Subject: [PATCH 13/39] fix nullcheck --- src/coreclr/jit/emitriscv64.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 37dec653d560a5..0d813e7de50eae 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1238,7 +1238,7 @@ void emitter::emitIns_R_AI(instruction ins, { assert(EA_IS_RELOC(attr)); assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); - assert(emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == addrReg && dataReg != REG_ZERO)); + assert(emitInsIsStore(ins) || isFloatReg(dataReg) || dataReg == REG_ZERO || (dataReg == addrReg)); assert(isGeneralRegister(addrReg)); // 2-ins: // auipc addrReg, off-hi-20bits @@ -5100,7 +5100,8 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR assert(memBase == indir->Addr()); ssize_t cns = addr->AsIntCon()->IconValue(); - regNumber addrReg = (emitInsIsStore(ins) || isFloatReg(dataReg)) ? codeGen->rsGetRsvdReg() : dataReg; + bool needTemp = emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == REG_ZERO); + regNumber addrReg = needTemp ? codeGen->rsGetRsvdReg() : dataReg; if (addr->AsIntCon()->FitsInAddrBase(emitComp)) { attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG); From 4277e8bb31dfa5148b56e1b22050604ba4bb67f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 27 Oct 2025 09:40:59 +0100 Subject: [PATCH 14/39] AddrNeedsReloc more appropriate for addresses --- src/coreclr/jit/emitriscv64.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 0d813e7de50eae..1182f72c1c7d15 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -5102,7 +5102,7 @@ void emitter::emitInsLoadStoreOp(instruction ins, emitAttr attr, regNumber dataR bool needTemp = emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == REG_ZERO); regNumber addrReg = needTemp ? codeGen->rsGetRsvdReg() : dataReg; - if (addr->AsIntCon()->FitsInAddrBase(emitComp)) + if (addr->AsIntCon()->FitsInAddrBase(emitComp) && addr->AsIntCon()->AddrNeedsReloc(emitComp)) { attr = EA_SET_FLG(attr, EA_DSP_RELOC_FLG); emitIns_R_AI(ins, attr, dataReg, addrReg, (size_t)cns, cns, addr->GetIconHandleFlag()); From 7d04735dae6dd768e9631a4a391e59d379d3ae3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 27 Oct 2025 16:05:47 +0100 Subject: [PATCH 15/39] add assert --- src/coreclr/jit/emitriscv64.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 1182f72c1c7d15..d458f8e126a523 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1237,6 +1237,7 @@ void emitter::emitIns_R_AI(instruction ins, ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { assert(EA_IS_RELOC(attr)); + assert(IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint((void*)addr)); assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); assert(emitInsIsStore(ins) || isFloatReg(dataReg) || dataReg == REG_ZERO || (dataReg == addrReg)); assert(isGeneralRegister(addrReg)); From 12596e1129c03dc3fc3e84690281cdc89f72fdc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 27 Oct 2025 16:09:34 +0100 Subject: [PATCH 16/39] use relative pointer synthesizing where possible in genSetRegToConst --- src/coreclr/jit/codegenriscv64.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 351f653435d885..c541bd97d11cfb 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -926,8 +926,9 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre emitAttr attr = emitActualTypeSize(targetType); // TODO-RISCV64-CQ: Currently we cannot do this for all handles because of // https://github.com/dotnet/runtime/issues/60712 - if (con->ImmedValNeedsReloc(compiler)) + if (con->FitsInAddrBase(compiler) && con->AddrNeedsReloc(compiler)) { + // Use PC-relative synthesizing when possible, as it results in fewer instructions (6-8 vs. 2 for PCrel). attr = EA_SET_FLG(attr, EA_CNS_RELOC_FLG); } From 33f0b88f24bf9f1d6f2f923c6382d4e5f53e21f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 27 Oct 2025 20:56:38 +0100 Subject: [PATCH 17/39] Revert "use relative pointer synthesizing where possible in genSetRegToConst" This reverts commit 12596e1129c03dc3fc3e84690281cdc89f72fdc5. --- src/coreclr/jit/codegenriscv64.cpp | 3 +-- src/coreclr/jit/emitriscv64.cpp | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index c541bd97d11cfb..351f653435d885 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -926,9 +926,8 @@ void CodeGen::genSetRegToConst(regNumber targetReg, var_types targetType, GenTre emitAttr attr = emitActualTypeSize(targetType); // TODO-RISCV64-CQ: Currently we cannot do this for all handles because of // https://github.com/dotnet/runtime/issues/60712 - if (con->FitsInAddrBase(compiler) && con->AddrNeedsReloc(compiler)) + if (con->ImmedValNeedsReloc(compiler)) { - // Use PC-relative synthesizing when possible, as it results in fewer instructions (6-8 vs. 2 for PCrel). attr = EA_SET_FLG(attr, EA_CNS_RELOC_FLG); } diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index d458f8e126a523..8bed90be15e5e0 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1237,9 +1237,9 @@ void emitter::emitIns_R_AI(instruction ins, ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { assert(EA_IS_RELOC(attr)); - assert(IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint((void*)addr)); + assert(emitComp->opts.compReloc || (IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint((void*)addr))); assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); - assert(emitInsIsStore(ins) || isFloatReg(dataReg) || dataReg == REG_ZERO || (dataReg == addrReg)); + assert(emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == REG_ZERO) || (dataReg == addrReg)); assert(isGeneralRegister(addrReg)); // 2-ins: // auipc addrReg, off-hi-20bits From 13b6421cf0892901969da78484d1404f4ddd95fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Fri, 31 Oct 2025 15:04:20 +0100 Subject: [PATCH 18/39] Record relocations for loads for indirect calls only when the load address is in range --- src/coreclr/jit/codegenriscv64.cpp | 7 +++--- src/coreclr/jit/emitriscv64.cpp | 38 ++++++++++++++++++++++++++++++ src/coreclr/jit/emitriscv64.h | 2 ++ src/coreclr/vm/jitinterface.cpp | 2 +- 4 files changed, 44 insertions(+), 5 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 351f653435d885..f26142f0472f5c 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2738,8 +2738,7 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) params.addr = nullptr; params.callType = EC_INDIR_R; params.ireg = REG_DEFAULT_HELPER_CALL_TARGET; - - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, params.ireg, (ssize_t)helperFunction.addr); + GetEmitter()->emitIns_R_R_Addr(INS_ld, EA_PTRSIZE, params.ireg, params.ireg, helperFunction.addr); regSet.verifyRegUsed(params.ireg); } @@ -3377,7 +3376,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, if (helperFunction.accessType == IAT_VALUE) { - params.addr = (void*)helperFunction.addr; + params.addr = helperFunction.addr; } else { @@ -3402,7 +3401,7 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, // assert that all registers in callTargetMask are in the callKillSet noway_assert((callTargetMask & killSet) == callTargetMask); - GetEmitter()->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, callTargetReg, callTargetReg, (ssize_t)pAddr); + GetEmitter()->emitIns_R_R_Addr(INS_ld, EA_PTRSIZE, callTargetReg, callTargetReg, pAddr); regSet.verifyRegUsed(callTargetReg); params.callType = EC_INDIR_R; diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 8bed90be15e5e0..22767484d7b5f7 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1305,6 +1305,44 @@ void emitter::emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNu appendToCurIG(id); } +//------------------------------------------------------------------------ +// emitIns_R_R_Addr: emit instruction sequence for a long (address pointer) immediate +// +// If the address is approximately in range, emits a PC-relative combo with a relocation: +// auipc regAddr, offset_hi20 +// ins regData, regAddr, offset_lo12 +// Otherwise, synthesizes an absolute address combo: +// li regAddr, (addr - lo12) +// ins regData, regAddr, lo12 +// +// Arguments: +// ins - an instruction that is a 64-bit adder with 12-bit immediate (addi/load/store) +// attr - attribute +// regData - destination register for addi/load, source for stores +// regAddr - temporary register to synthesize the address into (pass same as regData if possible) +// addr - the address +// +void emitter::emitIns_R_R_Addr(instruction ins, emitAttr attr, regNumber regData, regNumber regAddr, void* addr) +{ + assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); + assert(!EA_IS_RELOC(attr) && EA_SIZE(attr) == EA_PTRSIZE); + + if (emitComp->opts.compReloc || + (INDEBUG(emitComp->opts.compEnablePCRelAddr&&)(IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint(addr)))) + { + attr = EA_SET_FLG(attr, EA_PTR_DSP_RELOC); + emitIns_R_AI(ins, attr, regData, regAddr, (ssize_t)addr); + } + else + { + ssize_t imm = (ssize_t)addr; + ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); + imm -= lo12; + emitLoadImmediate(attr, regAddr, imm); + emitIns_R_R_I(ins, attr, regData, regAddr, lo12); + } +} + void emitter::emitIns_J_R(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg) { NYI_RISCV64("emitIns_J_R-----unimplemented/unused on RISCV64 yet----"); diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index bf69dd68a4ae0e..9a3337704df263 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -345,6 +345,8 @@ void emitIns_R_C(instruction ins, emitAttr attr, regNumber destReg, regNumber ad void emitIns_R_L(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg); +void emitIns_R_R_Addr(instruction ins, emitAttr attr, regNumber regDest, regNumber regAddr, void* addr); + void emitIns_J_R(instruction ins, emitAttr attr, BasicBlock* dst, regNumber reg); void emitIns_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs); diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 8f13be071f852c..b0d221dcc874f1 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12027,7 +12027,7 @@ void CEEJitInfo::recordRelocation(void * location, { PutRiscV64AuipcCombo((UINT32 *)locationRW, lo12, hi20); } - else + else // out of 32-bit range { // TODO: emit a stub if it's a jump m_fJumpStubOverflow = TRUE; From 7f1f98da0407f5ce34615deeb03b265fcad21c7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 12 Nov 2025 17:03:54 +0100 Subject: [PATCH 19/39] Check if direct calls are in range and synthesize the absolute address in register if not --- src/coreclr/jit/codegenriscv64.cpp | 65 ++++++++++++++++++++++++++++-- src/coreclr/jit/emitriscv64.cpp | 18 ++++++++- src/coreclr/jit/emitriscv64.h | 2 + 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 0ce0cc812717fa..829b74fd7b7b23 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -2731,8 +2731,24 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) if (helperFunction.accessType == IAT_VALUE) { // If the helper is a value, we need to use the address of the helper. - params.addr = helperFunction.addr; - params.callType = EC_FUNC_TOKEN; + params.addr = helperFunction.addr; + if (GetEmitter()->IsAddressInRange(params.addr)) + { + params.callType = EC_FUNC_TOKEN; + } + else + { + // li rd, addr_upper_bits + // jalr rd, addr_lower_12bits + ssize_t imm = (ssize_t)params.addr; + ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); + imm -= lo12; + regNumber tempReg = params.isJump ? rsGetRsvdReg() : REG_RA; + GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tempReg, imm); + params.callType = EC_INDIR_R; + params.ireg = tempReg; + params.addr = (void*)lo12; + } } else { @@ -3376,7 +3392,29 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, if (helperFunction.accessType == IAT_VALUE) { - params.addr = helperFunction.addr; + if (GetEmitter()->IsAddressInRange(helperFunction.addr)) + { + params.addr = helperFunction.addr; + + assert(params.addr != (void*)0x2aaaabdc5f48); + } + else + { + // li rd, addr_upper_bits + // jalr rd, addr_lower_12bits + ssize_t imm = (ssize_t)helperFunction.addr; + ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); + imm -= lo12; + if (callTargetReg == REG_NA) + { + callTargetReg = rsGetRsvdReg(); + } + assert(callTargetReg != REG_ZERO); + GetEmitter()->emitLoadImmediate(EA_PTRSIZE, callTargetReg, imm); + params.callType = EC_INDIR_R; + params.ireg = callTargetReg; + params.addr = (void*)lo12; + } } else { @@ -5601,6 +5639,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } else #endif // FEATURE_READYTORUN + { if (call->IsHelperCall()) { CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(params.methHnd); @@ -5615,10 +5654,27 @@ void CodeGen::genCallInstruction(GenTreeCall* call) // Direct call to a non-virtual user function. params.addr = call->gtDirectCallAddress; } + } assert(params.addr != nullptr); + if (GetEmitter()->IsAddressInRange(params.addr)) + { + params.callType = EC_FUNC_TOKEN; + } + else + { + // li rd, addr_upper_bits + // jalr rd, addr_lower_12bits + ssize_t imm = (ssize_t)params.addr; + ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); + imm -= lo12; + regNumber tempReg = params.isJump ? rsGetRsvdReg() : REG_RA; + GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tempReg, imm); + params.callType = EC_INDIR_R; + params.ireg = tempReg; + params.addr = (void*)lo12; + } - params.callType = EC_FUNC_TOKEN; genEmitCallWithCurrentGC(params); } } @@ -6254,6 +6310,7 @@ void CodeGen::genJumpToThrowHlpBlk_la( CORINFO_CONST_LOOKUP helperFunction = compiler->compGetHelperFtn((CorInfoHelpFunc)(compiler->acdHelper(codeKind))); + assert(emit->IsAddressInRange(helperFunction.addr)); // TODO: remove if (helperFunction.accessType == IAT_VALUE) { // INS_OPTS_C diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 6802bc0c107e7f..0cabb8fb513a1d 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1358,8 +1358,7 @@ void emitter::emitIns_R_R_Addr(instruction ins, emitAttr attr, regNumber regData assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); assert(!EA_IS_RELOC(attr) && EA_SIZE(attr) == EA_PTRSIZE); - if (emitComp->opts.compReloc || - (INDEBUG(emitComp->opts.compEnablePCRelAddr&&)(IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint(addr)))) + if (IsAddressInRange(addr)) { attr = EA_SET_FLG(attr, EA_PTR_DSP_RELOC); emitIns_R_AI(ins, attr, regData, regAddr, (ssize_t)addr); @@ -5132,6 +5131,21 @@ unsigned emitter::get_curTotalCodeSize() return emitTotalCodeSize; } +//---------------------------------------------------------------------------------------- +// IsAddressInRange: Returns whether an address should be attempted to be reached with an auipc + load/store/jalr/addi +// instruction combo. +// +// Arguments: +// addr - The address to check +// +// Return Value: +// A struct containing the current instruction execution characteristics +bool emitter::IsAddressInRange(void* addr) +{ + return emitComp->opts.compReloc || + (INDEBUG(emitComp->opts.compEnablePCRelAddr&&)(IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint(addr))); +} + #if defined(DEBUG) || defined(LATE_DISASM) //---------------------------------------------------------------------------------------- diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index e4f9d25f1d0c9f..802cc17fd54dd2 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -354,6 +354,8 @@ unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id); unsigned get_curTotalCodeSize(); // bytes of code +bool IsAddressInRange(void* addr); + //------------------------------------------------------------------------ // emitIsCmpJump: checks if it's a compare and jump (branch) // From 809cedf48a27a5a60b5e20cff3c654fd1195d6a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 12 Nov 2025 22:23:52 +0100 Subject: [PATCH 20/39] Remove hardcoded branch offset from genJumpToThrowHlpBlk_la --- src/coreclr/jit/codegenriscv64.cpp | 36 ++++++++++++++++++------------ 1 file changed, 22 insertions(+), 14 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 829b74fd7b7b23..5acc299e583ca8 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6308,6 +6308,9 @@ void CodeGen::genJumpToThrowHlpBlk_la( ins = ins == INS_beq ? INS_bne : INS_beq; } + BasicBlock* skipLabel = genCreateTempLabel(); + emit->emitIns_J_cond_la(ins, skipLabel, reg1, reg2); + CORINFO_CONST_LOOKUP helperFunction = compiler->compGetHelperFtn((CorInfoHelpFunc)(compiler->acdHelper(codeKind))); assert(emit->IsAddressInRange(helperFunction.addr)); // TODO: remove @@ -6315,30 +6318,35 @@ void CodeGen::genJumpToThrowHlpBlk_la( { // INS_OPTS_C // If the helper is a value, we need to use the address of the helper. - params.addr = helperFunction.addr; - params.callType = EC_FUNC_TOKEN; - - // TODO-RISCV64-RVC: Remove hardcoded branch offset here - ssize_t imm = 3 * sizeof(emitter::code_t); - emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, reg2, imm); + params.addr = helperFunction.addr; + if (GetEmitter()->IsAddressInRange(params.addr)) + { + params.callType = EC_FUNC_TOKEN; + } + else + { + // li rd, addr_upper_bits + // jalr rd, addr_lower_12bits + ssize_t imm = (ssize_t)params.addr; + ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); + imm -= lo12; + regNumber tempReg = params.isJump ? rsGetRsvdReg() : REG_RA; + GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tempReg, imm); + params.callType = EC_INDIR_R; + params.ireg = tempReg; + params.addr = (void*)lo12; + } } else { params.addr = nullptr; assert(helperFunction.accessType == IAT_PVALUE); - void* pAddr = helperFunction.addr; - params.callType = EC_INDIR_R; params.ireg = REG_DEFAULT_HELPER_CALL_TARGET; - // 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_ld, EA_PTR_DSP_RELOC, params.ireg, params.ireg, (ssize_t)pAddr); + emit->emitIns_R_AI(INS_ld, EA_PTR_DSP_RELOC, params.ireg, params.ireg, (ssize_t)helperFunction.addr); regSet.verifyRegUsed(params.ireg); } - BasicBlock* skipLabel = genCreateTempLabel(); - params.methHnd = compiler->eeFindHelper(compiler->acdHelper(codeKind)); // TODO-RISCV64: Why is this not using genEmitHelperCall? From f8b8083690a6d8f5880d6018d85157a48d24c349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 13 Nov 2025 08:49:45 +0100 Subject: [PATCH 21/39] asserts --- src/coreclr/jit/codegenriscv64.cpp | 1 - src/coreclr/jit/emitriscv64.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 5acc299e583ca8..877f5cb653b1e8 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6313,7 +6313,6 @@ void CodeGen::genJumpToThrowHlpBlk_la( CORINFO_CONST_LOOKUP helperFunction = compiler->compGetHelperFtn((CorInfoHelpFunc)(compiler->acdHelper(codeKind))); - assert(emit->IsAddressInRange(helperFunction.addr)); // TODO: remove if (helperFunction.accessType == IAT_VALUE) { // INS_OPTS_C diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 0cabb8fb513a1d..3487d5c040b05d 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1915,6 +1915,7 @@ void emitter::emitIns_Call(const EmitCallParams& params) assert(params.callType == EC_FUNC_TOKEN); assert(params.addr != NULL); + assert(IsAddressInRange(params.addr)); void* addr = (void*)(((size_t)params.addr) + (params.isJump ? 0 : 1)); // NOTE: low-bit0 is used for jalr ra/r0,rd,0 From 025883d69554f0e50b72f0004ab7b0014048d040 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 13 Nov 2025 11:22:08 +0100 Subject: [PATCH 22/39] Fix genFnEpilog emitting always a relocatable load / func pointer --- src/coreclr/jit/codegenriscv64.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 877f5cb653b1e8..17bc698f33a43d 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -655,18 +655,32 @@ void CodeGen::genFnEpilog(BasicBlock* block) switch (addrInfo.accessType) { case IAT_VALUE: - // TODO-RISCV64-CQ: using B/BL for optimization. + params.ireg = REG_INDIRECT_CALL_TARGET_REG; + if (GetEmitter()->IsAddressInRange(addrInfo.addr)) + { + params.callType = EC_FUNC_TOKEN; + params.addr = addrInfo.addr; + } + else + { + // li rd, addr_upper_bits + // jalr rd, addr_lower_12bits + ssize_t imm = (ssize_t)addrInfo.addr; + ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); + imm -= lo12; + GetEmitter()->emitLoadImmediate(EA_PTRSIZE, params.ireg, imm); + params.callType = EC_INDIR_R; + params.addr = (void*)lo12; + } + break; + case IAT_PVALUE: // Load the address into a register, load indirect and call through a register // We have to use REG_INDIRECT_CALL_TARGET_REG since we assume the argument registers are in use params.callType = EC_INDIR_R; params.ireg = REG_INDIRECT_CALL_TARGET_REG; - instGen_Set_Reg_To_Imm(EA_HANDLE_CNS_RELOC, params.ireg, (ssize_t)addrInfo.addr); - if (addrInfo.accessType == IAT_PVALUE) - { - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, params.ireg, params.ireg, 0); - regSet.verifyRegUsed(params.ireg); - } + GetEmitter()->emitIns_R_R_Addr(INS_ld, EA_PTRSIZE, params.ireg, params.ireg, addrInfo.addr); + regSet.verifyRegUsed(params.ireg); break; case IAT_RELPVALUE: From 5f3598059e3200a688d5134312378e986f232269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 13 Nov 2025 11:38:23 +0100 Subject: [PATCH 23/39] remove assert --- src/coreclr/jit/codegenriscv64.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 17bc698f33a43d..7bdcf05c2c040c 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -3409,8 +3409,6 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, if (GetEmitter()->IsAddressInRange(helperFunction.addr)) { params.addr = helperFunction.addr; - - assert(params.addr != (void*)0x2aaaabdc5f48); } else { From 7e6357e6467c2d148074e4f7c04e031a14f5ce74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 13 Nov 2025 12:39:57 +0100 Subject: [PATCH 24/39] Fix genProfiling(Enter|Leave)Callback emitting always a relocatable load / func pointer --- src/coreclr/jit/codegenriscv64.cpp | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 7bdcf05c2c040c..7d3a9edfade3cb 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6709,17 +6709,9 @@ void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed) return; } - ssize_t methHnd = (ssize_t)compiler->compProfilerMethHnd; - if (compiler->compProfilerMethHndIndirected) - { - instGen_Set_Reg_To_Imm(EA_PTR_DSP_RELOC, REG_PROFILER_ENTER_ARG_FUNC_ID, methHnd); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_PROFILER_ENTER_ARG_FUNC_ID, REG_PROFILER_ENTER_ARG_FUNC_ID, - 0); - } - else - { - instGen_Set_Reg_To_Imm(EA_PTRSIZE, REG_PROFILER_ENTER_ARG_FUNC_ID, methHnd); - } + instruction ins = compiler->compProfilerMethHndIndirected ? INS_ld : INS_addi; + GetEmitter()->emitIns_R_R_Addr(ins, EA_PTRSIZE, REG_PROFILER_ENTER_ARG_FUNC_ID, REG_PROFILER_ENTER_ARG_FUNC_ID, + compiler->compProfilerMethHnd); ssize_t callerSPOffset = -compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed()); genInstrWithConstant(INS_addi, EA_PTRSIZE, REG_PROFILER_ENTER_ARG_CALLER_SP, genFramePointerReg(), callerSPOffset, @@ -6755,17 +6747,9 @@ void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FC compiler->info.compProfilerCallback = true; - ssize_t methHnd = (ssize_t)compiler->compProfilerMethHnd; - if (compiler->compProfilerMethHndIndirected) - { - instGen_Set_Reg_To_Imm(EA_PTR_DSP_RELOC, REG_PROFILER_LEAVE_ARG_FUNC_ID, methHnd); - GetEmitter()->emitIns_R_R_I(INS_ld, EA_PTRSIZE, REG_PROFILER_LEAVE_ARG_FUNC_ID, REG_PROFILER_LEAVE_ARG_FUNC_ID, - 0); - } - else - { - instGen_Set_Reg_To_Imm(EA_PTRSIZE, REG_PROFILER_LEAVE_ARG_FUNC_ID, methHnd); - } + instruction ins = compiler->compProfilerMethHndIndirected ? INS_ld : INS_addi; + GetEmitter()->emitIns_R_R_Addr(ins, EA_PTRSIZE, REG_PROFILER_LEAVE_ARG_FUNC_ID, REG_PROFILER_LEAVE_ARG_FUNC_ID, + compiler->compProfilerMethHnd); gcInfo.gcMarkRegSetNpt(RBM_PROFILER_LEAVE_ARG_FUNC_ID); From ab556f8d97f777890ee796feb0d0854f31773396 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 13 Nov 2025 20:07:33 +0100 Subject: [PATCH 25/39] Centralize checking whether direct call is in range --- src/coreclr/jit/codegenriscv64.cpp | 143 ++++++----------------------- src/coreclr/jit/emitriscv64.cpp | 45 ++++----- 2 files changed, 49 insertions(+), 139 deletions(-) diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 7d3a9edfade3cb..8c9259977f49da 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -655,23 +655,9 @@ void CodeGen::genFnEpilog(BasicBlock* block) switch (addrInfo.accessType) { case IAT_VALUE: - params.ireg = REG_INDIRECT_CALL_TARGET_REG; - if (GetEmitter()->IsAddressInRange(addrInfo.addr)) - { - params.callType = EC_FUNC_TOKEN; - params.addr = addrInfo.addr; - } - else - { - // li rd, addr_upper_bits - // jalr rd, addr_lower_12bits - ssize_t imm = (ssize_t)addrInfo.addr; - ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); - imm -= lo12; - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, params.ireg, imm); - params.callType = EC_INDIR_R; - params.addr = (void*)lo12; - } + params.ireg = REG_INDIRECT_CALL_TARGET_REG; + params.callType = EC_FUNC_TOKEN; + params.addr = addrInfo.addr; break; case IAT_PVALUE: @@ -2745,24 +2731,9 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) if (helperFunction.accessType == IAT_VALUE) { // If the helper is a value, we need to use the address of the helper. - params.addr = helperFunction.addr; - if (GetEmitter()->IsAddressInRange(params.addr)) - { - params.callType = EC_FUNC_TOKEN; - } - else - { - // li rd, addr_upper_bits - // jalr rd, addr_lower_12bits - ssize_t imm = (ssize_t)params.addr; - ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); - imm -= lo12; - regNumber tempReg = params.isJump ? rsGetRsvdReg() : REG_RA; - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tempReg, imm); - params.callType = EC_INDIR_R; - params.ireg = tempReg; - params.addr = (void*)lo12; - } + params.addr = helperFunction.addr; + params.callType = EC_FUNC_TOKEN; + params.ireg = params.isJump ? rsGetRsvdReg() : REG_RA; } else { @@ -3402,31 +3373,18 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, CORINFO_CONST_LOOKUP helperFunction = compiler->compGetHelperFtn((CorInfoHelpFunc)helper); regMaskTP killSet = compiler->compHelperCallKillSet((CorInfoHelpFunc)helper); - params.callType = EC_FUNC_TOKEN; + if (callTargetReg == REG_NA) + { + // If a callTargetReg has not been explicitly provided, we will use REG_DEFAULT_HELPER_CALL_TARGET, but + // this is only a valid assumption if the helper call is known to kill REG_DEFAULT_HELPER_CALL_TARGET. + callTargetReg = REG_DEFAULT_HELPER_CALL_TARGET; + } + params.ireg = callTargetReg; if (helperFunction.accessType == IAT_VALUE) { - if (GetEmitter()->IsAddressInRange(helperFunction.addr)) - { - params.addr = helperFunction.addr; - } - else - { - // li rd, addr_upper_bits - // jalr rd, addr_lower_12bits - ssize_t imm = (ssize_t)helperFunction.addr; - ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); - imm -= lo12; - if (callTargetReg == REG_NA) - { - callTargetReg = rsGetRsvdReg(); - } - assert(callTargetReg != REG_ZERO); - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, callTargetReg, imm); - params.callType = EC_INDIR_R; - params.ireg = callTargetReg; - params.addr = (void*)lo12; - } + params.callType = EC_FUNC_TOKEN; + params.addr = helperFunction.addr; } else { @@ -3439,13 +3397,6 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, // ld reg, reg // jalr reg - if (callTargetReg == REG_NA) - { - // If a callTargetReg has not been explicitly provided, we will use REG_DEFAULT_HELPER_CALL_TARGET, but - // this is only a valid assumption if the helper call is known to kill REG_DEFAULT_HELPER_CALL_TARGET. - callTargetReg = REG_DEFAULT_HELPER_CALL_TARGET; - } - regMaskTP callTargetMask = genRegMask(callTargetReg); // assert that all registers in callTargetMask are in the callKillSet @@ -3455,7 +3406,6 @@ void CodeGen::genEmitHelperCall(unsigned helper, int argSize, emitAttr retSize, regSet.verifyRegUsed(callTargetReg); params.callType = EC_INDIR_R; - params.ireg = callTargetReg; } params.methHnd = compiler->eeFindHelper(helper); @@ -5569,32 +5519,22 @@ void CodeGen::genCallInstruction(GenTreeCall* call) genConsumeReg(target); } - regNumber targetReg; - ssize_t jalrOffset = 0; - if (target->isContainedIntOrIImmed()) { - // Load upper (64-12) bits to a temporary register. Lower 12 bits will be put inside JALR's instruction as - // offset. - targetReg = internalRegisters.GetSingle(call); - ssize_t imm = target->AsIntCon()->IconValue(); - jalrOffset = (imm << (64 - 12)) >> (64 - 12); - imm -= jalrOffset; - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, targetReg, imm); + params.callType = EC_FUNC_TOKEN; + params.ireg = internalRegisters.GetSingle(call); + params.addr = (void*)target->AsIntCon()->IconValue(); } else { - targetReg = target->GetRegNum(); + params.callType = EC_INDIR_R; + params.ireg = target->GetRegNum(); } // We have already generated code for gtControlExpr evaluating it into a register. // We just need to emit "call reg" in this case. // - assert(genIsValidIntReg(targetReg)); - - params.callType = EC_INDIR_R; - params.ireg = targetReg; - params.addr = (jalrOffset == 0) ? nullptr : (void*)jalrOffset; // We use addr to pass offset value + assert(genIsValidIntReg(params.ireg)); genEmitCallWithCurrentGC(params); } @@ -5668,24 +5608,8 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } } - assert(params.addr != nullptr); - if (GetEmitter()->IsAddressInRange(params.addr)) - { - params.callType = EC_FUNC_TOKEN; - } - else - { - // li rd, addr_upper_bits - // jalr rd, addr_lower_12bits - ssize_t imm = (ssize_t)params.addr; - ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); - imm -= lo12; - regNumber tempReg = params.isJump ? rsGetRsvdReg() : REG_RA; - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tempReg, imm); - params.callType = EC_INDIR_R; - params.ireg = tempReg; - params.addr = (void*)lo12; - } + params.callType = EC_FUNC_TOKEN; + params.ireg = params.isJump ? rsGetRsvdReg() : REG_RA; genEmitCallWithCurrentGC(params); } @@ -6329,24 +6253,9 @@ void CodeGen::genJumpToThrowHlpBlk_la( { // INS_OPTS_C // If the helper is a value, we need to use the address of the helper. - params.addr = helperFunction.addr; - if (GetEmitter()->IsAddressInRange(params.addr)) - { - params.callType = EC_FUNC_TOKEN; - } - else - { - // li rd, addr_upper_bits - // jalr rd, addr_lower_12bits - ssize_t imm = (ssize_t)params.addr; - ssize_t lo12 = (imm << (64 - 12)) >> (64 - 12); - imm -= lo12; - regNumber tempReg = params.isJump ? rsGetRsvdReg() : REG_RA; - GetEmitter()->emitLoadImmediate(EA_PTRSIZE, tempReg, imm); - params.callType = EC_INDIR_R; - params.ireg = tempReg; - params.addr = (void*)lo12; - } + params.addr = helperFunction.addr; + params.callType = EC_FUNC_TOKEN; + params.ireg = params.isJump ? rsGetRsvdReg() : REG_RA; } else { diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 3487d5c040b05d..e2b09b6ff5aad1 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1779,11 +1779,8 @@ void emitter::emitIns_Call(const EmitCallParams& params) /* Sanity check the arguments depending on callType */ assert(params.callType < EC_COUNT); - assert((params.callType != EC_FUNC_TOKEN) || - (params.ireg == REG_NA && params.xreg == REG_NA && params.xmul == 0 && params.disp == 0)); - assert(params.callType < EC_INDIR_R || params.addr == nullptr || isValidSimm12((ssize_t)params.addr)); - assert(params.callType != EC_INDIR_R || - (params.ireg < REG_COUNT && params.xreg == REG_NA && params.xmul == 0 && params.disp == 0)); + assert(isGeneralRegister(params.ireg)); + assert(params.callType < EC_INDIR_R || params.addr == nullptr); // RISCV64 never uses these assert(params.xreg == REG_NA && params.xmul == 0 && params.disp == 0); @@ -1812,6 +1809,19 @@ void emitter::emitIns_Call(const EmitCallParams& params) } #endif + ssize_t jalrOffset = 0; + if (params.callType == EC_FUNC_TOKEN && !IsAddressInRange(params.addr)) + { + // Load upper bits of the absolute call address into a register: + // li ireg, addr_upper + // jalr zero/ra, ireg, addr_lo12 (emitted below) + assert(params.addr != nullptr); + ssize_t imm = (ssize_t)params.addr; + jalrOffset = (imm << (64 - 12)) >> (64 - 12); // low 12-bits, sign-extended + imm -= jalrOffset; + emitLoadImmediate(EA_PTRSIZE, params.ireg, imm); // upper bits + } + /* Managed RetVal: emit sequence point for the call */ if (emitComp->opts.compDbgInfo && params.debugInfo.GetLocation().IsValid()) { @@ -1881,40 +1891,31 @@ void emitter::emitIns_Call(const EmitCallParams& params) id->idIns(INS_jalr); id->idInsOpt(INS_OPTS_C); - // INS_OPTS_C: placeholders. 1/2-ins: - // if (callType == EC_INDIR_R) - // jalr zero/ra, ireg, offset - // else if (callType == EC_FUNC_TOKEN || callType == EC_FUNC_ADDR) - // auipc t2/ra, offset-hi20 - // jalr zero/ra, t2/ra, offset-lo12 /* Record the address: method, indirection, or funcptr */ - if (params.callType == EC_INDIR_R) + if ((params.callType == EC_INDIR_R) || (params.callType == EC_FUNC_TOKEN && !IsAddressInRange(params.addr))) { /* This is an indirect call (either a virtual call or func ptr call) */ - // assert(callType == EC_INDIR_R); + + // jalr zero/ra, ireg, offset id->idSetIsCallRegPtr(); regNumber reg_jalr = params.isJump ? REG_R0 : REG_RA; id->idReg4(reg_jalr); id->idReg3(params.ireg); // NOTE: for EC_INDIR_R, using idReg3. - id->idSmallCns(0); // SmallCns will contain JALR's offset. - if (params.addr != nullptr) - { - // If addr is not NULL, it must contain JALR's offset, which is set to the lower 12 bits of address. - id->idSmallCns((size_t)params.addr); - } - assert(params.xreg == REG_NA); - + id->idSmallCns(jalrOffset); id->idCodeSize(4); } else { /* This is a simple direct call: "call helper/method/addr" */ + // auipc t2/ra, offset-hi20 + // jalr zero/ra, t2/ra, offset-lo12 + assert(params.callType == EC_FUNC_TOKEN); - assert(params.addr != NULL); + assert(params.addr != nullptr); assert(IsAddressInRange(params.addr)); void* addr = From e38cf3f7e5f75d20013196048134de6bbe36dae2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Mon, 17 Nov 2025 15:38:51 +0100 Subject: [PATCH 26/39] jump stubs --- src/coreclr/vm/jitinterface.cpp | 63 ++++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 9 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index c6fcc83e0b3922..a4649925de5bc7 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12045,20 +12045,65 @@ void CEEJitInfo::recordRelocation(void * location, { _ASSERTE(addlDelta == 0); - INT64 offset = (INT64)target - (INT64)location; + auto putTargetIntoAuipcCombo = [location, locationRW](void* targetAddr) -> bool + { + INT64 offset = (INT64)targetAddr - (INT64)location; - INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); - INT32 hi20 = INT32(offset - lo12); + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); + INT32 hi20 = INT32(offset - lo12); + + if (INT64(hi20) + INT64(lo12) != offset) + return false; // out of range - if (INT64(hi20) + INT64(lo12) == offset) - { PutRiscV64AuipcCombo((UINT32 *)locationRW, lo12, hi20); - } - else // out of 32-bit range + LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + I-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", + DBG_ADDR(location), DBG_ADDR(targetAddr), offset)); + return true; + }; + + if (putTargetIntoAuipcCombo(target)) + break; // success + + // Target is out of 32-bit range + // If we're fixing up a jump, try jumping through a stub + enum { - // TODO: emit a stub if it's a jump - m_fJumpStubOverflow = TRUE; + OpcodeJalr = 0x67, + OpcodeMask = 0x7F, + }; + bool isJalr = ((((UINT32 *)locationRW)[1] & OpcodeMask) == OpcodeJalr); + if (isJalr && !m_fJumpStubOverflow) + { + BYTE* loAddr = (BYTE*)location - (1l << 31) - (1l << 11); + BYTE* hiAddr = (BYTE*)location + (1l << 31) - (1l << 11) - 1; + + // Check for the wrap around cases + if (loAddr > location) + loAddr = (BYTE*)UINT64_MIN; // overflow + if (hiAddr < location) + hiAddr = (BYTE*)UINT64_MAX; // overflow + + PCODE jumpStubAddr = ExecutionManager::jumpStub(m_pMethodBeingCompiled, (PCODE)target, loAddr, hiAddr, nullptr, false); + + // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it + // to reserve extra memory on retry to increase chances that the retry succeeds. + m_reserveForJumpStubs = max((size_t)0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); + + if (jumpStubAddr != NULL) + { + if (!putTargetIntoAuipcCombo((void*)jumpStubAddr)) + { + _ASSERTE(!"jump stub was not in expected range"); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); + } + + LOG((LF_JIT, LL_INFO100000, "Using JumpStub at" FMT_ADDR "that jumps to" FMT_ADDR "\n", + DBG_ADDR(jumpStubAddr), DBG_ADDR(target))); + break; // success + } } + + m_fJumpStubOverflow = TRUE; } break; From 4c7f286e33a1b8701148c8f4106ee34513583d3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 19 Nov 2025 10:52:45 +0100 Subject: [PATCH 27/39] FitsIn --- src/coreclr/inc/utilcode.h | 10 +++++++++- .../tools/superpmi/superpmi-shared/compileresult.cpp | 2 +- .../tools/superpmi/superpmi-shared/spmiutil.cpp | 9 +++++---- src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h | 2 +- src/coreclr/utilcode/util.cpp | 9 +++++---- src/coreclr/vm/jitinterface.cpp | 11 ++++------- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 308fee339c38ce..c9f5fafdf56dfb 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3360,7 +3360,7 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode); //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 offsetLo12, INT32 offsetHi20); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset); //***************************************************************************** // Returns whether the offset fits into bl instruction @@ -3402,6 +3402,14 @@ inline bool FitsInRel28(INT64 val64) return (val64 >= -0x08000000LL) && (val64 < 0x08000000LL); } +//***************************************************************************** +// Returns whether the offset fits into a RISC-V auipc + I-type or S-type instruction combo +//***************************************************************************** +inline bool FitsInAuipcCombo(INT64 val64) +{ + return (val64 >= -(1ll << 31) - (1ll << 11)) && (val64 < (1ll << 31) - (1ll << 11)); +} + // // TEB access can be dangerous when using fibers because a fiber may // run on multiple threads. If the TEB pointer is retrieved and saved diff --git a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp index b0b999976d455d..8350515b01edd5 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp @@ -923,7 +923,7 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b INT64 offset = (INT64)tmp.target; INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); - PutRiscV64AuipcCombo((UINT32*)address, lo12, hi20); + PutRiscV64AuipcCombo((UINT32*)address, INT64(lo12) + INT64(hi20)); } wasRelocHandled = true; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index bad04b47393901..fef154f83b54fc 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -508,7 +508,7 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset) { enum { @@ -516,8 +516,9 @@ void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) OpcodeStoreFp = 0x27, OpcodeMask = 0x7F, }; - _ASSERTE((lo12 >> 11) == 0 || (lo12 >> 11) == -1); - _ASSERTE((hi20 & 0xfff) == 0); + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); + INT32 hi20 = INT32(offset - lo12); + _ASSERTE(INT64(lo12) + INT64(hi20) == offset); _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); pCode[0] |= hi20; @@ -525,7 +526,7 @@ void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; - _ASSERTE(GetRiscV64AuipcCombo(pCode) == INT64(hi20) + INT64(lo12)); + _ASSERTE(GetRiscV64AuipcCombo(pCode) == offset); } template diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index 9a7978b2e5d682..04beb4a694572f 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h @@ -106,7 +106,7 @@ UINT32 ExtractArm32MovImm(UINT32 instr); void PutArm32MovtConstant(UINT32* p, unsigned con); INT64 GetRiscV64AuipcCombo(UINT32 * pCode); -void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset); template inline constexpr unsigned ArrLen(T (&)[size]) diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index a5a80281d15d98..a170ffac206f35 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2379,7 +2379,7 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset) { enum { @@ -2387,8 +2387,9 @@ void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) OpcodeStoreFp = 0x27, OpcodeMask = 0x7F, }; - _ASSERTE((lo12 >> 11) == 0 || (lo12 >> 11) == -1); - _ASSERTE((hi20 & 0xfff) == 0); + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); + INT32 hi20 = INT32(offset - lo12); + _ASSERTE(INT64(lo12) + INT64(hi20) == offset); _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); pCode[0] |= hi20; @@ -2396,7 +2397,7 @@ void PutRiscV64AuipcCombo(UINT32 * pCode, INT32 lo12, INT32 hi20) int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; - _ASSERTE(GetRiscV64AuipcCombo(pCode) == INT64(hi20) + INT64(lo12)); + _ASSERTE(GetRiscV64AuipcCombo(pCode) == offset); } //====================================================================== diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index a4649925de5bc7..6e66e52c07015a 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12049,13 +12049,10 @@ void CEEJitInfo::recordRelocation(void * location, { INT64 offset = (INT64)targetAddr - (INT64)location; - INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); - INT32 hi20 = INT32(offset - lo12); - - if (INT64(hi20) + INT64(lo12) != offset) + if (!FitsInAuipcCombo(offset)) return false; // out of range - PutRiscV64AuipcCombo((UINT32 *)locationRW, lo12, hi20); + PutRiscV64AuipcCombo((UINT32 *)locationRW, offset); LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + I-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", DBG_ADDR(location), DBG_ADDR(targetAddr), offset)); return true; @@ -12074,8 +12071,8 @@ void CEEJitInfo::recordRelocation(void * location, bool isJalr = ((((UINT32 *)locationRW)[1] & OpcodeMask) == OpcodeJalr); if (isJalr && !m_fJumpStubOverflow) { - BYTE* loAddr = (BYTE*)location - (1l << 31) - (1l << 11); - BYTE* hiAddr = (BYTE*)location + (1l << 31) - (1l << 11) - 1; + BYTE* loAddr = (BYTE*)location - (1ll << 31) - (1ll << 11); + BYTE* hiAddr = (BYTE*)location + (1ll << 31) - (1ll << 11) - 1; // Check for the wrap around cases if (loAddr > location) From 315c8072babd00695c95e6100b1e1a5174b5bae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 19 Nov 2025 15:18:51 +0100 Subject: [PATCH 28/39] Add different relocation types to avoid telling jalr by disasm --- src/coreclr/inc/corinfo.h | 4 +- src/coreclr/jit/emitriscv64.cpp | 5 +- .../DependencyAnalysis/ObjectDataBuilder.cs | 4 +- .../Compiler/DependencyAnalysis/Relocation.cs | 101 +++++++++++------- .../Target_RiscV64/RiscV64Emitter.cs | 4 +- .../Compiler/ObjectWriter/ElfObjectWriter.cs | 16 ++- .../Compiler/ObjectWriter/PEObjectWriter.cs | 4 +- .../tools/Common/JitInterface/CorInfoImpl.cs | 12 ++- .../superpmi-shared/compileresult.cpp | 4 +- src/coreclr/vm/jitinterface.cpp | 12 +-- 10 files changed, 108 insertions(+), 58 deletions(-) diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index cecb60f03d95ff..0c4da3db2aa0fc 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -3423,7 +3423,9 @@ class ICorDynamicInfo : public ICorStaticInfo // // RISCV64 relocation types // -#define IMAGE_REL_RISCV64_PC 0x0003 +#define IMAGE_REL_RISCV64_CALL_PLT 0x0003 // auipc + jalr +#define IMAGE_REL_RISCV64_PCREL_I 0x0004 // auipc + I-type +#define IMAGE_REL_RISCV64_PCREL_S 0x0005 // auipc + S-type /**********************************************************************************/ #ifdef TARGET_64BIT diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index e2b09b6ff5aad1..17fbb34ecc33cf 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -2020,7 +2020,7 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id) dst += emitOutput_ITypeInstr(dst, INS_jalr, linkReg, tempReg, 0); assert(id->idIsDspReloc()); - emitRecordRelocation(origDst, (BYTE*)addr, IMAGE_REL_RISCV64_PC); + emitRecordRelocation(origDst, (BYTE*)addr, IMAGE_REL_RISCV64_CALL_PLT); } // If the method returns a GC ref, mark INTRET (A0) appropriately. @@ -2877,7 +2877,8 @@ BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruc dst += emitInsIsStore(*ins) ? emitOutput_STypeInstr(dst, *ins, addrReg, dataReg, 0) : emitOutput_ITypeInstr(dst, *ins, dataReg, addrReg, 0); - emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC); + uint16_t type = emitInsIsStore(*ins) ? IMAGE_REL_RISCV64_PCREL_S : IMAGE_REL_RISCV64_PCREL_I; + emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, type); return dst; } diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs index 484792d0497ba9..db44e0a685a55b 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/ObjectDataBuilder.cs @@ -300,7 +300,9 @@ public void EmitReloc(ISymbolNode symbol, RelocType relocType, int delta = 0) case RelocType.IMAGE_REL_BASED_LOONGARCH64_PC: case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: - case RelocType.IMAGE_REL_BASED_RISCV64_PC: + case RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT: + case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I: + case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S: Debug.Assert(delta == 0); // Do not vacate space for this kind of relocation, because // the space is embedded in the instruction. diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index 2be41335d6b152..f052f1a5a5c49f 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -24,7 +24,9 @@ public enum RelocType IMAGE_REL_BASED_ARM64_BRANCH26 = 0x15, // Arm64: B, BL IMAGE_REL_BASED_LOONGARCH64_PC = 0x16, // LoongArch64: pcalau12i+imm12 IMAGE_REL_BASED_LOONGARCH64_JIR = 0x17, // LoongArch64: pcaddu18i+jirl - IMAGE_REL_BASED_RISCV64_PC = 0x18, // RiscV64: auipc + IMAGE_REL_BASED_RISCV64_CALL_PLT = 0x18, // RiscV64: auipc + jalr + IMAGE_REL_BASED_RISCV64_PCREL_I = 0x19, // RiscV64: auipc + I-type + IMAGE_REL_BASED_RISCV64_PCREL_S = 0x20, // RiscV64: auipc + S-type IMAGE_REL_BASED_RELPTR32 = 0x7C, // 32-bit relative address from byte starting reloc // This is a special NGEN-specific relocation type // for relative pointer (used to make NGen relocation @@ -491,20 +493,40 @@ private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38) Debug.Assert(GetLoongArch64JIR(pCode) == imm38); } - private static unsafe int GetRiscV64PC(uint* pCode) + private static unsafe long GetRiscV64AuipcCombo(uint* pCode) { - uint auipcInstr = *pCode; - Debug.Assert((auipcInstr & 0x7f) == 0x00000017); - // first get the high 20 bits, - int imm = (int)((auipcInstr & 0xfffff000)); - // then get the low 12 bits, - uint nextInstr = *(pCode + 1); - Debug.Assert((nextInstr & 0x707f) == 0x00000013 || - (nextInstr & 0x707f) == 0x00000067 || - (nextInstr & 0x707f) == 0x00003003); - imm += ((int)(nextInstr)) >> 20; - - return imm; + const uint OpcodeAuipc = 0x17; + const uint OpcodeAddi = 0x13; + const uint OpcodeLoad = 0x03; + const uint OpcodeStore = 0x23; + const uint OpcodeLoadFp = 0x07; + const uint OpcodeStoreFp = 0x27; + const uint OpcodeJalr = 0x67; + const uint OpcodeMask = 0x7F; + + const uint Funct3AddiJalr = 0x0000; + const uint Funct3Mask = 0x7000; + + uint auipc = pCode[0]; + Debug.Assert((auipc & OpcodeMask) == OpcodeAuipc); + uint auipcRegDest = (auipc >> 7) & 0x1F; + Debug.Assert(auipcRegDest != 0); + + long hi20 = (((int)auipc) >> 12) << 12; + + uint instr = pCode[1]; + uint opcode = instr & OpcodeMask; + uint funct3 = instr & Funct3Mask; + Debug.Assert(opcode == OpcodeLoad || opcode == OpcodeStore || opcode == OpcodeLoadFp || opcode == OpcodeStoreFp || + ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); + uint addrReg = (instr >> 15) & 0x1F; + Debug.Assert(auipcRegDest == addrReg); + + long lo12 = (((int)instr) >> 25) << 5; // top 7 bits are in the same spot + int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + lo12 |= (instr >> bottomBitsPos) & 0x1F; + + return hi20 + lo12; } // INS_OPTS_RELOC: placeholders. 2-ins: @@ -518,26 +540,23 @@ private static unsafe int GetRiscV64PC(uint* pCode) // INS_OPTS_C // auipc reg, off-hi-20bits // jalr reg, reg, off-lo-12bits - private static unsafe void PutRiscV64PC(uint* pCode, long imm32) + private static unsafe void PutRiscV64AuipcCombo(uint* pCode, long offset) { - // Verify that we got a valid offset - Debug.Assert((imm32 >= (long)-0x80000000 - 0x800) && (imm32 < (long)0x80000000 - 0x800)); - - int doff = (int)(imm32 & 0xfff); - uint auipcInstr = *pCode; - Debug.Assert((auipcInstr & 0x7f) == 0x00000017); - - auipcInstr |= (uint)((imm32 + 0x800) & 0xfffff000); - *pCode = auipcInstr; - - uint nextInstr = *(pCode + 1); - Debug.Assert((nextInstr & 0x707f) == 0x00000013 || - (nextInstr & 0x707f) == 0x00000067 || - (nextInstr & 0x707f) == 0x00003003); - nextInstr |= (uint)((doff & 0xfff) << 20); - *(pCode + 1) = nextInstr; - - Debug.Assert(GetRiscV64PC(pCode) == imm32); + const uint OpcodeStore = 0x23; + const uint OpcodeStoreFp = 0x27; + const uint OpcodeMask = 0x7F; + + int lo12 = (int)((offset << (64 - 12)) >> (64 - 12)); + int hi20 = (int)(offset - lo12); + Debug.Assert((long)lo12 + (long)hi20 == offset); + + Debug.Assert(GetRiscV64AuipcCombo(pCode) == 0); + pCode[0] |= (uint)hi20; + uint opcode = pCode[1] & OpcodeMask; + int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + pCode[1] |= (uint)((lo12 >> 5) << 25); // top 7 bits are in the same spot + pCode[1] |= (uint)((lo12 & 0x1F) << bottomBitsPos); + Debug.Assert(GetRiscV64AuipcCombo(pCode) == offset); } public Relocation(RelocType relocType, int offset, ISymbolNode target) @@ -600,8 +619,10 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: PutLoongArch64JIR((uint*)location, value); break; - case RelocType.IMAGE_REL_BASED_RISCV64_PC: - PutRiscV64PC((uint*)location, value); + case RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT: + case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I: + case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S: + PutRiscV64AuipcCombo((uint*)location, value); break; default: Debug.Fail("Invalid RelocType: " + relocType); @@ -629,7 +650,9 @@ public static int GetSize(RelocType relocType) RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL => 8, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 16, RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR => 16, - RelocType.IMAGE_REL_BASED_RISCV64_PC => 16, + RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT => 16, + RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I => 16, + RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S => 16, _ => throw new NotSupportedException(), }; } @@ -684,8 +707,10 @@ public static unsafe long ReadValue(RelocType relocType, void* location) return (long)GetLoongArch64PC12((uint*)location); case RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR: return (long)GetLoongArch64JIR((uint*)location); - case RelocType.IMAGE_REL_BASED_RISCV64_PC: - return (long)GetRiscV64PC((uint*)location); + case RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT: + case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I: + case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S: + return (long)GetRiscV64AuipcCombo((uint*)location); default: Debug.Fail("Invalid RelocType: " + relocType); return 0; diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs index 86f7528c7262aa..efb40cf42d05c7 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs @@ -43,7 +43,7 @@ public void EmitMOV(Register regDst, Register regSrc) public void EmitMOV(Register regDst, ISymbolNode symbol) { - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PC); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I); //auipc reg, off-hi-20bits EmitPC(regDst); //addi reg, reg, off-lo-12bits @@ -124,7 +124,7 @@ public void EmitJMP(ISymbolNode symbol) } else { - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PC); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT); EmitPC(Register.X29); // auipc x29, 0 EmitJALR(Register.X0, Register.X29, 0); // jalr x0, 0(x29) } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs index c9ffd85b08f785..303e400bb43403 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -557,7 +557,9 @@ private void EmitRelocationsRiscV64(int sectionIndex, List r IMAGE_REL_BASED_DIR64 => R_RISCV_64, IMAGE_REL_BASED_HIGHLOW => R_RISCV_32, IMAGE_REL_BASED_RELPTR32 => R_RISCV_32_PCREL, - IMAGE_REL_BASED_RISCV64_PC => R_RISCV_CALL_PLT, + IMAGE_REL_BASED_RISCV64_CALL_PLT => R_RISCV_CALL_PLT, + IMAGE_REL_BASED_RISCV64_PCREL_I => R_RISCV_PCREL_HI20, + IMAGE_REL_BASED_RISCV64_PCREL_S => R_RISCV_PCREL_HI20, _ => throw new NotSupportedException("Unknown relocation type: " + symbolicRelocation.Type) }; @@ -565,6 +567,18 @@ private void EmitRelocationsRiscV64(int sectionIndex, List r BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); relocationStream.Write(relocationEntry); + + // TODO: This is wrong, we need to point to a label (position of previous instruction) + // if (symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I or IMAGE_REL_BASED_RISCV64_PCREL_S) + // { + // type = symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I + // ? R_RISCV_PCREL_LO12_I + // : R_RISCV_PCREL_LO12_S; + // BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)relocationStream.Position); + // BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); + // BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); + // relocationStream.Write(relocationEntry); + // } } } } diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs index c9cd75cd327cde..e31468b3813054 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/PEObjectWriter.cs @@ -892,7 +892,9 @@ private unsafe void ResolveRelocations(int sectionIndex, List Date: Wed, 19 Nov 2025 16:06:21 +0100 Subject: [PATCH 29/39] Tell S-type by relocation type instead of disassembling the fixup site --- src/coreclr/inc/utilcode.h | 4 ++-- .../Compiler/DependencyAnalysis/Relocation.cs | 24 +++++++++---------- .../superpmi-shared/compileresult.cpp | 3 ++- .../superpmi/superpmi-shared/spmiutil.cpp | 21 +++++++--------- .../tools/superpmi/superpmi-shared/spmiutil.h | 4 ++-- src/coreclr/utilcode/util.cpp | 20 ++++++---------- src/coreclr/vm/jitinterface.cpp | 10 ++++---- 7 files changed, 38 insertions(+), 48 deletions(-) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index c9f5fafdf56dfb..59c81a258893c4 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3355,12 +3355,12 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm); //***************************************************************************** // Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcCombo(UINT32 * pCode); +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype); //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype); //***************************************************************************** // Returns whether the offset fits into bl instruction diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs index f052f1a5a5c49f..eee60bcc907631 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Relocation.cs @@ -493,7 +493,7 @@ private static unsafe void PutLoongArch64JIR(uint* pCode, long imm38) Debug.Assert(GetLoongArch64JIR(pCode) == imm38); } - private static unsafe long GetRiscV64AuipcCombo(uint* pCode) + private static unsafe long GetRiscV64AuipcCombo(uint* pCode, bool isStype) { const uint OpcodeAuipc = 0x17; const uint OpcodeAddi = 0x13; @@ -519,11 +519,12 @@ private static unsafe long GetRiscV64AuipcCombo(uint* pCode) uint funct3 = instr & Funct3Mask; Debug.Assert(opcode == OpcodeLoad || opcode == OpcodeStore || opcode == OpcodeLoadFp || opcode == OpcodeStoreFp || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); + Debug.Assert(isStype == (opcode == OpcodeStore || opcode == OpcodeStoreFp)); uint addrReg = (instr >> 15) & 0x1F; Debug.Assert(auipcRegDest == addrReg); long lo12 = (((int)instr) >> 25) << 5; // top 7 bits are in the same spot - int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + int bottomBitsPos = isStype ? 7 : 20; lo12 |= (instr >> bottomBitsPos) & 0x1F; return hi20 + lo12; @@ -540,23 +541,18 @@ private static unsafe long GetRiscV64AuipcCombo(uint* pCode) // INS_OPTS_C // auipc reg, off-hi-20bits // jalr reg, reg, off-lo-12bits - private static unsafe void PutRiscV64AuipcCombo(uint* pCode, long offset) + private static unsafe void PutRiscV64AuipcCombo(uint* pCode, long offset, bool isStype) { - const uint OpcodeStore = 0x23; - const uint OpcodeStoreFp = 0x27; - const uint OpcodeMask = 0x7F; - int lo12 = (int)((offset << (64 - 12)) >> (64 - 12)); int hi20 = (int)(offset - lo12); Debug.Assert((long)lo12 + (long)hi20 == offset); - Debug.Assert(GetRiscV64AuipcCombo(pCode) == 0); + Debug.Assert(GetRiscV64AuipcCombo(pCode, isStype) == 0); pCode[0] |= (uint)hi20; - uint opcode = pCode[1] & OpcodeMask; - int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + int bottomBitsPos = isStype ? 7 : 20; pCode[1] |= (uint)((lo12 >> 5) << 25); // top 7 bits are in the same spot pCode[1] |= (uint)((lo12 & 0x1F) << bottomBitsPos); - Debug.Assert(GetRiscV64AuipcCombo(pCode) == offset); + Debug.Assert(GetRiscV64AuipcCombo(pCode, isStype) == offset); } public Relocation(RelocType relocType, int offset, ISymbolNode target) @@ -622,7 +618,8 @@ public static unsafe void WriteValue(RelocType relocType, void* location, long v case RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT: case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I: case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S: - PutRiscV64AuipcCombo((uint*)location, value); + bool isStype = (relocType is RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S); + PutRiscV64AuipcCombo((uint*)location, value, isStype); break; default: Debug.Fail("Invalid RelocType: " + relocType); @@ -710,7 +707,8 @@ public static unsafe long ReadValue(RelocType relocType, void* location) case RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT: case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I: case RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S: - return (long)GetRiscV64AuipcCombo((uint*)location); + bool isStype = (relocType is RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S); + return GetRiscV64AuipcCombo((uint*)location, isStype); default: Debug.Fail("Invalid RelocType: " + relocType); return 0; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp index 3f8dd195502623..19efee8cf61c6c 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp @@ -925,7 +925,8 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b INT64 offset = (INT64)tmp.target; INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); - PutRiscV64AuipcCombo((UINT32*)address, INT64(lo12) + INT64(hi20)); + BOOL isStype = (relocType == IMAGE_REL_RISCV64_PCREL_S); + PutRiscV64AuipcCombo((UINT32*)address, INT64(lo12) + INT64(hi20), isStype); } wasRelocHandled = true; } diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index fef154f83b54fc..ed14efc75a4bf4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -466,7 +466,7 @@ void PutArm32MovtConstant(UINT32* p, unsigned con) //***************************************************************************** // Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcCombo(UINT32 * pCode) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype) { enum { @@ -495,38 +495,33 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) UINT32 funct3 = instr & Funct3Mask; _ASSERTE(opcode == OpcodeLoad || opcode == OpcodeStore || opcode == OpcodeLoadFp || opcode == OpcodeStoreFp || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); + _ASSERTE(isStype == (opcode == OpcodeStore || opcode == OpcodeStoreFp)); int addrReg = (instr >> 15) & 0x1F; _ASSERTE(auipcRegDest == addrReg); INT64 lo12 = (INT32(instr) >> 25) << 5; // top 7 bits are in the same spot - int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + int bottomBitsPos = isStype ? 7 : 20; lo12 |= (instr >> bottomBitsPos) & 0x1F; return hi20 + lo12; } + //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype) { - enum - { - OpcodeStore = 0x23, - OpcodeStoreFp = 0x27, - OpcodeMask = 0x7F, - }; INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); _ASSERTE(INT64(lo12) + INT64(hi20) == offset); - _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); + _ASSERTE(GetRiscV64AuipcCombo(pCode, isStype) == 0); pCode[0] |= hi20; - UINT32 opcode = pCode[1] & OpcodeMask; - int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + int bottomBitsPos = isStype ? 7 : 20; pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; - _ASSERTE(GetRiscV64AuipcCombo(pCode) == offset); + _ASSERTE(GetRiscV64AuipcCombo(pCode, isStype) == offset); } template diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index 04beb4a694572f..abebe60e406a0b 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h @@ -105,8 +105,8 @@ bool Is32BitThumb2Instruction(UINT16* p); UINT32 ExtractArm32MovImm(UINT32 instr); void PutArm32MovtConstant(UINT32* p, unsigned con); -INT64 GetRiscV64AuipcCombo(UINT32 * pCode); -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset); +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype); template inline constexpr unsigned ArrLen(T (&)[size]) diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index a170ffac206f35..d0b34678c4075a 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2336,7 +2336,7 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38) //***************************************************************************** // Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcCombo(UINT32 * pCode) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype) { enum { @@ -2365,11 +2365,12 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) UINT32 funct3 = instr & Funct3Mask; _ASSERTE(opcode == OpcodeLoad || opcode == OpcodeStore || opcode == OpcodeLoadFp || opcode == OpcodeStoreFp || ((opcode == OpcodeAddi || opcode == OpcodeJalr) && funct3 == Funct3AddiJalr)); + _ASSERTE(isStype == (opcode == OpcodeStore || opcode == OpcodeStoreFp)); int addrReg = (instr >> 15) & 0x1F; _ASSERTE(auipcRegDest == addrReg); INT64 lo12 = (INT32(instr) >> 25) << 5; // top 7 bits are in the same spot - int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + int bottomBitsPos = isStype ? 7 : 20; lo12 |= (instr >> bottomBitsPos) & 0x1F; return hi20 + lo12; @@ -2379,25 +2380,18 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode) //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype) { - enum - { - OpcodeStore = 0x23, - OpcodeStoreFp = 0x27, - OpcodeMask = 0x7F, - }; INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); _ASSERTE(INT64(lo12) + INT64(hi20) == offset); - _ASSERTE(GetRiscV64AuipcCombo(pCode) == 0); + _ASSERTE(GetRiscV64AuipcCombo(pCode, isStype) == 0); pCode[0] |= hi20; - UINT32 opcode = pCode[1] & OpcodeMask; - int bottomBitsPos = (opcode == OpcodeStore || opcode == OpcodeStoreFp) ? 7 : 20; + int bottomBitsPos = isStype ? 7 : 20; pCode[1] |= (lo12 >> 5) << 25; // top 7 bits are in the same spot pCode[1] |= (lo12 & 0x1F) << bottomBitsPos; - _ASSERTE(GetRiscV64AuipcCombo(pCode) == offset); + _ASSERTE(GetRiscV64AuipcCombo(pCode, isStype) == offset); } //====================================================================== diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 3eae97305b5d7b..64dc8b94695e45 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12047,16 +12047,18 @@ void CEEJitInfo::recordRelocation(void * location, { _ASSERTE(addlDelta == 0); - auto putTargetIntoAuipcCombo = [location, locationRW](void* targetAddr) -> bool + BOOL isStype = (fRelocType == IMAGE_REL_RISCV64_PCREL_S); + + auto putTargetIntoAuipcCombo = [location, locationRW, isStype](void* targetAddr) -> bool { INT64 offset = (INT64)targetAddr - (INT64)location; if (!FitsInAuipcCombo(offset)) return false; // out of range - PutRiscV64AuipcCombo((UINT32 *)locationRW, offset); - LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + I-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", - DBG_ADDR(location), DBG_ADDR(targetAddr), offset)); + PutRiscV64AuipcCombo((UINT32 *)locationRW, offset, isStype); + LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + %c-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", + (isStype ? 'S' : 'I'), DBG_ADDR(location), DBG_ADDR(targetAddr), offset)); return true; }; From 4b8d487ee4e7b7fa21f19207507f132c98079449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 19 Nov 2025 16:11:43 +0100 Subject: [PATCH 30/39] comment --- .../tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs index 303e400bb43403..56dfc04411a83f 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -568,7 +568,7 @@ private void EmitRelocationsRiscV64(int sectionIndex, List r BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); relocationStream.Write(relocationEntry); - // TODO: This is wrong, we need to point to a label (position of previous instruction) + // TODO: This is wrong, LO12 needs to point to a label (previous instruction with HI20) // if (symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I or IMAGE_REL_BASED_RISCV64_PCREL_S) // { // type = symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I From 2874ec426e0dda5b845c939a548869de9e5745fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 19 Nov 2025 17:01:22 +0100 Subject: [PATCH 31/39] Refactor fix-up to use same code structure as arm64 --- src/coreclr/vm/jitinterface.cpp | 79 +++++++++++++++------------------ 1 file changed, 37 insertions(+), 42 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 64dc8b94695e45..1c64fa03a619a4 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12046,59 +12046,54 @@ void CEEJitInfo::recordRelocation(void * location, case IMAGE_REL_RISCV64_PCREL_S: { _ASSERTE(addlDelta == 0); + delta = (INT64)target - (INT64)location; - BOOL isStype = (fRelocType == IMAGE_REL_RISCV64_PCREL_S); - - auto putTargetIntoAuipcCombo = [location, locationRW, isStype](void* targetAddr) -> bool + if (!FitsInAuipcCombo(delta)) { - INT64 offset = (INT64)targetAddr - (INT64)location; - - if (!FitsInAuipcCombo(offset)) - return false; // out of range - - PutRiscV64AuipcCombo((UINT32 *)locationRW, offset, isStype); - LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + %c-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", - (isStype ? 'S' : 'I'), DBG_ADDR(location), DBG_ADDR(targetAddr), offset)); - return true; - }; - - if (putTargetIntoAuipcCombo(target)) - break; // success - - // Target is out of 32-bit range - // If we're fixing up a jump, try jumping through a stub - if ((fRelocType == IMAGE_REL_RISCV64_CALL_PLT) && !m_fJumpStubOverflow) - { - BYTE* loAddr = (BYTE*)location - (1ll << 31) - (1ll << 11); - BYTE* hiAddr = (BYTE*)location + (1ll << 31) - (1ll << 11) - 1; + // Target is out of 32-bit range + // If we're fixing up a jump, try jumping through a stub + PCODE jumpStubAddr = NULL; + if ((fRelocType == IMAGE_REL_RISCV64_CALL_PLT) && !m_fJumpStubOverflow) + { + BYTE* loAddr = (BYTE*)location - (1ll << 31) - (1ll << 11); + BYTE* hiAddr = (BYTE*)location + (1ll << 31) - (1ll << 11) - 1; - // Check for the wrap around cases - if (loAddr > location) - loAddr = (BYTE*)UINT64_MIN; // overflow - if (hiAddr < location) - hiAddr = (BYTE*)UINT64_MAX; // overflow + // Check for the wrap around cases + if (loAddr > location) + loAddr = (BYTE*)UINT64_MIN; // overflow + if (hiAddr < location) + hiAddr = (BYTE*)UINT64_MAX; // overflow - PCODE jumpStubAddr = ExecutionManager::jumpStub(m_pMethodBeingCompiled, (PCODE)target, loAddr, hiAddr, nullptr, false); + PCODE jumpStubAddr = ExecutionManager::jumpStub(m_pMethodBeingCompiled, (PCODE)target, loAddr, hiAddr, nullptr, false); - // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it - // to reserve extra memory on retry to increase chances that the retry succeeds. - m_reserveForJumpStubs = max((size_t)0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); + // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it + // to reserve extra memory on retry to increase chances that the retry succeeds. + m_reserveForJumpStubs = max((size_t)0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); + } - if (jumpStubAddr != NULL) + if (jumpStubAddr == NULL) { - if (!putTargetIntoAuipcCombo((void*)jumpStubAddr)) - { - _ASSERTE(!"jump stub was not in expected range"); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - } + // This forces the JIT to retry the method, which allows us to reserve more space for jump stubs and have a higher chance that + // we will find space for them. + m_fJumpStubOverflow = TRUE; + break; + } - LOG((LF_JIT, LL_INFO100000, "Using JumpStub at" FMT_ADDR "that jumps to" FMT_ADDR "\n", - DBG_ADDR(jumpStubAddr), DBG_ADDR(target))); - break; // success + delta = (INT64)jumpStubAddr - (INT64)location; + if (!FitsInAuipcCombo(delta)) + { + _ASSERTE(!"jump stub was not in expected range"); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); } + + LOG((LF_JIT, LL_INFO100000, "Using JumpStub at" FMT_ADDR "that jumps to" FMT_ADDR "\n", + DBG_ADDR(jumpStubAddr), DBG_ADDR(target))); } - m_fJumpStubOverflow = TRUE; + BOOL isStype = (fRelocType == IMAGE_REL_RISCV64_PCREL_S); + PutRiscV64AuipcCombo((UINT32 *)locationRW, delta, isStype); + LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + %c-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", + (isStype ? 'S' : 'I'), DBG_ADDR(location), DBG_ADDR(target), delta)); } break; From 5b5ce0a3e420cc886e5085c52b0d3a356b3385f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Wed, 19 Nov 2025 18:10:42 +0100 Subject: [PATCH 32/39] Use PCREL_I for ld --- .../DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs index d6c80b908f1ade..a9f99fa425d5b7 100644 --- a/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs +++ b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs @@ -89,7 +89,7 @@ public void EmitJALR(Register regDst, Register regSrc, int offset) public void EmitLD(Register regDst, ISymbolNode symbol) { - Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PC); + Builder.EmitReloc(symbol, RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I); //auipc reg, off-hi-20bits EmitPC(regDst); //ld reg, off-lo-12bits(reg) From 80a6998e89f5c7cd1b7859d4d47973f88b8b74d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Sowi=C5=84ski?= Date: Wed, 19 Nov 2025 20:16:36 +0100 Subject: [PATCH 33/39] fix musl build Co-authored-by: Adeel Mujahid <3840695+am11@users.noreply.github.com> --- src/coreclr/vm/jitinterface.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 6c1676a703cfe1..4520bcabee77a0 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12053,7 +12053,7 @@ void CEEJitInfo::recordRelocation(void * location, { // Target is out of 32-bit range // If we're fixing up a jump, try jumping through a stub - PCODE jumpStubAddr = NULL; + PCODE jumpStubAddr = (PCODE)NULL; if ((fRelocType == IMAGE_REL_RISCV64_CALL_PLT) && !m_fJumpStubOverflow) { BYTE* loAddr = (BYTE*)location - (1ll << 31) - (1ll << 11); @@ -12072,7 +12072,7 @@ void CEEJitInfo::recordRelocation(void * location, m_reserveForJumpStubs = max((size_t)0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); } - if (jumpStubAddr == NULL) + if (jumpStubAddr == (PCODE)NULL) { // This forces the JIT to retry the method, which allows us to reserve more space for jump stubs and have a higher chance that // we will find space for them. From 062652b50b8439ab6b71c0b9ecfb16c0b16791f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 20 Nov 2025 08:34:20 +0100 Subject: [PATCH 34/39] lower bool --- src/coreclr/inc/utilcode.h | 4 ++-- src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp | 2 +- src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp | 4 ++-- src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h | 4 ++-- src/coreclr/utilcode/util.cpp | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 59c81a258893c4..ee1cb2352d0b5e 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3355,12 +3355,12 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm); //***************************************************************************** // Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype); +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, bool isStype); //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, bool isStype); //***************************************************************************** // Returns whether the offset fits into bl instruction diff --git a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp index 19efee8cf61c6c..d58276757969b4 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp @@ -925,7 +925,7 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b INT64 offset = (INT64)tmp.target; INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); - BOOL isStype = (relocType == IMAGE_REL_RISCV64_PCREL_S); + bool isStype = (relocType == IMAGE_REL_RISCV64_PCREL_S); PutRiscV64AuipcCombo((UINT32*)address, INT64(lo12) + INT64(hi20), isStype); } wasRelocHandled = true; diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index ed14efc75a4bf4..a9867225e512f0 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -466,7 +466,7 @@ void PutArm32MovtConstant(UINT32* p, unsigned con) //***************************************************************************** // Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, bool isStype) { enum { @@ -510,7 +510,7 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype) //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, bool isStype) { INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index abebe60e406a0b..26a59a60981eee 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h @@ -105,8 +105,8 @@ bool Is32BitThumb2Instruction(UINT16* p); UINT32 ExtractArm32MovImm(UINT32 instr); void PutArm32MovtConstant(UINT32* p, unsigned con); -INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype); -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype); +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, bool isStype); +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, bool isStype); template inline constexpr unsigned ArrLen(T (&)[size]) diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index d0b34678c4075a..9180b814c8e996 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2336,7 +2336,7 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38) //***************************************************************************** // Extract the PC-Relative offset from auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, bool isStype) { enum { @@ -2380,7 +2380,7 @@ INT64 GetRiscV64AuipcCombo(UINT32 * pCode, BOOL isStype) //***************************************************************************** // Deposit the PC-Relative offset into auipc + I-type or S-type adder (addi/load/store/jalr) //***************************************************************************** -void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, BOOL isStype) +void PutRiscV64AuipcCombo(UINT32 * pCode, INT64 offset, bool isStype) { INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); From 917fb8d28c2ccc420470f5a874129365d1568cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 20 Nov 2025 14:40:40 +0100 Subject: [PATCH 35/39] Add symbol for PC-relative HI20 relocation and record another relocation for LO12 --- .../Compiler/ObjectWriter/ElfObjectWriter.cs | 40 ++++++++++++++----- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs index 56dfc04411a83f..8adb40bec10943 100644 --- a/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs +++ b/src/coreclr/tools/Common/Compiler/ObjectWriter/ElfObjectWriter.cs @@ -246,9 +246,24 @@ protected internal override unsafe void EmitRelocation( } } + if (relocType is IMAGE_REL_BASED_RISCV64_PCREL_I or IMAGE_REL_BASED_RISCV64_PCREL_S) + { + // Native RISC-V ELF for a PC-relative load/store/addi is 2 instructions and 2 relocations, e.g.: + // + // .Lpcrel_hi: + // auipc reg, pcrel_hi20(symbol) + // ld reg, reg, pcrel_lo12(.Lpcrel_hi) # note that this points at the label for 'auipc' + // + // Add a symbol for the auipc instruction so it can be pointed at by the LO12 relocation + string name = GetRiscV64SymbolNameForPcrelRelocation(sectionIndex, offset); + EmitSymbolDefinition(sectionIndex, name, offset, 2 * 4); + } + base.EmitRelocation(sectionIndex, offset, data, relocType, symbolName, addend); } + private static string GetRiscV64SymbolNameForPcrelRelocation(int sectionIndex, long offset) => $".Lpcrel_hi{sectionIndex}_{offset:x}"; + private protected override void EmitSymbolTable( IDictionary definedSymbols, SortedSet undefinedSymbols) @@ -568,17 +583,20 @@ private void EmitRelocationsRiscV64(int sectionIndex, List r BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); relocationStream.Write(relocationEntry); - // TODO: This is wrong, LO12 needs to point to a label (previous instruction with HI20) - // if (symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I or IMAGE_REL_BASED_RISCV64_PCREL_S) - // { - // type = symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I - // ? R_RISCV_PCREL_LO12_I - // : R_RISCV_PCREL_LO12_S; - // BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)relocationStream.Position); - // BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); - // BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), symbolicRelocation.Addend); - // relocationStream.Write(relocationEntry); - // } + if (symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I or IMAGE_REL_BASED_RISCV64_PCREL_S) + { + // Emit another relocation which points at the previous instruction + string symbolName = GetRiscV64SymbolNameForPcrelRelocation(sectionIndex, symbolicRelocation.Offset); + symbolIndex = _symbolNameToIndex[symbolName]; + type = symbolicRelocation.Type is IMAGE_REL_BASED_RISCV64_PCREL_I + ? R_RISCV_PCREL_LO12_I + : R_RISCV_PCREL_LO12_S; + + BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry, (ulong)symbolicRelocation.Offset + 4); + BinaryPrimitives.WriteUInt64LittleEndian(relocationEntry.Slice(8), ((ulong)symbolIndex << 32) | type); + BinaryPrimitives.WriteInt64LittleEndian(relocationEntry.Slice(16), 0); + relocationStream.Write(relocationEntry); + } } } } From 398e3b9cff496aebd1455b07c686d010097e63fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 20 Nov 2025 14:47:33 +0100 Subject: [PATCH 36/39] FitsInRiscV64 --- src/coreclr/inc/utilcode.h | 2 +- src/coreclr/vm/jitinterface.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index ee1cb2352d0b5e..7b2ca30277133c 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3405,7 +3405,7 @@ inline bool FitsInRel28(INT64 val64) //***************************************************************************** // Returns whether the offset fits into a RISC-V auipc + I-type or S-type instruction combo //***************************************************************************** -inline bool FitsInAuipcCombo(INT64 val64) +inline bool FitsInRiscV64AuipcCombo(INT64 val64) { return (val64 >= -(1ll << 31) - (1ll << 11)) && (val64 < (1ll << 31) - (1ll << 11)); } diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index 4520bcabee77a0..c3d073ed564891 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12049,7 +12049,7 @@ void CEEJitInfo::recordRelocation(void * location, _ASSERTE(addlDelta == 0); delta = (INT64)target - (INT64)location; - if (!FitsInAuipcCombo(delta)) + if (!FitsInRiscV64AuipcCombo(delta)) { // Target is out of 32-bit range // If we're fixing up a jump, try jumping through a stub @@ -12081,7 +12081,7 @@ void CEEJitInfo::recordRelocation(void * location, } delta = (INT64)jumpStubAddr - (INT64)location; - if (!FitsInAuipcCombo(delta)) + if (!FitsInRiscV64AuipcCombo(delta)) { _ASSERTE(!"jump stub was not in expected range"); EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); From c2ab64490f307bf93e28bd734e649e958888b17d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Thu, 20 Nov 2025 15:02:57 +0100 Subject: [PATCH 37/39] Explain where auipc combo bounds come from --- src/coreclr/inc/utilcode.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 7b2ca30277133c..295a8e6087d26a 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3407,6 +3407,11 @@ inline bool FitsInRel28(INT64 val64) //***************************************************************************** inline bool FitsInRiscV64AuipcCombo(INT64 val64) { + // A PC relative load/store/jalr/addi is 2 instructions, e.g.: + // auipc reg, offset_hi20 # range: [0x80000000, 0x7FFFF000] + // ld reg, reg, offset_lo12 # range: [0xFFFFF800, 0x000007FF] + // Both hi20 and lo12 immediates are sign-extended and combined with a 64-bit adder, + // which shifts the total 32-bit range into the negative by half of the 12-bit immediate range. return (val64 >= -(1ll << 31) - (1ll << 11)) && (val64 < (1ll << 31) - (1ll << 11)); } From 8c081a6e8579386a40d9711fb99a8883ce12a623 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 25 Nov 2025 12:41:24 +0100 Subject: [PATCH 38/39] Fix build --- src/coreclr/vm/jitinterface.cpp | 81 ++++++++++++++++----------------- 1 file changed, 40 insertions(+), 41 deletions(-) diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index bad101ff8eeed2..b25c8ce02afcbb 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12038,57 +12038,56 @@ void CEEJitInfo::recordRelocation(void * location, case CorInfoReloc::RISCV64_CALL_PLT: case CorInfoReloc::RISCV64_PCREL_I: case CorInfoReloc::RISCV64_PCREL_S: - { - _ASSERTE(addlDelta == 0); - delta = (INT64)target - (INT64)location; + { + _ASSERTE(addlDelta == 0); + delta = (INT64)target - (INT64)location; - if (!FitsInRiscV64AuipcCombo(delta)) + if (!FitsInRiscV64AuipcCombo(delta)) + { + // Target is out of 32-bit range + // If we're fixing up a jump, try jumping through a stub + PCODE jumpStubAddr = (PCODE)NULL; + if ((fRelocType == CorInfoReloc::RISCV64_CALL_PLT) && !m_fJumpStubOverflow) { - // Target is out of 32-bit range - // If we're fixing up a jump, try jumping through a stub - PCODE jumpStubAddr = (PCODE)NULL; - if ((fRelocType == CorInfoReloc::RISCV64_CALL_PLT) && !m_fJumpStubOverflow) - { - BYTE* loAddr = (BYTE*)location - (1ll << 31) - (1ll << 11); - BYTE* hiAddr = (BYTE*)location + (1ll << 31) - (1ll << 11) - 1; - - // Check for the wrap around cases - if (loAddr > location) - loAddr = (BYTE*)UINT64_MIN; // overflow - if (hiAddr < location) - hiAddr = (BYTE*)UINT64_MAX; // overflow + BYTE* loAddr = (BYTE*)location - (1ll << 31) - (1ll << 11); + BYTE* hiAddr = (BYTE*)location + (1ll << 31) - (1ll << 11) - 1; - PCODE jumpStubAddr = ExecutionManager::jumpStub(m_pMethodBeingCompiled, (PCODE)target, loAddr, hiAddr, nullptr, false); + // Check for the wrap around cases + if (loAddr > location) + loAddr = (BYTE*)UINT64_MIN; // overflow + if (hiAddr < location) + hiAddr = (BYTE*)UINT64_MAX; // overflow - // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it - // to reserve extra memory on retry to increase chances that the retry succeeds. - m_reserveForJumpStubs = max((size_t)0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); - } + PCODE jumpStubAddr = ExecutionManager::jumpStub(m_pMethodBeingCompiled, (PCODE)target, loAddr, hiAddr, nullptr, false); - if (jumpStubAddr == (PCODE)NULL) - { - // This forces the JIT to retry the method, which allows us to reserve more space for jump stubs and have a higher chance that - // we will find space for them. - m_fJumpStubOverflow = TRUE; - break; - } + // Keep track of conservative estimate of how much memory may be needed by jump stubs. We will use it + // to reserve extra memory on retry to increase chances that the retry succeeds. + m_reserveForJumpStubs = max((size_t)0x400, m_reserveForJumpStubs + 2*BACK_TO_BACK_JUMP_ALLOCATE_SIZE); + } - delta = (INT64)jumpStubAddr - (INT64)location; - if (!FitsInRiscV64AuipcCombo(delta)) - { - _ASSERTE(!"jump stub was not in expected range"); - EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); - } + if (jumpStubAddr == (PCODE)NULL) + { + // This forces the JIT to retry the method, which allows us to reserve more space for jump stubs and have a higher chance that + // we will find space for them. + m_fJumpStubOverflow = TRUE; + break; + } - LOG((LF_JIT, LL_INFO100000, "Using JumpStub at" FMT_ADDR "that jumps to" FMT_ADDR "\n", - DBG_ADDR(jumpStubAddr), DBG_ADDR(target))); + delta = (INT64)jumpStubAddr - (INT64)location; + if (!FitsInRiscV64AuipcCombo(delta)) + { + _ASSERTE(!"jump stub was not in expected range"); + EEPOLICY_HANDLE_FATAL_ERROR(COR_E_EXECUTIONENGINE); } - bool isStype = (fRelocType == CorInfoReloc::RISCV64_PCREL_S); - PutRiscV64AuipcCombo((UINT32 *)locationRW, delta, isStype); - LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + %c-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", - (isStype ? 'S' : 'I'), DBG_ADDR(location), DBG_ADDR(target), delta)); + LOG((LF_JIT, LL_INFO100000, "Using JumpStub at" FMT_ADDR "that jumps to" FMT_ADDR "\n", + DBG_ADDR(jumpStubAddr), DBG_ADDR(target))); } + + bool isStype = (fRelocType == CorInfoReloc::RISCV64_PCREL_S); + PutRiscV64AuipcCombo((UINT32 *)locationRW, delta, isStype); + LOG((LF_JIT, LL_INFO100000, "Fixed up an auipc + %c-type relocation at" FMT_ADDR "to" FMT_ADDR ", delta is 0x%08x\n", + (isStype ? 'S' : 'I'), DBG_ADDR(location), DBG_ADDR(target), delta)); break; } #endif // TARGET_RISCV64 From e5ce6314974f832c2f9cf6afe45137e28aa6be5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomek=20Sowi=C5=84ski?= Date: Tue, 25 Nov 2025 12:51:45 +0100 Subject: [PATCH 39/39] Fix types and format --- src/coreclr/jit/emitriscv64.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 9a2b4704fba643..1f1bff5ed0531b 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1235,7 +1235,7 @@ void emitter::emitIns_R_AI(instruction ins, ssize_t addr DEBUGARG(size_t targetHandle) DEBUGARG(GenTreeFlags gtFlags)) { assert(EA_IS_RELOC(attr)); - assert(emitComp->opts.compReloc || (IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint((void*)addr))); + assert(emitComp->opts.compReloc || (CorInfoReloc::RELATIVE32 == emitComp->eeGetRelocTypeHint((void*)addr))); assert(ins == INS_addi || emitInsIsLoadOrStore(ins)); assert(emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == REG_ZERO) || (dataReg == addrReg)); assert(isGeneralRegister(addrReg)); @@ -2877,7 +2877,7 @@ BYTE* emitter::emitOutputInstr_OptsReloc(BYTE* dst, const instrDesc* id, instruc dst += emitInsIsStore(*ins) ? emitOutput_STypeInstr(dst, *ins, addrReg, dataReg, 0) : emitOutput_ITypeInstr(dst, *ins, dataReg, addrReg, 0); - uint16_t type = emitInsIsStore(*ins) ? CorInfoReloc::RISCV64_PCREL_S : CorInfoReloc::RISCV64_PCREL_I; + CorInfoReloc type = emitInsIsStore(*ins) ? CorInfoReloc::RISCV64_PCREL_S : CorInfoReloc::RISCV64_PCREL_I; emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, type); return dst; } @@ -5145,8 +5145,8 @@ unsigned emitter::get_curTotalCodeSize() // A struct containing the current instruction execution characteristics bool emitter::IsAddressInRange(void* addr) { - return emitComp->opts.compReloc || - (INDEBUG(emitComp->opts.compEnablePCRelAddr&&)(IMAGE_REL_BASED_REL32 == emitComp->eeGetRelocTypeHint(addr))); + return emitComp->opts.compReloc || (INDEBUG(emitComp->opts.compEnablePCRelAddr&&)( + CorInfoReloc::RELATIVE32 == emitComp->eeGetRelocTypeHint(addr))); } #if defined(DEBUG) || defined(LATE_DISASM)