Skip to content
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
77fdbb8
Synthesize handles for function addresses with auipc+ld
tomeksowi Aug 18, 2025
60ec4a9
Use auipc+ld/addi for addresses also in non-compReloc
tomeksowi Aug 29, 2025
848ac99
Always record relocation
tomeksowi Aug 29, 2025
65014e2
clear gc info when address is built in register
tomeksowi Sep 1, 2025
b43e790
fix reloc type
tomeksowi Sep 1, 2025
bd0e79c
cleanup: emitAlllocInstr sets idGCref and idOpSize based on attr
tomeksowi Sep 1, 2025
1f016da
remove redundant assert
tomeksowi Sep 1, 2025
80bca29
remove unused debug fields
tomeksowi Sep 1, 2025
c8b605d
Merge branch 'main' into pc-rel-pointers
tomeksowi Oct 23, 2025
289740a
Estimate if data is in range, handle also stores
tomeksowi Oct 24, 2025
41cbc5a
Handle floating data
tomeksowi Oct 24, 2025
5f86c9d
spmi
tomeksowi Oct 24, 2025
b857350
fix comment
tomeksowi Oct 24, 2025
9fa6ef2
fix nullcheck
tomeksowi Oct 27, 2025
4277e8b
AddrNeedsReloc more appropriate for addresses
tomeksowi Oct 27, 2025
7d04735
add assert
tomeksowi Oct 27, 2025
12596e1
use relative pointer synthesizing where possible in genSetRegToConst
tomeksowi Oct 27, 2025
33f0b88
Revert "use relative pointer synthesizing where possible in genSetReg…
tomeksowi Oct 27, 2025
13b6421
Record relocations for loads for indirect calls only when the load ad…
tomeksowi Oct 31, 2025
15fd38b
Merge branch 'main' into pc-rel-pointers
tomeksowi Nov 7, 2025
7f1f98d
Check if direct calls are in range and synthesize the absolute addres…
tomeksowi Nov 12, 2025
809cedf
Remove hardcoded branch offset from genJumpToThrowHlpBlk_la
tomeksowi Nov 12, 2025
f8b8083
asserts
tomeksowi Nov 13, 2025
025883d
Fix genFnEpilog emitting always a relocatable load / func pointer
tomeksowi Nov 13, 2025
5f35980
remove assert
tomeksowi Nov 13, 2025
7e6357e
Fix genProfiling(Enter|Leave)Callback emitting always a relocatable l…
tomeksowi Nov 13, 2025
ab556f8
Centralize checking whether direct call is in range
tomeksowi Nov 13, 2025
e38cf3f
jump stubs
tomeksowi Nov 17, 2025
4c7f286
FitsIn
tomeksowi Nov 19, 2025
315c807
Add different relocation types to avoid telling jalr by disasm
tomeksowi Nov 19, 2025
5a5881e
Tell S-type by relocation type instead of disassembling the fixup site
tomeksowi Nov 19, 2025
4b8d487
comment
tomeksowi Nov 19, 2025
2874ec4
Refactor fix-up to use same code structure as arm64
tomeksowi Nov 19, 2025
455bcb7
Merge branch 'main' into pc-rel-pointers (resolve conflicts)
tomeksowi Nov 19, 2025
5b5ce0a
Use PCREL_I for ld
tomeksowi Nov 19, 2025
80a6998
fix musl build
tomeksowi Nov 19, 2025
062652b
lower bool
tomeksowi Nov 20, 2025
917fb8d
Add symbol for PC-relative HI20 relocation and record another relocat…
tomeksowi Nov 20, 2025
398e3b9
FitsInRiscV64
tomeksowi Nov 20, 2025
c2ab644
Explain where auipc combo bounds come from
tomeksowi Nov 20, 2025
abacec7
Merge branch 'main' into pc-rel-pointers
tomeksowi Nov 25, 2025
8c081a6
Fix build
tomeksowi Nov 25, 2025
e5ce631
Fix types and format
tomeksowi Nov 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion src/coreclr/inc/corinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -3436,7 +3436,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
Expand Down
16 changes: 12 additions & 4 deletions src/coreclr/inc/utilcode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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/ld/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, BOOL isStype);

//*****************************************************************************
// 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, INT64 offset, BOOL isStype);

//*****************************************************************************
// Returns whether the offset fits into bl instruction
Expand Down Expand Up @@ -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
Expand Down
209 changes: 47 additions & 162 deletions src/coreclr/jit/codegenriscv64.cpp

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2954,7 +2954,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
Expand Down
2 changes: 1 addition & 1 deletion src/coreclr/jit/compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -10156,7 +10156,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
Expand Down
212 changes: 115 additions & 97 deletions src/coreclr/jit/emitriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1211,19 +1211,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.
Expand All @@ -1243,41 +1230,26 @@ 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)); // 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
// auipc reg, off-hi-20bits
// ld reg, reg, off-lo-12bits
assert(EA_IS_RELOC(attr));
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(isGeneralRegister(addrReg));
// 2-ins:
// 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);

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);

Expand Down Expand Up @@ -1353,19 +1325,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))
Expand All @@ -1377,6 +1336,43 @@ 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 (IsAddressInRange(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(instruction ins, BasicBlock* dst)
{
assert(emitIsUncondJump(ins));
Expand Down Expand Up @@ -1749,7 +1745,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);
Expand Down Expand Up @@ -1783,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);
Expand Down Expand Up @@ -1816,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())
{
Expand Down Expand Up @@ -1885,40 +1891,32 @@ 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 =
(void*)(((size_t)params.addr) + (params.isJump ? 0 : 1)); // NOTE: low-bit0 is used for jalr ra/r0,rd,0
Expand Down Expand Up @@ -2022,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.
Expand Down Expand Up @@ -2867,25 +2865,20 @@ 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();

dst += emitOutput_UTypeInstr(dst, INS_auipc, reg1, 0);
BYTE* const dstBase = dst;

if (id->idIsCnsReloc())
{
*ins = INS_addi;
}
else
{
assert(id->idIsDspReloc());
*ins = INS_ld;
}
regNumber dataReg = id->idReg1();
regNumber addrReg = id->idReg2();

dst += emitOutput_ITypeInstr(dst, *ins, reg1, reg1, 0);

emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, IMAGE_REL_RISCV64_PC);
*ins = id->idIns();
assert(*ins == INS_addi || emitInsIsLoadOrStore(*ins));
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);

uint16_t type = emitInsIsStore(*ins) ? IMAGE_REL_RISCV64_PCREL_S : IMAGE_REL_RISCV64_PCREL_I;
emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, type);
return dst;
}

Expand Down Expand Up @@ -4659,11 +4652,21 @@ 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;
bool needTemp = emitInsIsStore(ins) || isFloatReg(dataReg) || (dataReg == REG_ZERO);
regNumber addrReg = needTemp ? codeGen->rsGetRsvdReg() : dataReg;
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());
}
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))
{
Expand Down Expand Up @@ -5131,6 +5134,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)

//----------------------------------------------------------------------------------------
Expand Down
Loading
Loading