diff --git a/src/coreclr/inc/corinfo.h b/src/coreclr/inc/corinfo.h index 7cd408af5905bb..a897217e5eaf7b 100644 --- a/src/coreclr/inc/corinfo.h +++ b/src/coreclr/inc/corinfo.h @@ -902,7 +902,9 @@ enum class CorInfoReloc LOONGARCH64_JIR, // LoongArch64: pcaddu18i+jirl // RISCV64 relocs - RISCV64_PC, // RiscV64: auipc + RISCV64_CALL_PLT, // RiscV64: auipc + jalr + RISCV64_PCREL_I, // RiscV64: auipc + I-type + RISCV64_PCREL_S, // RiscV64: auipc + S-type }; enum CorInfoGCType diff --git a/src/coreclr/inc/utilcode.h b/src/coreclr/inc/utilcode.h index 02b6dd9ca20bbd..295a8e6087d26a 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/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 @@ -3402,6 +3402,19 @@ 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 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)); +} + // // 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/jit/codegenriscv64.cpp b/src/coreclr/jit/codegenriscv64.cpp index 31279d9e40ee22..8c9259977f49da 100644 --- a/src/coreclr/jit/codegenriscv64.cpp +++ b/src/coreclr/jit/codegenriscv64.cpp @@ -655,18 +655,18 @@ 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; + params.callType = EC_FUNC_TOKEN; + params.addr = addrInfo.addr; + 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: @@ -899,15 +899,10 @@ 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_jal, size, reg, imm); + emit->emitIns_R_AI(INS_addi, size, reg, reg, imm); } else { @@ -2738,23 +2733,14 @@ void CodeGen::genCodeForReturnTrap(GenTreeOp* tree) // 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.ireg = params.isJump ? rsGetRsvdReg() : REG_RA; } else { params.addr = nullptr; params.callType = EC_INDIR_R; params.ireg = REG_DEFAULT_HELPER_CALL_TARGET; - - if (compiler->opts.compReloc) - { - GetEmitter()->emitIns_R_AI(INS_jal, 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); - } + GetEmitter()->emitIns_R_R_Addr(INS_ld, EA_PTRSIZE, params.ireg, params.ireg, helperFunction.addr); regSet.verifyRegUsed(params.ireg); } @@ -3376,23 +3362,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. */ @@ -3404,11 +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) { - params.addr = (void*)helperFunction.addr; + params.callType = EC_FUNC_TOKEN; + params.addr = helperFunction.addr; } else { @@ -3417,35 +3393,19 @@ 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 - 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 noway_assert((callTargetMask & killSet) == callTargetMask); - 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); - } - else - { - emitLoadConstAtAddr(GetEmitter(), callTargetReg, (ssize_t)pAddr); - } + GetEmitter()->emitIns_R_R_Addr(INS_ld, EA_PTRSIZE, callTargetReg, callTargetReg, pAddr); regSet.verifyRegUsed(callTargetReg); params.callType = EC_INDIR_R; - params.ireg = callTargetReg; } params.methHnd = compiler->eeFindHelper(helper); @@ -4253,15 +4213,8 @@ void CodeGen::genSetGSSecurityCookie(regNumber initReg, bool* pInitRegZeroed) } else { - if (compiler->opts.compReloc) - { - emit->emitIns_R_AI(INS_jal, 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); - } + 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); } @@ -4299,33 +4252,8 @@ void CodeGen::genEmitGSCookieCheck(bool tailCall) 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_jal, 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); - } + 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 @@ -5591,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); } @@ -5673,6 +5591,7 @@ void CodeGen::genCallInstruction(GenTreeCall* call) } else #endif // FEATURE_READYTORUN + { if (call->IsHelperCall()) { CorInfoHelpFunc helperNum = compiler->eeGetHelperNum(params.methHnd); @@ -5687,10 +5606,11 @@ void CodeGen::genCallInstruction(GenTreeCall* call) // Direct call to a non-virtual user function. params.addr = call->gtDirectCallAddress; } - - assert(params.addr != nullptr); + } params.callType = EC_FUNC_TOKEN; + params.ireg = params.isJump ? rsGetRsvdReg() : REG_RA; + genEmitCallWithCurrentGC(params); } } @@ -6324,6 +6244,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))); if (helperFunction.accessType == IAT_VALUE) @@ -6332,40 +6255,18 @@ void CodeGen::genJumpToThrowHlpBlk_la( // 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.ireg = params.isJump ? rsGetRsvdReg() : REG_RA; } 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; - 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_jal, 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); - } + 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? @@ -6717,17 +6618,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, @@ -6763,17 +6656,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); diff --git a/src/coreclr/jit/compiler.cpp b/src/coreclr/jit/compiler.cpp index e74188ffc43fef..7482fc2fe1ced8 100644 --- a/src/coreclr/jit/compiler.cpp +++ b/src/coreclr/jit/compiler.cpp @@ -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 diff --git a/src/coreclr/jit/compiler.h b/src/coreclr/jit/compiler.h index e71b86b926cddb..38e6dc63b470f2 100644 --- a/src/coreclr/jit/compiler.h +++ b/src/coreclr/jit/compiler.h @@ -10162,7 +10162,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 653c48536b0071..1f1bff5ed0531b 100644 --- a/src/coreclr/jit/emitriscv64.cpp +++ b/src/coreclr/jit/emitriscv64.cpp @@ -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. @@ -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 || (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)); + // 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); @@ -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)) @@ -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)); @@ -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); @@ -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); @@ -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()) { @@ -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 @@ -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, CorInfoReloc::RISCV64_PC); + emitRecordRelocation(origDst, (BYTE*)addr, CorInfoReloc::RISCV64_CALL_PLT); } // If the method returns a GC ref, mark INTRET (A0) appropriately. @@ -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, CorInfoReloc::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); + CorInfoReloc type = emitInsIsStore(*ins) ? CorInfoReloc::RISCV64_PCREL_S : CorInfoReloc::RISCV64_PCREL_I; + emitRecordRelocation(dstBase, id->idAddr()->iiaAddr, type); return dst; } @@ -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)) { @@ -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&&)( + CorInfoReloc::RELATIVE32 == emitComp->eeGetRelocTypeHint(addr))); +} + #if defined(DEBUG) || defined(LATE_DISASM) //---------------------------------------------------------------------------------------- diff --git a/src/coreclr/jit/emitriscv64.h b/src/coreclr/jit/emitriscv64.h index a66de99b999940..802cc17fd54dd2 100644 --- a/src/coreclr/jit/emitriscv64.h +++ b/src/coreclr/jit/emitriscv64.h @@ -340,17 +340,22 @@ 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_R_AR(instruction ins, emitAttr attr, regNumber ireg, regNumber reg, int offs); 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); unsigned get_curTotalCodeSize(); // bytes of code +bool IsAddressInRange(void* addr); + //------------------------------------------------------------------------ // emitIsCmpJump: checks if it's a compare and jump (branch) // diff --git a/src/coreclr/jit/gentree.cpp b/src/coreclr/jit/gentree.cpp index fb008955730152..34b3356844b52f 100644 --- a/src/coreclr/jit/gentree.cpp +++ b/src/coreclr/jit/gentree.cpp @@ -18296,7 +18296,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 bf5bbb2a3c3ff4..3edd4478dfd57c 100644 --- a/src/coreclr/jit/gentree.h +++ b/src/coreclr/jit/gentree.h @@ -3244,7 +3244,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/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 eb799d31c07afe..66ae7f7264e28d 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,41 @@ 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, bool isStype) { - 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)); + 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 = isStype ? 7 : 20; + lo12 |= (instr >> bottomBitsPos) & 0x1F; + + return hi20 + lo12; } // INS_OPTS_RELOC: placeholders. 2-ins: @@ -518,26 +541,18 @@ 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, bool isStype) { - // 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); + int lo12 = (int)((offset << (64 - 12)) >> (64 - 12)); + int hi20 = (int)(offset - lo12); + Debug.Assert((long)lo12 + (long)hi20 == offset); + + Debug.Assert(GetRiscV64AuipcCombo(pCode, isStype) == 0); + pCode[0] |= (uint)hi20; + 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, isStype) == offset); } public Relocation(RelocType relocType, int offset, ISymbolNode target) @@ -600,8 +615,11 @@ 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: + bool isStype = (relocType is RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S); + PutRiscV64AuipcCombo((uint*)location, value, isStype); break; default: Debug.Fail("Invalid RelocType: " + relocType); @@ -629,7 +647,9 @@ public static int GetSize(RelocType relocType) RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL => 8, RelocType.IMAGE_REL_BASED_LOONGARCH64_PC => 8, RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR => 8, - RelocType.IMAGE_REL_BASED_RISCV64_PC => 8, + RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT => 8, + RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I => 8, + RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S => 8, _ => throw new NotSupportedException(), }; } @@ -684,8 +704,11 @@ 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: + 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/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs b/src/coreclr/tools/Common/Compiler/DependencyAnalysis/Target_RiscV64/RiscV64Emitter.cs index 6a91e43a314545..a9f99fa425d5b7 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 @@ -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) @@ -118,7 +118,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..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) @@ -557,7 +572,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 +582,21 @@ 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); + + 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); + } } } } 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 RelocType.IMAGE_REL_BASED_THUMB_MOV32_PCREL, CorInfoReloc.LOONGARCH64_PC => RelocType.IMAGE_REL_BASED_LOONGARCH64_PC, CorInfoReloc.LOONGARCH64_JIR => RelocType.IMAGE_REL_BASED_LOONGARCH64_JIR, - CorInfoReloc.RISCV64_PC => RelocType.IMAGE_REL_BASED_RISCV64_PC, + CorInfoReloc.RISCV64_CALL_PLT => RelocType.IMAGE_REL_BASED_RISCV64_CALL_PLT, + CorInfoReloc.RISCV64_PCREL_I => RelocType.IMAGE_REL_BASED_RISCV64_PCREL_I, + CorInfoReloc.RISCV64_PCREL_S => RelocType.IMAGE_REL_BASED_RISCV64_PCREL_S, _ => throw new ArgumentException("Unsupported relocation type: " + reloc), }; diff --git a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs index db2153d1b35d14..9ebd407d86c006 100644 --- a/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs +++ b/src/coreclr/tools/Common/JitInterface/CorInfoTypes.cs @@ -497,7 +497,9 @@ public enum CorInfoReloc LOONGARCH64_JIR, // LoongArch64: pcaddu18i+jirl // RISCV64 relocs - RISCV64_PC, // RiscV64: auipc + RISCV64_CALL_PLT, // RiscV64: auipc + jalr + RISCV64_PCREL_I, // RiscV64: auipc + I-type + RISCV64_PCREL_S, // RiscV64: auipc + S-type } public enum CorInfoGCType diff --git a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp index 486721cc306964..2d4ab052137131 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/compileresult.cpp @@ -700,7 +700,9 @@ const char* relocationTypeToString(CorInfoReloc fRelocType) ADD_CASE(ARM32_THUMB_MOV32_PCREL); ADD_CASE(LOONGARCH64_PC); ADD_CASE(LOONGARCH64_JIR); - ADD_CASE(RISCV64_PC); + ADD_CASE(RISCV64_CALL_PLT); + ADD_CASE(RISCV64_PCREL_I); + ADD_CASE(RISCV64_PCREL_S); default: return "UNKNOWN"; #undef ADD_CASE @@ -904,14 +906,20 @@ void CompileResult::applyRelocs(RelocContext* rc, unsigned char* block1, ULONG b switch (relocType) { - case CorInfoReloc::RISCV64_PC: + case CorInfoReloc::RISCV64_CALL_PLT: + case CorInfoReloc::RISCV64_PCREL_I: + case CorInfoReloc::RISCV64_PCREL_S: { 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); + INT64 offset = (INT64)tmp.target; + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); + INT32 hi20 = INT32(offset - lo12); + bool isStype = (relocType == CorInfoReloc::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 20f5f1bf295b33..a9867225e512f0 100644 --- a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp +++ b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.cpp @@ -464,51 +464,64 @@ 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 or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcItype(UINT32 * pCode) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, bool isStype) { enum { - OpcodeAuipc = 0x00000017, - OpcodeAddi = 0x00000013, - OpcodeLd = 0x00003003, - OpcodeJalr = 0x00000067, - OpcodeUTypeMask = 0x0000007F, - OpcodeITypeMask = 0x0000307F, + OpcodeAuipc = 0x17, + OpcodeAddi = 0x13, + OpcodeLoad = 0x03, + OpcodeStore = 0x23, + OpcodeLoadFp = 0x07, + OpcodeStoreFp = 0x27, + 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); - 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)); + _ASSERTE(isStype == (opcode == OpcodeStore || opcode == OpcodeStoreFp)); + 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 = isStype ? 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, INT64 offset, bool isStype) { - INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); - _ASSERTE(INT64(hi20) + INT64(lo12) == offset); + _ASSERTE(INT64(lo12) + INT64(hi20) == offset); - _ASSERTE(GetRiscV64AuipcItype(pCode) == 0); + _ASSERTE(GetRiscV64AuipcCombo(pCode, isStype) == 0); pCode[0] |= hi20; - pCode[1] |= lo12 << 20; - _ASSERTE(GetRiscV64AuipcItype(pCode) == offset); + 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, isStype) == offset); } template diff --git a/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h b/src/coreclr/tools/superpmi/superpmi-shared/spmiutil.h index 93e6547d1cf1b5..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 GetRiscV64AuipcItype(UINT32 * pCode); -void PutRiscV64AuipcItype(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 e6c2bf271f46c3..9180b814c8e996 100644 --- a/src/coreclr/utilcode/util.cpp +++ b/src/coreclr/utilcode/util.cpp @@ -2334,51 +2334,64 @@ 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 or S-type adder (addi/load/store/jalr) //***************************************************************************** -INT64 GetRiscV64AuipcItype(UINT32 * pCode) +INT64 GetRiscV64AuipcCombo(UINT32 * pCode, bool isStype) { enum { - OpcodeAuipc = 0x00000017, - OpcodeAddi = 0x00000013, - OpcodeLd = 0x00003003, - OpcodeJalr = 0x00000067, - OpcodeUTypeMask = 0x0000007F, - OpcodeITypeMask = 0x0000307F, + OpcodeAuipc = 0x17, + OpcodeAddi = 0x13, + OpcodeLoad = 0x03, + OpcodeStore = 0x23, + OpcodeLoadFp = 0x07, + OpcodeStoreFp = 0x27, + 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); - 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)); + _ASSERTE(isStype == (opcode == OpcodeStore || opcode == OpcodeStoreFp)); + 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 = isStype ? 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, INT64 offset, bool isStype) { - INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); // low 12 bits, sign-extended + INT32 lo12 = (offset << (64 - 12)) >> (64 - 12); INT32 hi20 = INT32(offset - lo12); - _ASSERTE(INT64(hi20) + INT64(lo12) == offset); + _ASSERTE(INT64(lo12) + INT64(hi20) == offset); - _ASSERTE(GetRiscV64AuipcItype(pCode) == 0); + _ASSERTE(GetRiscV64AuipcCombo(pCode, isStype) == 0); pCode[0] |= hi20; - pCode[1] |= lo12 << 20; - _ASSERTE(GetRiscV64AuipcItype(pCode) == offset); + 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, isStype) == offset); } //====================================================================== diff --git a/src/coreclr/vm/jitinterface.cpp b/src/coreclr/vm/jitinterface.cpp index b892e1e6e86b1c..b25c8ce02afcbb 100644 --- a/src/coreclr/vm/jitinterface.cpp +++ b/src/coreclr/vm/jitinterface.cpp @@ -12035,12 +12035,59 @@ void CEEJitInfo::recordRelocation(void * location, #ifdef TARGET_RISCV64 - case CorInfoReloc::RISCV64_PC: + case CorInfoReloc::RISCV64_CALL_PLT: + case CorInfoReloc::RISCV64_PCREL_I: + case CorInfoReloc::RISCV64_PCREL_S: { _ASSERTE(addlDelta == 0); + delta = (INT64)target - (INT64)location; - INT64 offset = (INT64)target - (INT64)location; - PutRiscV64AuipcItype((UINT32 *)locationRW, offset); + 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) + { + 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 + + 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 == (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; + } + + delta = (INT64)jumpStubAddr - (INT64)location; + if (!FitsInRiscV64AuipcCombo(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))); + } + + 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 @@ -12087,13 +12134,13 @@ CorInfoReloc 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 CorInfoReloc::RELATIVE32; } -#endif // TARGET_AMD64 +#endif // TARGET_AMD64 || TARGET_RISCV64 // No hints return CorInfoReloc::NONE; @@ -13278,22 +13325,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 @@ -13302,12 +13349,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; @@ -13316,8 +13363,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); @@ -13326,24 +13373,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 @@ -13351,16 +13398,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 d71dc3d59304b5..f9ac4e0fcce467 100644 --- a/src/coreclr/vm/jitinterface.h +++ b/src/coreclr/vm/jitinterface.h @@ -749,7 +749,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; @@ -757,7 +757,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; @@ -809,7 +809,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. @@ -835,10 +835,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 @@ -930,10 +930,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