-
Notifications
You must be signed in to change notification settings - Fork 5.4k
[clr-interp] Add support for a new model of stack handling for funclets in the interpreter #119787
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
4b5b971
5d9a4fe
23435e7
1a50cdb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -735,7 +735,22 @@ uint32_t InterpCompiler::ConvertOffset(int32_t offset) | |
| return offset * sizeof(int32_t) + sizeof(void*); | ||
| } | ||
|
|
||
| int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc*, MemPoolAllocator> *relocs) | ||
| int32_t InterpCompiler::GetFuncletAdjustedVarOffset(int varIndex, bool forFunclet) | ||
| { | ||
| #ifndef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| if (forFunclet && m_pVars[varIndex].global) | ||
| { | ||
| // In funclets, global vars are accessed relative to the main method frame pointer | ||
| return -(m_pVars[varIndex].offset + FUNCLET_STACK_ADJUSTMENT_OFFSET); | ||
| } | ||
| else | ||
| #endif // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| { | ||
| return m_pVars[varIndex].offset; | ||
| } | ||
| } | ||
|
|
||
| int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc*, MemPoolAllocator> *relocs, bool forFunclet) | ||
| { | ||
| ins->nativeOffset = (int32_t)(ip - m_pMethodCode); | ||
|
|
||
|
|
@@ -749,7 +764,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc* | |
| if (opcode == INTOP_SWITCH) | ||
| { | ||
| int32_t numLabels = ins->data [0]; | ||
| *ip++ = m_pVars[ins->sVars[0]].offset; | ||
| *ip++ = GetFuncletAdjustedVarOffset(ins->sVars[0], forFunclet); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Re the comment I just left, in this context: could we do something like:
Then we aren't mixing InterpVar and indexOfInterpVar in various places and accessing the offset is always done in a way that makes the funclet-ness explicit. |
||
| *ip++ = numLabels; | ||
| // Add relocation for each label | ||
| for (int32_t i = 0; i < numLabels; i++) | ||
|
|
@@ -764,7 +779,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc* | |
| { | ||
| int32_t brBaseOffset = (int32_t)(startIp - m_pMethodCode); | ||
| for (int i = 0; i < g_interpOpSVars[opcode]; i++) | ||
| *ip++ = m_pVars[ins->sVars[i]].offset; | ||
| *ip++ = GetFuncletAdjustedVarOffset(ins->sVars[i], forFunclet); | ||
|
|
||
| if (ins->info.pTargetBB->nativeOffset >= 0) | ||
| { | ||
|
|
@@ -796,9 +811,13 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc* | |
| // Revert opcode emit | ||
| ip--; | ||
|
|
||
| int destOffset = m_pVars[ins->dVar].offset; | ||
| int srcOffset = m_pVars[ins->sVars[0]].offset; | ||
| srcOffset += fOffset; | ||
| int destOffset = GetFuncletAdjustedVarOffset(ins->dVar, forFunclet); | ||
| int srcOffset = GetFuncletAdjustedVarOffset(ins->sVars[0], forFunclet); | ||
| if (srcOffset >= 0) | ||
| srcOffset += fOffset; | ||
| else | ||
| srcOffset -= fOffset; | ||
|
Comment on lines
+816
to
+819
|
||
|
|
||
| if (fSize) | ||
| opcode = INTOP_MOV_VT; | ||
| else | ||
|
|
@@ -819,8 +838,8 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc* | |
| { | ||
| // This opcode references a var, int sVars[0], but it is not registered as a source for it | ||
| // aka g_interpOpSVars[INTOP_LDLOCA] is 0. | ||
| *ip++ = m_pVars[ins->dVar].offset; | ||
| *ip++ = m_pVars[ins->sVars[0]].offset; | ||
| *ip++ = GetFuncletAdjustedVarOffset(ins->dVar, forFunclet); | ||
| *ip++ = GetFuncletAdjustedVarOffset(ins->sVars[0], forFunclet); | ||
| } | ||
| else | ||
| { | ||
|
|
@@ -829,7 +848,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc* | |
| // variable we emit another offset. Finally, we will emit any additional data needed | ||
| // by the instruction. | ||
| if (g_interpOpDVars[opcode]) | ||
| *ip++ = m_pVars[ins->dVar].offset; | ||
| *ip++ = GetFuncletAdjustedVarOffset(ins->dVar, forFunclet); | ||
|
|
||
| if (g_interpOpSVars[opcode]) | ||
| { | ||
|
|
@@ -841,7 +860,7 @@ int32_t* InterpCompiler::EmitCodeIns(int32_t *ip, InterpInst *ins, TArray<Reloc* | |
| } | ||
| else | ||
| { | ||
| *ip++ = m_pVars[ins->sVars[i]].offset; | ||
| *ip++ = GetFuncletAdjustedVarOffset(ins->sVars[i], forFunclet); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -927,7 +946,7 @@ int32_t *InterpCompiler::EmitBBCode(int32_t *ip, InterpBasicBlock *bb, TArray<Re | |
| continue; | ||
| } | ||
|
|
||
| ip = EmitCodeIns(ip, ins, relocs); | ||
| ip = EmitCodeIns(ip, ins, relocs, bb->clauseNonTryType != BBClauseNone); | ||
| } | ||
|
|
||
| m_pCBB->nativeEndOffset = (int32_t)(ip - m_pMethodCode); | ||
|
|
@@ -1156,17 +1175,28 @@ class InterpGcSlotAllocator | |
|
|
||
| void AllocateOrReuseGcSlot(uint32_t offsetBytes, GcSlotFlags flags) | ||
| { | ||
| GcSlotId *pSlot = LocateGcSlotTableEntry(offsetBytes, flags); | ||
| bool allocateNewSlot = *pSlot == ((GcSlotId)-1); | ||
|
|
||
| if (allocateNewSlot) | ||
| GcSlotId slot; | ||
| bool allocateNewSlot; | ||
| if (flags & GC_SLOT_UNTRACKED) | ||
| { | ||
| // Important to pass GC_FRAMEREG_REL, the default is broken due to GET_CALLER_SP being unimplemented | ||
| *pSlot = m_encoder->GetStackSlotId(offsetBytes, flags, GC_FRAMEREG_REL); | ||
| allocateNewSlot = true; | ||
| slot = m_encoder->GetStackSlotId(offsetBytes, flags, (flags & GC_SLOT_UNTRACKED) ? GC_FRAMEREG_REL : GC_SP_REL);; | ||
| } | ||
| else | ||
| { | ||
| assert((flags & GC_SLOT_UNTRACKED) == 0); | ||
| GcSlotId *pSlot = LocateGcSlotTableEntry(offsetBytes, flags); | ||
|
|
||
| allocateNewSlot = *pSlot == ((GcSlotId)-1); | ||
|
|
||
| if (allocateNewSlot) | ||
| { | ||
| // Important to pass GC_FRAMEREG_REL, the default is broken due to GET_CALLER_SP being unimplemented | ||
| slot = *pSlot = m_encoder->GetStackSlotId(offsetBytes, flags, (flags & GC_SLOT_UNTRACKED) ? GC_FRAMEREG_REL : GC_SP_REL); | ||
| } | ||
| else | ||
| { | ||
| slot = *pSlot; | ||
| } | ||
| } | ||
|
|
||
| INTERP_DUMP( | ||
|
|
@@ -1175,7 +1205,7 @@ class InterpGcSlotAllocator | |
| (flags & GC_SLOT_UNTRACKED) ? "global " : "", | ||
| (flags & GC_SLOT_INTERIOR) ? "interior " : "", | ||
| (flags & GC_SLOT_PINNED) ? "pinned " : "", | ||
| *pSlot, | ||
| slot, | ||
| offsetBytes | ||
| ); | ||
| } | ||
|
|
@@ -2084,17 +2114,29 @@ void InterpCompiler::InitializeClauseBuildingBlocks(CORINFO_METHOD_INFO* methodI | |
| for (uint32_t j = clause.HandlerOffset; j < (clause.HandlerOffset + clause.HandlerLength); j++) | ||
| { | ||
| InterpBasicBlock* pBB = m_ppOffsetToBB[j]; | ||
| if (pBB != NULL && pBB->clauseType == BBClauseNone) | ||
|
|
||
| if (pBB == NULL) | ||
| continue; | ||
|
|
||
| uint8_t clauseType; | ||
| if ((clause.Flags == CORINFO_EH_CLAUSE_NONE) || (clause.Flags == CORINFO_EH_CLAUSE_FILTER)) | ||
| { | ||
| if ((clause.Flags == CORINFO_EH_CLAUSE_NONE) || (clause.Flags == CORINFO_EH_CLAUSE_FILTER)) | ||
| { | ||
| pBB->clauseType = BBClauseCatch; | ||
| } | ||
| else | ||
| { | ||
| assert((clause.Flags == CORINFO_EH_CLAUSE_FINALLY) || (clause.Flags == CORINFO_EH_CLAUSE_FAULT)); | ||
| pBB->clauseType = BBClauseFinally; | ||
| } | ||
| clauseType = BBClauseCatch; | ||
| } | ||
| else | ||
| { | ||
| assert((clause.Flags == CORINFO_EH_CLAUSE_FINALLY) || (clause.Flags == CORINFO_EH_CLAUSE_FAULT)); | ||
| clauseType = BBClauseFinally; | ||
| } | ||
|
|
||
| if (pBB->clauseType == BBClauseNone) | ||
| { | ||
| pBB->clauseType = clauseType; | ||
| } | ||
|
|
||
| if (pBB->clauseNonTryType == BBClauseNone) | ||
| { | ||
| pBB->clauseNonTryType = clauseType; | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -2118,10 +2160,17 @@ void InterpCompiler::InitializeClauseBuildingBlocks(CORINFO_METHOD_INFO* methodI | |
| for (uint32_t j = clause.FilterOffset; j < clause.HandlerOffset; j++) | ||
| { | ||
| InterpBasicBlock* pBB = m_ppOffsetToBB[j]; | ||
| if (pBB != NULL && pBB->clauseType == BBClauseNone) | ||
| if (pBB == NULL) | ||
| continue; | ||
|
|
||
| if (pBB->clauseType == BBClauseNone) | ||
| { | ||
| pBB->clauseType = BBClauseFilter; | ||
| } | ||
| if (pBB->clauseNonTryType == BBClauseNone) | ||
| { | ||
| pBB->clauseNonTryType = BBClauseFilter; | ||
| } | ||
| } | ||
| } | ||
| else if (clause.Flags == CORINFO_EH_CLAUSE_FINALLY|| clause.Flags == CORINFO_EH_CLAUSE_FAULT) | ||
|
|
@@ -2165,6 +2214,7 @@ void InterpCompiler::InitializeClauseBuildingBlocks(CORINFO_METHOD_INFO* methodI | |
| InterpBasicBlock* pAfterFinallyBB = m_ppOffsetToBB[clause.HandlerOffset + clause.HandlerLength]; | ||
| assert(pAfterFinallyBB != NULL); | ||
| pFinallyCallIslandBB->clauseType = pAfterFinallyBB->clauseType; | ||
| pFinallyCallIslandBB->clauseNonTryType = pAfterFinallyBB->clauseNonTryType; | ||
| pFinallyCallIslandBB = pFinallyCallIslandBB->pNextBB; | ||
| } | ||
| } | ||
|
|
@@ -2266,7 +2316,8 @@ void InterpCompiler::EmitLoadVar(int32_t var) | |
| InterpType interpType = m_pVars[var].interpType; | ||
| CORINFO_CLASS_HANDLE clsHnd = m_pVars[var].clsHnd; | ||
|
|
||
| if (m_pCBB->clauseType == BBClauseFilter) | ||
| #ifdef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| if (m_pCBB->clauseNonTryType == BBClauseFilter) | ||
| { | ||
| assert(m_pVars[var].ILGlobal); | ||
| AddIns(INTOP_LOAD_FRAMEVAR); | ||
|
|
@@ -2275,6 +2326,7 @@ void InterpCompiler::EmitLoadVar(int32_t var) | |
| EmitLdind(interpType, clsHnd, m_pVars[var].offset); | ||
| return; | ||
| } | ||
| #endif // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
|
|
||
| int32_t size = m_pVars[var].size; | ||
|
|
||
|
|
@@ -2301,14 +2353,16 @@ void InterpCompiler::EmitStoreVar(int32_t var) | |
| InterpType interpType = m_pVars[var].interpType; | ||
| CHECK_STACK(1); | ||
|
|
||
| if (m_pCBB->clauseType == BBClauseFilter) | ||
| #ifdef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| if (m_pCBB->clauseNonTryType == BBClauseFilter) | ||
| { | ||
| AddIns(INTOP_LOAD_FRAMEVAR); | ||
| PushInterpType(InterpTypeI, NULL); | ||
| m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); | ||
| EmitStind(interpType, m_pVars[var].clsHnd, m_pVars[var].offset, true /* reverseSVarOrder */); | ||
| return; | ||
| } | ||
| #endif // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
|
|
||
| #ifdef TARGET_64BIT | ||
| // nint and int32 can be used interchangeably. Add implicit conversions. | ||
|
|
@@ -3972,7 +4026,8 @@ void InterpCompiler::EmitLdLocA(int32_t var) | |
| m_shadowCopyOfThisPointerActuallyNeeded = true; | ||
| } | ||
|
|
||
| if (m_pCBB->clauseType == BBClauseFilter) | ||
| #ifdef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| if (m_pCBB->clauseNonTryType == BBClauseFilter) | ||
| { | ||
| AddIns(INTOP_LOAD_FRAMEVAR); | ||
| PushInterpType(InterpTypeI, NULL); | ||
|
|
@@ -3985,6 +4040,7 @@ void InterpCompiler::EmitLdLocA(int32_t var) | |
| m_pLastNewIns->SetDVar(m_pStackPointer[-1].var); | ||
| return; | ||
| } | ||
| #endif // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
|
|
||
| AddIns(INTOP_LDLOCA); | ||
| m_pLastNewIns->SetSVar(var); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -299,7 +299,12 @@ void InterpCompiler::AllocOffsets() | |
| ForEachInsVar(pIns, pIns, &InterpCompiler::SetVarLiveRangeCB); | ||
| insIndex++; | ||
| } | ||
| #ifdef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| int32_t currentOffset = m_totalVarsStackSize; | ||
| #else | ||
| // The first interp stack slot in a funclet is reserved to hold the pointer to the parent frame's stack, the rest is available for local vars | ||
| int32_t currentOffset = pBB->clauseNonTryType == BBClauseNone ? m_totalVarsStackSize : INTERP_STACK_SLOT_SIZE; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need an extra variable for the parent stack frame pointer? Cannot we just read it from the InterpMethodContextFrame?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe that works for funclets not invoked a non-exception finallys. The InterpMethodContextFrame parent pointer doesn't point to the outer frame in those cases.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could set it, the INTOP_CALL_FINALLY initializes the InterpMethodContextFrame explicitly.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, my concern is actually the case for when finallys are NOT invoked via INTOP_CALL_FINALLY. In that case it isn't set. But as we are probably going to go with the move the second pass out of native code, I'm not likely to invest in this change any more. |
||
| #endif // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
|
|
||
| insIndex = 0; | ||
| for (pIns = pBB->pFirstIns; pIns != NULL; pIns = pIns->pNext) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -178,4 +178,14 @@ enum class CalliFlags : int32_t | |
| PInvoke = 1 << 2, // The call is a PInvoke call | ||
| }; | ||
|
|
||
| // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS can be enabled to reuse the stack frame of the outer function for executing catch, finally and fault funclets | ||
| // This feature is not compatible with fully interpreted code, however, as while we execute the funclet we may overwrite the stack space of the DispatchEx | ||
| // function which is part of the actual funclet dispatch path. We may wish to re-enable this feature in the future if we run on a platform where | ||
| // we can guarantee that the DispatchEx and other related functions that make of the EH subsystem will not be interpreted. | ||
| //#define FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
|
|
||
| #ifndef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| #define FUNCLET_STACK_ADJUSTMENT_OFFSET 8 | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an explanation somewhere of why this define exists and why it's 8? I haven't found it yet, so you can ignore this if the answer is 'yes'. I expected the answer to be here.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point, the reason is that we need the "negative" numbers to be distinguishable from the normal accesses, and most notably, we need access to the 0 offset of the normal frame from the funclet. To make the handling of the negative offsets very simple in the interpreter, we adjust those offsets by an arbitrary value. Any positive number here would actually work. I happened to choose 8 since that is the size of a interpreter stack slot, but it might actually be easier to read if we chose a much bigger number like 10000 or something, so that the negated offsets would match up more easily to the positive space offsets. |
||
| #endif // FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
|
|
||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2157,7 +2157,7 @@ DWORD_PTR InterpreterCodeManager::CallFunclet(OBJECTREF throwable, void* pHandle | |
| // InterpMethodContextFrame. This is important for the stack walking code. | ||
| struct Frames | ||
| { | ||
| InterpMethodContextFrame interpMethodContextFrame = {0}; | ||
| InterpMethodContextFrame interpMethodContextFrame; | ||
|
kg marked this conversation as resolved.
|
||
| InterpreterFrame interpreterFrame; | ||
|
|
||
| Frames(TransitionBlock* pTransitionBlock) | ||
|
|
@@ -2176,9 +2176,25 @@ DWORD_PTR InterpreterCodeManager::CallFunclet(OBJECTREF throwable, void* pHandle | |
|
|
||
| StackVal retVal; | ||
|
|
||
| frames.interpMethodContextFrame.startIp = pOriginalFrame->startIp; | ||
| frames.interpMethodContextFrame.pStack = isFilter ? sp : pOriginalFrame->pStack; | ||
| frames.interpMethodContextFrame.pRetVal = (int8_t*)&retVal; | ||
| #ifndef FEATURE_REUSE_INTERPRETER_STACK_FOR_NORMAL_FUNCLETS | ||
| // Frame pointer for original method | ||
| int8_t* originalStack; | ||
| if (pOriginalFrame->IsFuncletFrame()) | ||
| { | ||
| originalStack = *(int8_t**)pOriginalFrame->pStack; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just checking: It's a funclet frame, so we reserved the first sizeof(void*) bytes of the funclet frame's stack to contain the address of the original frame's stack, and we're extracting it from there. |
||
| } | ||
| else | ||
| { | ||
| originalStack = pOriginalFrame->pStack - FUNCLET_STACK_ADJUSTMENT_OFFSET; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't love having this manual adjustment here. Is it possible to bake it into var offset allocation instead and just reserve a magic var at offset 0 for this? Then the stack would be at pStack, I think?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternately, could this data live in the frame instead of being tucked into a weird spot on the stack with offset math in multiple places?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm moving this logic to a helper method on the InterpMethodContextFrame. |
||
| } | ||
| *(int8_t**)sp = originalStack; | ||
| InterpreterFrameReporting frameReporting = isFilter ? InterpreterFrameReporting::FuncletNoReportGlobals : InterpreterFrameReporting::FuncletReportGlobals; | ||
| #else | ||
| sp = isFilter ? sp : pOriginalFrame->pStack; | ||
| InterpreterFrameReporting frameReporting = InterpreterFrameReporting::Normal; | ||
| #endif | ||
|
|
||
| frames.interpMethodContextFrame.ReInit(NULL, pOriginalFrame->startIp, (int8_t*)&retVal, frameReporting, sp); | ||
|
|
||
| ExceptionClauseArgs exceptionClauseArgs; | ||
| exceptionClauseArgs.ip = (const int32_t *)pHandler; | ||
|
|
@@ -2604,8 +2620,7 @@ OBJECTREF InterpreterCodeManager::GetInstance(PREGDISPLAY pContext, | |
| EECodeInfo * pCodeInfo) | ||
| { | ||
| PTR_InterpMethodContextFrame frame = dac_cast<PTR_InterpMethodContextFrame>(GetSP(pContext->pCurrentContext)); | ||
| TADDR baseStackSlot = dac_cast<TADDR>((uintptr_t)frame->pStack); | ||
| return *dac_cast<PTR_OBJECTREF>(baseStackSlot); | ||
| return *dac_cast<PTR_OBJECTREF>(frame->GetFunctionFrameStack()); | ||
| } | ||
|
|
||
| PTR_VOID InterpreterCodeManager::GetParamTypeArg(PREGDISPLAY pContext, | ||
|
|
@@ -2624,8 +2639,7 @@ PTR_VOID InterpreterCodeManager::GetParamTypeArg(PREGDISPLAY pContext, | |
| if (spOffsetGenericsContext != NO_GENERICS_INST_CONTEXT) | ||
| { | ||
| PTR_InterpMethodContextFrame frame = dac_cast<PTR_InterpMethodContextFrame>(GetSP(pContext->pCurrentContext)); | ||
| TADDR baseStackSlot = dac_cast<TADDR>((uintptr_t)frame->pStack); | ||
| TADDR taSlot = (TADDR)( spOffsetGenericsContext + baseStackSlot ); | ||
| TADDR taSlot = (TADDR)(spOffsetGenericsContext + frame->GetFunctionFrameStack()); | ||
| TADDR taExactGenericsToken = *PTR_TADDR(taSlot); | ||
| return PTR_VOID(taExactGenericsToken); | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not an absolute requirement, but if var offsets are no longer actual offsets in certain configurations, I'd like to see
var.offsetbecome something other than an int, so the type system stops us from using it incorrectly - I want any misuse of.offsetto fail to compile instead of silently doing the wrong thing.