This commit is contained in:
Tomasz Sowiński 2025-07-30 15:56:18 +02:00 committed by GitHub
commit 5bc9996bd4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 177 additions and 165 deletions

View File

@ -3348,6 +3348,16 @@ 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)
//*****************************************************************************
INT64 GetRiscV64AuipcItype(UINT32 * pCode);
//*****************************************************************************
// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr)
//*****************************************************************************
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset);
//*****************************************************************************
// Returns whether the offset fits into bl instruction
//*****************************************************************************

View File

@ -6511,12 +6511,7 @@ void CodeGen::genJumpToThrowHlpBlk_la(
params.addr = helperFunction.addr;
params.callType = EC_FUNC_TOKEN;
ssize_t imm = 9 << 2;
if (compiler->opts.compReloc)
{
imm = 3 << 2;
}
ssize_t imm = 3 * sizeof(emitter::code_t);
emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, reg2, imm);
}
else

View File

@ -1696,28 +1696,15 @@ void emitter::emitIns_Call(const EmitCallParams& params)
id->idSetIsNoGC(params.isJump || params.noSafePoint || emitNoGChelper(params.methHnd));
/* Set the instruction - special case jumping a function */
instruction ins;
ins = INS_jalr; // jalr
id->idIns(ins);
id->idIns(INS_jalr);
id->idInsOpt(INS_OPTS_C);
// TODO-RISCV64: maybe optimize.
// INS_OPTS_C: placeholders. 1/2/4-ins:
// INS_OPTS_C: placeholders. 1/2-ins:
// if (callType == EC_INDIR_R)
// jalr REG_R0/REG_RA, ireg, offset <---- 1-ins
// jalr zero/ra, ireg, offset
// else if (callType == EC_FUNC_TOKEN || callType == EC_FUNC_ADDR)
// if reloc:
// //pc + offset_38bits # only when reloc.
// auipc t2, addr-hi20
// jalr r0/1, t2, addr-lo12
//
// else:
// lui t2, dst_offset_lo32-hi
// ori t2, t2, dst_offset_lo32-lo
// lui t2, dst_offset_hi32-lo
// jalr REG_R0/REG_RA, t2, 0
// 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)
@ -1750,16 +1737,8 @@ void emitter::emitIns_Call(const EmitCallParams& params)
void* addr =
(void*)(((size_t)params.addr) + (params.isJump ? 0 : 1)); // NOTE: low-bit0 is used for jalr ra/r0,rd,0
id->idAddr()->iiaAddr = (BYTE*)addr;
if (emitComp->opts.compReloc)
{
id->idSetIsDspReloc();
id->idCodeSize(8);
}
else
{
id->idCodeSize(32);
}
id->idCodeSize(2 * sizeof(code_t));
id->idSetIsDspReloc();
}
#ifdef DEBUG
@ -1794,11 +1773,10 @@ void emitter::emitIns_Call(const EmitCallParams& params)
* Output a call instruction.
*/
unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, code_t code)
unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id)
{
unsigned char callInstrSize = sizeof(code_t); // 4 bytes
regMaskTP gcrefRegs;
regMaskTP byrefRegs;
regMaskTP gcrefRegs;
regMaskTP byrefRegs;
VARSET_TP GCvars(VarSetOps::UninitVal());
@ -1837,131 +1815,30 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, c
#endif // DEBUG
assert(id->idIns() == INS_jalr);
BYTE* origDst = dst;
if (id->idIsCallRegPtr())
{ // EC_INDIR_R
ssize_t offset = id->idSmallCns();
assert(isValidSimm12(offset));
code = emitInsCode(id->idIns());
code |= (code_t)id->idReg4() << 7;
code |= (code_t)id->idReg3() << 15;
code |= (code_t)offset << 20;
emitOutput_Instr(dst, code);
}
else if (id->idIsReloc())
{
// pc + offset_32bits
//
// auipc t2, addr-hi20
// jalr r0/1,t2,addr-lo12
emitOutput_Instr(dst, 0x00000397);
size_t addr = (size_t)(id->idAddr()->iiaAddr); // get addr.
int reg2 = (int)(addr & 1);
addr -= reg2;
if (!emitComp->opts.compReloc)
{
assert(isValidSimm32(addr - (ssize_t)dst));
}
assert((addr & 1) == 0);
dst += 4;
emitGCregDeadUpd(REG_DEFAULT_HELPER_CALL_TARGET, dst);
#ifdef DEBUG
code = emitInsCode(INS_auipc);
assert((code | (REG_DEFAULT_HELPER_CALL_TARGET << 7)) == 0x00000397);
assert((int)REG_DEFAULT_HELPER_CALL_TARGET == 7);
code = emitInsCode(INS_jalr);
assert(code == 0x00000067);
#endif
emitOutput_Instr(dst, 0x00000067 | (REG_DEFAULT_HELPER_CALL_TARGET << 15) | reg2 << 7);
emitRecordRelocation(dst - 4, (BYTE*)addr, IMAGE_REL_RISCV64_PC);
dst += emitOutput_ITypeInstr(dst, INS_jalr, id->idReg4(), id->idReg3(), TrimSignedToImm12(offset));
}
else
{
// lui t2, dst_offset_hi32-hi
// addi t2, t2, dst_offset_hi32-lo
// slli t2, t2, 11
// addi t2, t2, dst_offset_low32-hi
// slli t2, t2, 11
// addi t2, t2, dst_offset_low32-md
// slli t2, t2, 10
// jalr t2
size_t addr = (size_t)(id->idAddr()->iiaAddr); // get addr.
ssize_t imm = (ssize_t)(id->idAddr()->iiaAddr);
assert((uint64_t)(imm >> 32) <= 0x7fff); // RISC-V Linux Kernel SV48
regNumber linkReg = (regNumber)(addr & 1);
assert(linkReg == REG_ZERO || linkReg == REG_RA);
addr -= linkReg;
assert((addr & 1) == 0);
regNumber tempReg = (linkReg == REG_ZERO) ? REG_DEFAULT_HELPER_CALL_TARGET : REG_RA;
int reg2 = (int)(imm & 1);
imm -= reg2;
dst += emitOutput_UTypeInstr(dst, INS_auipc, tempReg, 0);
emitGCregDeadUpd(tempReg, dst);
dst += emitOutput_ITypeInstr(dst, INS_jalr, linkReg, tempReg, 0);
UINT32 high = imm >> 32;
code = emitInsCode(INS_lui);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= ((code_t)((high + 0x800) >> 12) & 0xfffff) << 12;
emitOutput_Instr(dst, code);
dst += 4;
emitGCregDeadUpd(REG_DEFAULT_HELPER_CALL_TARGET, dst);
code = emitInsCode(INS_addi);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= (code_t)(high & 0xfff) << 20;
emitOutput_Instr(dst, code);
dst += 4;
code = emitInsCode(INS_slli);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= (code_t)(11 << 20);
emitOutput_Instr(dst, code);
dst += 4;
UINT32 low = imm & 0xffffffff;
code = emitInsCode(INS_addi);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= ((low >> 21) & 0x7ff) << 20;
emitOutput_Instr(dst, code);
dst += 4;
code = emitInsCode(INS_slli);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= (code_t)(11 << 20);
emitOutput_Instr(dst, code);
dst += 4;
code = emitInsCode(INS_addi);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= ((low >> 10) & 0x7ff) << 20;
emitOutput_Instr(dst, code);
dst += 4;
code = emitInsCode(INS_slli);
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= (code_t)(10 << 20);
emitOutput_Instr(dst, code);
dst += 4;
code = emitInsCode(INS_jalr);
code |= (code_t)reg2 << 7;
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
code |= (low & 0x3ff) << 20;
// the offset default is 0;
emitOutput_Instr(dst, code);
assert(id->idIsDspReloc());
emitRecordRelocation(origDst, (BYTE*)addr, IMAGE_REL_RISCV64_PC);
}
dst += 4;
// If the method returns a GC ref, mark INTRET (A0) appropriately.
if (id->idGCref() == GCT_GCREF)
{
@ -2008,25 +1885,17 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, c
// So we're not really doing a "stack pop" here (note that "args" is 0), but we use this mechanism
// to record the call for GC info purposes. (It might be best to use an alternate call,
// and protect "emitStackPop" under the EMIT_TRACK_STACK_DEPTH preprocessor variable.)
emitStackPop(dst, /*isCall*/ true, callInstrSize, /*args*/ 0);
emitStackPop(dst, /*isCall*/ true, sizeof(code_t), /*args*/ 0);
// Do we need to record a call location for GC purposes?
//
if (!emitFullGCinfo)
{
emitRecordGCcall(dst, callInstrSize);
emitRecordGCcall(dst, sizeof(code_t));
}
}
if (id->idIsCallRegPtr())
{
callInstrSize = 1 << 2;
}
else
{
callInstrSize = id->idIsReloc() ? (2 << 2) : (8 << 2); // INS_OPTS_C: 2/9-ins.
}
return callInstrSize;
return dst - origDst;
}
void emitter::emitJumpDistBind()
@ -3257,7 +3126,7 @@ BYTE* emitter::emitOutputInstr_OptsC(BYTE* dst, instrDesc* id, const insGroup* i
assert(!id->idIsLargeCns());
*size = sizeof(instrDesc);
}
dst += emitOutputCall(ig, dst, id, 0);
dst += emitOutputCall(ig, dst, id);
return dst;
}

View File

@ -331,7 +331,7 @@ void emitIns_R_AI(instruction ins,
regNumber reg,
ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, code_t code);
unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id);
unsigned get_curTotalCodeSize(); // bytes of code

View File

@ -906,6 +906,31 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b
}
}
if (targetArch == SPMI_TARGET_ARCHITECTURE_RISCV64)
{
DWORDLONG fixupLocation = tmp.location;
DWORDLONG address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
switch (relocType)
{
case IMAGE_REL_RISCV64_PC:
{
if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
{
// 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);
}
wasRelocHandled = true;
}
break;
default:
break;
}
}
if (IsSpmiTarget64Bit())
{
if (!wasRelocHandled && (relocType == IMAGE_REL_BASED_DIR64))

View File

@ -477,6 +477,54 @@ void PutArm32MovtConstant(UINT32* p, unsigned con)
*((UINT16*)p + 1) = (UINT16)instr;
}
//*****************************************************************************
// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr)
//*****************************************************************************
INT64 GetRiscV64AuipcItype(UINT32 * pCode)
{
enum
{
OpcodeAuipc = 0x00000017,
OpcodeAddi = 0x00000013,
OpcodeLd = 0x00003003,
OpcodeJalr = 0x00000067,
OpcodeUTypeMask = 0x0000007F,
OpcodeITypeMask = 0x0000307F,
};
UINT32 auipc = pCode[0];
_ASSERTE((auipc & OpcodeUTypeMask) == 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);
int iTypeRegSrc = (iType >> 15) & 0x1F;
_ASSERTE(auipcRegDest == iTypeRegSrc);
INT64 lo12 = INT32(iType) >> 20;
return hi20 + lo12;
}
//*****************************************************************************
// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr)
//*****************************************************************************
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset)
{
INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended
INT32 hi20 = INT32(offset - lo12);
_ASSERTE(INT64(hi20) + INT64(lo12) == offset);
_ASSERTE(GetRiscV64AuipcItype(pCode) == 0);
pCode[0] |= hi20;
pCode[1] |= lo12 << 20;
_ASSERTE(GetRiscV64AuipcItype(pCode) == offset);
}
template<typename TPrint>
static std::string getFromPrinter(TPrint print)
{

View File

@ -97,6 +97,9 @@ bool Is32BitThumb2Instruction(UINT16* p);
UINT32 ExtractArm32MovImm(UINT32 instr);
void PutArm32MovtConstant(UINT32* p, unsigned con);
INT64 GetRiscV64AuipcItype(UINT32 * pCode);
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset);
template <typename T, int size>
inline constexpr unsigned ArrLen(T (&)[size])
{

View File

@ -2332,6 +2332,55 @@ void PutLoongArch64JIR(UINT32 * pCode, INT64 imm38)
_ASSERTE(GetLoongArch64JIR(pCode) == imm38);
}
//*****************************************************************************
// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr)
//*****************************************************************************
INT64 GetRiscV64AuipcItype(UINT32 * pCode)
{
enum
{
OpcodeAuipc = 0x00000017,
OpcodeAddi = 0x00000013,
OpcodeLd = 0x00003003,
OpcodeJalr = 0x00000067,
OpcodeUTypeMask = 0x0000007F,
OpcodeITypeMask = 0x0000307F,
};
UINT32 auipc = pCode[0];
_ASSERTE((auipc & OpcodeUTypeMask) == 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);
int iTypeRegSrc = (iType >> 15) & 0x1F;
_ASSERTE(auipcRegDest == iTypeRegSrc);
INT64 lo12 = INT32(iType) >> 20;
return hi20 + lo12;
}
//*****************************************************************************
// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr)
//*****************************************************************************
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset)
{
INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended
INT32 hi20 = INT32(offset - lo12);
_ASSERTE(INT64(hi20) + INT64(lo12) == offset);
_ASSERTE(GetRiscV64AuipcItype(pCode) == 0);
pCode[0] |= hi20;
pCode[1] |= lo12 << 20;
_ASSERTE(GetRiscV64AuipcItype(pCode) == offset);
}
//======================================================================
// This function returns true, if it can determine that the instruction pointer
// refers to a code address that belongs in the range of the given image.

View File

@ -11930,6 +11930,19 @@ void CEEJitInfo::recordRelocation(void * location,
#endif // TARGET_LOONGARCH64
#ifdef TARGET_RISCV64
case IMAGE_REL_RISCV64_PC:
{
_ASSERTE(addlDelta == 0);
INT64 offset = (INT64)target - (INT64)location;
PutRiscV64AuipcItype((UINT32 *)locationRW, offset);
}
break;
#endif // TARGET_RISCV64
default:
_ASSERTE(!"Unknown reloc type");
break;