6cb5427244fb708fb778f688254bea28d9d69043
[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 //------------------------------------------------------------------------
936 // This is declared as returning WORD instead of PRD_TYPE because of
937 // header issues with cgencpu.h including dbginterface.h.
938 WORD GetUnpatchedCodeData(LPCBYTE pAddr)
939 {
940 #ifndef _TARGET_X86_
941 #error Make sure this works before porting to platforms other than x86.
942 #endif
943     CONTRACT(WORD) {
944         NOTHROW;
945         GC_NOTRIGGER;
946         PRECONDITION(CORDebuggerAttached());
947         PRECONDITION(CheckPointer(pAddr));
948         SO_TOLERANT;
949     } CONTRACT_END;
950
951     // Ordering is because x86 is little-endien.
952     BYTE bLow  = pAddr[0];
953     BYTE bHigh = pAddr[1];
954
955 #ifndef DACCESS_COMPILE
956     // Need to make sure that the code we're reading is free of breakpoint patches.
957     PRD_TYPE unpatchedOpcode;
958     if (g_pDebugInterface->CheckGetPatchedOpcode((CORDB_ADDRESS_TYPE *)pAddr,
959                                                  &unpatchedOpcode))
960     {
961         // PRD_TYPE is supposed to be an opaque debugger structure representing data to remove a patch.
962         // Although PRD_TYPE is currently typedef'ed to be a DWORD_PTR, it's actually semantically just a BYTE. 
963         // (since a patch on x86 is just an 0xCC instruction).
964         // Ideally, the debugger subsystem would expose a patch-code stripper that returns BYTE/WORD/etc, and
965         // not force us to crack it ourselves here. 
966         bLow = (BYTE) unpatchedOpcode;
967     }
968     // 
969 #endif
970
971     WORD w = bLow + (bHigh << 8);
972     RETURN w;
973 }
974
975
976 #ifndef DACCESS_COMPILE
977
978 #if defined(_TARGET_X86_) && !defined(FEATURE_STUBS_AS_IL)
979 //-------------------------------------------------------------------------
980 // One-time creation of special prestub to initialize UMEntryThunks.
981 //-------------------------------------------------------------------------
982 Stub *GenerateUMThunkPrestub()
983 {
984     CONTRACT(Stub*)
985     {
986         STANDARD_VM_CHECK;
987         POSTCONDITION(CheckPointer(RETVAL));
988     }
989     CONTRACT_END;
990
991     CPUSTUBLINKER sl;
992     CPUSTUBLINKER *psl = &sl;
993
994     CodeLabel* rgRareLabels[] = { psl->NewCodeLabel(),
995                                   psl->NewCodeLabel(),
996                                   psl->NewCodeLabel()
997                                 };
998
999
1000     CodeLabel* rgRejoinLabels[] = { psl->NewCodeLabel(),
1001                                     psl->NewCodeLabel(),
1002                                     psl->NewCodeLabel()
1003                                 };
1004
1005     // emit the initial prolog
1006     psl->EmitComMethodStubProlog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1007
1008     // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1009     psl->X86EmitIndexRegLoad(kECX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1010
1011     // The call conv is a __stdcall   
1012     psl->X86EmitPushReg(kECX);
1013
1014     // call UMEntryThunk::DoRunTimeInit
1015     psl->X86EmitCall(psl->NewExternalCodeLabel((LPVOID)UMEntryThunk::DoRunTimeInit), 4);
1016
1017     // mov ecx, [esi+UMThkCallFrame.pUMEntryThunk]
1018     psl->X86EmitIndexRegLoad(kEAX, kESI, UMThkCallFrame::GetOffsetOfUMEntryThunk());
1019
1020     //    lea eax, [eax + UMEntryThunk.m_code]  // point to fixedup UMEntryThunk
1021     psl->X86EmitOp(0x8d, kEAX, kEAX, 
1022                    UMEntryThunk::GetCodeOffset() + UMEntryThunkCode::GetEntryPointOffset());
1023
1024     psl->EmitComMethodStubEpilog(UMThkCallFrame::GetMethodFrameVPtr(), rgRareLabels, rgRejoinLabels, FALSE /*Don't profile*/);
1025
1026     RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1027 }
1028 #endif // _TARGET_X86_ && !FEATURE_STUBS_AS_IL
1029
1030 Stub *GenerateInitPInvokeFrameHelper()
1031 {
1032     CONTRACT(Stub*)
1033     {
1034         STANDARD_VM_CHECK;
1035         POSTCONDITION(CheckPointer(RETVAL));
1036     }
1037     CONTRACT_END;
1038
1039     CPUSTUBLINKER sl;
1040     CPUSTUBLINKER *psl = &sl;
1041
1042     CORINFO_EE_INFO::InlinedCallFrameInfo FrameInfo;
1043     InlinedCallFrame::GetEEInfo(&FrameInfo);
1044
1045     // EDI contains address of the frame on stack (the frame ptr, not its negspace)
1046     unsigned negSpace = FrameInfo.offsetOfFrameVptr;
1047
1048     // mov esi, GetThread()
1049     psl->X86EmitCurrentThreadFetch(kESI, (1<<kEDI)|(1<<kEBX)|(1<<kECX)|(1<<kEDX));
1050
1051     // mov [edi + FrameInfo.offsetOfGSCookie], GetProcessGSCookie()
1052     psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfGSCookie - negSpace);
1053     psl->Emit32(GetProcessGSCookie());
1054
1055     // mov [edi + FrameInfo.offsetOfFrameVptr], InlinedCallFrame::GetFrameVtable()
1056     psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfFrameVptr - negSpace);
1057     psl->Emit32(InlinedCallFrame::GetMethodFrameVPtr());
1058
1059     // mov eax, [esi + offsetof(Thread, m_pFrame)]
1060     // mov [edi + FrameInfo.offsetOfFrameLink], eax
1061     psl->X86EmitIndexRegLoad(kEAX, kESI, offsetof(Thread, m_pFrame));
1062     psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfFrameLink - negSpace, kEAX);
1063
1064     // mov [edi + FrameInfo.offsetOfCalleeSavedEbp], ebp
1065     psl->X86EmitIndexRegStore(kEDI, FrameInfo.offsetOfCalleeSavedFP - negSpace, kEBP);
1066
1067     // mov [edi + FrameInfo.offsetOfReturnAddress], 0
1068     psl->X86EmitOffsetModRM(0xc7, (X86Reg)0x0, kEDI, FrameInfo.offsetOfReturnAddress - negSpace);
1069     psl->Emit32(0);
1070
1071     // mov [esi + offsetof(Thread, m_pFrame)], edi
1072     psl->X86EmitIndexRegStore(kESI, offsetof(Thread, m_pFrame), kEDI);
1073
1074     // leave current Thread in ESI
1075     psl->X86EmitReturn(0);
1076
1077     // A single process-wide stub that will never unload
1078     RETURN psl->Link(SystemDomain::GetGlobalLoaderAllocator()->GetExecutableHeap());
1079 }
1080
1081
1082
1083 #ifdef MDA_SUPPORTED
1084
1085 //-----------------------------------------------------------------------------
1086 Stub *NDirectMethodDesc::GenerateStubForMDA(LPVOID pNativeTarget, Stub *pInnerStub, BOOL fCalledByStub)
1087 {
1088     STANDARD_VM_CONTRACT;
1089
1090     CPUSTUBLINKER sl;
1091     sl.X86EmitPushEBPframe();
1092
1093     DWORD callConv = (DWORD)(IsThisCall() ? pmCallConvThiscall : (IsStdCall() ? pmCallConvStdcall : pmCallConvCdecl));
1094     _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1095
1096     MetaSig msig(this);
1097     if (msig.HasFPReturn())
1098     {
1099         // check for the HRESULT swapping impl flag
1100         DWORD dwImplFlags;
1101         IfFailThrow(GetMDImport()->GetMethodImplProps(GetMemberDef(), NULL, &dwImplFlags));
1102
1103         if (dwImplFlags & miPreserveSig)
1104         {
1105             // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1106             callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1107         }
1108     }
1109
1110     // init StackImbalanceCookie
1111     sl.X86EmitPushReg(kEAX);       // m_dwSavedEsp (just making space)
1112     sl.X86EmitPushImm32(callConv); // m_callConv
1113
1114     if (IsVarArgs())
1115     {
1116         // Re-push the return address as an argument to GetStackSizeForVarArgCall()
1117         if (fCalledByStub)
1118         {
1119             // We will be called by another stub that doesn't know the stack size,
1120             // so we need to skip a frame to get to the managed caller.
1121             sl.X86EmitIndexRegLoad(kEAX, kEBP, 0);
1122             sl.X86EmitIndexPush(kEAX, 4);
1123         }
1124         else
1125         {
1126             sl.X86EmitIndexPush(kEBP, 4);
1127         }
1128
1129         // This will return the number of stack arguments (in DWORDs)
1130         sl.X86EmitCall(sl.NewExternalCodeLabel((LPVOID)GetStackSizeForVarArgCall), 4);
1131         
1132         // shl eax,2
1133         sl.Emit16(0xe0c1);
1134         sl.Emit8(0x02);
1135         
1136         sl.X86EmitPushReg(kEAX); // m_dwStackArgSize
1137     }
1138     else
1139     {
1140         sl.X86EmitPushImm32(GetStackArgumentSize()); // m_dwStackArgSize
1141     }
1142
1143     LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1144     sl.X86EmitPushImmPtr(pTarget);       // m_pTarget
1145     sl.X86EmitPushImmPtr(this);          // m_pMD
1146
1147     // stack layout at this point
1148
1149     // |          ...          |
1150     // |    stack arguments    | EBP + 8
1151     // +-----------------------+
1152     // |    return address     | EBP + 4
1153     // +-----------------------+
1154     // |      saved EBP        | EBP + 0
1155     // +-----------------------+
1156     // | SIC::m_dwSavedEsp     |
1157     // | SIC::m_callConv       |
1158     // | SIC::m_dwStackArgSize |
1159     // | SIC::m_pTarget        |
1160     // | SIC::m_pMD            | EBP - 20
1161     // ------------------------
1162
1163     // call the helper
1164     sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1165
1166     //  pop StackImbalanceCookie
1167     sl.X86EmitMovSPReg(kEBP);
1168
1169     sl.X86EmitPopReg(kEBP);
1170     sl.X86EmitReturn((IsStdCall() || IsThisCall()) ? GetStackArgumentSize() : 0);
1171
1172     if (pInnerStub)
1173     {
1174         return sl.LinkInterceptor(GetLoaderAllocator()->GetStubHeap(), pInnerStub, pNativeTarget);
1175     }
1176     else
1177     {
1178         return sl.Link(GetLoaderAllocator()->GetStubHeap());
1179     }
1180 }
1181
1182 //-----------------------------------------------------------------------------
1183 // static
1184 Stub *COMDelegate::GenerateStubForMDA(MethodDesc *pInvokeMD, MethodDesc *pStubMD, LPVOID pNativeTarget, Stub *pInnerStub)
1185 {
1186     STANDARD_VM_CONTRACT;
1187
1188     WORD wStackArgSize = pStubMD->AsDynamicMethodDesc()->GetNativeStackArgSize();
1189
1190     // get unmanaged calling convention from pInvokeMD's metadata
1191     PInvokeStaticSigInfo sigInfo(pInvokeMD);
1192     DWORD callConv = (DWORD)sigInfo.GetCallConv();
1193     _ASSERTE((callConv & StackImbalanceCookie::HAS_FP_RETURN_VALUE) == 0);
1194
1195     MetaSig msig(pInvokeMD);
1196     if (msig.HasFPReturn())
1197     {
1198         // pass a flag to PInvokeStackImbalanceHelper that it should save & restore FPU return value
1199         callConv |= StackImbalanceCookie::HAS_FP_RETURN_VALUE;
1200     }
1201
1202     CPUSTUBLINKER sl;
1203     sl.X86EmitPushEBPframe();
1204
1205     LPVOID pTarget = (pInnerStub != NULL ? (LPVOID)pInnerStub->GetEntryPoint() : pNativeTarget);
1206
1207     // init StackImbalanceCookie
1208     sl.X86EmitPushReg(kEAX);             // m_dwSavedEsp (just making space)
1209     sl.X86EmitPushImm32(callConv);       // m_callConv
1210     sl.X86EmitPushImm32(wStackArgSize);  // m_dwStackArgSize
1211     sl.X86EmitPushImmPtr(pTarget);       // m_pTarget
1212     sl.X86EmitPushImmPtr(pInvokeMD);     // m_pMD
1213
1214     // stack layout at this point
1215
1216     // |          ...          |
1217     // |    stack arguments    | EBP + 8
1218     // +-----------------------+
1219     // |    return address     | EBP + 4
1220     // +-----------------------+
1221     // |      saved EBP        | EBP + 0
1222     // +-----------------------+
1223     // | SIC::m_dwSavedEsp     |
1224     // | SIC::m_callConv       |
1225     // | SIC::m_dwStackArgSize |
1226     // | SIC::m_pTarget        |
1227     // | SIC::m_pMD            | EBP - 20
1228     // ------------------------
1229
1230     // call the helper
1231     sl.X86EmitCall(sl.NewExternalCodeLabel(PInvokeStackImbalanceHelper), sizeof(StackImbalanceCookie));
1232
1233     //  pop StackImbalanceCookie
1234     sl.X86EmitMovSPReg(kEBP);
1235
1236     sl.X86EmitPopReg(kEBP);
1237     sl.X86EmitReturn(callConv == pmCallConvCdecl ? 0 : wStackArgSize);
1238
1239     if (pInnerStub != NULL)
1240     {
1241         return sl.LinkInterceptor(pInnerStub, pNativeTarget);
1242     }
1243     else
1244     {
1245         return sl.Link(); // don't use loader heap as we want to be able to free the stub
1246     }
1247 }
1248
1249 #endif // MDA_SUPPORTED
1250
1251 extern "C" VOID STDCALL StubRareEnableWorker(Thread *pThread)
1252 {
1253     WRAPPER_NO_CONTRACT;
1254
1255     //printf("RareEnable\n");
1256     pThread->RareEnablePreemptiveGC();
1257 }
1258
1259
1260
1261
1262 // Disable when calling into managed code from a place that fails via Exceptions
1263 extern "C" VOID STDCALL StubRareDisableTHROWWorker(Thread *pThread)
1264 {
1265     STATIC_CONTRACT_THROWS;
1266     STATIC_CONTRACT_GC_TRIGGERS;
1267
1268     // Do not add a CONTRACT here.  We haven't set up SEH.  We rely
1269     // on HandleThreadAbort and COMPlusThrowBoot dealing with this situation properly.
1270
1271     // WARNING!!!!
1272     // when we start executing here, we are actually in cooperative mode.  But we
1273     // haven't synchronized with the barrier to reentry yet.  So we are in a highly
1274     // dangerous mode.  If we call managed code, we will potentially be active in
1275     // the GC heap, even as GC's are occuring!
1276
1277     // Check for ShutDown scenario.  This happens only when we have initiated shutdown 
1278     // and someone is trying to call in after the CLR is suspended.  In that case, we
1279     // must either raise an unmanaged exception or return an HRESULT, depending on the
1280     // expectations of our caller.
1281     if (!CanRunManagedCode())
1282     {
1283         // DO NOT IMPROVE THIS EXCEPTION!  It cannot be a managed exception.  It
1284         // cannot be a real exception object because we cannot execute any managed
1285         // code here.
1286         pThread->m_fPreemptiveGCDisabled = 0;
1287         COMPlusThrowBoot(E_PROCESS_SHUTDOWN_REENTRY);
1288     }
1289
1290     // We must do the following in this order, because otherwise we would be constructing
1291     // the exception for the abort without synchronizing with the GC.  Also, we have no
1292     // CLR SEH set up, despite the fact that we may throw a ThreadAbortException.
1293     pThread->RareDisablePreemptiveGC();
1294     pThread->HandleThreadAbort();
1295 }
1296
1297 #ifndef FEATURE_PAL
1298 // Note that this logic is copied below, in PopSEHRecords
1299 __declspec(naked)
1300 VOID __cdecl PopSEHRecords(LPVOID pTargetSP)
1301 {
1302     // No CONTRACT possible on naked functions
1303     STATIC_CONTRACT_NOTHROW;
1304     STATIC_CONTRACT_GC_NOTRIGGER;
1305
1306     __asm{
1307         mov     ecx, [esp+4]        ;; ecx <- pTargetSP
1308         mov     eax, fs:[0]         ;; get current SEH record
1309   poploop:
1310         cmp     eax, ecx
1311         jge     done
1312         mov     eax, [eax]          ;; get next SEH record
1313         jmp     poploop
1314   done:
1315         mov     fs:[0], eax
1316         retn
1317     }
1318 }
1319 #endif // FEATURE_PAL
1320
1321 //////////////////////////////////////////////////////////////////////////////
1322 //
1323 // JITInterface
1324 //
1325 //////////////////////////////////////////////////////////////////////////////
1326
1327 /*********************************************************************/
1328 #ifdef EnC_SUPPORTED
1329 #pragma warning (disable : 4731)
1330 void ResumeAtJit(PCONTEXT pContext, LPVOID oldESP)
1331 {
1332     // No CONTRACT here, because we can't run the risk of it pushing any SEH into the
1333     // current method.
1334
1335     STATIC_CONTRACT_NOTHROW;
1336     STATIC_CONTRACT_GC_NOTRIGGER;
1337
1338 #ifdef _DEBUG
1339     DWORD curESP;
1340     __asm mov curESP, esp
1341 #endif
1342
1343     if (oldESP)
1344     {
1345         _ASSERTE(curESP < (DWORD)(size_t)oldESP);
1346         // should have popped the SEH records by now as stack has been overwritten
1347         _ASSERTE(GetCurrentSEHRecord() > oldESP);
1348     }
1349
1350     // For the "push Eip, ..., ret"
1351     _ASSERTE(curESP < pContext->Esp - sizeof(DWORD));
1352     pContext->Esp -= sizeof(DWORD);
1353
1354     __asm {
1355         mov     ebp, pContext
1356
1357         // Push Eip onto the targetESP, so that the final "ret" will consume it
1358         mov     ecx, [ebp]CONTEXT.Esp
1359         mov     edx, [ebp]CONTEXT.Eip
1360         mov     [ecx], edx
1361
1362         // Restore all registers except Esp, Ebp, Eip
1363         mov     eax, [ebp]CONTEXT.Eax
1364         mov     ebx, [ebp]CONTEXT.Ebx
1365         mov     ecx, [ebp]CONTEXT.Ecx
1366         mov     edx, [ebp]CONTEXT.Edx
1367         mov     esi, [ebp]CONTEXT.Esi
1368         mov     edi, [ebp]CONTEXT.Edi
1369
1370         push    [ebp]CONTEXT.Esp  // pContext->Esp is (targetESP-sizeof(DWORD))
1371         push    [ebp]CONTEXT.Ebp
1372         pop     ebp
1373         pop     esp
1374
1375         // esp is (targetESP-sizeof(DWORD)), and [esp] is the targetEIP.
1376         // The ret will set eip to targetEIP and esp will be automatically
1377         // incremented to targetESP
1378
1379         ret
1380     }
1381 }
1382 #pragma warning (default : 4731)
1383 #endif // !EnC_SUPPORTED
1384
1385
1386 #ifndef FEATURE_PAL
1387 #pragma warning(push)
1388 #pragma warning(disable: 4035)
1389 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1390 {
1391     LIMITED_METHOD_CONTRACT
1392
1393     __asm
1394     {
1395         push    ebx
1396         push    esi
1397         mov     eax, arg
1398         cpuid
1399         mov     esi, result
1400         mov     [esi+ 0], eax
1401         mov     [esi+ 4], ebx
1402         mov     [esi+ 8], ecx
1403         mov     [esi+12], edx
1404         pop     esi
1405         pop     ebx
1406     }
1407 }
1408
1409 // The following function uses Deterministic Cache Parameter leafs to determine the cache hierarchy information on Prescott & Above platforms. 
1410 //  This function takes 3 arguments:
1411 //     Arg1 is an input to ECX. Used as index to specify which cache level to return infoformation on by CPUID.
1412 //     Arg2 is an input to EAX. For deterministic code enumeration, we pass in 4H in arg2.
1413 //     Arg3 is a pointer to the return buffer
1414 //   No need to check whether or not CPUID is supported because we have already called CPUID with success to come here.
1415
1416 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1417 {
1418     LIMITED_METHOD_CONTRACT
1419
1420     __asm
1421     {
1422         push    ebx
1423         push    esi
1424         mov     ecx, arg1
1425         mov     eax, arg2
1426         cpuid
1427         mov     esi, result
1428         mov     [esi+ 0], eax
1429         mov     [esi+ 4], ebx
1430         mov     [esi+ 8], ecx
1431         mov     [esi+12], edx
1432         pop     esi
1433         pop     ebx
1434     }
1435 }
1436
1437 extern "C" DWORD __stdcall xmmYmmStateSupport()
1438 {
1439     // No CONTRACT
1440     STATIC_CONTRACT_NOTHROW;
1441     STATIC_CONTRACT_GC_NOTRIGGER;
1442
1443     __asm
1444     {
1445         mov     ecx, 0                  ; Specify xcr0
1446         xgetbv                          ; result in EDX:EAX
1447         and eax, 06H
1448         cmp eax, 06H                    ; check OS has enabled both XMM and YMM state support
1449         jne     not_supported
1450         mov     eax, 1
1451         jmp     done
1452     not_supported:
1453         mov     eax, 0
1454     done:
1455     }
1456 }
1457
1458 #pragma warning(pop)
1459
1460 #else // !FEATURE_PAL
1461
1462 extern "C" DWORD __stdcall getcpuid(DWORD arg, unsigned char result[16])
1463 {
1464     DWORD eax;
1465     __asm("  xor %%ecx, %%ecx\n" \
1466             "  cpuid\n" \
1467             "  mov %%eax, 0(%[result])\n" \
1468             "  mov %%ebx, 4(%[result])\n" \
1469             "  mov %%ecx, 8(%[result])\n" \
1470             "  mov %%edx, 12(%[result])\n" \
1471         : "=a"(eax) /*output in eax*/\
1472         : "a"(arg), [result]"r"(result) /*inputs - arg in eax, result in any register*/\
1473         : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1474         );
1475     return eax;
1476 }
1477
1478 extern "C" DWORD __stdcall getextcpuid(DWORD arg1, DWORD arg2, unsigned char result[16])
1479 {
1480     DWORD eax;
1481     __asm("  cpuid\n" \
1482             "  mov %%eax, 0(%[result])\n" \
1483             "  mov %%ebx, 4(%[result])\n" \
1484             "  mov %%ecx, 8(%[result])\n" \
1485             "  mov %%edx, 12(%[result])\n" \
1486         : "=a"(eax) /*output in eax*/\
1487         : "c"(arg1), "a"(arg2), [result]"r"(result) /*inputs - arg1 in ecx, arg2 in eax, result in any register*/\
1488         : "eax", "ebx", "ecx", "edx", "memory" /* registers that are clobbered, *result is clobbered */
1489         );
1490     return eax;
1491 }
1492
1493 extern "C" DWORD __stdcall xmmYmmStateSupport()
1494 {
1495     DWORD eax;
1496     __asm("  xgetbv\n" \
1497         : "=a"(eax) /*output in eax*/\
1498         : "c"(0) /*inputs - 0 in ecx*/\
1499         : "eax", "edx" /* registers that are clobbered*/
1500         );
1501     // check OS has enabled both XMM and YMM state support
1502     return ((eax & 0x06) == 0x06) ? 1 : 0;
1503 }
1504
1505 #endif // !FEATURE_PAL
1506
1507 // This function returns the number of logical processors on a given physical chip.  If it cannot
1508 // determine the number of logical cpus, or the machine is not populated uniformly with the same
1509 // type of processors, this function returns 1.
1510 DWORD GetLogicalCpuCount()
1511 {
1512     // No CONTRACT possible because GetLogicalCpuCount uses SEH
1513
1514     STATIC_CONTRACT_THROWS;
1515     STATIC_CONTRACT_GC_NOTRIGGER;
1516
1517     static DWORD val = 0;
1518
1519     // cache value for later re-use
1520     if (val)
1521     {
1522         return val;
1523     }
1524
1525     struct Param : DefaultCatchFilterParam
1526     {
1527         DWORD retVal;
1528     } param;
1529     param.pv = COMPLUS_EXCEPTION_EXECUTE_HANDLER;
1530     param.retVal = 1;
1531
1532     PAL_TRY(Param *, pParam, &param)
1533     {
1534         unsigned char buffer[16];
1535         DWORD* dwBuffer = NULL;
1536
1537         DWORD maxCpuId = getcpuid(0, buffer);
1538
1539         if (maxCpuId < 1)
1540             goto lDone;
1541
1542         dwBuffer = (DWORD*)buffer;
1543
1544         if (dwBuffer[1] == 'uneG') {
1545             if (dwBuffer[3] == 'Ieni') {
1546                 if (dwBuffer[2] == 'letn')  {  // get SMT/multicore enumeration for Intel EM64T 
1547
1548                     // TODO: Currently GetLogicalCpuCountFromOS() and GetLogicalCpuCountFallback() are broken on 
1549                     // multi-core processor, but we never call into those two functions since we don't halve the
1550                     // gen0size when it's prescott and above processor. We keep the old version here for earlier
1551                     // generation system(Northwood based), perf data suggests on those systems, halve gen0 size 
1552                     // still boost the performance(ex:Biztalk boosts about 17%). So on earlier systems(Northwood) 
1553                     // based, we still go ahead and halve gen0 size.  The logic in GetLogicalCpuCountFromOS() 
1554                     // and GetLogicalCpuCountFallback() works fine for those earlier generation systems. 
1555                     // If it's a Prescott and above processor or Multi-core, perf data suggests not to halve gen0 
1556                     // size at all gives us overall better performance. 
1557                     // This is going to be fixed with a new version in orcas time frame. 
1558
1559                     if( (maxCpuId > 3) && (maxCpuId < 0x80000000) ) 
1560                         goto lDone;
1561
1562                     val = GetLogicalCpuCountFromOS(); //try to obtain HT enumeration from OS API
1563                     if (val )
1564                     {
1565                         pParam->retVal = val;     // OS API HT enumeration successful, we are Done        
1566                         goto lDone;
1567                     }
1568
1569                     val = GetLogicalCpuCountFallback();    // OS API failed, Fallback to HT enumeration using CPUID
1570                     if( val )
1571                         pParam->retVal = val;
1572                 }
1573             }
1574         }
1575 lDone: ;
1576     }
1577     PAL_EXCEPT_FILTER(DefaultCatchFilter)
1578     {
1579     }
1580     PAL_ENDTRY
1581
1582     if (val == 0)
1583     {
1584         val = param.retVal;
1585     }
1586
1587     return param.retVal;
1588 }
1589
1590 void UMEntryThunkCode::Encode(BYTE* pTargetCode, void* pvSecretParam)
1591 {
1592     LIMITED_METHOD_CONTRACT;
1593
1594 #ifdef _DEBUG
1595     m_alignpad[0] = X86_INSTR_INT3;
1596     m_alignpad[1] = X86_INSTR_INT3;
1597 #endif // _DEBUG
1598     m_movEAX     = X86_INSTR_MOV_EAX_IMM32;
1599     m_uet        = pvSecretParam;
1600     m_jmp        = X86_INSTR_JMP_REL32;
1601     m_execstub   = (BYTE*) ((pTargetCode) - (4+((BYTE*)&m_execstub)));
1602
1603     FlushInstructionCache(GetCurrentProcess(),GetEntryPoint(),sizeof(UMEntryThunkCode));
1604 }
1605
1606 UMEntryThunk* UMEntryThunk::Decode(LPVOID pCallback)
1607 {
1608     LIMITED_METHOD_CONTRACT;
1609
1610     if (*((BYTE*)pCallback) != X86_INSTR_MOV_EAX_IMM32 ||
1611         ( ((size_t)pCallback) & 3) != 2) {
1612         return NULL;
1613     }
1614     return *(UMEntryThunk**)( 1 + (BYTE*)pCallback );
1615 }
1616
1617 BOOL DoesSlotCallPrestub(PCODE pCode)
1618 {
1619     CONTRACTL {
1620         NOTHROW;
1621         GC_NOTRIGGER;
1622         SO_TOLERANT;
1623         PRECONDITION(pCode != NULL);
1624         PRECONDITION(pCode != GetPreStubEntryPoint());
1625     } CONTRACTL_END;
1626
1627     // x86 has the following possible sequences for prestub logic:
1628     // 1. slot -> temporary entrypoint -> prestub
1629     // 2. slot -> precode -> prestub
1630     // 3. slot -> precode -> jumprel32 (NGEN case) -> prestub
1631
1632 #ifdef HAS_COMPACT_ENTRYPOINTS
1633     if (MethodDescChunk::GetMethodDescFromCompactEntryPoint(pCode, TRUE) != NULL)
1634     {
1635         return TRUE;
1636     }
1637 #endif // HAS_COMPACT_ENTRYPOINTS
1638
1639     if (!IS_ALIGNED(pCode, PRECODE_ALIGNMENT))
1640     {
1641         return FALSE;
1642     }
1643
1644 #ifdef HAS_FIXUP_PRECODE
1645     if (*PTR_BYTE(pCode) == X86_INSTR_CALL_REL32)
1646     {
1647         // Note that call could have been patched to jmp in the meantime
1648         pCode = rel32Decode(pCode+1);
1649
1650         // NGEN case
1651         if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1652             pCode = rel32Decode(pCode+1);
1653         }
1654
1655         return pCode == (TADDR)PrecodeFixupThunk;
1656     }
1657 #endif
1658
1659     if (*PTR_BYTE(pCode) != X86_INSTR_MOV_EAX_IMM32 ||
1660         *PTR_BYTE(pCode+5) != X86_INSTR_MOV_RM_R ||
1661         *PTR_BYTE(pCode+7) != X86_INSTR_JMP_REL32)
1662     {
1663         return FALSE;
1664     }
1665     pCode = rel32Decode(pCode+8);
1666
1667     // NGEN case
1668     if (*PTR_BYTE(pCode) == X86_INSTR_JMP_REL32) {
1669         pCode = rel32Decode(pCode+1);
1670     }
1671
1672     return pCode == GetPreStubEntryPoint();
1673 }
1674
1675 //==========================================================================================
1676 // In NGen image, virtual slots inherited from cross-module dependencies point to jump thunks.
1677 // These jump thunk initially point to VirtualMethodFixupStub which transfers control here.
1678 // This method 'VirtualMethodFixupWorker' will patch the jump thunk to point to the actual
1679 // inherited method body after we have execute the precode and a stable entry point.
1680 //
1681 EXTERN_C PVOID STDCALL VirtualMethodFixupWorker(Object * pThisPtr,  CORCOMPILE_VIRTUAL_IMPORT_THUNK *pThunk)
1682 {
1683     CONTRACTL
1684     {
1685         NOTHROW;
1686         GC_NOTRIGGER;
1687         MODE_COOPERATIVE;
1688         ENTRY_POINT;
1689     }
1690     CONTRACTL_END;
1691
1692     _ASSERTE(pThisPtr != NULL);
1693     VALIDATEOBJECT(pThisPtr);
1694
1695     MethodTable * pMT = pThisPtr->GetTrueMethodTable();
1696
1697     WORD slotNumber = pThunk->slotNum;
1698     _ASSERTE(slotNumber != (WORD)-1);
1699
1700     PCODE pCode = pMT->GetRestoredSlot(slotNumber);
1701
1702     if (!DoesSlotCallPrestub(pCode))
1703     {
1704         // Skip fixup precode jump for better perf
1705         PCODE pDirectTarget = Precode::TryToSkipFixupPrecode(pCode);
1706         if (pDirectTarget != NULL)
1707             pCode = pDirectTarget;
1708
1709         INT64 oldValue = *(INT64*)pThunk;
1710         BYTE* pOldValue = (BYTE*)&oldValue;
1711
1712         if (pOldValue[0] == X86_INSTR_CALL_REL32)
1713         {
1714             INT64 newValue = oldValue;
1715             BYTE* pNewValue = (BYTE*)&newValue;
1716             pNewValue[0] = X86_INSTR_JMP_REL32;
1717
1718             INT_PTR pcRelOffset = (BYTE*)pCode - &pThunk->callJmp[5];
1719             *(INT32 *)(&pNewValue[1]) = (INT32) pcRelOffset;
1720
1721             _ASSERTE(IS_ALIGNED(pThunk, sizeof(INT64)));
1722             if (EnsureWritableExecutablePagesNoThrow(pThunk, sizeof(INT64)))
1723                 FastInterlockCompareExchangeLong((INT64*)pThunk, newValue, oldValue);
1724
1725             FlushInstructionCache(GetCurrentProcess(), pThunk, 8);
1726         }
1727     }
1728
1729     return PVOID(pCode);
1730 }
1731
1732
1733 #ifdef FEATURE_READYTORUN
1734
1735 //
1736 // Allocation of dynamic helpers
1737 //
1738
1739 #define DYNAMIC_HELPER_ALIGNMENT sizeof(TADDR)
1740
1741 #define BEGIN_DYNAMIC_HELPER_EMIT(size) \
1742     SIZE_T cb = size; \
1743     SIZE_T cbAligned = ALIGN_UP(cb, DYNAMIC_HELPER_ALIGNMENT); \
1744     BYTE * pStart = (BYTE *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(cbAligned, DYNAMIC_HELPER_ALIGNMENT); \
1745     BYTE * p = pStart;
1746
1747 #define END_DYNAMIC_HELPER_EMIT() \
1748     _ASSERTE(pStart + cb == p); \
1749     while (p < pStart + cbAligned) *p++ = X86_INSTR_INT3; \
1750     ClrFlushInstructionCache(pStart, cbAligned); \
1751     return (PCODE)pStart
1752
1753 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1754 {
1755     STANDARD_VM_CONTRACT;
1756
1757     BEGIN_DYNAMIC_HELPER_EMIT(10);
1758
1759     *p++ = 0xB9; // mov ecx, XXXXXX
1760     *(INT32 *)p = (INT32)arg;
1761     p += 4;
1762
1763     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1764     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1765     p += 4;
1766
1767     END_DYNAMIC_HELPER_EMIT();
1768 }
1769
1770 void DynamicHelpers::EmitHelperWithArg(BYTE*& p, LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1771 {
1772     CONTRACTL
1773     {
1774         GC_NOTRIGGER;
1775         PRECONDITION(p != NULL && target != NULL);
1776     }
1777     CONTRACTL_END;
1778
1779     // Move an an argument into the second argument register and jump to a target function.
1780
1781     *p++ = 0xBA; // mov edx, XXXXXX
1782     *(INT32 *)p = (INT32)arg;
1783     p += 4;
1784
1785     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1786     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1787     p += 4;
1788 }
1789
1790 PCODE DynamicHelpers::CreateHelperWithArg(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1791 {
1792     BEGIN_DYNAMIC_HELPER_EMIT(10);
1793
1794     EmitHelperWithArg(p, pAllocator, arg, target);
1795
1796     END_DYNAMIC_HELPER_EMIT();
1797 }
1798
1799 PCODE DynamicHelpers::CreateHelper(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1800 {
1801     BEGIN_DYNAMIC_HELPER_EMIT(15);
1802
1803     *p++ = 0xB9; // mov ecx, XXXXXX
1804     *(INT32 *)p = (INT32)arg;
1805     p += 4;
1806
1807     *p++ = 0xBA; // mov edx, XXXXXX
1808     *(INT32 *)p = (INT32)arg2;
1809     p += 4;
1810
1811     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1812     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1813     p += 4;
1814
1815     END_DYNAMIC_HELPER_EMIT();
1816 }
1817
1818 PCODE DynamicHelpers::CreateHelperArgMove(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1819 {
1820     BEGIN_DYNAMIC_HELPER_EMIT(12);
1821
1822     *(UINT16 *)p = 0xD18B; // mov edx, ecx
1823     p += 2;
1824
1825     *p++ = 0xB9; // mov ecx, XXXXXX
1826     *(INT32 *)p = (INT32)arg;
1827     p += 4;
1828
1829     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1830     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1831     p += 4;
1832
1833     END_DYNAMIC_HELPER_EMIT();
1834 }
1835
1836 PCODE DynamicHelpers::CreateReturn(LoaderAllocator * pAllocator)
1837 {
1838     BEGIN_DYNAMIC_HELPER_EMIT(1);
1839
1840     *p++ = 0xC3; // ret
1841
1842     END_DYNAMIC_HELPER_EMIT();
1843 }
1844
1845 PCODE DynamicHelpers::CreateReturnConst(LoaderAllocator * pAllocator, TADDR arg)
1846 {
1847     BEGIN_DYNAMIC_HELPER_EMIT(6);
1848
1849     *p++ = 0xB8; // mov eax, XXXXXX
1850     *(INT32 *)p = (INT32)arg;
1851     p += 4;
1852
1853     *p++ = 0xC3; // ret
1854
1855     END_DYNAMIC_HELPER_EMIT();
1856 }
1857
1858 PCODE DynamicHelpers::CreateReturnIndirConst(LoaderAllocator * pAllocator, TADDR arg, INT8 offset)
1859 {
1860     BEGIN_DYNAMIC_HELPER_EMIT((offset != 0) ? 9 : 6);
1861
1862     *p++ = 0xA1; // mov eax, [XXXXXX]
1863     *(INT32 *)p = (INT32)arg;
1864     p += 4;
1865
1866     if (offset != 0)
1867     {
1868         // add eax, <offset>
1869         *p++ = 0x83;
1870         *p++ = 0xC0;
1871         *p++ = offset;
1872     }
1873
1874     *p++ = 0xC3; // ret
1875
1876     END_DYNAMIC_HELPER_EMIT();
1877 }
1878
1879 EXTERN_C VOID DynamicHelperArgsStub();
1880
1881 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, PCODE target)
1882 {
1883 #ifdef UNIX_X86_ABI
1884     BEGIN_DYNAMIC_HELPER_EMIT(18);
1885 #else
1886     BEGIN_DYNAMIC_HELPER_EMIT(12);
1887 #endif
1888
1889 #ifdef UNIX_X86_ABI
1890         // sub esp, 8
1891         *p++ = 0x83;
1892         *p++ = 0xec;
1893         *p++ = 0x8;
1894 #else
1895     // pop eax
1896     *p++ = 0x58;
1897 #endif
1898
1899     // push arg
1900     *p++ = 0x68;
1901     *(INT32 *)p = arg;
1902     p += 4;
1903
1904 #ifdef UNIX_X86_ABI
1905     // mov eax, target
1906     *p++ = 0xB8;
1907     *(INT32 *)p = target;
1908     p += 4;
1909 #else
1910     // push eax
1911     *p++ = 0x50;
1912 #endif
1913
1914     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1915 #ifdef UNIX_X86_ABI
1916     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1917 #else
1918     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1919 #endif
1920     p += 4;
1921
1922     END_DYNAMIC_HELPER_EMIT();
1923 }
1924
1925 PCODE DynamicHelpers::CreateHelperWithTwoArgs(LoaderAllocator * pAllocator, TADDR arg, TADDR arg2, PCODE target)
1926 {
1927 #ifdef UNIX_X86_ABI
1928     BEGIN_DYNAMIC_HELPER_EMIT(23);
1929 #else
1930     BEGIN_DYNAMIC_HELPER_EMIT(17);
1931 #endif
1932
1933 #ifdef UNIX_X86_ABI
1934         // sub esp, 4
1935         *p++ = 0x83;
1936         *p++ = 0xec;
1937         *p++ = 0x4;
1938 #else
1939     // pop eax
1940     *p++ = 0x58;
1941 #endif
1942
1943     // push arg
1944     *p++ = 0x68;
1945     *(INT32 *)p = arg;
1946     p += 4;
1947
1948     // push arg2
1949     *p++ = 0x68;
1950     *(INT32 *)p = arg2;
1951     p += 4;
1952
1953 #ifdef UNIX_X86_ABI
1954     // mov eax, target
1955     *p++ = 0xB8;
1956     *(INT32 *)p = target;
1957     p += 4;
1958 #else
1959     // push eax
1960     *p++ = 0x50;
1961 #endif
1962
1963     *p++ = X86_INSTR_JMP_REL32; // jmp rel32
1964 #ifdef UNIX_X86_ABI
1965     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, (PCODE)DynamicHelperArgsStub);
1966 #else
1967     *(INT32 *)p = rel32UsingJumpStub((INT32 *)p, target);
1968 #endif
1969     p += 4;
1970
1971     END_DYNAMIC_HELPER_EMIT();
1972 }
1973
1974 PCODE DynamicHelpers::CreateDictionaryLookupHelper(LoaderAllocator * pAllocator, CORINFO_RUNTIME_LOOKUP * pLookup, DWORD dictionaryIndexAndSlot, Module * pModule)
1975 {
1976     STANDARD_VM_CONTRACT;
1977
1978     PCODE helperAddress = (pLookup->helper == CORINFO_HELP_RUNTIMEHANDLE_METHOD ?
1979         GetEEFuncEntryPoint(JIT_GenericHandleMethodWithSlotAndModule) :
1980         GetEEFuncEntryPoint(JIT_GenericHandleClassWithSlotAndModule));
1981
1982     GenericHandleArgs * pArgs = (GenericHandleArgs *)(void *)pAllocator->GetDynamicHelpersHeap()->AllocAlignedMem(sizeof(GenericHandleArgs), DYNAMIC_HELPER_ALIGNMENT);
1983     pArgs->dictionaryIndexAndSlot = dictionaryIndexAndSlot;
1984     pArgs->signature = pLookup->signature;
1985     pArgs->module = (CORINFO_MODULE_HANDLE)pModule;
1986
1987     // It's available only via the run-time helper function
1988     if (pLookup->indirections == CORINFO_USEHELPER)
1989     {
1990         BEGIN_DYNAMIC_HELPER_EMIT(10);
1991
1992         // ecx contains the generic context parameter
1993         // mov edx,pArgs
1994         // jmp helperAddress
1995         EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
1996
1997         END_DYNAMIC_HELPER_EMIT();
1998     }
1999     else
2000     {
2001         int indirectionsSize = 0;
2002         for (WORD i = 0; i < pLookup->indirections; i++)
2003             indirectionsSize += (pLookup->offsets[i] >= 0x80 ? 6 : 3);
2004
2005         int codeSize = indirectionsSize + (pLookup->testForNull ? 21 : 3);
2006
2007         BEGIN_DYNAMIC_HELPER_EMIT(codeSize);
2008
2009         if (pLookup->testForNull)
2010         {
2011             // ecx contains the generic context parameter. Save a copy of it in the eax register
2012             // mov eax,ecx
2013             *(UINT16*)p = 0xc889; p += 2;
2014         }
2015
2016         for (WORD i = 0; i < pLookup->indirections; i++)
2017         {
2018             // mov ecx,qword ptr [ecx+offset]
2019             if (pLookup->offsets[i] >= 0x80)
2020             {
2021                 *(UINT16*)p = 0x898b; p += 2;
2022                 *(UINT32*)p = (UINT32)pLookup->offsets[i]; p += 4;
2023             }
2024             else
2025             {
2026                 *(UINT16*)p = 0x498b; p += 2;
2027                 *p++ = (BYTE)pLookup->offsets[i];
2028             }
2029         }
2030
2031         // No null test required
2032         if (!pLookup->testForNull)
2033         {
2034             // No fixups needed for R2R
2035
2036             // mov eax,ecx
2037             *(UINT16*)p = 0xc889; p += 2;
2038             *p++ = 0xC3;    // ret
2039         }
2040         else
2041         {
2042             // ecx contains the value of the dictionary slot entry
2043
2044             _ASSERTE(pLookup->indirections != 0);
2045
2046             // test ecx,ecx
2047             *(UINT16*)p = 0xc985; p += 2;
2048
2049             // je 'HELPER_CALL' (a jump of 3 bytes)
2050             *(UINT16*)p = 0x0374; p += 2;
2051
2052             // mov eax,ecx
2053             *(UINT16*)p = 0xc889; p += 2;
2054             *p++ = 0xC3;    // ret
2055
2056             // 'HELPER_CALL'
2057             {
2058                 // Put the generic context back into rcx (was previously saved in eax)
2059                 // mov ecx,eax
2060                 *(UINT16*)p = 0xc189; p += 2;
2061
2062                 // mov edx,pArgs
2063                 // jmp helperAddress
2064                 EmitHelperWithArg(p, pAllocator, (TADDR)pArgs, helperAddress);
2065             }
2066         }
2067
2068         END_DYNAMIC_HELPER_EMIT();
2069     }
2070 }
2071
2072 #endif // FEATURE_READYTORUN
2073
2074
2075 #endif // DACCESS_COMPILE