Move DynamicHelperFrame::UpdateRegDisplay into cgenx86.cpp (#11193)
[platform/upstream/coreclr.git] / src / vm / i386 / cgenx86.cpp
1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 // CGENX86.CPP -
5 //
6 // Various helper routines for generating x86 assembly code.
7 //
8 //
9
10 // Precompiled Header
11
12 #include "common.h"
13
14 #include "field.h"
15 #include "stublink.h"
16 #include "cgensys.h"
17 #include "frames.h"
18 #include "excep.h"
19 #include "dllimport.h"
20 #include "comdelegate.h"
21 #include "log.h"
22 #include "security.h"
23 #include "comdelegate.h"
24 #include "array.h"
25 #include "jitinterface.h"
26 #include "codeman.h"
27 #include "dbginterface.h"
28 #include "eeprofinterfaces.h"
29 #include "eeconfig.h"
30 #include "asmconstants.h"
31 #include "class.h"
32 #include "virtualcallstub.h"
33 #include "mdaassistants.h"
34 #include "jitinterface.h"
35
36 #ifdef FEATURE_COMINTEROP
37 #include "comtoclrcall.h"
38 #include "runtimecallablewrapper.h"
39 #include "comcache.h"
40 #include "olevariant.h"
41 #endif // FEATURE_COMINTEROP
42
43 #ifdef FEATURE_PREJIT
44 #include "compile.h"
45 #endif
46
47 #include "stublink.inl"
48
49 extern "C" DWORD STDCALL GetSpecificCpuTypeAsm(void);
50 extern "C" DWORD STDCALL GetSpecificCpuFeaturesAsm(DWORD *pInfo);
51
52 // NOTE on Frame Size C_ASSERT usage in this file 
53 // if the frame size changes then the stubs have to be revisited for correctness
54 // kindly revist the logic and then update the constants so that the C_ASSERT will again fire
55 // if someone changes the frame size.  You are expected to keep this hard coded constant
56 // up to date so that changes in the frame size trigger errors at compile time if the code is not altered
57
58 void generate_noref_copy (unsigned nbytes, StubLinkerCPU* sl);
59
60 #ifdef WIN64EXCEPTIONS
61 void UpdateRegDisplayFromCalleeSavedRegisters(REGDISPLAY * pRD, CalleeSavedRegisters * regs)
62 {
63     LIMITED_METHOD_CONTRACT;
64
65     T_CONTEXT * pContext = pRD->pCurrentContext;
66 #define CALLEE_SAVED_REGISTER(regname) pContext->regname = regs->regname;
67     ENUM_CALLEE_SAVED_REGISTERS();
68 #undef CALLEE_SAVED_REGISTER
69
70     KNONVOLATILE_CONTEXT_POINTERS * pContextPointers = pRD->pCurrentContextPointers;
71 #define CALLEE_SAVED_REGISTER(regname) pContextPointers->regname = (DWORD*)&regs->regname;
72     ENUM_CALLEE_SAVED_REGISTERS();
73 #undef CALLEE_SAVED_REGISTER
74 }
75
76 void ClearRegDisplayArgumentAndScratchRegisters(REGDISPLAY * pRD)
77 {
78     LIMITED_METHOD_CONTRACT;
79
80 #define ARGUMENT_AND_SCRATCH_REGISTER(regname) pRD->pCurrentContextPointers->regname = NULL;
81     ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
82 #undef ARGUMENT_AND_SCRATCH_REGISTER
83 }
84 #endif // WIN64EXCEPTIONS
85
86 #ifndef DACCESS_COMPILE
87
88 //=============================================================================
89 // Runtime test to see if the OS has enabled support for the SSE2 instructions
90 //
91 //
92 BOOL Runtime_Test_For_SSE2()
93 {
94 #ifdef FEATURE_CORESYSTEM
95     return TRUE;
96 #else
97
98     BOOL result = IsProcessorFeaturePresent(PF_XMMI64_INSTRUCTIONS_AVAILABLE);
99
100     if (result == FALSE)
101         return FALSE;
102
103     // **********************************************************************
104     // ***                                                                ***
105     // ***   IMPORTANT NOTE:                                              ***
106     // ***                                                                ***
107     // ***     All of these RunningOnXXX APIs return true when            ***
108     // ***     the OS that you are running on is that OS or later.        ***
109     // ***     For example RunningOnWin2003() will return true            ***
110     // ***     when you are running on Win2k3, Vista, Win7 or later.      ***
111     // ***                                                                ***
112     // **********************************************************************
113
114
115     // Windows 7 and later should alwys be using SSE2 instructions
116     //  this is true for both for native and Wow64
117     //
118     if (RunningOnWin7())
119         return TRUE;
120
121     if (RunningInWow64())
122     {
123         // There is an issue with saving/restoring the SSE2 registers under wow64 
124         // So we figure out if we are running on an impacted OS and Service Pack level
125         //     See DevDiv Bugs 89587 for the wow64 bug.
126         //
127
128         _ASSERTE(ExOSInfoAvailable());  // This is always available on Vista and later
129
130         //
131         // The issue is fixed in Windows Server 2008 or Vista/SP1
132         //
133         // It is not fixed in Vista/RTM, so check for that case
134         // 
135         if ((ExOSInfoRunningOnServer() == FALSE))
136         {
137             OSVERSIONINFOEX osvi;
138
139             ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
140             osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
141             osvi.wServicePackMajor = 0;
142
143             DWORDLONG dwlConditionMask = 0;
144             VER_SET_CONDITION( dwlConditionMask, CLR_VER_SERVICEPACKMAJOR, VER_EQUAL);
145                 
146             if (VerifyVersionInfo(&osvi, CLR_VER_SERVICEPACKMAJOR, dwlConditionMask))
147                 result = FALSE;
148         }
149     }
150
151     return result;
152 #endif
153 }
154
155 //---------------------------------------------------------------
156 // Returns the type of CPU (the value of x of x86)
157 // (Please note, that it returns 6 for P5-II)
158 //---------------------------------------------------------------
159 void GetSpecificCpuInfo(CORINFO_CPU * cpuInfo)
160 {
161     LIMITED_METHOD_CONTRACT;
162
163     static CORINFO_CPU val = { 0, 0, 0 };
164
165     if (val.dwCPUType)
166     {
167         *cpuInfo = val;
168         return;
169     }
170
171     CORINFO_CPU tempVal;
172     tempVal.dwCPUType = GetSpecificCpuTypeAsm();  // written in ASM & doesn't participate in contracts
173     _ASSERTE(tempVal.dwCPUType);
174     
175 #ifdef _DEBUG
176     {
177         SO_NOT_MAINLINE_REGION();
178
179     /* Set Family+Model+Stepping string (eg., x690 for Banias, or xF30 for P4 Prescott)
180      * instead of Family only
181      */
182      
183     const DWORD cpuDefault = 0xFFFFFFFF;
184     static ConfigDWORD cpuFamily;
185     DWORD configCpuFamily = cpuFamily.val_DontUse_(CLRConfig::INTERNAL_CPUFamily, cpuDefault);
186     if (configCpuFamily != cpuDefault)
187     {
188         assert((configCpuFamily & 0xFFF) == configCpuFamily);
189         tempVal.dwCPUType = (tempVal.dwCPUType & 0xFFFF0000) | configCpuFamily;
190     }
191     }
192 #endif
193
194     tempVal.dwFeatures = GetSpecificCpuFeaturesAsm(&tempVal.dwExtendedFeatures);  // written in ASM & doesn't participate in contracts
195
196 #ifdef _DEBUG
197     {
198         SO_NOT_MAINLINE_REGION();
199
200     /* Set the 32-bit feature mask
201      */
202     
203     const DWORD cpuFeaturesDefault = 0xFFFFFFFF;
204     static ConfigDWORD cpuFeatures;
205     DWORD configCpuFeatures = cpuFeatures.val_DontUse_(CLRConfig::INTERNAL_CPUFeatures, cpuFeaturesDefault);
206     if (configCpuFeatures != cpuFeaturesDefault)
207     {
208         tempVal.dwFeatures = configCpuFeatures;
209     }
210     }
211 #endif
212
213     val = *cpuInfo = tempVal;
214 }
215
216 #endif // #ifndef DACCESS_COMPILE
217
218
219 #ifndef WIN64EXCEPTIONS
220 //---------------------------------------------------------------------------------------
221 //
222 // Initialize the EHContext using the resume PC and the REGDISPLAY.  The EHContext is currently used in two
223 // scenarios: to store the register state before calling an EH clause, and to retrieve the ambient SP of a 
224 // particular stack frame.  resumePC means different things in the two scenarios.  In the former case, it
225 // is the IP at which we are going to resume execution when we call an EH clause.  In the latter case, it 
226 // is just the current IP.
227 //
228 // Arguments:
229 //    resumePC - refer to the comment above
230 //    regs     - This is the REGDISPLAY obtained from the CrawlFrame used in the stackwalk.  It represents the
231 //               stack frame of the method containing the EH clause we are about to call.  For getting the 
232 //               ambient SP, this is the stack frame we are interested in.
233 //
234
235 void EHContext::Setup(PCODE resumePC, PREGDISPLAY regs)
236 {
237     LIMITED_METHOD_DAC_CONTRACT;
238
239     // EAX ECX EDX are scratch
240     this->Esp  = regs->SP;
241     this->Ebx = *regs->pEbx;
242     this->Esi = *regs->pEsi;
243     this->Edi = *regs->pEdi;
244     this->Ebp = *regs->pEbp;
245
246     this->Eip = (ULONG)(size_t)resumePC;
247 }
248
249 //
250 // Update the registers using new context
251 //
252 // This is necessary to reflect GC pointer changes during the middle of a unwind inside a 
253 // finally clause, because:
254 // 1. GC won't see the part of stack inside try (which has thrown an exception) that is already 
255 // unwinded and thus GC won't update GC pointers for this portion of the stack, but rather the 
256 // call stack in finally.
257 // 2. upon return of finally, the unwind process continues and unwinds stack based on the part 
258 // of stack inside try and won't see the updated values in finally.
259 // As a result, we need to manually update the context using register values upon return of finally
260 //
261 // Note that we only update the registers for finally clause because
262 // 1. For filter handlers, stack walker is able to see the whole stack (including the try part)
263 // with the help of ExceptionFilterFrame as filter handlers are called in first pass
264 // 2. For catch handlers, the current unwinding is already finished
265 //
266 void EHContext::UpdateFrame(PREGDISPLAY regs)
267 {
268     LIMITED_METHOD_CONTRACT;
269     
270     // EAX ECX EDX are scratch. 
271     // No need to update ESP as unwinder takes care of that for us
272
273     LOG((LF_EH, LL_INFO1000, "Updating saved EBX: *%p= %p\n", regs->pEbx, this->Ebx));
274     LOG((LF_EH, LL_INFO1000, "Updating saved ESI: *%p= %p\n", regs->pEsi, this->Esi));
275     LOG((LF_EH, LL_INFO1000, "Updating saved EDI: *%p= %p\n", regs->pEdi, this->Edi));
276     LOG((LF_EH, LL_INFO1000, "Updating saved EBP: *%p= %p\n", regs->pEbp, this->Ebp));
277     
278     *regs->pEbx = this->Ebx;
279     *regs->pEsi = this->Esi;
280     *regs->pEdi = this->Edi;
281     *regs->pEbp = this->Ebp;
282 }
283 #endif // WIN64EXCEPTIONS
284
285 void TransitionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
286 {
287     CONTRACT_VOID
288     {
289         NOTHROW;
290         GC_NOTRIGGER;
291         MODE_ANY;
292         HOST_NOCALLS;
293         SUPPORTS_DAC;
294     }
295     CONTRACT_END;
296
297     ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
298
299     MethodDesc * pFunc = GetFunction();
300     _ASSERTE(pFunc != NULL);
301
302     UpdateRegDisplayHelper(pRD, pFunc->CbStackPop());
303
304     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    TransitionFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
305
306     RETURN;
307 }
308
309 void TransitionFrame::UpdateRegDisplayHelper(const PREGDISPLAY pRD, UINT cbStackPop)
310 {
311     CONTRACT_VOID
312     {
313         NOTHROW;
314         GC_NOTRIGGER;
315         MODE_ANY;
316         HOST_NOCALLS;
317         SUPPORTS_DAC;
318     }
319     CONTRACT_END;
320
321     CalleeSavedRegisters* regs = GetCalleeSavedRegisters();
322
323     pRD->PCTAddr = GetReturnAddressPtr();
324
325 #ifdef WIN64EXCEPTIONS
326
327     DWORD CallerSP = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
328
329     pRD->IsCallerContextValid = FALSE;
330     pRD->IsCallerSPValid      = FALSE;
331
332     pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);;
333     pRD->pCurrentContext->Esp = CallerSP;
334
335     UpdateRegDisplayFromCalleeSavedRegisters(pRD, regs);
336     ClearRegDisplayArgumentAndScratchRegisters(pRD);
337
338     SyncRegDisplayToCurrentContext(pRD);
339
340 #else // WIN64EXCEPTIONS
341
342     // reset pContext; it's only valid for active (top-most) frame
343     pRD->pContext = NULL;
344
345 #define CALLEE_SAVED_REGISTER(regname) pRD->p##regname = (DWORD*) &regs->regname;
346     ENUM_CALLEE_SAVED_REGISTERS();
347 #undef CALLEE_SAVED_REGISTER
348
349     pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
350     pRD->SP  = (DWORD)(pRD->PCTAddr + sizeof(TADDR) + cbStackPop);
351
352 #endif // WIN64EXCEPTIONS
353
354     RETURN;
355 }
356
357 void HelperMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
358 {
359     CONTRACT_VOID
360     {
361         NOTHROW;
362         GC_NOTRIGGER;
363         MODE_ANY;
364         HOST_NOCALLS;
365         PRECONDITION(m_MachState.isValid());               // InsureInit has been called
366         SUPPORTS_DAC;
367     }
368     CONTRACT_END;
369
370     ENABLE_FORBID_GC_LOADER_USE_IN_THIS_SCOPE();
371
372     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    HelperMethodFrame::UpdateRegDisplay cached ip:%p, sp:%p\n", m_MachState.GetRetAddr(), m_MachState.esp()));
373
374     pRD->PCTAddr = dac_cast<TADDR>(m_MachState.pRetAddr());
375
376 #ifdef WIN64EXCEPTIONS
377
378     pRD->IsCallerContextValid = FALSE;
379     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
380
381 #ifdef DACCESS_COMPILE
382     PORTABILITY_ASSERT("HelperMethodFrame::UpdateRegDisplay");
383 #endif // DACCESS_COMPILE
384
385     pRD->pCurrentContext->Eip = pRD->ControlPC = m_MachState.GetRetAddr();
386     pRD->pCurrentContext->Esp = pRD->SP = (DWORD) m_MachState.esp();
387
388 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContext->regname = *((DWORD*) m_MachState.p##regname());
389     ENUM_CALLEE_SAVED_REGISTERS();
390 #undef CALLEE_SAVED_REGISTER
391
392 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = (DWORD*) m_MachState.p##regname();
393     ENUM_CALLEE_SAVED_REGISTERS();
394 #undef CALLEE_SAVED_REGISTER
395
396     //
397     // Clear all knowledge of scratch registers.  We're skipping to any
398     // arbitrary point on the stack, and frames aren't required to preserve or
399     // keep track of these anyways.
400     //
401
402     ClearRegDisplayArgumentAndScratchRegisters(pRD);
403
404 #else // WIN64EXCEPTIONS
405
406     // reset pContext; it's only valid for active (top-most) frame
407     pRD->pContext = NULL;
408
409 #ifdef DACCESS_COMPILE
410
411     //
412     // In the dac case we may have gotten here
413     // without the frame being initialized, so
414     // try and initialize on the fly.
415     //
416
417     if (!m_MachState.isValid())
418     {
419         MachState unwindState;
420
421         InsureInit(false, &unwindState);
422         pRD->PCTAddr = dac_cast<TADDR>(unwindState.pRetAddr());
423         pRD->ControlPC = unwindState.GetRetAddr();
424         pRD->SP = unwindState._esp;
425
426         // Get some special host instance memory
427         // so we have a place to point to.
428         // This host memory has no target address
429         // and so won't be looked up or used for
430         // anything else.
431         MachState* thisState = (MachState*)
432             DacAllocHostOnlyInstance(sizeof(*thisState), true);
433
434         thisState->_edi = unwindState._edi;
435         pRD->pEdi = (DWORD *)&thisState->_edi;
436         thisState->_esi = unwindState._esi;
437         pRD->pEsi = (DWORD *)&thisState->_esi;
438         thisState->_ebx = unwindState._ebx;
439         pRD->pEbx = (DWORD *)&thisState->_ebx;
440         thisState->_ebp = unwindState._ebp;
441         pRD->pEbp = (DWORD *)&thisState->_ebp;
442
443         // InsureInit always sets m_RegArgs to zero
444         // in the real code.  I'm not sure exactly
445         // what should happen in the on-the-fly case,
446         // but go with what would happen from an InsureInit.
447
448         RETURN;
449     }
450
451 #endif // #ifdef DACCESS_COMPILE
452
453     // DACCESS: The MachState pointers are kept as PTR_TADDR so
454     // the host pointers here refer to the appropriate size and
455     // these casts are not a problem.
456     pRD->pEdi = (DWORD*) m_MachState.pEdi();
457     pRD->pEsi = (DWORD*) m_MachState.pEsi();
458     pRD->pEbx = (DWORD*) m_MachState.pEbx();
459     pRD->pEbp = (DWORD*) m_MachState.pEbp();
460
461     pRD->ControlPC = m_MachState.GetRetAddr();
462     pRD->SP  = (DWORD) m_MachState.esp();
463
464 #endif // WIN64EXCEPTIONS
465
466     RETURN;
467 }
468
469 #ifdef _DEBUG_IMPL
470 // Confirm that if the machine state was not initialized, then
471 // any unspilled callee saved registers did not change
472 EXTERN_C MachState* STDCALL HelperMethodFrameConfirmState(HelperMethodFrame* frame, void* esiVal, void* ediVal, void* ebxVal, void* ebpVal) 
473     {
474     CONTRACTL
475     {
476         NOTHROW;
477         GC_NOTRIGGER;
478         MODE_ANY;
479         DEBUG_ONLY;
480     }
481     CONTRACTL_END;
482
483     MachState* state = frame->MachineState(); 
484
485     // if we've already executed this check once for this helper method frame then
486     // we don't do the check again because it is very expensive.
487     if (frame->HaveDoneConfirmStateCheck())
488     {
489         return state;
490     }
491
492     // probe to avoid a kazillion violations in the code that follows.
493     BEGIN_DEBUG_ONLY_CODE;
494     if (!state->isValid())
495     {
496         frame->InsureInit(false, NULL);
497         _ASSERTE(state->_pEsi != &state->_esi || state->_esi  == (TADDR)esiVal);
498         _ASSERTE(state->_pEdi != &state->_edi || state->_edi  == (TADDR)ediVal);
499         _ASSERTE(state->_pEbx != &state->_ebx || state->_ebx  == (TADDR)ebxVal);
500         _ASSERTE(state->_pEbp != &state->_ebp || state->_ebp  == (TADDR)ebpVal);
501     }
502     END_DEBUG_ONLY_CODE;
503
504     // set that we have executed this check once for this helper method frame.
505     frame->SetHaveDoneConfirmStateCheck();
506
507     return state;
508 }
509 #endif
510
511 void ExternalMethodFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
512 {
513     CONTRACT_VOID
514     {
515         NOTHROW;
516         GC_NOTRIGGER;
517         MODE_ANY;
518         HOST_NOCALLS;
519         SUPPORTS_DAC;
520     }
521     CONTRACT_END;
522
523     UpdateRegDisplayHelper(pRD, CbStackPopUsingGCRefMap(GetGCRefMap()));
524
525     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    ExternalMethodFrane::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
526
527     RETURN;
528 }
529
530
531 void StubDispatchFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
532 {
533     CONTRACT_VOID
534     {
535         NOTHROW;
536         GC_NOTRIGGER;
537         MODE_ANY;
538         HOST_NOCALLS;
539         SUPPORTS_DAC;
540     }
541     CONTRACT_END;
542
543     PTR_BYTE pGCRefMap = GetGCRefMap();
544     if (pGCRefMap != NULL)
545     {
546         UpdateRegDisplayHelper(pRD, CbStackPopUsingGCRefMap(pGCRefMap));
547     }
548     else
549     if (GetFunction() != NULL)
550     {
551         FramedMethodFrame::UpdateRegDisplay(pRD);
552     }
553     else
554     {
555         UpdateRegDisplayHelper(pRD, 0);
556
557         // If we do not have owning MethodDesc, we need to pretend that 
558         // the call happened on the call instruction to get the ESP unwound properly.
559         //
560         // This path is hit when we are throwing null reference exception from
561         // code:VSD_ResolveWorker or code:StubDispatchFixupWorker
562         pRD->ControlPC = GetAdjustedCallAddress(pRD->ControlPC);
563     }
564
565     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    StubDispatchFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
566
567     RETURN;
568 }
569
570 PCODE StubDispatchFrame::GetReturnAddress()
571 {
572     CONTRACTL
573     {
574         NOTHROW;
575         GC_NOTRIGGER;
576     }
577     CONTRACTL_END;
578
579     PCODE retAddress = FramedMethodFrame::GetReturnAddress();
580     if (GetFunction() == NULL && GetGCRefMap() == NULL)
581     {
582         // See comment in code:StubDispatchFrame::UpdateRegDisplay
583         retAddress = GetAdjustedCallAddress(retAddress);
584     }
585     return retAddress;
586 }
587
588 void FaultingExceptionFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
589 {
590     CONTRACT_VOID
591     {
592         NOTHROW;
593         GC_NOTRIGGER;
594         MODE_ANY;
595         HOST_NOCALLS;
596         SUPPORTS_DAC;
597     }
598     CONTRACT_END;
599
600     pRD->PCTAddr = GetReturnAddressPtr();
601
602 #ifdef WIN64EXCEPTIONS
603
604     memcpy(pRD->pCurrentContext, &m_ctx, sizeof(CONTEXT));
605
606     pRD->SP = m_ctx.Esp;
607     pRD->ControlPC = m_ctx.Eip;
608
609 #define ARGUMENT_AND_SCRATCH_REGISTER(regname) pRD->pCurrentContextPointers->regname = &m_ctx.regname;
610     ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
611 #undef ARGUMENT_AND_SCRATCH_REGISTER
612
613 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = &m_ctx.regname;
614     ENUM_CALLEE_SAVED_REGISTERS();
615 #undef CALLEE_SAVED_REGISTER
616
617     pRD->IsCallerContextValid = FALSE;
618     pRD->IsCallerSPValid = FALSE;        // Don't add usage of this field.  This is only temporary.
619
620 #else // WIN64EXCEPTIONS
621
622     // reset pContext; it's only valid for active (top-most) frame
623     pRD->pContext = NULL;
624
625     CalleeSavedRegisters* regs = GetCalleeSavedRegisters();
626
627 #define CALLEE_SAVED_REGISTER(regname) pRD->p##regname = (DWORD*) &regs->regname;
628     ENUM_CALLEE_SAVED_REGISTERS();
629 #undef CALLEE_SAVED_REGISTER
630
631     pRD->SP = m_Esp;
632     pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
633
634 #endif // WIN64EXCEPTIONS
635
636     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    FaultingExceptionFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
637
638     RETURN;
639 }
640
641 void InlinedCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
642 {
643     CONTRACT_VOID
644     {
645         NOTHROW;
646         GC_NOTRIGGER;
647         // We should skip over InlinedCallFrame if it is not active.
648         // It will be part of a JITed method's frame, and the stack-walker
649         // can handle such a case.
650 #ifdef PROFILING_SUPPORTED        
651         PRECONDITION(CORProfilerStackSnapshotEnabled() || InlinedCallFrame::FrameHasActiveCall(this));
652 #endif
653         HOST_NOCALLS;
654         MODE_ANY;
655         SUPPORTS_DAC;
656     }
657     CONTRACT_END;
658
659     // @TODO: Remove this after the debugger is fixed to avoid stack-walks from bad places
660     // @TODO: This may be still needed for sampling profilers
661     if (!InlinedCallFrame::FrameHasActiveCall(this))
662     {
663         LOG((LF_CORDB, LL_ERROR, "WARNING: InlinedCallFrame::UpdateRegDisplay called on inactive frame %p\n", this));
664         return;
665     }
666     
667     DWORD stackArgSize = (DWORD) dac_cast<TADDR>(m_Datum);   
668
669     if (stackArgSize & ~0xFFFF)
670     {
671         NDirectMethodDesc * pMD = PTR_NDirectMethodDesc(m_Datum);
672
673         /* if this is not an NDirect frame, something is really wrong */
674
675         _ASSERTE(pMD->SanityCheck() && pMD->IsNDirect());
676
677         stackArgSize = pMD->GetStackArgumentSize();
678     }
679
680     /* The return address is just above the "ESP" */
681     pRD->PCTAddr = PTR_HOST_MEMBER_TADDR(InlinedCallFrame, this,
682                                          m_pCallerReturnAddress);
683
684 #ifdef WIN64EXCEPTIONS
685
686     pRD->IsCallerContextValid = FALSE;
687     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
688
689     pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
690     pRD->pCurrentContext->Esp = (DWORD) dac_cast<TADDR>(m_pCallSiteSP);
691     pRD->pCurrentContext->Ebp = (DWORD) m_pCalleeSavedFP;
692
693     ClearRegDisplayArgumentAndScratchRegisters(pRD);
694
695 #define CALLEE_SAVED_REGISTER(regname) pRD->pCurrentContextPointers->regname = NULL;
696     ENUM_CALLEE_SAVED_REGISTERS();
697 #undef CALLEE_SAVED_REGISTER
698
699     pRD->pCurrentContextPointers->Ebp = (DWORD*) &m_pCalleeSavedFP;
700
701     SyncRegDisplayToCurrentContext(pRD);
702
703 #else // WIN64EXCEPTIONS
704
705     // reset pContext; it's only valid for active (top-most) frame
706     pRD->pContext = NULL;
707
708     pRD->pEbp = (DWORD*) &m_pCalleeSavedFP;
709
710     pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
711     /* Now we need to pop off the outgoing arguments */
712     pRD->SP  = (DWORD) dac_cast<TADDR>(m_pCallSiteSP) + stackArgSize;
713
714 #endif // WIN64EXCEPTIONS
715
716     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    InlinedCallFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
717
718     RETURN;
719 }
720
721 #ifdef FEATURE_HIJACK
722 //==========================
723 // Resumable Exception Frame
724 //
725 TADDR ResumableFrame::GetReturnAddressPtr()
726 {
727     LIMITED_METHOD_DAC_CONTRACT;
728     return dac_cast<TADDR>(m_Regs) + offsetof(CONTEXT, Eip);
729 }
730
731 void ResumableFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
732 {
733     CONTRACT_VOID
734     {
735         NOTHROW;
736         GC_NOTRIGGER;
737         MODE_ANY;
738         HOST_NOCALLS;
739         SUPPORTS_DAC;
740     }
741     CONTRACT_END;
742
743     pRD->PCTAddr = dac_cast<TADDR>(m_Regs) + offsetof(CONTEXT, Eip);
744
745 #ifdef WIN64EXCEPTIONS
746
747     CopyMemory(pRD->pCurrentContext, m_Regs, sizeof(T_CONTEXT));
748
749     pRD->SP = m_Regs->Esp;
750     pRD->ControlPC = m_Regs->Eip;
751
752 #define ARGUMENT_AND_SCRATCH_REGISTER(reg) pRD->pCurrentContextPointers->reg = &m_Regs->reg;
753     ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
754 #undef ARGUMENT_AND_SCRATCH_REGISTER
755
756 #define CALLEE_SAVED_REGISTER(reg) pRD->pCurrentContextPointers->reg = &m_Regs->reg;
757     ENUM_CALLEE_SAVED_REGISTERS();
758 #undef CALLEE_SAVED_REGISTER
759
760     pRD->IsCallerContextValid = FALSE;
761     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
762
763 #else // WIN64EXCEPTIONS
764
765     // reset pContext; it's only valid for active (top-most) frame
766     pRD->pContext = NULL;
767
768     CONTEXT* pUnwoundContext = m_Regs;
769
770 #if !defined(DACCESS_COMPILE)
771     // "pContextForUnwind" field is only used on X86 since not only is it initialized just for it,
772     // but its used only under the confines of STACKWALKER_MAY_POP_FRAMES preprocessor define,
773     // which is defined for x86 only (refer to its definition in stackwalk.cpp).
774     if (pRD->pContextForUnwind != NULL)
775     {
776         pUnwoundContext = pRD->pContextForUnwind;
777
778         pUnwoundContext->Eax = m_Regs->Eax;
779         pUnwoundContext->Ecx = m_Regs->Ecx;
780         pUnwoundContext->Edx = m_Regs->Edx;
781
782         pUnwoundContext->Edi = m_Regs->Edi;
783         pUnwoundContext->Esi = m_Regs->Esi;
784         pUnwoundContext->Ebx = m_Regs->Ebx;
785         pUnwoundContext->Ebp = m_Regs->Ebp;
786         pUnwoundContext->Eip = m_Regs->Eip;
787     }
788 #endif // !defined(DACCESS_COMPILE)
789
790     pRD->pEax = &pUnwoundContext->Eax;
791     pRD->pEcx = &pUnwoundContext->Ecx;
792     pRD->pEdx = &pUnwoundContext->Edx;
793
794     pRD->pEdi = &pUnwoundContext->Edi;
795     pRD->pEsi = &pUnwoundContext->Esi;
796     pRD->pEbx = &pUnwoundContext->Ebx;
797     pRD->pEbp = &pUnwoundContext->Ebp;
798
799     pRD->ControlPC = pUnwoundContext->Eip;
800
801     pRD->SP  = m_Regs->Esp;
802
803 #endif // !WIN64EXCEPTIONS
804
805     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    ResumableFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
806
807     RETURN;
808 }
809
810 // The HijackFrame has to know the registers that are pushed by OnHijackTripThread
811 //  -> HijackFrame::UpdateRegDisplay should restore all the registers pushed by OnHijackTripThread
812 void HijackFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
813 {
814     CONTRACTL {
815         NOTHROW;
816         GC_NOTRIGGER;
817         HOST_NOCALLS;
818         SUPPORTS_DAC;
819     }
820     CONTRACTL_END;
821
822     pRD->PCTAddr = dac_cast<TADDR>(m_Args) + offsetof(HijackArgs, Eip);
823
824 #ifdef WIN64EXCEPTIONS
825
826     pRD->IsCallerContextValid = FALSE;
827     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
828
829     pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
830     pRD->pCurrentContext->Esp = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
831
832 #define RESTORE_REG(reg) { pRD->pCurrentContext->reg = m_Args->reg; pRD->pCurrentContextPointers->reg = &m_Args->reg; }
833 #define CALLEE_SAVED_REGISTER(reg) RESTORE_REG(reg)
834     ENUM_CALLEE_SAVED_REGISTERS();
835 #undef CALLEE_SAVED_REGISTER
836
837 #define ARGUMENT_AND_SCRATCH_REGISTER(reg) RESTORE_REG(reg)
838     ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
839 #undef ARGUMENT_AND_SCRATCH_REGISTER
840 #undef RESTORE_REG
841
842     SyncRegDisplayToCurrentContext(pRD);
843
844 #else // WIN64EXCEPTIONS
845
846     // This only describes the top-most frame
847     pRD->pContext = NULL;
848
849 #define RESTORE_REG(reg) { pRD->p##reg = &m_Args->reg; }
850 #define CALLEE_SAVED_REGISTER(reg) RESTORE_REG(reg)
851     ENUM_CALLEE_SAVED_REGISTERS();
852 #undef CALLEE_SAVED_REGISTER
853
854 #define ARGUMENT_AND_SCRATCH_REGISTER(reg) RESTORE_REG(reg)
855     ENUM_ARGUMENT_AND_SCRATCH_REGISTERS();
856 #undef ARGUMENT_AND_SCRATCH_REGISTER
857 #undef RESTORE_REG
858
859     pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
860     pRD->SP  = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
861
862 #endif // WIN64EXCEPTIONS
863
864     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    HijackFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
865 }
866
867 #endif  // FEATURE_HIJACK
868
869 void PInvokeCalliFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
870 {
871     CONTRACT_VOID
872     {
873         NOTHROW;
874         GC_NOTRIGGER;
875         MODE_ANY;
876         HOST_NOCALLS;
877         SUPPORTS_DAC;
878     }
879     CONTRACT_END;
880
881     VASigCookie *pVASigCookie = GetVASigCookie();
882     UpdateRegDisplayHelper(pRD, pVASigCookie->sizeOfArgs+sizeof(int));
883
884     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    PInvokeCalliFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
885
886     RETURN;
887 }
888
889 void TailCallFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
890 {
891     CONTRACT_VOID
892     {
893         NOTHROW;
894         GC_NOTRIGGER;
895         MODE_ANY;
896         HOST_NOCALLS;
897         SUPPORTS_DAC;
898     }
899     CONTRACT_END;
900
901     pRD->PCTAddr = GetReturnAddressPtr();
902
903 #ifdef WIN64EXCEPTIONS
904
905     pRD->IsCallerContextValid = FALSE;
906     pRD->IsCallerSPValid      = FALSE;        // Don't add usage of this field.  This is only temporary.
907
908     pRD->pCurrentContext->Eip = *PTR_PCODE(pRD->PCTAddr);
909     pRD->pCurrentContext->Esp = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
910
911     UpdateRegDisplayFromCalleeSavedRegisters(pRD, &m_regs);
912     ClearRegDisplayArgumentAndScratchRegisters(pRD);
913
914     SyncRegDisplayToCurrentContext(pRD);
915
916 #else
917
918     // reset pContext; it's only valid for active (top-most) frame
919     pRD->pContext = NULL;
920
921 #define CALLEE_SAVED_REGISTER(regname) pRD->p##regname = (DWORD*) &m_regs.regname;
922     ENUM_CALLEE_SAVED_REGISTERS();
923 #undef CALLEE_SAVED_REGISTER
924
925     pRD->ControlPC = *PTR_PCODE(pRD->PCTAddr);
926     pRD->SP  = (DWORD)(pRD->PCTAddr + sizeof(TADDR));
927
928 #endif
929
930     LOG((LF_GCROOTS, LL_INFO100000, "STACKWALK    TailCallFrame::UpdateRegDisplay(ip:%p, sp:%p)\n", pRD->ControlPC, pRD->SP));
931
932     RETURN;
933 }
934
935 #ifdef FEATURE_READYTORUN
936 void DynamicHelperFrame::UpdateRegDisplay(const PREGDISPLAY pRD)
937 {
938     WRAPPER_NO_CONTRACT;
939     UpdateRegDisplayHelper(pRD, 0);
940 }
941 #endif // FEATURE_READYTORUN
942
943 //------------------------------------------------------------------------
944 // This is declared as returning WORD instead of PRD_TYPE because of
945 // header issues with cgencpu.h including dbginterface.h.
946 WORD GetUnpatchedCodeData(LPCBYTE pAddr)
947 {
948 #ifndef _TARGET_X86_
949 #error Make sure this works before porting to platforms other than x86.
950 #endif
951     CONTRACT(WORD) {
952         NOTHROW;
953         GC_NOTRIGGER;
954         PRECONDITION(CORDebuggerAttached());
955         PRECONDITION(CheckPointer(pAddr));
956         SO_TOLERANT;
957     } CONTRACT_END;
958
959     // Ordering is because x86 is little-endien.
960     BYTE bLow  = pAddr[0];
961     BYTE bHigh = pAddr[1];
962
963 #ifndef DACCESS_COMPILE
964     // Need to make sure that the code we're reading is free of breakpoint patches.
965     PRD_TYPE unpatchedOpcode;
966     if (g_pDebugInterface->CheckGetPatchedOpcode((CORDB_ADDRESS_TYPE *)pAddr,
967                                                  &unpatchedOpcode))
968     {
969         // PRD_TYPE is supposed to be an opaque debugger structure representing data to remove a patch.
970         // Although PRD_TYPE is currently typedef'ed to be a DWORD_PTR, it's actually semantically just a BYTE. 
971         // (since a patch on x86 is just an 0xCC instruction).
972         // Ideally, the debugger subsystem would expose a patch-code stripper that returns BYTE/WORD/etc, and
973         // not force us to crack it ourselves here. 
974         bLow = (BYTE) unpatchedOpcode;
975     }
976     // 
977 #endif
978
979     WORD w = bLow + (bHigh << 8);
980     RETURN w;
981 }
982
983
984 #ifndef DACCESS_COMPILE
985
986 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
987 //-------------------------------------------------------------------------
988 // One-time creation of special prestub to initialize UMEntryThunks.
989 //-------------------------------------------------------------------------
990 Stub *GenerateUMThunkPrestub()
991 {
992     CONTRACT(Stub*)
993     {
994         STANDARD_VM_CHECK;
995         POSTCONDITION(CheckPointer(RETVAL));
996     }
997     CONTRACT_END;
998
999     CPUSTUBLINKER sl;
1000     CPUSTUBLINKER *psl = &sl;
1001
1002     CodeLabel* rgRareLabels[] = { psl->NewCodeLabel(),
1003                                   psl->NewCodeLabel(),
1004                                   psl->NewCodeLabel()
1005                                 };
1006
1007
1008     CodeLabel* rgRejoinLabels[] = { psl->NewCodeLabel(),
1009                                     psl->NewCodeLabel(),
1010                                     psl->NewCodeLabel()
1011                                 };
1012
1013     // emit the initial prolog
1014     psl->EmitComMethodStubProlog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1015
1016     // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1017     psl->X86EmitIndexRegLoad(kECX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1018
1019     // The call conv is a __stdcall   
1020     psl->X86EmitPushReg(kECX);
1021
1022     // call UMEntryThunk::DoRunTimeInit
1023     psl->X86EmitCall(psl->NewExternalCodeLabel((LPVOID)UMEntryThunk::DoRunTimeInit), 4);
1024
1025     // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1026     psl->X86EmitIndexRegLoad(kEAX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1027
1028     //    lea eax, [eax + UMEntryThunk.m_code]  // point to fixedup UMEntryThunk
1029     psl->X86EmitOp(0x8d, kEAX, kEAX, 
1030                    UMEntryThunk::GetCodeOffset() + UMEntryThunkCode::GetEntryPointOffset());
1031
1032     psl->EmitComMethodStubEpilog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1033
1034     RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1035 }
1036 #endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
1037
1038 Stub *GenerateInitPInvokeFrameHelper()
1039 {
1040     CONTRACT(Stub*)
1041     {
1042         STANDARD_VM_CHECK;
1043         POSTCONDITION(CheckPointer(RETVAL));
1044     }
1045     CONTRACT_END;
1046
1047     CPUSTUBLINKER sl;
1048     CPUSTUBLINKER *psl = &sl;
1049
1050     CORINFO_EE_INFO::InlinedCallFrameInfo FrameInfo;
1051     InlinedCallFrame::GetEEInfo(&FrameInfo);
1052
1053     // EDI contains address of the frame on stack (the frame ptr, not its negspace)
1054     unsigned negSpace = FrameInfo.offsetOfFrameVptr;
1055
1056     // mov esi, GetThread()
1057     psl->X86EmitCurrentThreadFetch(kESI, (1<<kEDI)|(1<<kEBX)|(1<<kECX)|(1<<kEDX));
1058
1059     // mov [edi + FrameInfo.offsetOfGSCookie], GetProcessGSCookie()
1060     psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfGSCookie - negSpace);
1061     psl->Emit32(GetProcessGSCookie());
1062
1063     // mov [edi + FrameInfo.offsetOfFrameVptr], InlinedCallFrame::GetFrameVtable()
1064     psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfFrameVptr - negSpace);
1065     psl->Emit32(InlinedCallFrame::GetMethodFrameVPtr());
1066
1067     // mov eax, [esi + offsetof(Thread, m_pFrame)]
1068     // mov [edi + FrameInfo.offsetOfFrameLink], eax
1069     psl->X86EmitIndexRegLoad(kEAX, kESI, offsetof(Thread, m_pFrame));
1070     psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfFrameLink - negSpace, kEAX);
1071
1072     // mov [edi + FrameInfo.offsetOfCalleeSavedEbp], ebp
1073     psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfCalleeSavedFP - negSpace, kEBP);
1074
1075     // mov [edi + FrameInfo.offsetOfReturnAddress], 0
1076     psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfReturnAddress - negSpace);
1077     psl->Emit32(0);
1078
1079     // mov [esi + offsetof(Thread, m_pFrame)], edi
1080     psl->X86EmitIndexRegStore(kESI, offsetof(Thread, m_pFrame), kEDI);
1081
1082     // leave current Thread in ESI
1083     psl->X86EmitReturn(0);
1084
1085     // A single process-wide stub that will never unload
1086     RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1087 }
1088
1089
1090
1091 #ifdef MDA_SUPPORTED
1092
1093 //-----------------------------------------------------------------------------
1094 Stub *NDirectMethodDesc::GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub, BOOL fCalledByStub)
1095 {
1096     STANDARD_VM_CONTRACT;
1097
1098     CPUSTUBLINKER sl;
1099     sl.X86EmitPushEBPframe();
1100
1101     DWORD callConv = (DWORD)(IsThisCall() ? pmCallConvThiscall : (IsStdCall() ? pmCallConvStdcall : pmCallConvCdecl));
1102     _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1103
1104     MetaSig msig(this);
1105     if (msig.HasFPReturn())
1106     {
1107         // check for the HRESULT swapping impl flag
1108         DWORD dwImplFlags;
1109         IfFailThrow(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &dwImplFlags));
1110
1111         if (dwImplFlags & miPreserveSig)
1112         {
1113             // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1114             callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1115         }
1116     }
1117
1118     // init StackImbalanceCookie
1119     sl.X86EmitPushReg(kEAX);       // m_dwSavedEsp (just making space)
1120     sl.X86EmitPushImm32(callConv); // m_callConv
1121
1122     if (IsVarArgs())
1123     {
1124         // Re-push the return address as an argument to GetStackSizeForVarArgCall()
1125         if (fCalledByStub)
1126         {
1127             // We will be called by another stub that doesn't know the stack size,
1128             // so we need to skip a frame to get to the managed caller.
1129             sl.X86EmitIndexRegLoad(kEAX, kEBP, 0);
1130             sl.X86EmitIndexPush(kEAX, 4);
1131         }
1132         else
1133         {
1134             sl.X86EmitIndexPush(kEBP, 4);
1135         }
1136
1137         // This will return the number of stack arguments (in DWORDs)
1138         sl.X86EmitCall(sl.NewExternalCodeLabel((LPVOID)GetStackSizeForVarArgCall), 4);
1139         
1140         // shl eax,2
1141         sl.Emit16(0xe0c1);
1142         sl.Emit8(0x02);
1143         
1144         sl.X86EmitPushReg(kEAX); // m_dwStackArgSize
1145     }
1146     else
1147     {
1148         sl.X86EmitPushImm32(GetStackArgumentSize()); // m_dwStackArgSize
1149     }
1150
1151     LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1152     sl.X86EmitPushImmPtr(pTarget);       // m_pTarget
1153     sl.X86EmitPushImmPtr(this);          // m_pMD
1154
1155     // stack layout at this point
1156
1157     // |          ...          |
1158     // |    stack arguments    | EBP + 8
1159     // +-----------------------+
1160     // |    return address     | EBP + 4
1161     // +-----------------------+
1162     // |      saved EBP        | EBP + 0
1163     // +-----------------------+
1164     // | SIC::m_dwSavedEsp     |
1165     // | SIC::m_callConv       |
1166     // | SIC::m_dwStackArgSize |
1167     // | SIC::m_pTarget        |
1168     // | SIC::m_pMD            | EBP - 20
1169     // ------------------------
1170
1171     // call the helper
1172     sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1173
1174     //  pop StackImbalanceCookie
1175     sl.X86EmitMovSPReg(kEBP);
1176
1177     sl.X86EmitPopReg(kEBP);
1178     sl.X86EmitReturn((IsStdCall() || IsThisCall()) ? GetStackArgumentSize() : 0);
1179
1180     if (pInnerStub)
1181     {
1182         return sl.LinkInterceptor(GetLoaderAllocator()->GetStubHeap(), pInnerStub, pNativeTarget);
1183     }
1184     else
1185     {
1186         return sl.Link(GetLoaderAllocator()->GetStubHeap());
1187     }
1188 }
1189
1190 //-----------------------------------------------------------------------------
1191 // static
1192 Stub *COMDelegate::GenerateStubForMDA(MethodDesc *pInvokeMD, MethodDesc *pStubMD, LPVOID pNativeTarget, Stub *pInnerStub)
1193 {
1194     STANDARD_VM_CONTRACT;
1195
1196     WORD wStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
1197
1198     // get unmanaged calling convention from pInvokeMD's metadata
1199     PInvokeStaticSigInfo sigInfo(pInvokeMD);
1200     DWORD callConv = (DWORD)sigInfo.GetCallConv();
1201     _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1202
1203     MetaSig msig(pInvokeMD);
1204     if (msig.HasFPReturn())
1205     {
1206         // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1207         callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1208     }
1209
1210     CPUSTUBLINKER sl;
1211     sl.X86EmitPushEBPframe();
1212
1213     LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1214
1215     // init StackImbalanceCookie
1216     sl.X86EmitPushReg(kEAX);             // m_dwSavedEsp (just making space)
1217     sl.X86EmitPushImm32(callConv);       // m_callConv
1218     sl.X86EmitPushImm32(wStackArgSize);  // m_dwStackArgSize
1219     sl.X86EmitPushImmPtr(pTarget);       // m_pTarget
1220     sl.X86EmitPushImmPtr(pInvokeMD);     // m_pMD
1221
1222     // stack layout at this point
1223
1224     // |          ...          |
1225     // |    stack arguments    | EBP + 8
1226     // +-----------------------+
1227     // |    return address     | EBP + 4
1228     // +-----------------------+
1229     // |      saved EBP        | EBP + 0
1230     // +-----------------------+
1231     // | SIC::m_dwSavedEsp     |
1232     // | SIC::m_callConv       |
1233     // | SIC::m_dwStackArgSize |
1234     // | SIC::m_pTarget        |
1235     // | SIC::m_pMD            | EBP - 20
1236     // ------------------------
1237
1238     // call the helper
1239     sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1240
1241     //  pop StackImbalanceCookie
1242     sl.X86EmitMovSPReg(kEBP);
1243
1244     sl.X86EmitPopReg(kEBP);
1245     sl.X86EmitReturn(callConv == pmCallConvCdecl ? 0 : wStackArgSize);
1246
1247     if (pInnerStub != NULL)
1248     {
1249         return sl.LinkInterceptor(pInnerStub, pNativeTarget);
1250     }
1251     else
1252     {
1253         return sl.Link(); // don't use loader heap as we want to be able to free the stub
1254     }
1255 }
1256
1257 #endif // MDA_SUPPORTED
1258
1259 extern "C" VOID STDCALL StubRareEnableWorker(Thread *pThread)
1260 {
1261     WRAPPER_NO_CONTRACT;
1262
1263     //printf("RareEnable\n");
1264     pThread->RareEnablePreemptiveGC();
1265 }
1266
1267
1268
1269
1270 // Disable when calling into managed code from a place that fails via Exceptions
1271 extern "C" VOID STDCALL StubRareDisableTHROWWorker(Thread *pThread)
1272 {
1273     STATIC_CONTRACT_THROWS;
1274     STATIC_CONTRACT_GC_TRIGGERS;
1275
1276     // Do not add a CONTRACT here.  We haven't set up SEH.  We rely
1277     // on HandleThreadAbort and COMPlusThrowBoot dealing with this situation properly.
1278
1279     // WARNING!!!!
1280     // when we start executing here, we are actually in cooperative mode.  But we
1281     // haven't synchronized with the barrier to reentry yet.  So we are in a highly
1282     // dangerous mode.  If we call managed code, we will potentially be active in
1283     // the GC heap, even as GC's are occuring!
1284
1285     // Check for ShutDown scenario.  This happens only when we have initiated shutdown 
1286     // and someone is trying to call in after the CLR is suspended.  In that case, we
1287     // must either raise an unmanaged exception or return an HRESULT, depending on the
1288     // expectations of our caller.
1289     if (!CanRunManagedCode())
1290     {
1291         // DO NOT IMPROVE THIS EXCEPTION!  It cannot be a managed exception.  It
1292         // cannot be a real exception object because we cannot execute any managed
1293         // code here.
1294         pThread->m_fPreemptiveGCDisabled = 0;
1295         COMPlusThrowBoot(E_PROCESS_SHUTDOWN_REENTRY);
1296     }
1297
1298     // We must do the following in this order, because otherwise we would be constructing
1299     // the exception for the abort without synchronizing with the GC.  Also, we have no
1300     // CLR SEH set up, despite the fact that we may throw a ThreadAbortException.
1301     pThread->RareDisablePreemptiveGC();
1302     pThread->HandleThreadAbort();
1303 }
1304
1305 #ifndef FEATURE_PAL
1306 // Note that this logic is copied below, in PopSEHRecords
1307 __declspec(naked)
1308 VOID __cdecl PopSEHRecords(LPVOID pTargetSP)
1309 {
1310     // No CONTRACT possible on naked functions
1311     STATIC_CONTRACT_NOTHROW;
1312     STATIC_CONTRACT_GC_NOTRIGGER;
1313
1314     __asm{
1315         mov     ecx, [esp+4]        ;; ecx <- pTargetSP
1316         mov     eax, fs:[0]         ;; get current SEH record
1317   poploop:
1318         cmp     eax, ecx
1319         jge     done
1320         mov     eax, [eax]          ;; get next SEH record
1321         jmp     poploop
1322   done:
1323         mov     fs:[0], eax
1324         retn
1325     }
1326 }
1327 #endif // FEATURE_PAL
1328
1329 //////////////////////////////////////////////////////////////////////////////
1330 //
1331 // JITInterface
1332 //
1333 //////////////////////////////////////////////////////////////////////////////
1334
1335 /*********************************************************************/
1336 #ifdef EnC_SUPPORTED
1337 #pragma warning (disable : 4731)
1338 void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP)
1339 {
1340     // No CONTRACT here, because we can't run the risk of it pushing any SEH into the
1341     // current method.
1342
1343     STATIC_CONTRACT_NOTHROW;
1344     STATIC_CONTRACT_GC_NOTRIGGER;
1345
1346 #ifdef _DEBUG
1347     DWORD curESP;
1348     __asm mov curESP, esp
1349 #endif
1350
1351     if (oldESP)
1352     {
1353         _ASSERTE(curESP < (DWORD)(size_t)oldESP);
1354         // should have popped the SEH records by now as stack has been overwritten
1355         _ASSERTE(GetCurrentSEHRecord() > oldESP);
1356     }
1357
1358     // For the "push Eip, ..., ret"
1359     _ASSERTE(curESP < pContext->Esp - sizeof(DWORD));
1360     pContext->Esp -= sizeof(DWORD);
1361
1362     __asm {
1363         mov     ebp, pContext
1364
1365         // Push Eip onto the targetESP, so that the final "ret" will consume it
1366         mov     ecx, [ebp]CONTEXT.Esp
1367         mov     edx, [ebp]CONTEXT.Eip
1368         mov     [ecx], edx
1369
1370         // Restore all registers except Esp, Ebp, Eip
1371         mov     eax, [ebp]CONTEXT.Eax
1372         mov     ebx, [ebp]CONTEXT.Ebx
1373         mov     ecx, [ebp]CONTEXT.Ecx
1374         mov     edx, [ebp]CONTEXT.Edx
1375         mov     esi, [ebp]CONTEXT.Esi
1376         mov     edi, [ebp]CONTEXT.Edi
1377
1378         push    [ebp]CONTEXT.Esp  // pContext->Esp is (targetESP-sizeof(DWORD))
1379         push    [ebp]CONTEXT.Ebp
1380         pop     ebp
1381         pop     esp
1382
1383         // esp is (targetESP-sizeof(DWORD)), and [esp] is the targetEIP.
1384         // The ret will set eip to targetEIP and esp will be automatically
1385         // incremented to targetESP
1386
1387         ret
1388     }
1389 }
1390 #pragma warning (default : 4731)
1391 #endif // !EnC_SUPPORTED
1392
1393
1394 #ifndef FEATURE_PAL
1395 #pragma warning(push)
1396 #pragma warning(disable: 4035)
1397 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1398 {
1399     LIMITED_METHOD_CONTRACT
1400
1401     __asm
1402     {
1403         push    ebx
1404         push    esi
1405         mov     eax, arg
1406         cpuid
1407         mov     esi, result
1408         mov     [esi+ 0], eax
1409         mov     [esi+ 4], ebx
1410         mov     [esi+ 8], ecx
1411         mov     [esi+12], edx
1412         pop     esi
1413         pop     ebx
1414     }
1415 }
1416
1417 // The following function uses Deterministic Cache Parameter leafs to determine the cache hierarchy information on Prescott & Above platforms. 
1418 //  This function takes 3 arguments:
1419 //     Arg1 is an input to ECX. Used as index to specify which cache level to return infoformation on by CPUID.
1420 //     Arg2 is an input to EAX. For deterministic code enumeration, we pass in 4H in arg2.
1421 //     Arg3 is a pointer to the return buffer
1422 //   No need to check whether or not CPUID is supported because we have already called CPUID with success to come here.
1423
1424 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1425 {
1426     LIMITED_METHOD_CONTRACT
1427
1428     __asm
1429     {
1430         push    ebx
1431         push    esi
1432         mov     ecx, arg1
1433         mov     eax, arg2
1434         cpuid
1435         mov     esi, result
1436         mov     [esi+ 0], eax
1437         mov     [esi+ 4], ebx
1438         mov     [esi+ 8], ecx
1439         mov     [esi+12], edx
1440         pop     esi
1441         pop     ebx
1442     }
1443 }
1444
1445 extern "C" DWORD __stdcall xmmYmmStateSupport()
1446 {
1447     // No CONTRACT
1448     STATIC_CONTRACT_NOTHROW;
1449     STATIC_CONTRACT_GC_NOTRIGGER;
1450
1451     __asm
1452     {
1453         mov     ecx, 0                  ; Specify xcr0
1454         xgetbv                          ; result in EDX:EAX
1455         and eax, 06H
1456         cmp eax, 06H                    ; check OS has enabled both XMM and YMM state support
1457         jne     not_supported
1458         mov     eax, 1
1459         jmp     done
1460     not_supported:
1461         mov     eax, 0
1462     done:
1463     }
1464 }
1465
1466 #pragma warning(pop)
1467
1468 #else // !FEATURE_PAL
1469
1470 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1471 {
1472     DWORD eax;
1473     __asm("  xor %%ecx, %%ecx\n" \
1474             "  cpuid\n" \
1475             "  mov %%eax, 0(%[result])\n" \
1476             "  mov %%ebx, 4(%[result])\n" \
1477             "  mov %%ecx, 8(%[result])\n" \
1478             "  mov %%edx, 12(%[result])\n" \
1479         : "=a"(eax) /*output in eax*/\
1480         : "a"(arg), [result]"r"(result) /*inputs - arg in eax, result in any register*/\
1481         : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1482         );
1483     return eax;
1484 }
1485
1486 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1487 {
1488     DWORD eax;
1489     __asm("  cpuid\n" \
1490             "  mov %%eax, 0(%[result])\n" \
1491             "  mov %%ebx, 4(%[result])\n" \
1492             "  mov %%ecx, 8(%[result])\n" \
1493             "  mov %%edx, 12(%[result])\n" \
1494         : "=a"(eax) /*output in eax*/\
1495         : "c"(arg1), "a"(arg2), [result]"r"(result) /*inputs - arg1 in ecx, arg2 in eax, result in any register*/\
1496         : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1497         );
1498     return eax;
1499 }
1500
1501 extern "C" DWORD __stdcall xmmYmmStateSupport()
1502 {
1503     DWORD eax;
1504     __asm("  xgetbv\n" \
1505         : "=a"(eax) /*output in eax*/\
1506         : "c"(0) /*inputs - 0 in ecx*/\
1507         : "eax", "edx" /* registers that are clobbered*/
1508         );
1509     // check OS has enabled both XMM and YMM state support
1510     return ((eax & 0x06) == 0x06) ? 1 : 0;
1511 }
1512
1513 #endif // !FEATURE_PAL
1514
1515 // This function returns the number of logical processors on a given physical chip.  If it cannot
1516 // determine the number of logical cpus, or the machine is not populated uniformly with the same
1517 // type of processors, this function returns 1.
1518 DWORD GetLogicalCpuCount()
1519 {
1520     // No CONTRACT possible because GetLogicalCpuCount uses SEH
1521
1522     STATIC_CONTRACT_THROWS;
1523     STATIC_CONTRACT_GC_NOTRIGGER;
1524
1525     static DWORD val = 0;
1526
1527     // cache value for later re-use
1528     if (val)
1529     {
1530         return val;
1531     }
1532
1533     struct Param : DefaultCatchFilterParam
1534     {
1535         DWORD retVal;
1536     } param;
1537     param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
1538     param.retVal = 1;
1539
1540     PAL_TRY(Param *, pParam, &param)
1541     {
1542         unsigned char buffer[16];
1543         DWORD* dwBuffer = NULL;
1544
1545         DWORD maxCpuId = getcpuid(0, buffer);
1546
1547         if (maxCpuId < 1)
1548             goto lDone;
1549
1550         dwBuffer = (DWORD*)buffer;
1551
1552         if (dwBuffer[1] == 'uneG') {
1553             if (dwBuffer[3] == 'Ieni') {
1554                 if (dwBuffer[2] == 'letn')  {  // get SMT/multicore enumeration for Intel EM64T 
1555
1556                     // TODO: Currently GetLogicalCpuCountFromOS() and GetLogicalCpuCountFallback() are broken on 
1557                     // multi-core processor, but we never call into those two functions since we don't halve the
1558                     // gen0size when it's prescott and above processor. We keep the old version here for earlier
1559                     // generation system(Northwood based), perf data suggests on those systems, halve gen0 size 
1560                     // still boost the performance(ex:Biztalk boosts about 17%). So on earlier systems(Northwood) 
1561                     // based, we still go ahead and halve gen0 size.  The logic in GetLogicalCpuCountFromOS() 
1562                     // and GetLogicalCpuCountFallback() works fine for those earlier generation systems. 
1563                     // If it's a Prescott and above processor or Multi-core, perf data suggests not to halve gen0 
1564                     // size at all gives us overall better performance. 
1565                     // This is going to be fixed with a new version in orcas time frame. 
1566
1567                     if( (maxCpuId > 3) && (maxCpuId < 0x80000000) ) 
1568                         goto lDone;
1569
1570                     val = GetLogicalCpuCountFromOS(); //try to obtain HT enumeration from OS API
1571                     if (val )
1572                     {
1573                         pParam->retVal = val;     // OS API HT enumeration successful, we are Done        
1574                         goto lDone;
1575                     }
1576
1577                     val = GetLogicalCpuCountFallback();    // OS API failed, Fallback to HT enumeration using CPUID
1578                     if( val )
1579                         pParam->retVal = val;
1580                 }
1581             }
1582         }
1583 lDone: ;
1584     }
1585     PAL_EXCEPT_FILTER(DefaultCatchFilter)
1586     {
1587     }
1588     PAL_ENDTRY
1589
1590     if (val == 0)
1591     {
1592         val = param.retVal;
1593     }
1594
1595     return param.retVal;
1596 }
1597
1598 void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
1599 {
1600     LIMITED_METHOD_CONTRACT;
1601
1602 #ifdef _DEBUG
1603     m_alignpad[0] = X86_INSTR_INT3;
1604     m_alignpad[1] = X86_INSTR_INT3;
1605 #endif // _DEBUG
1606     m_movEAX     = X86_INSTR_MOV_EAX_IMM32;
1607     m_uet        = pvSecretParam;
1608     m_jmp        = X86_INSTR_JMP_REL32;
1609     m_execstub   = (BYTE*) ((pTargetCode) - (4+((BYTE*)&m_execstub)));
1610
1611     FlushInstructionCache(GetCurrentProcess(),GetEntryPoint(),sizeof(UMEntryThunkCode));
1612 }
1613
1614 UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
1615 {
1616     LIMITED_METHOD_CONTRACT;
1617
1618     if (*((BYTE*)pCallback) != X86_INSTR_MOV_EAX_IMM32 ||
1619         ( ((size_t)pCallback) & 3) != 2) {
1620         return NULL;
1621     }
1622     return *(UMEntryThunk**)( 1 + (BYTE*)pCallback );
1623 }
1624
1625 BOOL DoesSlotCallPrestub(PCODE pCode)
1626 {
1627     CONTRACTL {
1628         NOTHROW;
1629         GC_NOTRIGGER;
1630         SO_TOLERANT;
1631         PRECONDITION(pCode != NULL);
1632         PRECONDITION(pCode != GetPreStubEntryPoint());
1633     } CONTRACTL_END;
1634
1635     // x86 has the following possible sequences for prestub logic:
1636     // 1. slot -> temporary entrypoint -> prestub
1637     // 2. slot -> precode -> prestub
1638     // 3. slot -> precode -> jumprel32 (NGEN case) -> prestub
1639
1640 #ifdef HAS_COMPACT_ENTRYPOINTS
1641     if (MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, TRUE) != NULL)
1642     {
1643         return TRUE;
1644     }
1645 #endif // HAS_COMPACT_ENTRYPOINTS
1646
1647     if (!IS_ALIGNED(pCode, PRECODE_ALIGNMENT))
1648     {
1649         return FALSE;
1650     }
1651
1652 #ifdef HAS_FIXUP_PRECODE
1653     if (*PTR_BYTE(pCode) == X86_INSTR_CALL_REL32)
1654     {
1655         // Note that call could have been patched to jmp in the meantime
1656         pCode = rel32Decode(pCode+1);
1657
1658         // NGEN case
1659         if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1660             pCode = rel32Decode(pCode+1);
1661         }
1662
1663         return pCode == (TADDR)PrecodeFixupThunk;
1664     }
1665 #endif
1666
1667     if (*PTR_BYTE(pCode) != X86_INSTR_MOV_EAX_IMM32 ||
1668         *PTR_BYTE(pCode+5) != X86_INSTR_MOV_RM_R ||
1669         *PTR_BYTE(pCode+7) != X86_INSTR_JMP_REL32)
1670     {
1671         return FALSE;
1672     }
1673     pCode = rel32Decode(pCode+8);
1674
1675     // NGEN case
1676     if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1677         pCode = rel32Decode(pCode+1);
1678     }
1679
1680     return pCode == GetPreStubEntryPoint();
1681 }
1682
1683 //==========================================================================================
1684 // In NGen image, virtual slots inherited from cross-module dependencies point to jump thunks.
1685 // These jump thunk initially point to VirtualMethodFixupStub which transfers control here.
1686 // This method 'VirtualMethodFixupWorker' will patch the jump thunk to point to the actual
1687 // inherited method body after we have execute the precode and a stable entry point.
1688 //
1689 EXTERN_C PVOID STDCALL VirtualMethodFixupWorker(Object * pThisPtr,  CORCOMPILE_VIRTUAL_IMPORT_THUNK *pThunk)
1690 {
1691     CONTRACTL
1692     {
1693         NOTHROW;
1694         GC_NOTRIGGER;
1695         MODE_COOPERATIVE;
1696         ENTRY_POINT;
1697     }
1698     CONTRACTL_END;
1699
1700     _ASSERTE(pThisPtr != NULL);
1701     VALIDATEOBJECT(pThisPtr);
1702
1703     MethodTable * pMT = pThisPtr->GetTrueMethodTable();
1704
1705     WORD slotNumber = pThunk->slotNum;
1706     _ASSERTE(slotNumber != (WORD)-1);
1707
1708     PCODE pCode = pMT->GetRestoredSlot(slotNumber);
1709
1710     if (!DoesSlotCallPrestub(pCode))
1711     {
1712         // Skip fixup precode jump for better perf
1713         PCODE pDirectTarget = Precode::TryToSkipFixupPrecode(pCode);
1714         if (pDirectTarget != NULL)
1715             pCode = pDirectTarget;
1716
1717         INT64 oldValue = *(INT64*)pThunk;
1718         BYTE* pOldValue = (BYTE*)&oldValue;
1719
1720         if (pOldValue[0] == X86_INSTR_CALL_REL32)
1721         {
1722             INT64 newValue = oldValue;
1723             BYTE* pNewValue = (BYTE*)&newValue;
1724             pNewValue[0] = X86_INSTR_JMP_REL32;
1725
1726             INT_PTR pcRelOffset = (BYTE*)pCode - &pThunk->callJmp[5];
1727             *(INT32 *)(&pNewValue[1]) = (INT32) pcRelOffset;
1728
1729             _ASSERTE(IS_ALIGNED(pThunk, sizeof(INT64)));
1730             if (EnsureWritableExecutablePagesNoThrow(pThunk, sizeof(INT64)))
1731                 FastInterlockCompareExchangeLong((INT64*)pThunk, newValue, oldValue);
1732
1733             FlushInstructionCache(GetCurrentProcess(), pThunk, 8);
1734         }
1735     }
1736
1737     return PVOID(pCode);
1738 }
1739
1740
1741 #ifdef FEATURE_READYTORUN
1742
1743 //
1744 // Allocation of dynamic helpers
1745 //
1746
1747 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
1748
1749 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
1750     SIZE_T cb = size; \
1751     SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
1752     BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
1753     BYTE * p = pStart;
1754
1755 #define END_DYNAMIC_HELPER_EMIT() \
1756     _ASSERTE(pStart + cb == p); \
1757     while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \
1758     ClrFlushInstructionCache(pStart, cbAligned); \
1759     return (PCODE)pStart
1760
1761 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1762 {
1763     STANDARD_VM_CONTRACT;
1764
1765     BEGIN_DYNAMIC_HELPER_EMIT(10);
1766
1767     *p++ = 0xB9; // mov ecx, XXXXXX
1768     *(INT32 *)p = (INT32)arg;
1769     p += 4;
1770
1771     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1772     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1773     p += 4;
1774
1775     END_DYNAMIC_HELPER_EMIT();
1776 }
1777
1778 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1779 {
1780     CONTRACTL
1781     {
1782         GC_NOTRIGGER;
1783         PRECONDITION(p != NULL && target != NULL);
1784     }
1785     CONTRACTL_END;
1786
1787     // Move an an argument into the second argument register and jump to a target function.
1788
1789     *p++ = 0xBA; // mov edx, XXXXXX
1790     *(INT32 *)p = (INT32)arg;
1791     p += 4;
1792
1793     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1794     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1795     p += 4;
1796 }
1797
1798 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1799 {
1800     BEGIN_DYNAMIC_HELPER_EMIT(10);
1801
1802     EmitHelperWithArg(p, pAllocator, arg, target);
1803
1804     END_DYNAMIC_HELPER_EMIT();
1805 }
1806
1807 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1808 {
1809     BEGIN_DYNAMIC_HELPER_EMIT(15);
1810
1811     *p++ = 0xB9; // mov ecx, XXXXXX
1812     *(INT32 *)p = (INT32)arg;
1813     p += 4;
1814
1815     *p++ = 0xBA; // mov edx, XXXXXX
1816     *(INT32 *)p = (INT32)arg2;
1817     p += 4;
1818
1819     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1820     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1821     p += 4;
1822
1823     END_DYNAMIC_HELPER_EMIT();
1824 }
1825
1826 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1827 {
1828     BEGIN_DYNAMIC_HELPER_EMIT(12);
1829
1830     *(UINT16 *)p = 0xD18B; // mov edx, ecx
1831     p += 2;
1832
1833     *p++ = 0xB9; // mov ecx, XXXXXX
1834     *(INT32 *)p = (INT32)arg;
1835     p += 4;
1836
1837     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1838     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1839     p += 4;
1840
1841     END_DYNAMIC_HELPER_EMIT();
1842 }
1843
1844 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
1845 {
1846     BEGIN_DYNAMIC_HELPER_EMIT(1);
1847
1848     *p++ = 0xC3; // ret
1849
1850     END_DYNAMIC_HELPER_EMIT();
1851 }
1852
1853 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
1854 {
1855     BEGIN_DYNAMIC_HELPER_EMIT(6);
1856
1857     *p++ = 0xB8; // mov eax, XXXXXX
1858     *(INT32 *)p = (INT32)arg;
1859     p += 4;
1860
1861     *p++ = 0xC3; // ret
1862
1863     END_DYNAMIC_HELPER_EMIT();
1864 }
1865
1866 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
1867 {
1868     BEGIN_DYNAMIC_HELPER_EMIT((offset != 0) ? 9 : 6);
1869
1870     *p++ = 0xA1; // mov eax, [XXXXXX]
1871     *(INT32 *)p = (INT32)arg;
1872     p += 4;
1873
1874     if (offset != 0)
1875     {
1876         // add eax, <offset>
1877         *p++ = 0x83;
1878         *p++ = 0xC0;
1879         *p++ = offset;
1880     }
1881
1882     *p++ = 0xC3; // ret
1883
1884     END_DYNAMIC_HELPER_EMIT();
1885 }
1886
1887 EXTERN_C VOID DynamicHelperArgsStub();
1888
1889 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1890 {
1891 #ifdef UNIX_X86_ABI
1892     BEGIN_DYNAMIC_HELPER_EMIT(18);
1893 #else
1894     BEGIN_DYNAMIC_HELPER_EMIT(12);
1895 #endif
1896
1897 #ifdef UNIX_X86_ABI
1898         // sub esp, 8
1899         *p++ = 0x83;
1900         *p++ = 0xec;
1901         *p++ = 0x8;
1902 #else
1903     // pop eax
1904     *p++ = 0x58;
1905 #endif
1906
1907     // push arg
1908     *p++ = 0x68;
1909     *(INT32 *)p = arg;
1910     p += 4;
1911
1912 #ifdef UNIX_X86_ABI
1913     // mov eax, target
1914     *p++ = 0xB8;
1915     *(INT32 *)p = target;
1916     p += 4;
1917 #else
1918     // push eax
1919     *p++ = 0x50;
1920 #endif
1921
1922     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1923 #ifdef UNIX_X86_ABI
1924     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1925 #else
1926     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1927 #endif
1928     p += 4;
1929
1930     END_DYNAMIC_HELPER_EMIT();
1931 }
1932
1933 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1934 {
1935 #ifdef UNIX_X86_ABI
1936     BEGIN_DYNAMIC_HELPER_EMIT(23);
1937 #else
1938     BEGIN_DYNAMIC_HELPER_EMIT(17);
1939 #endif
1940
1941 #ifdef UNIX_X86_ABI
1942         // sub esp, 4
1943         *p++ = 0x83;
1944         *p++ = 0xec;
1945         *p++ = 0x4;
1946 #else
1947     // pop eax
1948     *p++ = 0x58;
1949 #endif
1950
1951     // push arg
1952     *p++ = 0x68;
1953     *(INT32 *)p = arg;
1954     p += 4;
1955
1956     // push arg2
1957     *p++ = 0x68;
1958     *(INT32 *)p = arg2;
1959     p += 4;
1960
1961 #ifdef UNIX_X86_ABI
1962     // mov eax, target
1963     *p++ = 0xB8;
1964     *(INT32 *)p = target;
1965     p += 4;
1966 #else
1967     // push eax
1968     *p++ = 0x50;
1969 #endif
1970
1971     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1972 #ifdef UNIX_X86_ABI
1973     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1974 #else
1975     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1976 #endif
1977     p += 4;
1978
1979     END_DYNAMIC_HELPER_EMIT();
1980 }
1981
1982 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
1983 {
1984     STANDARD_VM_CONTRACT;
1985
1986     PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
1987         GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
1988         GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
1989
1990     GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
1991     pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
1992     pArgs->signature = pLookup->signature;
1993     pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
1994
1995     // It's available only via the run-time helper function
1996     if (pLookup->indirections == CORINFO_USEHELPER)
1997     {
1998         BEGIN_DYNAMIC_HELPER_EMIT(10);
1999
2000         // ecx contains the generic context parameter
2001         // mov edx,pArgs
2002         // jmp helperAddress
2003         EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2004
2005         END_DYNAMIC_HELPER_EMIT();
2006     }
2007     else
2008     {
2009         int indirectionsSize = 0;
2010         for (WORD i = 0; i < pLookup->indirections; i++)
2011             indirectionsSize += (pLookup->offsets[i] >= 0x80 ? 6 : 3);
2012
2013         int codeSize = indirectionsSize + (pLookup->testForNull ? 21 : 3);
2014
2015         BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
2016
2017         if (pLookup->testForNull)
2018         {
2019             // ecx contains the generic context parameter. Save a copy of it in the eax register
2020             // mov eax,ecx
2021             *(UINT16*)p = 0xc889; p += 2;
2022         }
2023
2024         for (WORD i = 0; i < pLookup->indirections; i++)
2025         {
2026             // mov ecx,qword ptr [ecx+offset]
2027             if (pLookup->offsets[i] >= 0x80)
2028             {
2029                 *(UINT16*)p = 0x898b; p += 2;
2030                 *(UINT32*)p = (UINT32)pLookup->offsets[i]; p += 4;
2031             }
2032             else
2033             {
2034                 *(UINT16*)p = 0x498b; p += 2;
2035                 *p++ = (BYTE)pLookup->offsets[i];
2036             }
2037         }
2038
2039         // No null test required
2040         if (!pLookup->testForNull)
2041         {
2042             // No fixups needed for R2R
2043
2044             // mov eax,ecx
2045             *(UINT16*)p = 0xc889; p += 2;
2046             *p++ = 0xC3;    // ret
2047         }
2048         else
2049         {
2050             // ecx contains the value of the dictionary slot entry
2051
2052             _ASSERTE(pLookup->indirections != 0);
2053
2054             // test ecx,ecx
2055             *(UINT16*)p = 0xc985; p += 2;
2056
2057             // je 'HELPER_CALL' (a jump of 3 bytes)
2058             *(UINT16*)p = 0x0374; p += 2;
2059
2060             // mov eax,ecx
2061             *(UINT16*)p = 0xc889; p += 2;
2062             *p++ = 0xC3;    // ret
2063
2064             // 'HELPER_CALL'
2065             {
2066                 // Put the generic context back into rcx (was previously saved in eax)
2067                 // mov ecx,eax
2068                 *(UINT16*)p = 0xc189; p += 2;
2069
2070                 // mov edx,pArgs
2071                 // jmp helperAddress
2072                 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2073             }
2074         }
2075
2076         END_DYNAMIC_HELPER_EMIT();
2077     }
2078 }
2079
2080 #endif // FEATURE_READYTORUN
2081
2082
2083 #endif // DACCESS_COMPILE