Skip to content

Commit

Permalink
[RISC-V] ELT Profiler Bring-Up (#91313)
Browse files Browse the repository at this point in the history
* [RISC-V] Generate profiling function callbacks

Initial implementation based on ARM64 code.

* [RISC-V] Fix asm stub for calling profiler callbacks

* Fix argument registers according to RISC-V calling convention
* Fix field offsets for PROFILE_PLATFORM_SPECIFIC_DATA
* Make sure field offsets for PROFILE_PLATFORM_SPECIFIC_DATA stay fixed by static asserting the offsets in asmconstants.h

* [RISC-V] Pass arguments for Profile(Enter|Leave|Tailcall)Naked stubs in t0 and t1 because t2 is used to store the call address of the stub

* [RISC-V] Remove unimplemented definition of EmitRet

* [RISC-V] Copy struct from registers into a buffer so that profiler can see whole struct arguments laid out in memory

* [RISC-V] Implement ProfileArgIterator::GetReturnBufferAddr()

Since the RISC-V ABI says values are returned like the first named argument, re-use the struct copying routine from argument parsing as much as possible.

* Factor out duplicated test results checking routine in SlowPathELTProfiler::Shutdown()

* [RISC-V] Clean up PROFILE_PLATFORM_SPECIFIC_DATA

* Remove unused t0 field
* Remove 'unused' field and widen 'flags' to 64 bits to maintain alignment and shave off one sw instruction

* [RISC-V] Remove commented out code

* [RISC-V] Fix formatting

* [RISC-V] Apply format patch from failed check

* [RISC-V] Post-review fixes
  • Loading branch information
tomeksowi authored Sep 5, 2023
1 parent cf65ea2 commit 913a844
Show file tree
Hide file tree
Showing 8 changed files with 269 additions and 215 deletions.
104 changes: 69 additions & 35 deletions src/coreclr/jit/codegenriscv64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -816,8 +816,8 @@ void CodeGen::genCaptureFuncletPrologEpilogInfo()

if (compiler->lvaPSPSym != BAD_VAR_NUM)
{
if (CallerSP_to_PSP_slot_delta !=
compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym)) // for debugging
if (CallerSP_to_PSP_slot_delta != compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym)) // for
// debugging
{
printf("lvaGetCallerSPRelativeOffset(lvaPSPSym): %d\n",
compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym));
Expand Down Expand Up @@ -7162,35 +7162,6 @@ void CodeGen::instGen_MemoryBarrier(BarrierKind barrierKind)
GetEmitter()->emitIns_I(INS_fence, EA_4BYTE, INS_BARRIER_FULL);
}

//-----------------------------------------------------------------------------------
// genProfilingLeaveCallback: Generate the profiling function leave or tailcall callback.
// Technically, this is not part of the epilog; it is called when we are generating code for a GT_RETURN node.
//
// Arguments:
// helper - which helper to call. Either CORINFO_HELP_PROF_FCN_LEAVE or CORINFO_HELP_PROF_FCN_TAILCALL
//
// Return Value:
// None
//
void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FCN_LEAVE*/)
{
assert((helper == CORINFO_HELP_PROF_FCN_LEAVE) || (helper == CORINFO_HELP_PROF_FCN_TAILCALL));

// Only hook if profiler says it's okay.
if (!compiler->compIsProfilerHookNeeded())
{
return;
}

compiler->info.compProfilerCallback = true;

// Need to save on to the stack level, since the helper call will pop the argument
unsigned saveStackLvl2 = genStackLevel;

/* Restore the stack level */
SetStackLevel(saveStackLvl2);
}

/*-----------------------------------------------------------------------------
*
* Push/Pop any callee-saved registers we have used
Expand Down Expand Up @@ -8102,6 +8073,7 @@ void CodeGen::genFnPrologCalleeRegArgs()
assert(!regArgMaskLive);
}

#ifdef PROFILING_SUPPORTED
//-----------------------------------------------------------------------------------
// genProfilingEnterCallback: Generate the profiling function enter callback.
//
Expand All @@ -8110,17 +8082,79 @@ void CodeGen::genFnPrologCalleeRegArgs()
// pInitRegZeroed - OUT parameter. *pInitRegZeroed set to 'false' if 'initReg' is
// set to non-zero value after this call.
//
// Return Value:
// None
//
void CodeGen::genProfilingEnterCallback(regNumber initReg, bool* pInitRegZeroed)
{
assert(compiler->compGeneratingProlog);

// Give profiler a chance to back out of hooking this method
if (!compiler->compIsProfilerHookNeeded())
{
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);
}

ssize_t callerSPOffset = -compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed());
genInstrWithConstant(INS_addi, EA_PTRSIZE, REG_PROFILER_ENTER_ARG_CALLER_SP, genFramePointerReg(), callerSPOffset,
REG_PROFILER_ENTER_ARG_CALLER_SP);

genEmitHelperCall(CORINFO_HELP_PROF_FCN_ENTER, 0, EA_UNKNOWN);

if ((genRegMask(initReg) & RBM_PROFILER_ENTER_TRASH))
{
*pInitRegZeroed = false;
}
}

//-----------------------------------------------------------------------------------
// genProfilingLeaveCallback: Generate the profiling function leave or tailcall callback.
// Technically, this is not part of the epilog; it is called when we are generating code for a GT_RETURN node.
//
// Arguments:
// helper - which helper to call. Either CORINFO_HELP_PROF_FCN_LEAVE or CORINFO_HELP_PROF_FCN_TAILCALL
//
void CodeGen::genProfilingLeaveCallback(unsigned helper /*= CORINFO_HELP_PROF_FCN_LEAVE*/)
{
assert((helper == CORINFO_HELP_PROF_FCN_LEAVE) || (helper == CORINFO_HELP_PROF_FCN_TAILCALL));

if (!compiler->compIsProfilerHookNeeded())
{
return;
}

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

gcInfo.gcMarkRegSetNpt(RBM_PROFILER_LEAVE_ARG_FUNC_ID);

ssize_t callerSPOffset = -compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed());
genInstrWithConstant(INS_addi, EA_PTRSIZE, REG_PROFILER_LEAVE_ARG_CALLER_SP, genFramePointerReg(), callerSPOffset,
REG_PROFILER_LEAVE_ARG_CALLER_SP);

gcInfo.gcMarkRegSetNpt(RBM_PROFILER_LEAVE_ARG_CALLER_SP);

genEmitHelperCall(helper, 0, EA_UNKNOWN);
}
#endif // PROFILING_SUPPORTED

#endif // TARGET_RISCV64
18 changes: 9 additions & 9 deletions src/coreclr/jit/targetriscv64.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,18 +192,18 @@
#define REG_PREV(reg) ((regNumber)((unsigned)(reg) - 1))

// The following registers are used in emitting Enter/Leave/Tailcall profiler callbacks
#define REG_PROFILER_ENTER_ARG_FUNC_ID REG_R16
#define RBM_PROFILER_ENTER_ARG_FUNC_ID RBM_R16
#define REG_PROFILER_ENTER_ARG_CALLER_SP REG_R17
#define RBM_PROFILER_ENTER_ARG_CALLER_SP RBM_R17
#define REG_PROFILER_LEAVE_ARG_FUNC_ID REG_R16
#define RBM_PROFILER_LEAVE_ARG_FUNC_ID RBM_R16
#define REG_PROFILER_LEAVE_ARG_CALLER_SP REG_R17
#define RBM_PROFILER_LEAVE_ARG_CALLER_SP RBM_R17
#define REG_PROFILER_ENTER_ARG_FUNC_ID REG_T0
#define RBM_PROFILER_ENTER_ARG_FUNC_ID RBM_T0
#define REG_PROFILER_ENTER_ARG_CALLER_SP REG_T1
#define RBM_PROFILER_ENTER_ARG_CALLER_SP RBM_T1
#define REG_PROFILER_LEAVE_ARG_FUNC_ID REG_PROFILER_ENTER_ARG_FUNC_ID
#define RBM_PROFILER_LEAVE_ARG_FUNC_ID RBM_PROFILER_ENTER_ARG_FUNC_ID
#define REG_PROFILER_LEAVE_ARG_CALLER_SP REG_PROFILER_ENTER_ARG_CALLER_SP
#define RBM_PROFILER_LEAVE_ARG_CALLER_SP RBM_PROFILER_ENTER_ARG_CALLER_SP

// The registers trashed by profiler enter/leave/tailcall hook
#define RBM_PROFILER_ENTER_TRASH (RBM_CALLEE_TRASH & ~(RBM_ARG_REGS|RBM_FLTARG_REGS|RBM_FP))
#define RBM_PROFILER_LEAVE_TRASH (RBM_CALLEE_TRASH & ~(RBM_ARG_REGS|RBM_FLTARG_REGS|RBM_FP))
#define RBM_PROFILER_LEAVE_TRASH RBM_PROFILER_ENTER_TRASH
#define RBM_PROFILER_TAILCALL_TRASH RBM_PROFILER_LEAVE_TRASH

// Which register are int and long values returned in ?
Expand Down
10 changes: 7 additions & 3 deletions src/coreclr/vm/proftoeeinterfaceimpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,18 @@ class ProfileArgIterator
private:
void *m_handle;
ArgIterator m_argIterator;
#if defined(UNIX_AMD64_ABI) || defined(TARGET_ARM64)
#if defined(UNIX_AMD64_ABI) || defined(TARGET_ARM64) || defined(TARGET_RISCV64)
UINT64 m_bufferPos;

#if defined(UNIX_AMD64_ABI)
#if defined(UNIX_AMD64_ABI) || defined(TARGET_RISCV64)
// On certain architectures we can pass args in non-sequential registers,
// this function will copy the struct so it is laid out as it would be in memory
// so it can be passed to the profiler
LPVOID CopyStructFromRegisters();
LPVOID CopyStructFromRegisters(
#ifdef TARGET_RISCV64
const ArgLocDesc* argLocDesc
#endif
);
#endif

#if defined(TARGET_ARM64)
Expand Down
29 changes: 29 additions & 0 deletions src/coreclr/vm/riscv64/asmconstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -250,5 +250,34 @@ ASMCONSTANTS_C_ASSERT(CallCountingStubData__TargetForMethod == offsetof(CallCoun
#define CallCountingStubData__TargetForThresholdReached 0x10
ASMCONSTANTS_C_ASSERT(CallCountingStubData__TargetForThresholdReached == offsetof(CallCountingStubData, TargetForThresholdReached))

#ifdef PROFILING_SUPPORTED
#define PROFILE_ENTER 1
#define PROFILE_LEAVE 2
#define PROFILE_TAILCALL 4

#define SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA 312
#define PROFILE_PLATFORM_SPECIFIC_DATA__argumentRegisters 16
#define PROFILE_PLATFORM_SPECIFIC_DATA__functionId 80
#define PROFILE_PLATFORM_SPECIFIC_DATA__floatArgumentRegisters 88
#define PROFILE_PLATFORM_SPECIFIC_DATA__probeSp 152
#define PROFILE_PLATFORM_SPECIFIC_DATA__profiledSp 160
#define PROFILE_PLATFORM_SPECIFIC_DATA__hiddenArg 168
#define PROFILE_PLATFORM_SPECIFIC_DATA__flags 176

ASMCONSTANTS_C_ASSERT(SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA == sizeof(PROFILE_PLATFORM_SPECIFIC_DATA))

#define ASMCONSTANTS_C_ASSERT_OFFSET(type, field) \
ASMCONSTANTS_C_ASSERT(type##__##field == offsetof(type, field))
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, argumentRegisters)
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, functionId)
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, floatArgumentRegisters)
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, probeSp)
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, profiledSp)
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, hiddenArg)
ASMCONSTANTS_C_ASSERT_OFFSET(PROFILE_PLATFORM_SPECIFIC_DATA, flags)
#undef ASMCONSTANTS_C_ASSERT_OFFSET

#endif // PROFILING_SUPPORTED

#undef ASMCONSTANTS_RUNTIME_ASSERT
#undef ASMCONSTANTS_C_ASSERT
37 changes: 15 additions & 22 deletions src/coreclr/vm/riscv64/asmhelpers.S
Original file line number Diff line number Diff line change
Expand Up @@ -894,43 +894,36 @@ LEAF_ENTRY JIT_ProfilerEnterLeaveTailcallStub, _TEXT
ret
LEAF_END JIT_ProfilerEnterLeaveTailcallStub, _TEXT

// ------------------------------------------------------------------
#define PROFILE_ENTER 1
#define PROFILE_LEAVE 2
#define PROFILE_TAILCALL 4
#define SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA 272

// ------------------------------------------------------------------
.macro GenerateProfileHelper helper, flags
NESTED_ENTRY \helper\()Naked, _TEXT, NoHandler
// On entry:
// t1 = functionIDOrClientID
// t2 = profiledSp
// t0 = functionIDOrClientID
// t1 = profiledSp
// t6 = throwable
//
// On exit:
// Values of a0-a7, fa0-fa7, fp are preserved.
// Values of other volatile registers are not preserved.

// Fill in PROFILE_PLATFORM_SPECIFIC_DATA struct
PROLOG_SAVE_REG_PAIR_INDEXED fp, ra, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Allocate space and save Fp, Pc.
SAVE_ARGUMENT_REGISTERS sp, 16 // Save t0 and argument registers (a0-a7).
sd zero, 88(sp) // Clear functionId.
SAVE_FLOAT_ARGUMENT_REGISTERS sp, 96 // Save floating-point/SIMD registers (fa0-fa7).
addi t6, fp, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Compute probeSp - initial value of Sp on entry to the helper.
sd t6, 224(sp) // Save probeSp.
sd t2, 232(sp) // Save profiledSp.

sd zero, 240(sp) // Clear hiddenArg.
SAVE_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__argumentRegisters
sd zero, PROFILE_PLATFORM_SPECIFIC_DATA__functionId(sp)
SAVE_FLOAT_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__floatArgumentRegisters
addi t6, sp, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA // Compute probeSp - initial value of Sp on entry to the helper.
sd t6, PROFILE_PLATFORM_SPECIFIC_DATA__probeSp(sp)
sd t1, PROFILE_PLATFORM_SPECIFIC_DATA__profiledSp(sp)
sd zero, PROFILE_PLATFORM_SPECIFIC_DATA__hiddenArg(sp)
addi t6, zero, \flags
sw t6, 248(sp) // Save flags.
sw zero, 252(sp) // clear unused field.
sd t6, PROFILE_PLATFORM_SPECIFIC_DATA__flags(sp)

addi a1, t1, 0
addi a2, sp, 0
addi a0, t0, 0
addi a1, sp, 0
call C_FUNC(\helper)

RESTORE_ARGUMENT_REGISTERS sp, 16 // Restore t0 and argument registers.
RESTORE_FLOAT_ARGUMENT_REGISTERS sp, 96 // Restore floating-point/SIMD registers.
RESTORE_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__argumentRegisters
RESTORE_FLOAT_ARGUMENT_REGISTERS sp, PROFILE_PLATFORM_SPECIFIC_DATA__floatArgumentRegisters
EPILOG_RESTORE_REG_PAIR_INDEXED fp, ra, SIZEOF__PROFILE_PLATFORM_SPECIFIC_DATA
EPILOG_RETURN

Expand Down
27 changes: 23 additions & 4 deletions src/coreclr/vm/riscv64/cgencpu.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ struct CalleeSavedRegisters {
#define NUM_ARGUMENT_REGISTERS 8
typedef DPTR(struct ArgumentRegisters) PTR_ArgumentRegisters;
struct ArgumentRegisters {
INT64 a[8]; // a0 ....a7
INT64 a[NUM_ARGUMENT_REGISTERS]; // a0 ....a7
};

#define ARGUMENTREGISTERS_SIZE sizeof(ArgumentRegisters)
Expand All @@ -124,9 +124,30 @@ struct ArgumentRegisters {
typedef DPTR(struct FloatArgumentRegisters) PTR_FloatArgumentRegisters;
struct FloatArgumentRegisters {
//TODO: not supports RISCV64-SIMD.
double f[8]; // f0-f7
double f[NUM_FLOAT_ARGUMENT_REGISTERS]; // f0-f7
};

//**********************************************************************
// Profiling
//**********************************************************************

#ifdef PROFILING_SUPPORTED

struct PROFILE_PLATFORM_SPECIFIC_DATA
{
void* Fp;
void* Pc;
ArgumentRegisters argumentRegisters;
FunctionID functionId;
FloatArgumentRegisters floatArgumentRegisters;
void* probeSp;
void* profiledSp;
void* hiddenArg;
UINT64 flags;
// Scratch space to reconstruct struct passed in registers
BYTE buffer[sizeof(ArgumentRegisters) + sizeof(FloatArgumentRegisters)];
};
#endif // PROFILING_SUPPORTED

//**********************************************************************
// Exception handling
Expand Down Expand Up @@ -359,8 +380,6 @@ class StubLinkerCPU : public StubLinker
void EmitLoad(FloatReg dest, IntReg srcAddr, int offset = 0);
void EmitStore(IntReg src, IntReg destAddr, int offset = 0);
void EmitStore(FloatReg src, IntReg destAddr, int offset = 0);

void EmitRet(IntReg reg);
};

extern "C" void SinglecastDelegateInvokeStub();
Expand Down
Loading

0 comments on commit 913a844

Please sign in to comment.