diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index a65140ea80331a..a3097b59da3e73 100644 --- a/src/coreclr/inc/utilcode.h +++ b/src/coreclr/inc/utilcode.h @@ -3315,6 +3315,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 //***************************************************************************** diff --git a/src/coreclr/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 640cb5ec6bf330..68c82f6b8a9f33 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -6538,12 +6538,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 diff --git a/src/coreclr/jit/emitriscv64.cpp b/src/coreclr/jit/emitriscv64.cpp index 6f772120f480e8..8256f6b5dbaf36 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -1725,28 +1725,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) @@ -1779,16 +1766,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 @@ -1823,11 +1802,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()); @@ -1866,130 +1844,29 @@ 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); + dst += emitOutput_ITypeInstr(dst, INS_jalr, id->idReg4(), id->idReg3(), TrimSignedToImm12(offset)); } - else if (id->idIsReloc()) + else { - // 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)); - } - + 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; - 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); + dst += emitOutput_UTypeInstr(dst, INS_auipc, tempReg, 0); + emitGCregDeadUpd(tempReg, dst); + dst += emitOutput_ITypeInstr(dst, INS_jalr, linkReg, tempReg, 0); - emitRecordRelocation(dst - 4, (BYTE*)addr, IMAGE_REL_RISCV64_PC); + assert(id->idIsDspReloc()); + emitRecordRelocation(origDst, (BYTE*)addr, IMAGE_REL_RISCV64_PC); } - 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 - - ssize_t imm = (ssize_t)(id->idAddr()->iiaAddr); - assert((uint64_t)(imm >> 32) <= 0x7fff); // RISC-V Linux Kernel SV48 - - int reg2 = (int)(imm & 1); - imm -= reg2; - - 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); - } - - dst += 4; // If the method returns a GC ref, mark INTRET (A0) appropriately. if (id->idGCref() == GCT_GCREF) @@ -2037,25 +1914,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() @@ -3453,7 +3322,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; } diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index cbb2b11ec96747..4fa54b756bc54d 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -339,7 +339,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 diff --git a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp index 8c9252869fe243..e878cd59d82ff3 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp @@ -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)) diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp index 9bba21b1bfd8e5..2b17d0ac8352ad 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -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 static std::string getFromPrinter(TPrint print) { diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index 9ff49500321217..72e3b2b0f93a50 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h @@ -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 inline constexpr unsigned ArrLen(T (&)[size]) { diff --git a/src/coreclr/utilcode/util.cpp b/src/coreclr/utilcode/util.cpp index dac90c2ad97925..e6c2bf271f46c3 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -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. diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index e6dbcde9e8b53a..3ba5b8a3c13e96 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -11876,6 +11876,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;