Skip to content

Commit 35ffd43

Browse files
authored
[RISC-V] Use auipc for code addresses in INS_OPTS_C (#117196)
* auipc in EmitOutputCall also non-reloc * Always record relocation for auipc+jalr like on arm64 * Stop squirelling link register in lowest bit of the call address * Handle auipc+I-type reloc in superpmi
1 parent cd63792 commit 35ffd43

File tree

9 files changed

+176
-164
lines changed

9 files changed

+176
-164
lines changed

src/coreclr/inc/utilcode.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3348,6 +3348,16 @@ void PutLoongArch64PC12(UINT32 * pCode, INT64 imm);
33483348
//*****************************************************************************
33493349
void PutLoongArch64JIR(UINT32 * pCode, INT64 imm);
33503350

3351+
//*****************************************************************************
3352+
// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr)
3353+
//*****************************************************************************
3354+
INT64 GetRiscV64AuipcItype(UINT32 * pCode);
3355+
3356+
//*****************************************************************************
3357+
// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr)
3358+
//*****************************************************************************
3359+
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset);
3360+
33513361
//*****************************************************************************
33523362
// Returns whether the offset fits into bl instruction
33533363
//*****************************************************************************

src/coreclr/jit/codegenriscv64.cpp

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6520,12 +6520,7 @@ void CodeGen::genJumpToThrowHlpBlk_la(
65206520
params.addr = helperFunction.addr;
65216521
params.callType = EC_FUNC_TOKEN;
65226522

6523-
ssize_t imm = 9 << 2;
6524-
if (compiler->opts.compReloc)
6525-
{
6526-
imm = 3 << 2;
6527-
}
6528-
6523+
ssize_t imm = 3 * sizeof(emitter::code_t);
65296524
emit->emitIns_R_R_I(ins, EA_PTRSIZE, reg1, reg2, imm);
65306525
}
65316526
else

src/coreclr/jit/emitriscv64.cpp

Lines changed: 26 additions & 157 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,28 +1696,15 @@ void emitter::emitIns_Call(const EmitCallParams& params)
16961696
id->idSetIsNoGC(params.isJump || params.noSafePoint || emitNoGChelper(params.methHnd));
16971697

16981698
/* Set the instruction - special case jumping a function */
1699-
instruction ins;
1700-
1701-
ins = INS_jalr; // jalr
1702-
id->idIns(ins);
1699+
id->idIns(INS_jalr);
17031700

17041701
id->idInsOpt(INS_OPTS_C);
1705-
// TODO-RISCV64: maybe optimize.
1706-
1707-
// INS_OPTS_C: placeholders. 1/2/4-ins:
1702+
// INS_OPTS_C: placeholders. 1/2-ins:
17081703
// if (callType == EC_INDIR_R)
1709-
// jalr REG_R0/REG_RA, ireg, offset <---- 1-ins
1704+
// jalr zero/ra, ireg, offset
17101705
// else if (callType == EC_FUNC_TOKEN || callType == EC_FUNC_ADDR)
1711-
// if reloc:
1712-
// //pc + offset_38bits # only when reloc.
1713-
// auipc t2, addr-hi20
1714-
// jalr r0/1, t2, addr-lo12
1715-
//
1716-
// else:
1717-
// lui t2, dst_offset_lo32-hi
1718-
// ori t2, t2, dst_offset_lo32-lo
1719-
// lui t2, dst_offset_hi32-lo
1720-
// jalr REG_R0/REG_RA, t2, 0
1706+
// auipc t2/ra, offset-hi20
1707+
// jalr zero/ra, t2/ra, offset-lo12
17211708

17221709
/* Record the address: method, indirection, or funcptr */
17231710
if (params.callType == EC_INDIR_R)
@@ -1750,16 +1737,8 @@ void emitter::emitIns_Call(const EmitCallParams& params)
17501737
void* addr =
17511738
(void*)(((size_t)params.addr) + (params.isJump ? 0 : 1)); // NOTE: low-bit0 is used for jalr ra/r0,rd,0
17521739
id->idAddr()->iiaAddr = (BYTE*)addr;
1753-
1754-
if (emitComp->opts.compReloc)
1755-
{
1756-
id->idSetIsDspReloc();
1757-
id->idCodeSize(8);
1758-
}
1759-
else
1760-
{
1761-
id->idCodeSize(32);
1762-
}
1740+
id->idCodeSize(2 * sizeof(code_t));
1741+
id->idSetIsDspReloc();
17631742
}
17641743

17651744
#ifdef DEBUG
@@ -1794,11 +1773,10 @@ void emitter::emitIns_Call(const EmitCallParams& params)
17941773
* Output a call instruction.
17951774
*/
17961775

1797-
unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, code_t code)
1776+
unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id)
17981777
{
1799-
unsigned char callInstrSize = sizeof(code_t); // 4 bytes
1800-
regMaskTP gcrefRegs;
1801-
regMaskTP byrefRegs;
1778+
regMaskTP gcrefRegs;
1779+
regMaskTP byrefRegs;
18021780

18031781
VARSET_TP GCvars(VarSetOps::UninitVal());
18041782

@@ -1837,130 +1815,29 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, c
18371815
#endif // DEBUG
18381816

18391817
assert(id->idIns() == INS_jalr);
1818+
BYTE* origDst = dst;
18401819
if (id->idIsCallRegPtr())
18411820
{ // EC_INDIR_R
18421821
ssize_t offset = id->idSmallCns();
1843-
assert(isValidSimm12(offset));
1844-
code = emitInsCode(id->idIns());
1845-
code |= (code_t)id->idReg4() << 7;
1846-
code |= (code_t)id->idReg3() << 15;
1847-
code |= (code_t)offset << 20;
1848-
emitOutput_Instr(dst, code);
1822+
dst += emitOutput_ITypeInstr(dst, INS_jalr, id->idReg4(), id->idReg3(), TrimSignedToImm12(offset));
18491823
}
1850-
else if (id->idIsReloc())
1824+
else
18511825
{
1852-
// pc + offset_32bits
1853-
//
1854-
// auipc t2, addr-hi20
1855-
// jalr r0/1,t2,addr-lo12
1856-
1857-
emitOutput_Instr(dst, 0x00000397);
1858-
18591826
size_t addr = (size_t)(id->idAddr()->iiaAddr); // get addr.
18601827

1861-
int reg2 = (int)(addr & 1);
1862-
addr -= reg2;
1863-
1864-
if (!emitComp->opts.compReloc)
1865-
{
1866-
assert(isValidSimm32(addr - (ssize_t)dst));
1867-
}
1868-
1828+
regNumber linkReg = (regNumber)(addr & 1);
1829+
assert(linkReg == REG_ZERO || linkReg == REG_RA);
1830+
addr -= linkReg;
18691831
assert((addr & 1) == 0);
1832+
regNumber tempReg = (linkReg == REG_ZERO) ? REG_DEFAULT_HELPER_CALL_TARGET : REG_RA;
18701833

1871-
dst += 4;
1872-
emitGCregDeadUpd(REG_DEFAULT_HELPER_CALL_TARGET, dst);
1873-
1874-
#ifdef DEBUG
1875-
code = emitInsCode(INS_auipc);
1876-
assert((code | (REG_DEFAULT_HELPER_CALL_TARGET << 7)) == 0x00000397);
1877-
assert((int)REG_DEFAULT_HELPER_CALL_TARGET == 7);
1878-
code = emitInsCode(INS_jalr);
1879-
assert(code == 0x00000067);
1880-
#endif
1881-
emitOutput_Instr(dst, 0x00000067 | (REG_DEFAULT_HELPER_CALL_TARGET << 15) | reg2 << 7);
1834+
dst += emitOutput_UTypeInstr(dst, INS_auipc, tempReg, 0);
1835+
emitGCregDeadUpd(tempReg, dst);
1836+
dst += emitOutput_ITypeInstr(dst, INS_jalr, linkReg, tempReg, 0);
18821837

1883-
emitRecordRelocation(dst - 4, (BYTE*)addr, IMAGE_REL_RISCV64_PC);
1838+
assert(id->idIsDspReloc());
1839+
emitRecordRelocation(origDst, (BYTE*)addr, IMAGE_REL_RISCV64_PC);
18841840
}
1885-
else
1886-
{
1887-
// lui t2, dst_offset_hi32-hi
1888-
// addi t2, t2, dst_offset_hi32-lo
1889-
// slli t2, t2, 11
1890-
// addi t2, t2, dst_offset_low32-hi
1891-
// slli t2, t2, 11
1892-
// addi t2, t2, dst_offset_low32-md
1893-
// slli t2, t2, 10
1894-
// jalr t2
1895-
1896-
ssize_t imm = (ssize_t)(id->idAddr()->iiaAddr);
1897-
assert((uint64_t)(imm >> 32) <= 0x7fff); // RISC-V Linux Kernel SV48
1898-
1899-
int reg2 = (int)(imm & 1);
1900-
imm -= reg2;
1901-
1902-
UINT32 high = imm >> 32;
1903-
code = emitInsCode(INS_lui);
1904-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1905-
code |= ((code_t)((high + 0x800) >> 12) & 0xfffff) << 12;
1906-
emitOutput_Instr(dst, code);
1907-
dst += 4;
1908-
1909-
emitGCregDeadUpd(REG_DEFAULT_HELPER_CALL_TARGET, dst);
1910-
1911-
code = emitInsCode(INS_addi);
1912-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1913-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1914-
code |= (code_t)(high & 0xfff) << 20;
1915-
emitOutput_Instr(dst, code);
1916-
dst += 4;
1917-
1918-
code = emitInsCode(INS_slli);
1919-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1920-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1921-
code |= (code_t)(11 << 20);
1922-
emitOutput_Instr(dst, code);
1923-
dst += 4;
1924-
1925-
UINT32 low = imm & 0xffffffff;
1926-
1927-
code = emitInsCode(INS_addi);
1928-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1929-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1930-
code |= ((low >> 21) & 0x7ff) << 20;
1931-
emitOutput_Instr(dst, code);
1932-
dst += 4;
1933-
1934-
code = emitInsCode(INS_slli);
1935-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1936-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1937-
code |= (code_t)(11 << 20);
1938-
emitOutput_Instr(dst, code);
1939-
dst += 4;
1940-
1941-
code = emitInsCode(INS_addi);
1942-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1943-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1944-
code |= ((low >> 10) & 0x7ff) << 20;
1945-
emitOutput_Instr(dst, code);
1946-
dst += 4;
1947-
1948-
code = emitInsCode(INS_slli);
1949-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 7;
1950-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1951-
code |= (code_t)(10 << 20);
1952-
emitOutput_Instr(dst, code);
1953-
dst += 4;
1954-
1955-
code = emitInsCode(INS_jalr);
1956-
code |= (code_t)reg2 << 7;
1957-
code |= (code_t)REG_DEFAULT_HELPER_CALL_TARGET << 15;
1958-
code |= (low & 0x3ff) << 20;
1959-
// the offset default is 0;
1960-
emitOutput_Instr(dst, code);
1961-
}
1962-
1963-
dst += 4;
19641841

19651842
// If the method returns a GC ref, mark INTRET (A0) appropriately.
19661843
if (id->idGCref() == GCT_GCREF)
@@ -2008,25 +1885,17 @@ unsigned emitter::emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, c
20081885
// So we're not really doing a "stack pop" here (note that "args" is 0), but we use this mechanism
20091886
// to record the call for GC info purposes. (It might be best to use an alternate call,
20101887
// and protect "emitStackPop" under the EMIT_TRACK_STACK_DEPTH preprocessor variable.)
2011-
emitStackPop(dst, /*isCall*/ true, callInstrSize, /*args*/ 0);
1888+
emitStackPop(dst, /*isCall*/ true, sizeof(code_t), /*args*/ 0);
20121889

20131890
// Do we need to record a call location for GC purposes?
20141891
//
20151892
if (!emitFullGCinfo)
20161893
{
2017-
emitRecordGCcall(dst, callInstrSize);
1894+
emitRecordGCcall(dst, sizeof(code_t));
20181895
}
20191896
}
2020-
if (id->idIsCallRegPtr())
2021-
{
2022-
callInstrSize = 1 << 2;
2023-
}
2024-
else
2025-
{
2026-
callInstrSize = id->idIsReloc() ? (2 << 2) : (8 << 2); // INS_OPTS_C: 2/9-ins.
2027-
}
20281897

2029-
return callInstrSize;
1898+
return dst - origDst;
20301899
}
20311900

20321901
void emitter::emitJumpDistBind()
@@ -3257,7 +3126,7 @@ BYTE* emitter::emitOutputInstr_OptsC(BYTE* dst, instrDesc* id, const insGroup* i
32573126
assert(!id->idIsLargeCns());
32583127
*size = sizeof(instrDesc);
32593128
}
3260-
dst += emitOutputCall(ig, dst, id, 0);
3129+
dst += emitOutputCall(ig, dst, id);
32613130
return dst;
32623131
}
32633132

src/coreclr/jit/emitriscv64.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ void emitIns_R_AI(instruction ins,
331331
regNumber reg,
332332
ssize_t disp DEBUGARG(size_t targetHandle = 0) DEBUGARG(GenTreeFlags gtFlags = GTF_EMPTY));
333333

334-
unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id, code_t code);
334+
unsigned emitOutputCall(const insGroup* ig, BYTE* dst, instrDesc* id);
335335

336336
unsigned get_curTotalCodeSize(); // bytes of code
337337

src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -906,6 +906,31 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b
906906
}
907907
}
908908

909+
if (targetArch == SPMI_TARGET_ARCHITECTURE_RISCV64)
910+
{
911+
DWORDLONG fixupLocation = tmp.location;
912+
DWORDLONG address = section_begin + (size_t)fixupLocation - (size_t)originalAddr;
913+
914+
switch (relocType)
915+
{
916+
case IMAGE_REL_RISCV64_PC:
917+
{
918+
if ((section_begin <= address) && (address < section_end)) // A reloc for our section?
919+
{
920+
// Similar to x64's IMAGE_REL_BASED_REL32 handling we
921+
// will handle this by also hardcoding the bottom bits
922+
// of the target into the instruction.
923+
PutRiscV64AuipcItype((UINT32*)address, (INT32)tmp.target);
924+
}
925+
wasRelocHandled = true;
926+
}
927+
break;
928+
929+
default:
930+
break;
931+
}
932+
}
933+
909934
if (IsSpmiTarget64Bit())
910935
{
911936
if (!wasRelocHandled && (relocType == IMAGE_REL_BASED_DIR64))

src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,6 +477,54 @@ void PutArm32MovtConstant(UINT32* p, unsigned con)
477477
*((UINT16*)p + 1) = (UINT16)instr;
478478
}
479479

480+
//*****************************************************************************
481+
// Extract the PC-Relative offset from auipc + I-type adder (addi/ld/jalr)
482+
//*****************************************************************************
483+
INT64 GetRiscV64AuipcItype(UINT32 * pCode)
484+
{
485+
enum
486+
{
487+
OpcodeAuipc = 0x00000017,
488+
OpcodeAddi = 0x00000013,
489+
OpcodeLd = 0x00003003,
490+
OpcodeJalr = 0x00000067,
491+
OpcodeUTypeMask = 0x0000007F,
492+
OpcodeITypeMask = 0x0000307F,
493+
};
494+
495+
UINT32 auipc = pCode[0];
496+
_ASSERTE((auipc & OpcodeUTypeMask) == OpcodeAuipc);
497+
int auipcRegDest = (auipc >> 7) & 0x1F;
498+
_ASSERTE(auipcRegDest != 0);
499+
500+
INT64 hi20 = (INT32(auipc) >> 12) << 12;
501+
502+
UINT32 iType = pCode[1];
503+
UINT32 opcode = iType & OpcodeITypeMask;
504+
_ASSERTE(opcode == OpcodeAddi || opcode == OpcodeLd || opcode == OpcodeJalr);
505+
int iTypeRegSrc = (iType >> 15) & 0x1F;
506+
_ASSERTE(auipcRegDest == iTypeRegSrc);
507+
508+
INT64 lo12 = INT32(iType) >> 20;
509+
510+
return hi20 + lo12;
511+
}
512+
513+
//*****************************************************************************
514+
// Deposit the PC-Relative offset into auipc + I-type adder (addi/ld/jalr)
515+
//*****************************************************************************
516+
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset)
517+
{
518+
INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended
519+
INT32 hi20 = INT32(offset - lo12);
520+
_ASSERTE(INT64(hi20) + INT64(lo12) == offset);
521+
522+
_ASSERTE(GetRiscV64AuipcItype(pCode) == 0);
523+
pCode[0] |= hi20;
524+
pCode[1] |= lo12 << 20;
525+
_ASSERTE(GetRiscV64AuipcItype(pCode) == offset);
526+
}
527+
480528
template<typename TPrint>
481529
static std::string getFromPrinter(TPrint print)
482530
{

src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,9 @@ bool Is32BitThumb2Instruction(UINT16* p);
9797
UINT32 ExtractArm32MovImm(UINT32 instr);
9898
void PutArm32MovtConstant(UINT32* p, unsigned con);
9999

100+
INT64 GetRiscV64AuipcItype(UINT32 * pCode);
101+
void PutRiscV64AuipcItype(UINT32 * pCode, INT64 offset);
102+
100103
template <typename T, int size>
101104
inline constexpr unsigned ArrLen(T (&)[size])
102105
{

0 commit comments

Comments
 (0)