diff options
Diffstat (limited to 'src/jit/codegencommon.cpp')
-rw-r--r-- | src/jit/codegencommon.cpp | 577 |
1 files changed, 1 insertions, 576 deletions
diff --git a/src/jit/codegencommon.cpp b/src/jit/codegencommon.cpp index 3c0d0b600b..4ac7fcbf40 100644 --- a/src/jit/codegencommon.cpp +++ b/src/jit/codegencommon.cpp @@ -6483,581 +6483,6 @@ void CodeGen::genReportGenericContextArg(regNumber initReg, bool* pInitRegZeroed #endif // !ARM64 !ARM } -#ifdef PROFILING_SUPPORTED - -//----------------------------------------------------------------------------------- -// genProfilingEnterCallback: Generate the profiling function enter callback. -// -// Arguments: -// initReg - register to use as scratch register -// pInitRegZeroed - OUT parameter. *pInitRegZeroed set to 'false' if 'initReg' is -// not zero after this call. -// -// Return Value: -// None -// -// Notes: -// The x86 profile enter helper has the following requirements (see ProfileEnterNaked in -// VM\i386\asmhelpers.asm for details): -// 1. The calling sequence for calling the helper is: -// push FunctionIDOrClientID -// call ProfileEnterHelper -// 2. The calling function has an EBP frame. -// 3. EBP points to the saved ESP which is the first thing saved in the function. Thus, -// the following prolog is assumed: -// push ESP -// mov EBP, ESP -// 4. All registers are preserved. -// 5. The helper pops the FunctionIDOrClientID argument from the stack. -// -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; - } - -#if defined(_TARGET_AMD64_) -#if !defined(UNIX_AMD64_ABI) - - unsigned varNum; - LclVarDsc* varDsc; - - // Since the method needs to make a profiler callback, it should have out-going arg space allocated. - noway_assert(compiler->lvaOutgoingArgSpaceVar != BAD_VAR_NUM); - noway_assert(compiler->lvaOutgoingArgSpaceSize >= (4 * REGSIZE_BYTES)); - - // Home all arguments passed in arg registers (RCX, RDX, R8 and R9). - // In case of vararg methods, arg regs are already homed. - // - // Note: Here we don't need to worry about updating gc'info since enter - // callback is generated as part of prolog which is non-gc interruptible. - // Moreover GC cannot kick while executing inside profiler callback which is a - // profiler requirement so it can examine arguments which could be obj refs. - if (!compiler->info.compIsVarArgs) - { - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->info.compArgsCount; varNum++, varDsc++) - { - noway_assert(varDsc->lvIsParam); - - if (!varDsc->lvIsRegArg) - { - continue; - } - - var_types storeType = varDsc->lvaArgType(); - regNumber argReg = varDsc->lvArgReg; - - instruction store_ins = ins_Store(storeType); - -#ifdef FEATURE_SIMD - if ((storeType == TYP_SIMD8) && genIsValidIntReg(argReg)) - { - store_ins = INS_mov; - } -#endif // FEATURE_SIMD - - getEmitter()->emitIns_S_R(store_ins, emitTypeSize(storeType), argReg, varNum, 0); - } - } - - // Emit profiler EnterCallback(ProfilerMethHnd, caller's SP) - // RCX = ProfilerMethHnd - if (compiler->compProfilerMethHndIndirected) - { - // Profiler hooks enabled during Ngen time. - // Profiler handle needs to be accessed through an indirection of a pointer. - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - else - { - // No need to record relocations, if we are generating ELT hooks under the influence - // of COMPlus_JitELTHookEnabled=1 - if (compiler->opts.compJitELTHookEnabled) - { - genSetRegToIcon(REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd, TYP_I_IMPL); - } - else - { - instGen_Set_Reg_To_Imm(EA_8BYTE, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - } - - // RDX = caller's SP - // Notes - // 1) Here we can query caller's SP offset since prolog will be generated after final frame layout. - // 2) caller's SP relative offset to FramePointer will be negative. We need to add absolute value - // of that offset to FramePointer to obtain caller's SP value. - assert(compiler->lvaOutgoingArgSpaceVar != BAD_VAR_NUM); - int callerSPOffset = compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed()); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_ARG_1, genFramePointerReg(), -callerSPOffset); - - // Can't have a call until we have enough padding for rejit - genPrologPadForReJit(); - - // This will emit either - // "call ip-relative 32-bit offset" or - // "mov rax, helper addr; call rax" - genEmitHelperCall(CORINFO_HELP_PROF_FCN_ENTER, 0, EA_UNKNOWN); - - // TODO-AMD64-CQ: Rather than reloading, see if this could be optimized by combining with prolog - // generation logic that moves args around as required by first BB entry point conditions - // computed by LSRA. Code pointers for investigating this further: genFnPrologCalleeRegArgs() - // and genEnregisterIncomingStackArgs(). - // - // Now reload arg registers from home locations. - // Vararg methods: - // - we need to reload only known (i.e. fixed) reg args. - // - if floating point type, also reload it into corresponding integer reg - for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->info.compArgsCount; varNum++, varDsc++) - { - noway_assert(varDsc->lvIsParam); - - if (!varDsc->lvIsRegArg) - { - continue; - } - - var_types loadType = varDsc->lvaArgType(); - regNumber argReg = varDsc->lvArgReg; - - instruction load_ins = ins_Load(loadType); - -#ifdef FEATURE_SIMD - if ((loadType == TYP_SIMD8) && genIsValidIntReg(argReg)) - { - load_ins = INS_mov; - } -#endif // FEATURE_SIMD - - getEmitter()->emitIns_R_S(load_ins, emitTypeSize(loadType), argReg, varNum, 0); - -#if FEATURE_VARARG - if (compiler->info.compIsVarArgs && varTypeIsFloating(loadType)) - { - regNumber intArgReg = compiler->getCallArgIntRegister(argReg); - instruction ins = ins_CopyFloatToInt(loadType, TYP_LONG); - inst_RV_RV(ins, argReg, intArgReg, loadType); - } -#endif // FEATURE_VARARG - } - - // If initReg is one of RBM_CALLEE_TRASH, then it needs to be zero'ed before using. - if ((RBM_CALLEE_TRASH & genRegMask(initReg)) != 0) - { - *pInitRegZeroed = false; - } - -#else // !defined(UNIX_AMD64_ABI) - - // Emit profiler EnterCallback(ProfilerMethHnd, caller's SP) - // R14 = ProfilerMethHnd - if (compiler->compProfilerMethHndIndirected) - { - // Profiler hooks enabled during Ngen time. - // Profiler handle needs to be accessed through an indirection of a pointer. - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_PROFILER_ENTER_ARG_0, - (ssize_t)compiler->compProfilerMethHnd); - } - else - { - // No need to record relocations, if we are generating ELT hooks under the influence - // of COMPlus_JitELTHookEnabled=1 - if (compiler->opts.compJitELTHookEnabled) - { - genSetRegToIcon(REG_PROFILER_ENTER_ARG_0, (ssize_t)compiler->compProfilerMethHnd, TYP_I_IMPL); - } - else - { - instGen_Set_Reg_To_Imm(EA_8BYTE, REG_PROFILER_ENTER_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - } - - // R15 = caller's SP - // Notes - // 1) Here we can query caller's SP offset since prolog will be generated after final frame layout. - // 2) caller's SP relative offset to FramePointer will be negative. We need to add absolute value - // of that offset to FramePointer to obtain caller's SP value. - assert(compiler->lvaOutgoingArgSpaceVar != BAD_VAR_NUM); - int callerSPOffset = compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed()); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_PROFILER_ENTER_ARG_1, genFramePointerReg(), -callerSPOffset); - - // Can't have a call until we have enough padding for rejit - genPrologPadForReJit(); - - // We can use any callee trash register (other than RAX, RDI, RSI) for call target. - // We use R11 here. This will emit either - // "call ip-relative 32-bit offset" or - // "mov r11, helper addr; call r11" - genEmitHelperCall(CORINFO_HELP_PROF_FCN_ENTER, 0, EA_UNKNOWN, REG_DEFAULT_PROFILER_CALL_TARGET); - - // If initReg is one of RBM_CALLEE_TRASH, then it needs to be zero'ed before using. - if ((RBM_CALLEE_TRASH & genRegMask(initReg)) != 0) - { - *pInitRegZeroed = false; - } - -#endif // !defined(UNIX_AMD64_ABI) - -#elif defined(_TARGET_X86_) || defined(_TARGET_ARM_) - - unsigned saveStackLvl2 = genStackLevel; - -#if defined(_TARGET_X86_) -// Important note: when you change enter probe layout, you must also update SKIP_ENTER_PROF_CALLBACK() -// for x86 stack unwinding - -#if defined(UNIX_X86_ABI) - // Manually align the stack to be 16-byte aligned. This is similar to CodeGen::genAlignStackBeforeCall() - getEmitter()->emitIns_R_I(INS_sub, EA_4BYTE, REG_SPBASE, 0xC); -#endif // UNIX_X86_ABI - - // Push the profilerHandle - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_AR_R(INS_push, EA_PTR_DSP_RELOC, REG_NA, REG_NA, (ssize_t)compiler->compProfilerMethHnd); - } - else - { - inst_IV(INS_push, (size_t)compiler->compProfilerMethHnd); - } - -#elif defined(_TARGET_ARM_) - // On Arm arguments are prespilled on stack, which frees r0-r3. - // For generating Enter callout we would need two registers and one of them has to be r0 to pass profiler handle. - // The call target register could be any free register. - regNumber argReg = REG_PROFILER_ENTER_ARG; - regMaskTP argRegMask = genRegMask(argReg); - assert((regSet.rsMaskPreSpillRegArg & argRegMask) != 0); - - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, argReg, (ssize_t)compiler->compProfilerMethHnd); - regSet.verifyRegUsed(argReg); - } - else - { - instGen_Set_Reg_To_Imm(EA_4BYTE, argReg, (ssize_t)compiler->compProfilerMethHnd); - } -#else // _TARGET_* - NYI("Pushing the profilerHandle & caller's sp for the profiler callout and locking registers"); -#endif // _TARGET_* - - // - // Can't have a call until we have enough padding for rejit - // - genPrologPadForReJit(); - - // This will emit either - // "call ip-relative 32-bit offset" or - // "mov rax, helper addr; call rax" - genEmitHelperCall(CORINFO_HELP_PROF_FCN_ENTER, - 0, // argSize. Again, we have to lie about it - EA_UNKNOWN); // retSize - -#if defined(_TARGET_X86_) - // Check that we have place for the push. - assert(compiler->fgPtrArgCntMax >= 1); - -#if defined(UNIX_X86_ABI) - // Restoring alignment manually. This is similar to CodeGen::genRemoveAlignmentAfterCall - getEmitter()->emitIns_R_I(INS_add, EA_4BYTE, REG_SPBASE, 0x10); -#endif // UNIX_X86_ABI - -#elif defined(_TARGET_ARM_) - if (initReg == argReg) - { - *pInitRegZeroed = false; - } -#else // _TARGET_* - NYI("Pushing the profilerHandle & caller's sp for the profiler callout and locking registers"); -#endif // _TARGET_* - - /* Restore the stack level */ - - SetStackLevel(saveStackLvl2); - -#else // target - NYI("Emit Profiler Enter callback"); -#endif // target -} - -//----------------------------------------------------------------------------------- -// 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 -// -// Notes: -// The x86 profile leave/tailcall helper has the following requirements (see ProfileLeaveNaked and -// ProfileTailcallNaked in VM\i386\asmhelpers.asm for details): -// 1. The calling sequence for calling the helper is: -// push FunctionIDOrClientID -// call ProfileLeaveHelper or ProfileTailcallHelper -// 2. The calling function has an EBP frame. -// 3. EBP points to the saved ESP which is the first thing saved in the function. Thus, -// the following prolog is assumed: -// push ESP -// mov EBP, ESP -// 4. helper == CORINFO_HELP_PROF_FCN_LEAVE: All registers are preserved. -// helper == CORINFO_HELP_PROF_FCN_TAILCALL: Only argument registers are preserved. -// 5. The helper pops the FunctionIDOrClientID argument from the stack. -// -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; - -#if defined(_TARGET_AMD64_) -#if !defined(UNIX_AMD64_ABI) - - // Since the method needs to make a profiler callback, it should have out-going arg space allocated. - noway_assert(compiler->lvaOutgoingArgSpaceVar != BAD_VAR_NUM); - noway_assert(compiler->lvaOutgoingArgSpaceSize >= (4 * REGSIZE_BYTES)); - - // If thisPtr needs to be kept alive and reported, it cannot be one of the callee trash - // registers that profiler callback kills. - if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvIsInReg()) - { - regMaskTP thisPtrMask = genRegMask(compiler->lvaTable[compiler->info.compThisArg].lvRegNum); - noway_assert((RBM_PROFILER_LEAVE_TRASH & thisPtrMask) == 0); - } - - // At this point return value is computed and stored in RAX or XMM0. - // On Amd64, Leave callback preserves the return register. We keep - // RAX alive by not reporting as trashed by helper call. Also note - // that GC cannot kick-in while executing inside profiler callback, - // which is a requirement of profiler as well since it needs to examine - // return value which could be an obj ref. - - // RCX = ProfilerMethHnd - if (compiler->compProfilerMethHndIndirected) - { - // Profiler hooks enabled during Ngen time. - // Profiler handle needs to be accessed through an indirection of an address. - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - else - { - // Don't record relocations, if we are generating ELT hooks under the influence - // of COMPlus_JitELTHookEnabled=1 - if (compiler->opts.compJitELTHookEnabled) - { - genSetRegToIcon(REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd, TYP_I_IMPL); - } - else - { - instGen_Set_Reg_To_Imm(EA_8BYTE, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - } - - // RDX = caller's SP - // TODO-AMD64-Cleanup: Once we start doing codegen after final frame layout, retain the "if" portion - // of the stmnts to execute unconditionally and clean-up rest. - if (compiler->lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT) - { - // Caller's SP relative offset to FramePointer will be negative. We need to add absolute - // value of that offset to FramePointer to obtain caller's SP value. - int callerSPOffset = compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed()); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_ARG_1, genFramePointerReg(), -callerSPOffset); - } - else - { - // If we are here means that it is a tentative frame layout during which we - // cannot use caller's SP offset since it is an estimate. For now we require the - // method to have at least a single arg so that we can use it to obtain caller's - // SP. - LclVarDsc* varDsc = compiler->lvaTable; - NYI_IF((varDsc == nullptr) || !varDsc->lvIsParam, "Profiler ELT callback for a method without any params"); - - // lea rdx, [FramePointer + Arg0's offset] - getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_ARG_1, 0, 0); - } - - // We can use any callee trash register (other than RAX, RCX, RDX) for call target. - // We use R8 here. This will emit either - // "call ip-relative 32-bit offset" or - // "mov r8, helper addr; call r8" - genEmitHelperCall(helper, 0, EA_UNKNOWN, REG_ARG_2); - -#else // !defined(UNIX_AMD64_ABI) - - // RDI = ProfilerMethHnd - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_R_AI(INS_mov, EA_PTR_DSP_RELOC, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - else - { - if (compiler->opts.compJitELTHookEnabled) - { - genSetRegToIcon(REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd, TYP_I_IMPL); - } - else - { - instGen_Set_Reg_To_Imm(EA_8BYTE, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - } - - // RSI = caller's SP - if (compiler->lvaDoneFrameLayout == Compiler::FINAL_FRAME_LAYOUT) - { - int callerSPOffset = compiler->lvaToCallerSPRelativeOffset(0, isFramePointerUsed()); - getEmitter()->emitIns_R_AR(INS_lea, EA_PTRSIZE, REG_ARG_1, genFramePointerReg(), -callerSPOffset); - } - else - { - LclVarDsc* varDsc = compiler->lvaTable; - NYI_IF((varDsc == nullptr) || !varDsc->lvIsParam, "Profiler ELT callback for a method without any params"); - - // lea rdx, [FramePointer + Arg0's offset] - getEmitter()->emitIns_R_S(INS_lea, EA_PTRSIZE, REG_ARG_1, 0, 0); - } - - // We can use any callee trash register (other than RAX, RDI, RSI) for call target. - // We use R11 here. This will emit either - // "call ip-relative 32-bit offset" or - // "mov r11, helper addr; call r11" - genEmitHelperCall(helper, 0, EA_UNKNOWN, REG_DEFAULT_PROFILER_CALL_TARGET); - -#endif // !defined(UNIX_AMD64_ABI) - -#elif defined(_TARGET_X86_) - -#if defined(UNIX_X86_ABI) - // Manually align the stack to be 16-byte aligned. This is similar to CodeGen::genAlignStackBeforeCall() - getEmitter()->emitIns_R_I(INS_sub, EA_4BYTE, REG_SPBASE, 0xC); - AddStackLevel(0xC); - AddNestedAlignment(0xC); -#endif // UNIX_X86_ABI - - // - // Push the profilerHandle - // - - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_AR_R(INS_push, EA_PTR_DSP_RELOC, REG_NA, REG_NA, (ssize_t)compiler->compProfilerMethHnd); - } - else - { - inst_IV(INS_push, (size_t)compiler->compProfilerMethHnd); - } - genSinglePush(); - -#if defined(UNIX_X86_ABI) - int argSize = -REGSIZE_BYTES; // negative means caller-pop (cdecl) -#else - int argSize = REGSIZE_BYTES; -#endif - genEmitHelperCall(helper, argSize, EA_UNKNOWN /* retSize */); - - // Check that we have place for the push. - assert(compiler->fgPtrArgCntMax >= 1); - -#if defined(UNIX_X86_ABI) - // Restoring alignment manually. This is similar to CodeGen::genRemoveAlignmentAfterCall - getEmitter()->emitIns_R_I(INS_add, EA_4BYTE, REG_SPBASE, 0x10); - SubtractStackLevel(0x10); - SubtractNestedAlignment(0xC); -#endif // UNIX_X86_ABI - -#elif defined(_TARGET_ARM_) - // - // Push the profilerHandle - // - - // Contract between JIT and Profiler Leave callout on arm: - // Return size <= 4 bytes: REG_PROFILER_RET_SCRATCH will contain return value - // Return size > 4 and <= 8: <REG_PROFILER_RET_SCRATCH,r1> will contain return value. - // Floating point or double or HFA return values will be in s0-s15 in case of non-vararg methods. - // It is assumed that profiler Leave callback doesn't trash registers r1,REG_PROFILER_RET_SCRATCH and s0-s15. - // - // In the following cases r0 doesn't contain a return value and hence need not be preserved before emitting Leave - // callback. - bool r0Trashed; - emitAttr attr = EA_UNKNOWN; - - if (compiler->info.compRetType == TYP_VOID || (!compiler->info.compIsVarArgs && !compiler->opts.compUseSoftFP && - (varTypeIsFloating(compiler->info.compRetType) || - compiler->IsHfa(compiler->info.compMethodInfo->args.retTypeClass)))) - { - r0Trashed = false; - } - else - { - // Has a return value and r0 is in use. For emitting Leave profiler callout we would need r0 for passing - // profiler handle. Therefore, r0 is moved to REG_PROFILER_RETURN_SCRATCH as per contract. - if (RBM_ARG_0 & gcInfo.gcRegGCrefSetCur) - { - attr = EA_GCREF; - gcInfo.gcMarkRegSetGCref(RBM_PROFILER_RET_SCRATCH); - } - else if (RBM_ARG_0 & gcInfo.gcRegByrefSetCur) - { - attr = EA_BYREF; - gcInfo.gcMarkRegSetByref(RBM_PROFILER_RET_SCRATCH); - } - else - { - attr = EA_4BYTE; - } - - getEmitter()->emitIns_R_R(INS_mov, attr, REG_PROFILER_RET_SCRATCH, REG_ARG_0); - regSet.verifyRegUsed(REG_PROFILER_RET_SCRATCH); - gcInfo.gcMarkRegSetNpt(RBM_ARG_0); - r0Trashed = true; - } - - if (compiler->compProfilerMethHndIndirected) - { - getEmitter()->emitIns_R_AI(INS_ldr, EA_PTR_DSP_RELOC, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - regSet.verifyRegUsed(REG_ARG_0); - } - else - { - instGen_Set_Reg_To_Imm(EA_4BYTE, REG_ARG_0, (ssize_t)compiler->compProfilerMethHnd); - } - - genEmitHelperCall(CORINFO_HELP_PROF_FCN_LEAVE, - 0, // argSize - EA_UNKNOWN); // retSize - - // Restore state that existed before profiler callback - if (r0Trashed) - { - getEmitter()->emitIns_R_R(INS_mov, attr, REG_ARG_0, REG_PROFILER_RET_SCRATCH); - regSet.verifyRegUsed(REG_ARG_0); - gcInfo.gcMarkRegSetNpt(RBM_PROFILER_RET_SCRATCH); - } - -#else // target - NYI("Emit Profiler Leave callback"); -#endif // target - - /* Restore the stack level */ - SetStackLevel(saveStackLvl2); -} - -#endif // PROFILING_SUPPORTED - /***************************************************************************** Esp frames : @@ -11628,7 +11053,7 @@ void CodeGen::genReturn(GenTree* treeNode) } } - genProfilingLeaveCallback(); + genProfilingLeaveCallback(CORINFO_HELP_PROF_FCN_LEAVE); if (varTypeIsGC(compiler->info.compRetType)) { |