diff options
Diffstat (limited to 'src/vm/arm/stubs.cpp')
-rw-r--r-- | src/vm/arm/stubs.cpp | 925 |
1 files changed, 41 insertions, 884 deletions
diff --git a/src/vm/arm/stubs.cpp b/src/vm/arm/stubs.cpp index 078a16cb8f..18eb9969c7 100644 --- a/src/vm/arm/stubs.cpp +++ b/src/vm/arm/stubs.cpp @@ -1682,526 +1682,75 @@ void StubLinkerCPU::ThumbEmitCallManagedMethod(MethodDesc *pMD, bool fTailcall) } } -// Common code used to generate either an instantiating method stub or an unboxing stub (in the case where the -// unboxing stub also needs to provide a generic instantiation parameter). The stub needs to add the -// instantiation parameter provided in pHiddenArg and re-arrange the rest of the incoming arguments as a -// result (since on ARM this hidden parameter is inserted before explicit user arguments we need a type of -// shuffle thunk in the reverse direction of the type used for static delegates). If pHiddenArg == NULL it -// indicates that we're in the unboxing case and should add sizeof(MethodTable*) to the incoming this pointer -// before dispatching to the target. In this case the instantiating parameter is always the non-shared -// MethodTable pointer we can deduce directly from the incoming 'this' reference. -void StubLinkerCPU::ThumbEmitCallWithGenericInstantiationParameter(MethodDesc *pMD, void *pHiddenArg) +VOID StubLinkerCPU::EmitComputedInstantiatingMethodStub(MethodDesc* pSharedMD, struct ShuffleEntry *pShuffleEntryArray, void* extraArg) { - // There is a simple case and a complex case. - // 1) In the simple case the addition of the hidden arg doesn't push any user args onto the stack. In - // this case we only have to re-arrange/initialize some argument registers and tail call to the - // target. - // 2) In the complex case we have to modify the stack by pushing some of the register based user - // arguments. We can't tail call in this case because we've altered the size of the stack and our - // caller doesn't expect this and can't compensate. Instead we'll need to create a stack frame - // (including an explicit Frame to make it crawlable to the runtime) and copy the incoming arguments - // over. - // - // First we need to analyze the signature of the target method both with and without the extra - // instantiation argument. We use ArgIterator to determine the difference in location - // (register or stack offset) for each argument between the two cases. This forms a set instructions that - // tell us how to copy incoming arguments into outgoing arguments (and if those instructions don't include - // any writes to stack locations in the outgoing case then we know we can generate a simple thunk). - - SigTypeContext sTypeContext(pMD, TypeHandle()); - - // Incoming, source, method signature. - MetaSig sSrcSig(pMD->GetSignature(), - pMD->GetModule(), - &sTypeContext, - MetaSig::sigMember); - - // Outgoing, destination, method signature. - MetaSig sDstSig(pMD->GetSignature(), - pMD->GetModule(), - &sTypeContext, - MetaSig::sigMember); - - sDstSig.SetHasParamTypeArg(); - - // Wrap calling convention parsers round the source and destination signatures. These will be responsible - // for determining where each argument lives in registers or on the stack. - ArgIterator sSrcArgLocations(&sSrcSig); - ArgIterator sDstArgLocations(&sDstSig); - - // Define an argument descriptor type that describes how a single 4 byte portion of an argument is mapped - // in the source and destination signature. We only have to worry about general registers and stack - // locations here; floating point argument registers are left unmodified by this thunk. - struct ArgDesc - { - int m_idxSrc; // Source register or stack offset - int m_idxDst; // Destination register or stack offset - bool m_fSrcIsReg; // Source index is a register number - bool m_fDstIsReg; // Destination index is a register number - }; - - // The number of argument move descriptors we'll need is a function of the number of 4-byte registers or - // stack slots the arguments occupy. The following calculation will over-estimate in a few side cases, but - // not by much (it assumes all four argument registers are used plus the number of stack slots that - // MetaSig calculates are needed for the rest of the arguments). - DWORD cArgDescriptors = 4 + (sSrcArgLocations.SizeOfArgStack() / 4); - - // Allocate the array of argument descriptors. - CQuickArray<ArgDesc> rgArgDescs; - rgArgDescs.AllocThrows(cArgDescriptors); - - // We only need to map translations for arguments that could come after the instantiation parameter we're - // inserting. On the ARM the only implicit argument that could follow is a vararg signature cookie, but - // it's disallowed in this case. So we simply walk the user arguments. - _ASSERTE(!sSrcSig.IsVarArg()); - - INT srcOffset; - INT dstOffset; - - DWORD idxCurrentDesc = 0; - while ((srcOffset = sSrcArgLocations.GetNextOffset()) != TransitionBlock::InvalidOffset) - { - dstOffset = sDstArgLocations.GetNextOffset(); - - // Get the placement for a single argument in the source and destination signatures (may include - // multiple registers and/or stack locations if the argument is larger than 4 bytes). - ArgLocDesc sSrcArgLoc; - sSrcArgLocations.GetArgLoc(srcOffset, &sSrcArgLoc); - ArgLocDesc sDstArgLoc; - sDstArgLocations.GetArgLoc(dstOffset, &sDstArgLoc); - - // Fill in as many single-slot descriptors as the argument needs. Note that we ignore any floating - // point register cases (m_cFloatReg > 0) since these will never change due to the hidden arg - // insertion. - while (sSrcArgLoc.m_cGenReg || sSrcArgLoc.m_cStack) - { - _ASSERTE(idxCurrentDesc < cArgDescriptors); - - if (sSrcArgLoc.m_cGenReg) - { - sSrcArgLoc.m_cGenReg--; - rgArgDescs[idxCurrentDesc].m_idxSrc = sSrcArgLoc.m_idxGenReg++; - rgArgDescs[idxCurrentDesc].m_fSrcIsReg = true; - } - else - { - _ASSERTE(sSrcArgLoc.m_cStack > 0); - sSrcArgLoc.m_cStack--; - rgArgDescs[idxCurrentDesc].m_idxSrc = sSrcArgLoc.m_idxStack++; - rgArgDescs[idxCurrentDesc].m_fSrcIsReg = false; - } - - if (sDstArgLoc.m_cGenReg) - { - sDstArgLoc.m_cGenReg--; - rgArgDescs[idxCurrentDesc].m_idxDst = sDstArgLoc.m_idxGenReg++; - rgArgDescs[idxCurrentDesc].m_fDstIsReg = true; - } - else - { - _ASSERTE(sDstArgLoc.m_cStack > 0); - sDstArgLoc.m_cStack--; - rgArgDescs[idxCurrentDesc].m_idxDst = sDstArgLoc.m_idxStack++; - rgArgDescs[idxCurrentDesc].m_fDstIsReg = false; - } - - idxCurrentDesc++; - } - } - - bool isRelative = MethodTable::VTableIndir2_t::isRelative - && pMD->IsVtableSlot(); - -#ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS - _ASSERTE(!isRelative); -#endif - - // Update descriptor count to the actual number used. - cArgDescriptors = idxCurrentDesc; - - // Note the position at which we have the first move to a stack location - DWORD idxFirstMoveToStack = -1; + STANDARD_VM_CONTRACT; - // We have a problem where register to register moves are concerned. Since we're adding an argument the - // moves will be from a lower numbered register to a higher numbered one (e.g. r0 -> r1). But the argument - // descriptors we just produced will order them starting from the lowest registers. If we emit move - // instructions in this order we'll end up copying the value of the lowest register into all of the rest - // (e.g. r0 -> r1, r1 -> r2 etc.). We don't have this problem with stack based arguments since the - // argument stacks don't overlap in the same fashion. To solve this we'll reverse the order of the - // descriptors with register destinations (there will be at most four of these so it's fairly cheap). - if (cArgDescriptors > 1) + struct ShuffleEntry *pEntry = pShuffleEntryArray; + while (pEntry->srcofs != ShuffleEntry::SENTINEL) { - // Start by assuming we have all four register destination descriptors. - int idxLastRegDesc = min(3, cArgDescriptors - 1); + _ASSERTE(pEntry->dstofs & ShuffleEntry::REGMASK); + _ASSERTE(pEntry->srcofs & ShuffleEntry::REGMASK); + _ASSERTE(!(pEntry->dstofs & ShuffleEntry::FPREGMASK)); + _ASSERTE(!(pEntry->srcofs & ShuffleEntry::FPREGMASK)); + _ASSERTE(pEntry->dstofs != ShuffleEntry::HELPERREG); + _ASSERTE(pEntry->srcofs != ShuffleEntry::HELPERREG); - // Adjust that count to match reality. - while (idxLastRegDesc >= 0 && !rgArgDescs[idxLastRegDesc].m_fDstIsReg) - { - idxLastRegDesc--; - } - - if (idxLastRegDesc < 0) - { - // No register is used to pass any of the parameters. No need to reverse the order of the descriptors - idxFirstMoveToStack = 0; - } - else - { - _ASSERTE(idxLastRegDesc >= 0 && ((DWORD)idxLastRegDesc) < cArgDescriptors); - - // First move to stack location happens after the last move to register location - idxFirstMoveToStack = idxLastRegDesc+1; - - // Calculate how many descriptors we'll need to swap. - DWORD cSwaps = (idxLastRegDesc + 1) / 2; - // Finally we can swap the descriptors. - int idxFirstRegDesc = 0; - while (cSwaps) - { - ArgDesc sTempDesc = rgArgDescs[idxLastRegDesc]; - rgArgDescs[idxLastRegDesc] = rgArgDescs[idxFirstRegDesc]; - rgArgDescs[idxFirstRegDesc] = sTempDesc; - - _ASSERTE(idxFirstRegDesc < idxLastRegDesc); - idxFirstRegDesc++; - idxLastRegDesc--; - cSwaps--; - } - } + ThumbEmitMovRegReg(ThumbReg(pEntry->dstofs & ShuffleEntry::OFSMASK), + ThumbReg(pEntry->srcofs & ShuffleEntry::OFSMASK)); + pEntry++; } - // If we're ever required to write to the destination stack then we can't implement this case with a - // simple tail call stub. (That's not technically true: there are edge cases caused by 64-bit alignment - // requirements that might allow us to use a simple stub since the extra argument fits in a "hole" in the - // arguments, but these are infrequent enough that it's likely not worth the effort of detecting them). - ArgDesc *pLastArg = cArgDescriptors ? &rgArgDescs[cArgDescriptors - 1] : NULL; - if ((pLastArg == NULL) || pLastArg->m_fDstIsReg) + MetaSig msig(pSharedMD); + ArgIterator argit(&msig); + if (argit.HasParamType()) { - // Simple case where we can just rearrange a few argument registers and tail call. - - for (idxCurrentDesc = 0; idxCurrentDesc < cArgDescriptors; idxCurrentDesc++) - { - // Because we're in the simple case we know we'll never be asked to move a value onto the stack - // and since we're adding a parameter we should never be required to move a value from the stack - // to a register either. So all of the descriptors should be register to register moves. - _ASSERTE(rgArgDescs[idxCurrentDesc].m_fSrcIsReg && rgArgDescs[idxCurrentDesc].m_fDstIsReg); - ThumbEmitMovRegReg(ThumbReg(rgArgDescs[idxCurrentDesc].m_idxDst), - ThumbReg(rgArgDescs[idxCurrentDesc].m_idxSrc)); - } - // Place instantiation parameter into the correct register. ArgLocDesc sInstArgLoc; - sDstArgLocations.GetParamTypeLoc(&sInstArgLoc); + argit.GetParamTypeLoc(&sInstArgLoc); int regHidden = sInstArgLoc.m_idxGenReg; _ASSERTE(regHidden != -1); - if (pHiddenArg) - { - // mov regHidden, #pHiddenArg - ThumbEmitMovConstant(ThumbReg(regHidden), (TADDR)pHiddenArg); - } - else - { - // Extract MethodTable pointer (the hidden arg) from the object instance. - // ldr regHidden, [r0] - ThumbEmitLoadRegIndirect(ThumbReg(regHidden), ThumbReg(0), 0); - } - - if (pHiddenArg == NULL) - { - // Unboxing stub case. - - // Skip over the MethodTable* to find the address of the unboxed value type. - // add r0, #sizeof(MethodTable*) - ThumbEmitIncrement(ThumbReg(0), sizeof(MethodTable*)); - } - - // Emit a tail call to the target method. - if (isRelative) + if (extraArg == NULL) { - ThumbEmitProlog(1, 0, FALSE); - } - - ThumbEmitCallManagedMethod(pMD, true); - - if (isRelative) - { - ThumbEmitEpilog(); - } - } - else - { - // Complex case where we need to emit a new stack frame and copy the arguments. - - // Calculate the size of the new stack frame: - // - // +------------+ - // SP -> | | <-- Space for helper arg, if isRelative is true - // +------------+ - // | | <-+ - // : : | Outgoing arguments - // | | <-+ - // +------------+ - // | Padding | <-- Optional, maybe required so that SP is 64-bit aligned - // +------------+ - // | GS Cookie | - // +------------+ - // +-> | vtable ptr | - // | +------------+ - // | | m_Next | - // | +------------+ - // | | R4 | <-+ - // Stub | +------------+ | - // Helper | : : | - // Frame | +------------+ | Callee saved registers - // | | R11 | | - // | +------------+ | - // | | LR/RetAddr | <-+ - // | +------------+ - // | | R0 | <-+ - // | +------------+ | - // | : : | Argument registers - // | +------------+ | - // +-> | R3 | <-+ - // +------------+ - // Old SP -> | | - // - DWORD cbStackArgs = (pLastArg->m_idxDst + 1) * 4; - DWORD cbStackFrame = cbStackArgs + sizeof(GSCookie) + sizeof(StubHelperFrame); - cbStackFrame = ALIGN_UP(cbStackFrame, 8); - - if (isRelative) - { - cbStackFrame += 4; - } - - DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr - - // Prolog: - ThumbEmitProlog(8, // Save r4-r11,lr (count doesn't include lr) - cbStackFrameWithoutSavedRegs, // Additional space in the stack frame required - TRUE); // Push argument registers - - DWORD offsetOfFrame = cbStackFrame - sizeof(StubHelperFrame); - - // Initialize and link the StubHelperFrame and associated GS cookie. - EmitStubLinkFrame(StubHelperFrame::GetMethodFrameVPtr(), offsetOfFrame, StubHelperFrame::GetOffsetOfTransitionBlock()); - - // Initialize temporary registers used when copying arguments: - // r6 == pointer to first incoming stack-based argument - // r7 == pointer to first outgoing stack-based argument - - // add r6, sp, #cbStackFrame - ThumbEmitAdd(ThumbReg(6), thumbRegSp, cbStackFrame); - - // mov r7, sp - ThumbEmitMovRegReg(ThumbReg(7), thumbRegSp); - - // Copy incoming to outgoing arguments. Stack arguments are generally written consecutively and as - // such we use post-increment forms of register indirect addressing to keep our input (r6) and output - // (r7) pointers up to date. But sometimes we'll skip four bytes due to 64-bit alignment requirements - // and need to bump one or both of the pointers to compensate. We determine - // - // At this point, the ArgumentDescriptor array is divied into two parts: - // - // 1) Reverse sorted register to register moves (see the comment earlier in the method for details) - // 2) Register or Stack to Stack moves (if any) in the original order. - // - // Its possible that the register to register moves may move to a target register that happens - // to be a source for the register -> stack move. If this happens, and we emit the argument moves - // in the current order, then we can lose the contents of the register involved in register->stack - // move (stack->stack moves are not a problem as the locations dont overlap). - // - // To address this, we will emit the argument moves in two loops: - // - // 1) First loop will emit the moves that have stack location as the target - // 2) Second loop will emit moves that have register as the target. - DWORD idxCurrentLoopBegin = 0, idxCurrentLoopEnd = cArgDescriptors; - if (idxFirstMoveToStack != -1) - { - _ASSERTE(idxFirstMoveToStack < cArgDescriptors); - idxCurrentLoopBegin = idxFirstMoveToStack; - - for (idxCurrentDesc = idxCurrentLoopBegin; idxCurrentDesc < idxCurrentLoopEnd; idxCurrentDesc++) + if (pSharedMD->RequiresInstMethodTableArg()) { - ArgDesc *pArgDesc = &rgArgDescs[idxCurrentDesc]; - - if (pArgDesc->m_fSrcIsReg) - { - // Source value is in a register. - - _ASSERTE(!pArgDesc->m_fDstIsReg); - // Register to stack. Calculate delta from last stack write; normally it will be 4 bytes - // and our pointer has already been set up correctly by the post increment of the last - // write. But in some cases we need to skip four bytes due to a 64-bit alignment - // requirement. In those cases we need to emit an extra add to keep the pointer correct. - // Note that the first stack argument is guaranteed to be 64-bit aligned by the ABI and as - // such the first stack slot is never skipped. - if ((pArgDesc->m_idxDst > 0) && - (pArgDesc->m_idxDst != (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 1))) - { - _ASSERTE(pArgDesc->m_idxDst == (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 2)); - ThumbEmitIncrement(ThumbReg(7), 4); - } - - // str srcReg, [r7], #4 - ThumbEmitStoreIndirectPostIncrement(pArgDesc->m_idxSrc, ThumbReg(7), 4); - } - else - { - // Source value is on the stack. We should have no cases where a stack argument moves back to - // a register (because we're adding an argument). - _ASSERTE(!pArgDesc->m_fDstIsReg); - - // Stack to stack move. We need to use register (r6) to store the value temporarily between - // the read and the write. See the comments above for why we need to check stack deltas and - // possibly insert extra add instructions in some cases. - if ((pArgDesc->m_idxSrc > 0) && - (pArgDesc->m_idxSrc != (rgArgDescs[idxCurrentDesc - 1].m_idxSrc + 1))) - { - _ASSERTE(pArgDesc->m_idxSrc == (rgArgDescs[idxCurrentDesc - 1].m_idxSrc + 2)); - ThumbEmitIncrement(ThumbReg(6), 4); - } - if ((pArgDesc->m_idxDst > 0) && - (pArgDesc->m_idxDst != (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 1))) - { - _ASSERTE(pArgDesc->m_idxDst == (rgArgDescs[idxCurrentDesc - 1].m_idxDst + 2)); - ThumbEmitIncrement(ThumbReg(7), 4); - } - - // ldr r8, [r6], #4 - ThumbEmitLoadIndirectPostIncrement(ThumbReg(8), ThumbReg(6), 4); - - // str r8, [r7], #4 - ThumbEmitStoreIndirectPostIncrement(ThumbReg(8), ThumbReg(7), 4); - } + // Unboxing stub case + // Extract MethodTable pointer (the hidden arg) from the object instance. + // ldr regHidden, [r0] + ThumbEmitLoadRegIndirect(ThumbReg(regHidden), ThumbReg(0), 0); } - - // Update the indexes to be used for the second loop - idxCurrentLoopEnd = idxCurrentLoopBegin; - idxCurrentLoopBegin = 0; - } - - // Now, perform the register to register moves - for (idxCurrentDesc = idxCurrentLoopBegin; idxCurrentDesc < idxCurrentLoopEnd; idxCurrentDesc++) - { - ArgDesc *pArgDesc = &rgArgDescs[idxCurrentDesc]; - - // All moves to stack locations have been done (if applicable). - // Since we are moving to a register destination, the source - // will also be a register and cannot be a stack location (refer to the previous loop). - _ASSERTE(pArgDesc->m_fSrcIsReg && pArgDesc->m_fDstIsReg); - - // Register to register case. - ThumbEmitMovRegReg(pArgDesc->m_idxDst, pArgDesc->m_idxSrc); - } - - - // Place instantiation parameter into the correct register. - ArgLocDesc sInstArgLoc; - sDstArgLocations.GetParamTypeLoc(&sInstArgLoc); - int regHidden = sInstArgLoc.m_idxGenReg; - _ASSERTE(regHidden != -1); - if (pHiddenArg) - { - // mov regHidden, #pHiddenArg - ThumbEmitMovConstant(ThumbReg(regHidden), (TADDR)pHiddenArg); } else { - // Extract MethodTable pointer (the hidden arg) from the object instance. - // ldr regHidden, [r0] - ThumbEmitLoadRegIndirect(ThumbReg(regHidden), ThumbReg(0), 0); - } - - if (pHiddenArg == NULL) - { - // Unboxing stub case. - - // Skip over the MethodTable* to find the address of the unboxed value type. - // add r0, #sizeof(MethodTable*) - ThumbEmitIncrement(ThumbReg(0), sizeof(MethodTable*)); + // mov regHidden, #pHiddenArg + ThumbEmitMovConstant(ThumbReg(regHidden), (TADDR)extraArg); } - - // Emit a regular (non-tail) call to the target method. - ThumbEmitCallManagedMethod(pMD, false); - - // Unlink the StubHelperFrame. - EmitStubUnlinkFrame(); - - // Epilog - ThumbEmitEpilog(); - } -} - -#if defined(FEATURE_SHARE_GENERIC_CODE) -// The stub generated by this method passes an extra dictionary argument before jumping to -// shared-instantiation generic code. -// -// pSharedMD is either -// * An InstantiatedMethodDesc for a generic method whose code is shared across instantiations. -// In this case, the extra argument is the InstantiatedMethodDesc for the instantiation-specific stub itself. -// or * A MethodDesc for a static method in a generic class whose code is shared across instantiations. -// In this case, the extra argument is the MethodTable pointer of the instantiated type. -VOID StubLinkerCPU::EmitInstantiatingMethodStub(MethodDesc* pSharedMD, void* extra) -{ - CONTRACTL - { - THROWS; - GC_TRIGGERS; - INJECT_FAULT(COMPlusThrowOM();); - PRECONDITION(pSharedMD->RequiresInstMethodTableArg() || pSharedMD->RequiresInstMethodDescArg()); } - CONTRACTL_END; - - // Share code with the instantiating version of the unboxing stub (see below). - ThumbEmitCallWithGenericInstantiationParameter(pSharedMD, extra); -} -#endif // FEATURE_SHARE_GENERIC_CODE - -void StubLinkerCPU::EmitUnboxMethodStub(MethodDesc *pMD) -{ - if (pMD->RequiresInstMethodTableArg()) + if (extraArg == NULL) { - // In this case we also have to add an instantiating parameter (which is always the MethodTable* from - // the instance we're called on). Most of this code is shared with the instantiating method stub - // above, the NULL parameter informs the emitter that we're both an unboxing stub and that the extra - // parameter can be deduced from the 'this' reference. - ThumbEmitCallWithGenericInstantiationParameter(pMD, NULL); + // Unboxing stub case + // Skip over the MethodTable* to find the address of the unboxed value type. + // add r0, #sizeof(MethodTable*) + ThumbEmitIncrement(ThumbReg(0), sizeof(MethodTable*)); } - else - { - // We assume that we'll never see a case where a boxed value type method will require an instantiated - // method desc as a parameter. The stubs on other platforms make this assumption (and indeed this - // method isn't even passed an additional instantiation parameter). This is trivially true for the - // non-interface call case: the only methods callable directly on the boxed instance are the methods - // of Object, none of which are generic. For the interface dispatch case we're relying on the fact - // that the jit always provides the instantiating argument explicitly. - _ASSERTE(!pMD->RequiresInstMethodDescArg()); - - // Address of the value type is address of the boxed instance plus four. - // add r0, #4 - ThumbEmitIncrement(ThumbReg(0), 4); - bool isRelative = MethodTable::VTableIndir2_t::isRelative - && pMD->IsVtableSlot(); + bool isRelative = MethodTable::VTableIndir2_t::isRelative + && pSharedMD->IsVtableSlot(); #ifndef FEATURE_NGEN_RELOCS_OPTIMIZATIONS - _ASSERTE(!isRelative); + _ASSERTE(!isRelative); #endif + if (isRelative) + { + ThumbEmitProlog(1, 0, FALSE); + } - if (isRelative) - { - ThumbEmitProlog(1, 0, FALSE); - } - - // Tail call the real target. - ThumbEmitCallManagedMethod(pMD, true /* tail call */); + ThumbEmitCallManagedMethod(pSharedMD, true); - if (isRelative) - { - ThumbEmitEpilog(); - } + if (isRelative) + { + ThumbEmitEpilog(); } } @@ -2558,355 +2107,6 @@ void InitJITHelpers1() } } -// +64 stack-based arguments here -// -- MulticastFrame end -// +48 r0-r3 argument registers -// +44 lr return address -// +40 fp frame pointer -// +12 r4-r10 callee saved registers -// +8 datum (typically a MethodDesc*) -// +4 m_Next -// +0 the frame vptr -// -- MulticastFrame start -// -4 gs cookie -// -... floating point argument registers -void StubLinkerCPU::EmitMulticastInvoke(UINT_PTR hash) -{ - //Decode Multicast Delegate hash - unsigned int numStackBytes = hash >> 8; - _ASSERTE(numStackBytes <= 0x7fff); - - unsigned int numFPRegs = (hash & 0xf8) >> 3; - _ASSERTE(numFPRegs <= 16); - - unsigned int numGenRegs = hash & 0x7; - _ASSERTE(numGenRegs <= 4); - - DWORD offsetOfFPRegs = 0; - - DWORD cbStackFrame = numStackBytes; - if (numFPRegs) - { - cbStackFrame = ALIGN_UP(cbStackFrame, 8); - offsetOfFPRegs = cbStackFrame; - cbStackFrame += 4 * numFPRegs; - } - cbStackFrame += sizeof(GSCookie) + sizeof(MulticastFrame); - cbStackFrame = ALIGN_UP(cbStackFrame, 8); - DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr - - // Prolog: - ThumbEmitProlog(8, // Save r4-r11,lr (count doesn't include lr) - cbStackFrameWithoutSavedRegs, // Additional space in the stack frame required - TRUE); // Push argument registers - - DWORD offsetOfFrame = cbStackFrame - sizeof(MulticastFrame); - - // Move the MethodDesc* we're calling to r12. - // ldr r12, [r0, #offsetof(DelegateObject, _methodPtrAux)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); - - // Initialize MulticastFrame::m_pMD to the MethodDesc* we're calling - // str r12, [sp + #(offsetOfFrame + offsetof(MulticastFrame, m_pMD))] - ThumbEmitStoreRegIndirect(ThumbReg(12), thumbRegSp, offsetOfFrame + MulticastFrame::GetOffsetOfDatum()); - - if (numFPRegs) - { - ThumbEmitAdd(ThumbReg(4), thumbRegSp, offsetOfFPRegs); - - // save floating point arguments at offsetOfFPRegs - //vstm{IA} R4,{s0-s(numFPRegs -1)} - Emit16(0xec84); - Emit16(0x0a00 | (WORD)numFPRegs); - } - - // Initialize and link the MulticastFrame and associated GS cookie. - EmitStubLinkFrame(MulticastFrame::GetMethodFrameVPtr(), offsetOfFrame, MulticastFrame::GetOffsetOfTransitionBlock()); - - //r7 as counter. Initialize it to 0. - // mov r7, 0 - ThumbEmitMovConstant(ThumbReg(7), 0); - - //initialize r9 to _invocationCount - ThumbEmitLoadRegIndirect(ThumbReg(9), ThumbReg(0), DelegateObject::GetOffsetOfInvocationCount()); - - CodeLabel *pLoopLabel = NewCodeLabel(); - CodeLabel *pEndLoopLabel = NewCodeLabel(); - - //loop: - EmitLabel(pLoopLabel); - - // cmp r7, r9 - ThumbEmitCmpReg(ThumbReg(7), ThumbReg(9)); - - // if equal goto endloop - // beq endloop - ThumbEmitCondFlagJump(pEndLoopLabel, 0); - - UINT32 count = 0; - if(numStackBytes) - { - //r1 = pos for stack args in Frame - ThumbEmitAdd(ThumbReg(1), ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgs()); - - //r2 = stack pos for args of calling func - ThumbEmitMovRegReg(ThumbReg(2), thumbRegSp); - - // ..move stack args.. - _ASSERTE(numStackBytes%4 == 0); - while (count != numStackBytes) - { - ThumbEmitLoadIndirectPostIncrement(ThumbReg(0), ThumbReg(1), 4); - ThumbEmitStoreIndirectPostIncrement(ThumbReg(0), ThumbReg(2), 4); - count += 4; - } - } - - count = 1; - while(count < numGenRegs) - { - ThumbEmitLoadRegIndirect(ThumbReg(count), ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters() + count*4); - count++; - } - - if(numFPRegs) - { - ThumbEmitAdd(ThumbReg(0), thumbRegSp, offsetOfFPRegs); - //vldm{IA}.32 R0, s0-s(numFPRegs-1) - Emit16(0xec90); - Emit16(0x0a00 | (WORD)numFPRegs); - } - - //ldr r0, [r4+0x30] // get the first argument - ThumbEmitLoadRegIndirect(ThumbReg(0),ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgumentRegisters()); - - // ldr r6, [r0+0x14] //invocationList - ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(0), DelegateObject::GetOffsetOfInvocationList()); - - // r6 - address of first delegate in invocation list - // add r6,r6,0xC - ThumbEmitAdd(ThumbReg(6), ThumbReg(6), PtrArray::GetDataOffset()); - - //ldr r8,[r6+r7*4] //get delegate object - ThumbEmitLoadOffsetScaledReg(ThumbReg(8), ThumbReg(6), ThumbReg(7), 2); - - // ldr r0, [r8+0x04] //_target from the delegate - ThumbEmitLoadRegIndirect(ThumbReg(0), ThumbReg(8), DelegateObject::GetOffsetOfTarget()); - - // ldr r8, [r8+0xC] // methodPtr from the delegate - ThumbEmitLoadRegIndirect(ThumbReg(8), ThumbReg(8), DelegateObject::GetOffsetOfMethodPtr()); - - //call delegate - ThumbEmitCallRegister(ThumbReg(8)); - - //increment counter - ThumbEmitAdd(ThumbReg(7), ThumbReg(7), 1); - - // The debugger may need to stop here, so grab the offset of this code. - EmitPatchLabel(); - - //goto loop - ThumbEmitNearJump(pLoopLabel); - - //endloop: - EmitLabel(pEndLoopLabel); - - - //At this point of the stub: - //r4 must point to Frame - //and r5 must be current Thread* - - EmitStubUnlinkFrame(); - - // Epilog - ThumbEmitEpilog(); -} - -void StubLinkerCPU::EmitSecureDelegateInvoke(UINT_PTR hash) -{ - //Decode Multicast Delegate hash - unsigned int numStackBytes = hash >> 8; - _ASSERTE(numStackBytes <= 0x7fff); - - DWORD cbStackFrame = numStackBytes + sizeof(GSCookie) + sizeof(SecureDelegateFrame); - cbStackFrame = ALIGN_UP(cbStackFrame, 8); - DWORD cbStackFrameWithoutSavedRegs = cbStackFrame - (13 * 4); // r0-r11,lr - - // Prolog: - ThumbEmitProlog(8, // Save r4-r11,lr (count doesn't include lr) - cbStackFrameWithoutSavedRegs, // Additional space in the stack frame required - TRUE); // Push argument registers - - DWORD offsetOfFrame = cbStackFrame - sizeof(SecureDelegateFrame); - - // Move the MethodDesc* we're calling to r12. - // ldr r12, [r0, #offsetof(DelegateObject, _invocationCount)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfInvocationCount()); - - // Initialize SecureDelegateFrame::m_pMD to the MethodDesc* we're calling - // str r12, [sp + #(offsetOfFrame + offsetof(SecureDelegateFrame, m_pMD))] - ThumbEmitStoreRegIndirect(ThumbReg(12), thumbRegSp, offsetOfFrame + SecureDelegateFrame::GetOffsetOfDatum()); - - // Initialize and link the SecureDelegateFrame and associated GS cookie. - EmitStubLinkFrame(SecureDelegateFrame::GetMethodFrameVPtr(), offsetOfFrame, SecureDelegateFrame::GetOffsetOfTransitionBlock()); - - // At this point: - // r0 : secure delegate - // r4 : SecureDelegateFrame * - // r5 : Thread * - - if (numStackBytes) - { - // Copy stack based arguments from the calling frame into this one. Use the following registers: - // r6 : pointer to source arguments - // r7 : pointer to destination arguments - // r8 : temporary storage during copy operation - - // add r6, r4, #MulticastFrame::GetOffsetOfArgs() - ThumbEmitAdd(ThumbReg(6), ThumbReg(4), MulticastFrame::GetOffsetOfTransitionBlock() + TransitionBlock::GetOffsetOfArgs()); - - // mov r7, sp - ThumbEmitMovRegReg(ThumbReg(7), thumbRegSp); - - // Unrolled loop to copy the stack based arguments. Might want to consider a second path with a loop - // for large argument lists if anyone complains about this. - _ASSERTE((numStackBytes % 4) == 0); - for (unsigned int i = 0; i < numStackBytes; i += 4) - { - // Read one 4-byte value from the source stack and copy it to the new stack, post-incrementing - // both source and destination as we go. - // ldr r8, [r6], #4 - // str r8, [r7], #4 - ThumbEmitLoadIndirectPostIncrement(ThumbReg(8), ThumbReg(6), 4); - ThumbEmitStoreIndirectPostIncrement(ThumbReg(8), ThumbReg(7), 4); - } - } - - // Stack-based arguments are copied. Floating point argument registers and r1-r3 are all still correct. - // All we need to do now is calculate the real value for r0 and the target address. Secure delegates wrap - // an inner delegate (kept in _invocationList). We retrieve this inner delegate and then perform the usual - // delegate invocation pattern on that. - - // Get "real" delegate. - // ldr r0, [r0, #offsetof(DelegateObject, _invocationList)] - ThumbEmitLoadRegIndirect(ThumbReg(0), ThumbReg(0), DelegateObject::GetOffsetOfInvocationList()); - - // Load the destination address from the inner delegate. - // ldr r12, [r0, #offsetof(DelegateObject, _methodPtr)] - ThumbEmitLoadRegIndirect(ThumbReg(12), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtr()); - - // This is only required for unbound delegates which use VSD stubs..but does not harm if done unconditionally - // add r4, r0+#offsetof(DelegateObject, _methodPtrAux) ; // r4 now contains indirection cell - ThumbEmitAdd(ThumbReg(4), ThumbReg(0), DelegateObject::GetOffsetOfMethodPtrAux()); - - // Replace the delegate reference with the object cached as the delegate's target. - // ldr r0, [r0, #offsetof(DelegateObject, _target)] - ThumbEmitLoadRegIndirect(ThumbReg(0), ThumbReg(0), DelegateObject::GetOffsetOfTarget()); - - // Perform the call. - // blx r12 - ThumbEmitCallRegister(ThumbReg(12)); - - // restore frame pointer in r4 - ThumbEmitAdd(ThumbReg(4), thumbRegSp, offsetOfFrame); - - // Unlink SecureDelegateFrame. This requires the frame pointer in r4 and the thread pointer in r5. - EmitStubUnlinkFrame(); - - // Epilog - ThumbEmitEpilog(); -} - -//The function expects r4 to point to frame -//and r5 must be current Thread* -void StubLinkerCPU::EmitStubUnlinkFrame() -{ -#ifdef _DEBUG - // EmitStubUnlinkFrame is emitted just before the epilog. - // Thus, at this point, all other callee-saved registers - // could be used since we are anyways going to restore them - // via epilog execution. - - // Ensure that GSCookie is valid - // - // ldr r6, [r4-4]; Load the value of GSCookie - ThumbEmitSub(ThumbReg(6), ThumbReg(4), 4); - ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(6), 0); - - // mov r7, s_gsCookie - ThumbEmitMovConstant(ThumbReg(7), GetProcessGSCookie()); - - // cmp r6, r7 ; Are the GSCookie values in sync? - ThumbEmitCmpReg(ThumbReg(6), ThumbReg(7)); - - CodeLabel *pAllDoneLabel = NewCodeLabel(); - - // beq AllDone; yes, GSCookie is good. - ThumbEmitCondFlagJump(pAllDoneLabel, 0); - - // If we are here, then GSCookie was bad. - // Call into DoJITFailFast. - // - // mov r12, DoJITFailFast - ThumbEmitMovConstant(ThumbReg(12), (int)DoJITFailFast); - // bl r12 - ThumbEmitCallRegister(ThumbReg(12)); - // Emit a breakpoint - we are not expected to come here at all - // if we performed a FailFast. - ThumbEmitBreakpoint(); - - //AllDone: - EmitLabel(pAllDoneLabel); -#endif // _DEBUG - - // Unlink the MulticastFrame. - // ldr r6, [r4 + #offsetof(MulticastFrame, m_Next)] - // str r6, [r5 + #offsetof(Thread, m_pFrame)] - ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(4), Frame::GetOffsetOfNextLink()); - ThumbEmitStoreRegIndirect(ThumbReg(6), ThumbReg(5), offsetof(Thread, m_pFrame)); - -} - -//pFrameVptr = vtable ptr of Frame -//offsetOfFrame = Frame offset in bytes from sp -//After this method: r4 points to the Frame on stack -// and r5 has current Thread* -void StubLinkerCPU::EmitStubLinkFrame(TADDR pFrameVptr, int offsetOfFrame, int offsetOfTransitionBlock) -{ - // Initialize r4 to point to where we start filling the frame. - ThumbEmitAdd(ThumbReg(4), thumbRegSp, offsetOfFrame - sizeof(GSCookie)); - - // Write the initial GS cookie value - // mov r5, s_gsCookie - // str r5, [r4] - ThumbEmitMovConstant(ThumbReg(5), s_gsCookie); - ThumbEmitStoreIndirectPostIncrement(ThumbReg(5), ThumbReg(4), 4); - - // Initialize the vtable pointer. - // mov r5, #vfptr - // str r5, [r4 + #offsetof(Frame, _vfptr)] - ThumbEmitMovConstant(ThumbReg(5), pFrameVptr); - ThumbEmitStoreRegIndirect(ThumbReg(5), ThumbReg(4), 0); - - // Link the frame to the thread's frame chain. - // r5 <- current Thread* - // ldr r6, [r5 + #offsetof(Thread, m_pFrame)] - // str r6, [r4 + #offsetof(MulticastFrame, m_Next)] - // str r4, [r5 + #offsetof(Thread, m_pFrame)] - - ThumbEmitGetThread(ThumbReg(5)); -#ifdef FEATURE_PAL - // reload argument registers that could have been corrupted by the call - for (int reg = 0; reg < 4; reg++) - ThumbEmitLoadRegIndirect(ThumbReg(reg), ThumbReg(4), - offsetOfTransitionBlock + TransitionBlock::GetOffsetOfArgumentRegisters() + offsetof(ArgumentRegisters, r[reg])); -#endif - - ThumbEmitLoadRegIndirect(ThumbReg(6), ThumbReg(5), Thread::GetOffsetOfCurrentFrame()); - ThumbEmitStoreRegIndirect(ThumbReg(6), ThumbReg(4), Frame::GetOffsetOfNextLink()); - ThumbEmitStoreRegIndirect(ThumbReg(4), ThumbReg(5), Thread::GetOffsetOfCurrentFrame()); -} - #endif // CROSSGEN_COMPILE void StubLinkerCPU::ThumbEmitNearJump(CodeLabel *target) @@ -2931,49 +2131,6 @@ void StubLinkerCPU::ThumbEmitCondRegJump(CodeLabel *target, BOOL nonzero, ThumbR EmitLabelRef(target, reinterpret_cast<ThumbCondJump&>(gThumbCondJump), variation); } -UINT_PTR StubLinkerCPU::HashMulticastInvoke(MetaSig *pSig) -{ - // Generate a hash key as follows: - // Bit0-2 : num of general purpose registers used - // Bit3-7 : num of FP regs used (counting in terms of s0,s1...) - // Bit8-22 : num of stack bytes used - - ArgIterator delegateCallConv(pSig); - - UINT numStackBytes = delegateCallConv.SizeOfArgStack(); - - if (numStackBytes > 0x7FFF) - COMPlusThrow(kNotSupportedException, W("NotSupported_TooManyArgs")); - - int cGenReg = 1; // r0 is always used for this pointer - int cFPReg = 0; - - // if it has a return buffer argument r1 is also used - if(delegateCallConv.HasRetBuffArg()) - cGenReg = 2; - - int argOffset; - while ((argOffset = delegateCallConv.GetNextOffset()) != TransitionBlock::InvalidOffset) - { - ArgLocDesc currArgLoc; - delegateCallConv.GetArgLoc(argOffset, &currArgLoc); - - if(currArgLoc.m_idxGenReg != -1) - cGenReg = currArgLoc.m_idxGenReg + currArgLoc.m_cGenReg; - - if(currArgLoc.m_idxFloatReg != -1) - cFPReg = currArgLoc.m_idxFloatReg + currArgLoc.m_cFloatReg; - } - - // only r0-r3 can be used for arguments - _ASSERTE(cGenReg <= 4); - - // only s0-s15 can be used for arguments - _ASSERTE(cFPReg <= 16); - - return (numStackBytes << 8 | cFPReg << 3 | cGenReg); -} - void StubLinkerCPU::ThumbCopyOneTailCallArg(UINT * pnSrcAlign, const ArgLocDesc * pArgLoc, UINT * pcbStackSpace) { if (pArgLoc->m_fRequires64BitAlignment && (*pnSrcAlign & 1)) { |