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
5//
6#include "common.h"
7
8#ifdef FEATURE_INTERPRETER
9
10#include "interpreter.h"
11#include "interpreter.hpp"
12#include "cgencpu.h"
13#include "stublink.h"
14#include "openum.h"
15#include "fcall.h"
16#include "frames.h"
17#include "gcheaputilities.h"
18#include <float.h>
19#include "jitinterface.h"
20#include "safemath.h"
21#include "exceptmacros.h"
22#include "runtimeexceptionkind.h"
23#include "runtimehandles.h"
24#include "vars.hpp"
25#include "cycletimer.h"
26
27inline CORINFO_CALLINFO_FLAGS combine(CORINFO_CALLINFO_FLAGS flag1, CORINFO_CALLINFO_FLAGS flag2)
28{
29 return (CORINFO_CALLINFO_FLAGS) (flag1 | flag2);
30}
31
32static CorInfoType asCorInfoType(CORINFO_CLASS_HANDLE clsHnd)
33{
34 TypeHandle typeHnd(clsHnd);
35 return CEEInfo::asCorInfoType(typeHnd.GetInternalCorElementType(), typeHnd, NULL);
36}
37
38InterpreterMethodInfo::InterpreterMethodInfo(CEEInfo* comp, CORINFO_METHOD_INFO* methInfo)
39 : m_method(methInfo->ftn),
40 m_module(methInfo->scope),
41 m_ILCode(methInfo->ILCode),
42 m_ILCodeEnd(methInfo->ILCode + methInfo->ILCodeSize),
43 m_maxStack(methInfo->maxStack),
44#if INTERP_PROFILE
45 m_totIlInstructionsExeced(0),
46 m_maxIlInstructionsExeced(0),
47#endif
48 m_ehClauseCount(methInfo->EHcount),
49 m_varArgHandleArgNum(NO_VA_ARGNUM),
50 m_numArgs(methInfo->args.numArgs),
51 m_numLocals(methInfo->locals.numArgs),
52 m_flags(0),
53 m_argDescs(NULL),
54 m_returnType(methInfo->args.retType),
55 m_invocations(0),
56 m_methodCache(NULL)
57{
58 // Overflow sanity check. (Can ILCodeSize ever be zero?)
59 assert(m_ILCode <= m_ILCodeEnd);
60
61 // Does the calling convention indicate an implicit "this" (first arg) or generic type context arg (last arg)?
62 SetFlag<Flag_hasThisArg>((methInfo->args.callConv & CORINFO_CALLCONV_HASTHIS) != 0);
63 if (GetFlag<Flag_hasThisArg>())
64 {
65 GCX_PREEMP();
66 CORINFO_CLASS_HANDLE methClass = comp->getMethodClass(methInfo->ftn);
67 DWORD attribs = comp->getClassAttribs(methClass);
68 SetFlag<Flag_thisArgIsObjPtr>((attribs & CORINFO_FLG_VALUECLASS) == 0);
69 }
70
71#if INTERP_PROFILE || defined(_DEBUG)
72 {
73 const char* clsName;
74#if defined(_DEBUG)
75 m_methName = ::eeGetMethodFullName(comp, methInfo->ftn, &clsName);
76#else
77 m_methName = comp->getMethodName(methInfo->ftn, &clsName);
78#endif
79 char* myClsName = new char[strlen(clsName) + 1];
80 strcpy(myClsName, clsName);
81 m_clsName = myClsName;
82 }
83#endif // INTERP_PROFILE
84
85 // Do we have a ret buff? If its a struct or refany, then *maybe*, depending on architecture...
86 bool hasRetBuff = (methInfo->args.retType == CORINFO_TYPE_VALUECLASS || methInfo->args.retType == CORINFO_TYPE_REFANY);
87#if defined(FEATURE_HFA)
88 // ... unless its an HFA type (and not varargs)...
89 if (hasRetBuff && CorInfoTypeIsFloatingPoint(comp->getHFAType(methInfo->args.retTypeClass)) && methInfo->args.getCallConv() != CORINFO_CALLCONV_VARARG)
90 {
91 hasRetBuff = false;
92 }
93#endif
94#if defined(_ARM_) || defined(_AMD64_)|| defined(_ARM64_)
95 // ...or it fits into one register.
96 if (hasRetBuff && getClassSize(methInfo->args.retTypeClass) <= sizeof(void*))
97 {
98 hasRetBuff = false;
99 }
100#endif
101 SetFlag<Flag_hasRetBuffArg>(hasRetBuff);
102
103 MetaSig sig(reinterpret_cast<MethodDesc*>(methInfo->ftn));
104 SetFlag<Flag_hasGenericsContextArg>((methInfo->args.callConv & CORINFO_CALLCONV_PARAMTYPE) != 0);
105 SetFlag<Flag_isVarArg>((methInfo->args.callConv & CORINFO_CALLCONV_VARARG) != 0);
106 SetFlag<Flag_typeHasGenericArgs>(methInfo->args.sigInst.classInstCount > 0);
107 SetFlag<Flag_methHasGenericArgs>(methInfo->args.sigInst.methInstCount > 0);
108 _ASSERTE_MSG(!GetFlag<Flag_hasGenericsContextArg>()
109 || ((GetFlag<Flag_typeHasGenericArgs>() & !(GetFlag<Flag_hasThisArg>() && GetFlag<Flag_thisArgIsObjPtr>())) || GetFlag<Flag_methHasGenericArgs>()),
110 "If the method takes a generic parameter, is a static method of generic class (or meth of a value class), and/or itself takes generic parameters");
111
112 if (GetFlag<Flag_hasThisArg>())
113 {
114 m_numArgs++;
115 }
116 if (GetFlag<Flag_hasRetBuffArg>())
117 {
118 m_numArgs++;
119 }
120 if (GetFlag<Flag_isVarArg>())
121 {
122 m_numArgs++;
123 }
124 if (GetFlag<Flag_hasGenericsContextArg>())
125 {
126 m_numArgs++;
127 }
128 if (m_numArgs == 0)
129 {
130 m_argDescs = NULL;
131 }
132 else
133 {
134 m_argDescs = new ArgDesc[m_numArgs];
135 }
136
137 // Now we'll do the locals.
138 m_localDescs = new LocalDesc[m_numLocals];
139 // Allocate space for the pinning reference bits (lazily).
140 m_localIsPinningRefBits = NULL;
141
142 // Now look at each local.
143 CORINFO_ARG_LIST_HANDLE localsPtr = methInfo->locals.args;
144 CORINFO_CLASS_HANDLE vcTypeRet;
145 unsigned curLargeStructOffset = 0;
146 for (unsigned k = 0; k < methInfo->locals.numArgs; k++)
147 {
148 // TODO: if this optimization succeeds, the switch below on localType
149 // can become much simpler.
150 m_localDescs[k].m_offset = 0;
151#ifdef _DEBUG
152 vcTypeRet = NULL;
153#endif
154 CorInfoTypeWithMod localTypWithMod = comp->getArgType(&methInfo->locals, localsPtr, &vcTypeRet);
155 // If the local vars is a pinning reference, set the bit to indicate this.
156 if ((localTypWithMod & CORINFO_TYPE_MOD_PINNED) != 0)
157 {
158 SetPinningBit(k);
159 }
160
161 CorInfoType localType = strip(localTypWithMod);
162 switch (localType)
163 {
164 case CORINFO_TYPE_VALUECLASS:
165 case CORINFO_TYPE_REFANY: // Just a special case: vcTypeRet is handle for TypedReference in this case...
166 {
167 InterpreterType tp = InterpreterType(comp, vcTypeRet);
168 unsigned size = static_cast<unsigned>(tp.Size(comp));
169 size = max(size, sizeof(void*));
170 m_localDescs[k].m_type = tp;
171 if (tp.IsLargeStruct(comp))
172 {
173 m_localDescs[k].m_offset = curLargeStructOffset;
174 curLargeStructOffset += size;
175 }
176 }
177 break;
178
179 case CORINFO_TYPE_VAR:
180 NYI_INTERP("argument of generic parameter type"); // Should not happen;
181 break;
182
183 default:
184 m_localDescs[k].m_type = InterpreterType(localType);
185 break;
186 }
187 m_localDescs[k].m_typeStackNormal = m_localDescs[k].m_type.StackNormalize();
188 localsPtr = comp->getArgNext(localsPtr);
189 }
190 m_largeStructLocalSize = curLargeStructOffset;
191}
192
193void InterpreterMethodInfo::InitArgInfo(CEEInfo* comp, CORINFO_METHOD_INFO* methInfo, short* argOffsets_)
194{
195 unsigned numSigArgsPlusThis = methInfo->args.numArgs;
196 if (GetFlag<Flag_hasThisArg>())
197 {
198 numSigArgsPlusThis++;
199 }
200
201 // The m_argDescs array is constructed in the following "canonical" order:
202 // 1. 'this' pointer
203 // 2. signature arguments
204 // 3. return buffer
205 // 4. type parameter -or- vararg cookie
206 //
207 // argOffsets_ is passed in this order, and serves to establish the offsets to arguments
208 // when the interpreter is invoked using the native calling convention (i.e., not directly).
209 //
210 // When the interpreter is invoked directly, the arguments will appear in the same order
211 // and form as arguments passed to MethodDesc::CallDescr(). This ordering is as follows:
212 // 1. 'this' pointer
213 // 2. return buffer
214 // 3. signature arguments
215 //
216 // MethodDesc::CallDescr() does not support generic parameters or varargs functions.
217
218 _ASSERTE_MSG((methInfo->args.callConv & (CORINFO_CALLCONV_EXPLICITTHIS)) == 0,
219 "Don't yet handle EXPLICITTHIS calling convention modifier.");
220 switch (methInfo->args.callConv & CORINFO_CALLCONV_MASK)
221 {
222 case CORINFO_CALLCONV_DEFAULT:
223 case CORINFO_CALLCONV_VARARG:
224 {
225 unsigned k = 0;
226 ARG_SLOT* directOffset = NULL;
227 short directRetBuffOffset = 0;
228 short directVarArgOffset = 0;
229 short directTypeParamOffset = 0;
230
231 // If there's a "this" argument, handle it.
232 if (GetFlag<Flag_hasThisArg>())
233 {
234 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_UNDEF);
235#ifdef FEATURE_STUBS_AS_IL
236 MethodDesc *pMD = reinterpret_cast<MethodDesc*>(methInfo->ftn);
237 // The signature of the ILStubs may be misleading.
238 // If a StubTarget is ever set, we'll find the correct type by inspecting the
239 // target, rather than the stub.
240 if (pMD->IsILStub())
241 {
242
243 if (pMD->AsDynamicMethodDesc()->IsUnboxingILStub())
244 {
245 // This is an unboxing stub where the thisptr is passed as a boxed VT.
246 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_CLASS);
247 }
248 else
249 {
250 MethodDesc *pTargetMD = pMD->AsDynamicMethodDesc()->GetILStubResolver()->GetStubTargetMethodDesc();
251 if (pTargetMD != NULL)
252 {
253 if (pTargetMD->GetMethodTable()->IsValueType())
254 {
255 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_BYREF);
256 }
257 else
258 {
259 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_CLASS);
260 }
261
262 }
263 }
264 }
265
266#endif // FEATURE_STUBS_AS_IL
267 if (m_argDescs[k].m_type == InterpreterType(CORINFO_TYPE_UNDEF))
268 {
269 CORINFO_CLASS_HANDLE cls = comp->getMethodClass(methInfo->ftn);
270 DWORD attribs = comp->getClassAttribs(cls);
271 if (attribs & CORINFO_FLG_VALUECLASS)
272 {
273 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_BYREF);
274 }
275 else
276 {
277 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_CLASS);
278 }
279 }
280 m_argDescs[k].m_typeStackNormal = m_argDescs[k].m_type;
281 m_argDescs[k].m_nativeOffset = argOffsets_[k];
282 m_argDescs[k].m_directOffset = reinterpret_cast<short>(ArgSlotEndianessFixup(directOffset, sizeof(void*)));
283 directOffset++;
284 k++;
285 }
286
287 // If there is a return buffer, it will appear next in the arguments list for a direct call.
288 // Reserve its offset now, for use after the explicit arguments.
289#if defined(_ARM_)
290 // On ARM, for direct calls we always treat HFA return types as having ret buffs.
291 // So figure out if we have an HFA return type.
292 bool hasHFARetType =
293 methInfo->args.retType == CORINFO_TYPE_VALUECLASS
294 && CorInfoTypeIsFloatingPoint(comp->getHFAType(methInfo->args.retTypeClass))
295 && methInfo->args.getCallConv() != CORINFO_CALLCONV_VARARG;
296#endif // defined(_ARM_)
297
298 if (GetFlag<Flag_hasRetBuffArg>()
299#if defined(_ARM_)
300 // On ARM, for direct calls we always treat HFA return types as having ret buffs.
301 || hasHFARetType
302#endif // defined(_ARM_)
303 )
304 {
305 directRetBuffOffset = reinterpret_cast<short>(ArgSlotEndianessFixup(directOffset, sizeof(void*)));
306 directOffset++;
307 }
308#if defined(_AMD64_)
309 if (GetFlag<Flag_isVarArg>())
310 {
311 directVarArgOffset = reinterpret_cast<short>(ArgSlotEndianessFixup(directOffset, sizeof(void*)));
312 directOffset++;
313 }
314 if (GetFlag<Flag_hasGenericsContextArg>())
315 {
316 directTypeParamOffset = reinterpret_cast<short>(ArgSlotEndianessFixup(directOffset, sizeof(void*)));
317 directOffset++;
318 }
319#endif
320
321 // Now record the argument types for the rest of the arguments.
322 InterpreterType it;
323 CORINFO_CLASS_HANDLE vcTypeRet;
324 CORINFO_ARG_LIST_HANDLE argPtr = methInfo->args.args;
325 for (; k < numSigArgsPlusThis; k++)
326 {
327 CorInfoTypeWithMod argTypWithMod = comp->getArgType(&methInfo->args, argPtr, &vcTypeRet);
328 CorInfoType argType = strip(argTypWithMod);
329 switch (argType)
330 {
331 case CORINFO_TYPE_VALUECLASS:
332 case CORINFO_TYPE_REFANY: // Just a special case: vcTypeRet is handle for TypedReference in this case...
333 it = InterpreterType(comp, vcTypeRet);
334 break;
335 default:
336 // Everything else is just encoded as a shifted CorInfoType.
337 it = InterpreterType(argType);
338 break;
339 }
340 m_argDescs[k].m_type = it;
341 m_argDescs[k].m_typeStackNormal = it.StackNormalize();
342 m_argDescs[k].m_nativeOffset = argOffsets_[k];
343 // When invoking the interpreter directly, large value types are always passed by reference.
344 if (it.IsLargeStruct(comp))
345 {
346 m_argDescs[k].m_directOffset = reinterpret_cast<short>(ArgSlotEndianessFixup(directOffset, sizeof(void*)));
347 }
348 else
349 {
350 m_argDescs[k].m_directOffset = reinterpret_cast<short>(ArgSlotEndianessFixup(directOffset, it.Size(comp)));
351 }
352 argPtr = comp->getArgNext(argPtr);
353 directOffset++;
354 }
355
356 if (GetFlag<Flag_hasRetBuffArg>())
357 {
358 // The generic type context is an unmanaged pointer (native int).
359 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_BYREF);
360 m_argDescs[k].m_typeStackNormal = m_argDescs[k].m_type;
361 m_argDescs[k].m_nativeOffset = argOffsets_[k];
362 m_argDescs[k].m_directOffset = directRetBuffOffset;
363 k++;
364 }
365
366 if (GetFlag<Flag_hasGenericsContextArg>())
367 {
368 // The vararg cookie is an unmanaged pointer (native int).
369 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_NATIVEINT);
370 m_argDescs[k].m_typeStackNormal = m_argDescs[k].m_type;
371 m_argDescs[k].m_nativeOffset = argOffsets_[k];
372 m_argDescs[k].m_directOffset = directTypeParamOffset;
373 directOffset++;
374 k++;
375 }
376 if (GetFlag<Flag_isVarArg>())
377 {
378 // The generic type context is an unmanaged pointer (native int).
379 m_argDescs[k].m_type = InterpreterType(CORINFO_TYPE_NATIVEINT);
380 m_argDescs[k].m_typeStackNormal = m_argDescs[k].m_type;
381 m_argDescs[k].m_nativeOffset = argOffsets_[k];
382 m_argDescs[k].m_directOffset = directVarArgOffset;
383 k++;
384 }
385 }
386 break;
387
388 case CORINFO_CALLCONV_C:
389 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_C");
390 break;
391
392 case CORINFO_CALLCONV_STDCALL:
393 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_STDCALL");
394 break;
395
396 case CORINFO_CALLCONV_THISCALL:
397 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_THISCALL");
398 break;
399
400 case CORINFO_CALLCONV_FASTCALL:
401 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_FASTCALL");
402 break;
403
404 case CORINFO_CALLCONV_FIELD:
405 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_FIELD");
406 break;
407
408 case CORINFO_CALLCONV_LOCAL_SIG:
409 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_LOCAL_SIG");
410 break;
411
412 case CORINFO_CALLCONV_PROPERTY:
413 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_PROPERTY");
414 break;
415
416 case CORINFO_CALLCONV_NATIVEVARARG:
417 NYI_INTERP("InterpreterMethodInfo::InitArgInfo -- CORINFO_CALLCONV_NATIVEVARARG");
418 break;
419
420 default:
421 _ASSERTE_ALL_BUILDS(__FILE__, false); // shouldn't get here
422 }
423}
424
425InterpreterMethodInfo::~InterpreterMethodInfo()
426{
427 if (m_methodCache != NULL)
428 {
429 delete reinterpret_cast<ILOffsetToItemCache*>(m_methodCache);
430 }
431}
432
433void InterpreterMethodInfo::AllocPinningBitsIfNeeded()
434{
435 if (m_localIsPinningRefBits != NULL)
436 return;
437
438 unsigned numChars = (m_numLocals + 7) / 8;
439 m_localIsPinningRefBits = new char[numChars];
440 for (unsigned i = 0; i < numChars; i++)
441 {
442 m_localIsPinningRefBits[i] = char(0);
443 }
444}
445
446
447void InterpreterMethodInfo::SetPinningBit(unsigned locNum)
448{
449 _ASSERTE_MSG(locNum < m_numLocals, "Precondition");
450 AllocPinningBitsIfNeeded();
451
452 unsigned ind = locNum / 8;
453 unsigned bitNum = locNum - (ind * 8);
454 m_localIsPinningRefBits[ind] |= (1 << bitNum);
455}
456
457bool InterpreterMethodInfo::GetPinningBit(unsigned locNum)
458{
459 _ASSERTE_MSG(locNum < m_numLocals, "Precondition");
460 if (m_localIsPinningRefBits == NULL)
461 return false;
462
463 unsigned ind = locNum / 8;
464 unsigned bitNum = locNum - (ind * 8);
465 return (m_localIsPinningRefBits[ind] & (1 << bitNum)) != 0;
466}
467
468void Interpreter::ArgState::AddArg(unsigned canonIndex, short numSlots, bool noReg, bool twoSlotAlign)
469{
470#if defined(_AMD64_)
471 assert(!noReg);
472 assert(!twoSlotAlign);
473 AddArgAmd64(canonIndex, numSlots, /*isFloatingType*/false);
474#else // !_AMD64_
475#if defined(_X86_) || defined(_ARM64_)
476 assert(!twoSlotAlign); // Shouldn't use this flag on x86 (it wouldn't work right in the stack, at least).
477#endif
478 // If the argument requires two-slot alignment, make sure we have it. This is the
479 // ARM model: both in regs and on the stack.
480 if (twoSlotAlign)
481 {
482 if (!noReg && numRegArgs < NumberOfIntegerRegArgs())
483 {
484 if ((numRegArgs % 2) != 0)
485 {
486 numRegArgs++;
487 }
488 }
489 else
490 {
491 if ((callerArgStackSlots % 2) != 0)
492 {
493 callerArgStackSlots++;
494 }
495 }
496 }
497
498#if defined(_ARM64_)
499 // On ARM64 we're not going to place an argument 'partially' on the stack
500 // if all slots fits into registers, they go into registers, otherwise they go into stack.
501 if (!noReg && numRegArgs+numSlots <= NumberOfIntegerRegArgs())
502#else
503 if (!noReg && numRegArgs < NumberOfIntegerRegArgs())
504#endif
505 {
506 argIsReg[canonIndex] = ARS_IntReg;
507 argOffsets[canonIndex] = numRegArgs * sizeof(void*);
508 numRegArgs += numSlots;
509 // If we overflowed the regs, we consume some stack arg space.
510 if (numRegArgs > NumberOfIntegerRegArgs())
511 {
512 callerArgStackSlots += (numRegArgs - NumberOfIntegerRegArgs());
513 }
514 }
515 else
516 {
517#if defined(_X86_)
518 // On X86, stack args are pushed in order. We will add the total size of the arguments to this offset,
519 // so we set this to a negative number relative to the SP before the first arg push.
520 callerArgStackSlots += numSlots;
521 ClrSafeInt<short> offset(-callerArgStackSlots);
522#elif defined(_ARM_) || defined(_ARM64_)
523 // On ARM, args are pushed in *reverse* order. So we will create an offset relative to the address
524 // of the first stack arg; later, we will add the size of the non-stack arguments.
525 ClrSafeInt<short> offset(callerArgStackSlots);
526#endif
527 offset *= static_cast<short>(sizeof(void*));
528 assert(!offset.IsOverflow());
529 argOffsets[canonIndex] = offset.Value();
530#if defined(_ARM_) || defined(_ARM64_)
531 callerArgStackSlots += numSlots;
532#endif
533 }
534#endif // !_AMD64_
535}
536
537#if defined(_AMD64_)
538// AMD64 calling convention allows any type that can be contained in 64 bits to be passed in registers,
539// if not contained or they are of a size not a power of 2, then they are passed by reference on the stack.
540// RCX, RDX, R8, R9 are the int arg registers. XMM0-3 overlap with the integer registers and are used
541// for floating point arguments.
542void Interpreter::ArgState::AddArgAmd64(unsigned canonIndex, unsigned short numSlots, bool isFloatingType)
543{
544 // If floating type and there are slots use a float reg slot.
545 if (isFloatingType && (numFPRegArgSlots < MaxNumFPRegArgSlots))
546 {
547 assert(numSlots == 1);
548 argIsReg[canonIndex] = ARS_FloatReg;
549 argOffsets[canonIndex] = numFPRegArgSlots * sizeof(void*);
550 fpArgsUsed |= (0x1 << (numFPRegArgSlots + 1));
551 numFPRegArgSlots += 1;
552 numRegArgs += 1; // Increment int reg count due to shadowing.
553 return;
554 }
555
556 // If we have an integer/aligned-struct arg or a reference of a struct that got copied on
557 // to the stack, it would go into a register or a stack slot.
558 if (numRegArgs != NumberOfIntegerRegArgs())
559 {
560 argIsReg[canonIndex] = ARS_IntReg;
561 argOffsets[canonIndex] = numRegArgs * sizeof(void*);
562 numRegArgs += 1;
563 numFPRegArgSlots += 1; // Increment FP reg count due to shadowing.
564 }
565 else
566 {
567 argIsReg[canonIndex] = ARS_NotReg;
568 ClrSafeInt<short> offset(callerArgStackSlots * sizeof(void*));
569 assert(!offset.IsOverflow());
570 argOffsets[canonIndex] = offset.Value();
571 callerArgStackSlots += 1;
572 }
573}
574#endif
575
576void Interpreter::ArgState::AddFPArg(unsigned canonIndex, unsigned short numSlots, bool twoSlotAlign)
577{
578#if defined(_AMD64_)
579 assert(!twoSlotAlign);
580 assert(numSlots == 1);
581 AddArgAmd64(canonIndex, numSlots, /*isFloatingType*/ true);
582#elif defined(_X86_)
583 assert(false); // Don't call this on x86; we pass all FP on the stack.
584#elif defined(_ARM_)
585 // We require "numSlots" alignment.
586 assert(numFPRegArgSlots + numSlots <= MaxNumFPRegArgSlots);
587 argIsReg[canonIndex] = ARS_FloatReg;
588
589 if (twoSlotAlign)
590 {
591 // If we require two slot alignment, the number of slots must be a multiple of two.
592 assert((numSlots % 2) == 0);
593
594 // Skip a slot if necessary.
595 if ((numFPRegArgSlots % 2) != 0)
596 {
597 numFPRegArgSlots++;
598 }
599 // We always use new slots for two slot aligned args precision...
600 argOffsets[canonIndex] = numFPRegArgSlots * sizeof(void*);
601 for (unsigned short i = 0; i < numSlots/2; i++)
602 {
603 fpArgsUsed |= (0x3 << (numFPRegArgSlots + i));
604 }
605 numFPRegArgSlots += numSlots;
606 }
607 else
608 {
609 if (numSlots == 1)
610 {
611 // A single-precision (float) argument. We must do "back-filling" where possible, searching
612 // for previous unused registers.
613 unsigned slot = 0;
614 while (slot < 32 && (fpArgsUsed & (1 << slot))) slot++;
615 assert(slot < 32); // Search succeeded.
616 assert(slot <= numFPRegArgSlots); // No bits at or above numFPRegArgSlots are set (regs used).
617 argOffsets[canonIndex] = slot * sizeof(void*);
618 fpArgsUsed |= (0x1 << slot);
619 if (slot == numFPRegArgSlots)
620 numFPRegArgSlots += numSlots;
621 }
622 else
623 {
624 // We can always allocate at after the last used slot.
625 argOffsets[numFPRegArgSlots] = numFPRegArgSlots * sizeof(void*);
626 for (unsigned i = 0; i < numSlots; i++)
627 {
628 fpArgsUsed |= (0x1 << (numFPRegArgSlots + i));
629 }
630 numFPRegArgSlots += numSlots;
631 }
632 }
633#elif defined(_ARM64_)
634
635 assert(numFPRegArgSlots + numSlots <= MaxNumFPRegArgSlots);
636 assert(!twoSlotAlign);
637 argIsReg[canonIndex] = ARS_FloatReg;
638
639 argOffsets[canonIndex] = numFPRegArgSlots * sizeof(void*);
640 for (unsigned i = 0; i < numSlots; i++)
641 {
642 fpArgsUsed |= (0x1 << (numFPRegArgSlots + i));
643 }
644 numFPRegArgSlots += numSlots;
645
646#else
647#error "Unsupported architecture"
648#endif
649}
650
651
652// static
653CorJitResult Interpreter::GenerateInterpreterStub(CEEInfo* comp,
654 CORINFO_METHOD_INFO* info,
655 /*OUT*/ BYTE **nativeEntry,
656 /*OUT*/ ULONG *nativeSizeOfCode,
657 InterpreterMethodInfo** ppInterpMethodInfo,
658 bool jmpCall)
659{
660 //
661 // First, ensure that the compiler-specific statics are initialized.
662 //
663
664 InitializeCompilerStatics(comp);
665
666 //
667 // Next, use switches and IL scanning to determine whether to interpret this method.
668 //
669
670#if INTERP_TRACING
671#define TRACE_SKIPPED(cls, meth, reason) \
672 if (s_DumpInterpreterStubsFlag.val(CLRConfig::INTERNAL_DumpInterpreterStubs)) { \
673 fprintf(GetLogFile(), "Skipping %s:%s (%s).\n", cls, meth, reason); \
674 }
675#else
676#define TRACE_SKIPPED(cls, meth, reason)
677#endif
678
679
680 // If jmpCall, we only need to do computations involving method info.
681 if (!jmpCall)
682 {
683 const char* clsName;
684 const char* methName = comp->getMethodName(info->ftn, &clsName);
685 if ( !s_InterpretMeths.contains(methName, clsName, info->args.pSig)
686 || s_InterpretMethsExclude.contains(methName, clsName, info->args.pSig))
687 {
688 TRACE_SKIPPED(clsName, methName, "not in set of methods to interpret");
689 return CORJIT_SKIPPED;
690 }
691
692 unsigned methHash = comp->getMethodHash(info->ftn);
693 if ( methHash < s_InterpretMethHashMin.val(CLRConfig::INTERNAL_InterpreterMethHashMin)
694 || methHash > s_InterpretMethHashMax.val(CLRConfig::INTERNAL_InterpreterMethHashMax))
695 {
696 TRACE_SKIPPED(clsName, methName, "hash not within range to interpret");
697 return CORJIT_SKIPPED;
698 }
699
700 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(info->ftn);
701
702#if !INTERP_ILSTUBS
703 if (pMD->IsILStub())
704 {
705 TRACE_SKIPPED(clsName, methName, "interop stubs not supported");
706 return CORJIT_SKIPPED;
707 }
708 else
709#endif // !INTERP_ILSTUBS
710
711 if (!s_InterpreterDoLoopMethods && MethodMayHaveLoop(info->ILCode, info->ILCodeSize))
712 {
713 TRACE_SKIPPED(clsName, methName, "has loop, not interpreting loop methods.");
714 return CORJIT_SKIPPED;
715 }
716
717 s_interpreterStubNum++;
718
719#if INTERP_TRACING
720 if (s_interpreterStubNum < s_InterpreterStubMin.val(CLRConfig::INTERNAL_InterpreterStubMin)
721 || s_interpreterStubNum > s_InterpreterStubMax.val(CLRConfig::INTERNAL_InterpreterStubMax))
722 {
723 TRACE_SKIPPED(clsName, methName, "stub num not in range, not interpreting.");
724 return CORJIT_SKIPPED;
725 }
726
727 if (s_DumpInterpreterStubsFlag.val(CLRConfig::INTERNAL_DumpInterpreterStubs))
728 {
729 unsigned hash = comp->getMethodHash(info->ftn);
730 fprintf(GetLogFile(), "Generating interpretation stub (# %d = 0x%x, hash = 0x%x) for %s:%s.\n",
731 s_interpreterStubNum, s_interpreterStubNum, hash, clsName, methName);
732 fflush(GetLogFile());
733 }
734#endif
735 }
736
737 //
738 // Finally, generate an interpreter entry-point stub.
739 //
740
741 // @TODO: this structure clearly needs some sort of lifetime management. It is the moral equivalent
742 // of compiled code, and should be associated with an app domain. In addition, when I get to it, we should
743 // delete it when/if we actually compile the method. (Actually, that's complicated, since there may be
744 // VSD stubs still bound to the interpreter stub. The check there will get to the jitted code, but we want
745 // to eventually clean those up at some safe point...)
746 InterpreterMethodInfo* interpMethInfo = new InterpreterMethodInfo(comp, info);
747 if (ppInterpMethodInfo != nullptr)
748 {
749 *ppInterpMethodInfo = interpMethInfo;
750 }
751 interpMethInfo->m_stubNum = s_interpreterStubNum;
752 MethodDesc* methodDesc = reinterpret_cast<MethodDesc*>(info->ftn);
753 if (!jmpCall)
754 {
755 interpMethInfo = RecordInterpreterMethodInfoForMethodHandle(info->ftn, interpMethInfo);
756 }
757
758#if FEATURE_INTERPRETER_DEADSIMPLE_OPT
759 unsigned offsetOfLd;
760 if (IsDeadSimpleGetter(comp, methodDesc, &offsetOfLd))
761 {
762 interpMethInfo->SetFlag<InterpreterMethodInfo::Flag_methIsDeadSimpleGetter>(true);
763 if (offsetOfLd == ILOffsetOfLdFldInDeadSimpleInstanceGetterDbg)
764 {
765 interpMethInfo->SetFlag<InterpreterMethodInfo::Flag_methIsDeadSimpleGetterIsDbgForm>(true);
766 }
767 else
768 {
769 assert(offsetOfLd == ILOffsetOfLdFldInDeadSimpleInstanceGetterOpt);
770 }
771 }
772#endif // FEATURE_INTERPRETER_DEADSIMPLE_OPT
773
774 // Used to initialize the arg offset information.
775 Stub* stub = NULL;
776
777 // We assume that the stack contains (with addresses growing upwards, assuming a downwards-growing stack):
778 //
779 // [Non-reg arg N-1]
780 // ...
781 // [Non-reg arg <# of reg args>]
782 // [return PC]
783 //
784 // Then push the register args to get:
785 //
786 // [Non-reg arg N-1]
787 // ...
788 // [Non-reg arg <# of reg args>]
789 // [return PC]
790 // [reg arg <# of reg args>-1]
791 // ...
792 // [reg arg 0]
793 //
794 // Pass the address of this argument array, and the MethodDesc pointer for the method, as arguments to
795 // Interpret.
796 //
797 // So the structure of the code will look like this (in the non-ILstub case):
798 //
799#if defined(_X86_) || defined(_AMD64_)
800 // push ebp
801 // mov ebp, esp
802 // [if there are register arguments in ecx or edx, push them]
803 // ecx := addr of InterpretMethodInfo for the method to be intepreted.
804 // edx = esp /*pointer to argument structure*/
805 // call to Interpreter::InterpretMethod
806 // [if we pushed register arguments, increment esp by the right amount.]
807 // pop ebp
808 // ret <n> ; where <n> is the number of argument stack slots in the call to the stub.
809#elif defined (_ARM_)
810 // TODO.
811#endif
812
813 // TODO: much of the interpreter stub code should be is shareable. In the non-IL stub case,
814 // at least, we could have a small per-method stub that puts the address of the method-specific
815 // InterpreterMethodInfo into eax, and then branches to a shared part. Probably we would want to
816 // always push all integer args on x86, as we do already on ARM. On ARM, we'd need several versions
817 // of the shared stub, for different numbers of floating point register args, cross different kinds of
818 // HFA return values. But these could still be shared, and the per-method stub would decide which of
819 // these to target.
820 //
821 // In the IL stub case, which uses eax, it would be problematic to do this sharing.
822
823 StubLinkerCPU sl;
824 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(info->ftn);
825 if (!jmpCall)
826 {
827 sl.Init();
828#if defined(_X86_) || defined(_AMD64_)
829#if defined(_X86_)
830 sl.X86EmitPushReg(kEBP);
831 sl.X86EmitMovRegReg(kEBP, static_cast<X86Reg>(kESP_Unsafe));
832#endif
833#elif defined(_ARM_)
834 // On ARM we use R12 as a "scratch" register -- callee-trashed, not used
835 // for arguments.
836 ThumbReg r11 = ThumbReg(11);
837 ThumbReg r12 = ThumbReg(12);
838
839#elif defined(_ARM64_)
840 // x8 through x15 are scratch registers on ARM64.
841 IntReg x8 = IntReg(8);
842 IntReg x9 = IntReg(9);
843#else
844#error unsupported platform
845#endif
846 }
847
848 MetaSig sig(methodDesc);
849
850 unsigned totalArgs = info->args.numArgs;
851 unsigned sigArgsPlusThis = totalArgs;
852 bool hasThis = false;
853 bool hasRetBuff = false;
854 bool isVarArg = false;
855 bool hasGenericsContextArg = false;
856
857 // Below, we will increment "totalArgs" for any of the "this" argument,
858 // a ret buff argument, and/or a generics context argument.
859 //
860 // There will be four arrays allocated below, each with this increased "totalArgs" elements:
861 // argOffsets, argIsReg, argPerm, and, later, m_argDescs.
862 //
863 // They will be indexed in the order (0-based, [] indicating optional)
864 //
865 // [this] sigArgs [retBuff] [VASigCookie] [genCtxt]
866 //
867 // We will call this "canonical order". It is architecture-independent, and
868 // does not necessarily correspond to the architecture-dependent physical order
869 // in which the registers are actually passed. (That's actually the purpose of
870 // "argPerm": to record the correspondence between canonical order and physical
871 // order.) We could have chosen any order for the first three of these, but it's
872 // simplest to let m_argDescs have all the passed IL arguments passed contiguously
873 // at the beginning, allowing it to be indexed by IL argument number.
874
875 int genericsContextArgIndex = 0;
876 int retBuffArgIndex = 0;
877 int vaSigCookieIndex = 0;
878
879 if (sig.HasThis())
880 {
881 assert(info->args.callConv & CORINFO_CALLCONV_HASTHIS);
882 hasThis = true;
883 totalArgs++; sigArgsPlusThis++;
884 }
885
886 if (methodDesc->HasRetBuffArg())
887 {
888 hasRetBuff = true;
889 retBuffArgIndex = totalArgs;
890 totalArgs++;
891 }
892
893 if (sig.GetCallingConventionInfo() & CORINFO_CALLCONV_VARARG)
894 {
895 isVarArg = true;
896 vaSigCookieIndex = totalArgs;
897 totalArgs++;
898 }
899
900 if (sig.GetCallingConventionInfo() & CORINFO_CALLCONV_PARAMTYPE)
901 {
902 assert(info->args.callConv & CORINFO_CALLCONV_PARAMTYPE);
903 hasGenericsContextArg = true;
904 genericsContextArgIndex = totalArgs;
905 totalArgs++;
906 }
907
908 // The non-this sig args have indices starting after these.
909
910 // We will first encode the arg offsets as *negative* offsets from the address above the first
911 // stack arg, and later add in the total size of the stack args to get a positive offset.
912 // The first sigArgsPlusThis elements are the offsets of the IL-addressable arguments. After that,
913 // there may be up to two more: generics context arg, if present, and return buff pointer, if present.
914 // (Note that the latter is actually passed after the "this" pointer, or else first if no "this" pointer
915 // is present. We re-arrange to preserve the easy IL-addressability.)
916 ArgState argState(totalArgs);
917
918 // This is the permutation that translates from an index in the argOffsets/argIsReg arrays to
919 // the platform-specific order in which the arguments are passed.
920 unsigned* argPerm = new unsigned[totalArgs];
921
922 // The number of register argument slots we end up pushing.
923 unsigned short regArgsFound = 0;
924
925 unsigned physArgIndex = 0;
926
927#if defined(_ARM_)
928 // The stub linker has a weird little limitation: all stubs it's used
929 // for on ARM push some callee-saved register, so the unwind info
930 // code was written assuming at least one would be pushed. I don't know how to
931 // fix it, so I'm meeting this requirement, by pushing one callee-save.
932#define STUB_LINK_EMIT_PROLOG_REQUIRES_CALLEE_SAVE_PUSH 1
933
934#if STUB_LINK_EMIT_PROLOG_REQUIRES_CALLEE_SAVE_PUSH
935 const int NumberOfCalleeSaveRegsToPush = 1;
936#else
937 const int NumberOfCalleeSaveRegsToPush = 0;
938#endif
939 // The "1" here is for the return address.
940 const int NumberOfFixedPushes = 1 + NumberOfCalleeSaveRegsToPush;
941#elif defined(_ARM64_)
942 // FP, LR
943 const int NumberOfFixedPushes = 2;
944#endif
945
946#if defined(FEATURE_HFA)
947#if defined(_ARM_) || defined(_ARM64_)
948 // On ARM, a non-retBuffArg method that returns a struct type might be an HFA return. Figure
949 // that out.
950 unsigned HFARetTypeSize = 0;
951#endif
952#if defined(_ARM64_)
953 unsigned cHFAVars = 0;
954#endif
955 if (info->args.retType == CORINFO_TYPE_VALUECLASS
956 && CorInfoTypeIsFloatingPoint(comp->getHFAType(info->args.retTypeClass))
957 && info->args.getCallConv() != CORINFO_CALLCONV_VARARG)
958 {
959 HFARetTypeSize = getClassSize(info->args.retTypeClass);
960#if defined(_ARM_)
961 // Round up to a double boundary;
962 HFARetTypeSize = ((HFARetTypeSize+ sizeof(double) - 1) / sizeof(double)) * sizeof(double);
963#elif defined(_ARM64_)
964 // We don't need to round it up to double. Unlike ARM, whether it's a float or a double each field will
965 // occupy one slot. We'll handle the stack alignment in the prolog where we have all the information about
966 // what is going to be pushed on the stack.
967 // Instead on ARM64 we'll need to know how many slots we'll need.
968 // for instance a VT with two float fields will have the same size as a VT with 1 double field. (ARM64TODO: Verify it)
969 // It works on ARM because the overlapping layout of the floating point registers
970 // but it won't work on ARM64.
971 cHFAVars = (comp->getHFAType(info->args.retTypeClass) == CORINFO_TYPE_FLOAT) ? HFARetTypeSize/sizeof(float) : HFARetTypeSize/sizeof(double);
972#endif
973 }
974
975#endif // defined(FEATURE_HFA)
976
977 _ASSERTE_MSG((info->args.callConv & (CORINFO_CALLCONV_EXPLICITTHIS)) == 0,
978 "Don't yet handle EXPLICITTHIS calling convention modifier.");
979
980 switch (info->args.callConv & CORINFO_CALLCONV_MASK)
981 {
982 case CORINFO_CALLCONV_DEFAULT:
983 case CORINFO_CALLCONV_VARARG:
984 {
985 unsigned firstSigArgIndex = 0;
986 if (hasThis)
987 {
988 argPerm[0] = physArgIndex; physArgIndex++;
989 argState.AddArg(0);
990 firstSigArgIndex++;
991 }
992
993 if (hasRetBuff)
994 {
995 argPerm[retBuffArgIndex] = physArgIndex; physArgIndex++;
996 argState.AddArg(retBuffArgIndex);
997 }
998
999 if (isVarArg)
1000 {
1001 argPerm[vaSigCookieIndex] = physArgIndex; physArgIndex++;
1002 interpMethInfo->m_varArgHandleArgNum = vaSigCookieIndex;
1003 argState.AddArg(vaSigCookieIndex);
1004 }
1005
1006#if defined(_ARM_) || defined(_AMD64_) || defined(_ARM64_)
1007 // Generics context comes before args on ARM. Would be better if I factored this out as a call,
1008 // to avoid large swatches of duplicate code.
1009 if (hasGenericsContextArg)
1010 {
1011 argPerm[genericsContextArgIndex] = physArgIndex; physArgIndex++;
1012 argState.AddArg(genericsContextArgIndex);
1013 }
1014#endif // _ARM_ || _AMD64_ || _ARM64_
1015
1016 CORINFO_ARG_LIST_HANDLE argPtr = info->args.args;
1017 // Some arguments are have been passed in registers, some in memory. We must generate code that
1018 // moves the register arguments to memory, and determines a pointer into the stack from which all
1019 // the arguments can be accessed, according to the offsets in "argOffsets."
1020 //
1021 // In the first pass over the arguments, we will label and count the register arguments, and
1022 // initialize entries in "argOffsets" for the non-register arguments -- relative to the SP at the
1023 // time of the call. Then when we have counted the number of register arguments, we will adjust
1024 // the offsets for the non-register arguments to account for those. Then, in the second pass, we
1025 // will push the register arguments on the stack, and capture the final stack pointer value as
1026 // the argument vector pointer.
1027 CORINFO_CLASS_HANDLE vcTypeRet;
1028 // This iteration starts at the first signature argument, and iterates over all the
1029 // canonical indices for the signature arguments.
1030 for (unsigned k = firstSigArgIndex; k < sigArgsPlusThis; k++)
1031 {
1032 argPerm[k] = physArgIndex; physArgIndex++;
1033
1034 CorInfoTypeWithMod argTypWithMod = comp->getArgType(&info->args, argPtr, &vcTypeRet);
1035 CorInfoType argType = strip(argTypWithMod);
1036 switch (argType)
1037 {
1038 case CORINFO_TYPE_UNDEF:
1039 case CORINFO_TYPE_VOID:
1040 case CORINFO_TYPE_VAR:
1041 _ASSERTE_ALL_BUILDS(__FILE__, false); // Should not happen;
1042 break;
1043
1044 // One integer slot arguments:
1045 case CORINFO_TYPE_BOOL:
1046 case CORINFO_TYPE_CHAR:
1047 case CORINFO_TYPE_BYTE:
1048 case CORINFO_TYPE_UBYTE:
1049 case CORINFO_TYPE_SHORT:
1050 case CORINFO_TYPE_USHORT:
1051 case CORINFO_TYPE_INT:
1052 case CORINFO_TYPE_UINT:
1053 case CORINFO_TYPE_NATIVEINT:
1054 case CORINFO_TYPE_NATIVEUINT:
1055 case CORINFO_TYPE_BYREF:
1056 case CORINFO_TYPE_CLASS:
1057 case CORINFO_TYPE_STRING:
1058 case CORINFO_TYPE_PTR:
1059 argState.AddArg(k);
1060 break;
1061
1062 // Two integer slot arguments.
1063 case CORINFO_TYPE_LONG:
1064 case CORINFO_TYPE_ULONG:
1065#if defined(_X86_)
1066 // Longs are always passed on the stack -- with no obvious alignment.
1067 argState.AddArg(k, 2, /*noReg*/true);
1068#elif defined(_ARM_)
1069 // LONGS have 2-reg alignment; inc reg if necessary.
1070 argState.AddArg(k, 2, /*noReg*/false, /*twoSlotAlign*/true);
1071#elif defined(_AMD64_) || defined(_ARM64_)
1072 argState.AddArg(k);
1073#else
1074#error unknown platform
1075#endif
1076 break;
1077
1078 // One float slot args:
1079 case CORINFO_TYPE_FLOAT:
1080#if defined(_X86_)
1081 argState.AddArg(k, 1, /*noReg*/true);
1082#elif defined(_ARM_)
1083 argState.AddFPArg(k, 1, /*twoSlotAlign*/false);
1084#elif defined(_AMD64_) || defined(_ARM64_)
1085 argState.AddFPArg(k, 1, false);
1086#else
1087#error unknown platform
1088#endif
1089 break;
1090
1091 // Two float slot args
1092 case CORINFO_TYPE_DOUBLE:
1093#if defined(_X86_)
1094 argState.AddArg(k, 2, /*noReg*/true);
1095#elif defined(_ARM_)
1096 argState.AddFPArg(k, 2, /*twoSlotAlign*/true);
1097#elif defined(_AMD64_) || defined(_ARM64_)
1098 argState.AddFPArg(k, 1, false);
1099#else
1100#error unknown platform
1101#endif
1102 break;
1103
1104 // Value class args:
1105 case CORINFO_TYPE_VALUECLASS:
1106 case CORINFO_TYPE_REFANY:
1107 {
1108 unsigned sz = getClassSize(vcTypeRet);
1109 unsigned szSlots = max(1, sz / sizeof(void*));
1110#if defined(_X86_)
1111 argState.AddArg(k, static_cast<short>(szSlots), /*noReg*/true);
1112#elif defined(_AMD64_)
1113 argState.AddArg(k, static_cast<short>(szSlots));
1114#elif defined(_ARM_) || defined(_ARM64_)
1115 CorInfoType hfaType = comp->getHFAType(vcTypeRet);
1116 if (CorInfoTypeIsFloatingPoint(hfaType))
1117 {
1118 argState.AddFPArg(k, szSlots,
1119#if defined(_ARM_)
1120 /*twoSlotAlign*/ (hfaType == CORINFO_TYPE_DOUBLE)
1121#elif defined(_ARM64_)
1122 /*twoSlotAlign*/ false // unlike ARM32 FP args always consume 1 slot on ARM64
1123#endif
1124 );
1125 }
1126 else
1127 {
1128 unsigned align = comp->getClassAlignmentRequirement(vcTypeRet, FALSE);
1129 argState.AddArg(k, static_cast<short>(szSlots), /*noReg*/false,
1130#if defined(_ARM_)
1131 /*twoSlotAlign*/ (align == 8)
1132#elif defined(_ARM64_)
1133 /*twoSlotAlign*/ false
1134#endif
1135 );
1136 }
1137#else
1138#error unknown platform
1139#endif
1140 }
1141 break;
1142
1143
1144 default:
1145 _ASSERTE_MSG(false, "should not reach here, unknown arg type");
1146 }
1147 argPtr = comp->getArgNext(argPtr);
1148 }
1149
1150#if defined(_X86_)
1151 // Generics context comes last on _X86_. Would be better if I factored this out as a call,
1152 // to avoid large swatches of duplicate code.
1153 if (hasGenericsContextArg)
1154 {
1155 argPerm[genericsContextArgIndex] = physArgIndex; physArgIndex++;
1156 argState.AddArg(genericsContextArgIndex);
1157 }
1158
1159 // Now we have counted the number of register arguments, so we can update the offsets for the
1160 // non-register arguments. "+ 2" below is to account for the return address from the call, and
1161 // pushing of EBP.
1162 unsigned short stackArgBaseOffset = (argState.numRegArgs + 2 + argState.callerArgStackSlots) * sizeof(void*);
1163 unsigned intRegArgBaseOffset = 0;
1164
1165#elif defined(_ARM_)
1166
1167 // We're choosing to always push all arg regs on ARM -- this is the only option
1168 // that ThumbEmitProlog currently gives.
1169 argState.numRegArgs = 4;
1170
1171 // On ARM, we push the (integer) arg regs before we push the return address, so we don't add an
1172 // extra constant. And the offset is the address of the last pushed argument, which is the first
1173 // stack argument in signature order.
1174
1175 // Round up to a double boundary...
1176 unsigned fpStackSlots = ((argState.numFPRegArgSlots + 1) / 2) * 2;
1177 unsigned intRegArgBaseOffset = (fpStackSlots + NumberOfFixedPushes) * sizeof(void*);
1178 unsigned short stackArgBaseOffset = intRegArgBaseOffset + (argState.numRegArgs) * sizeof(void*);
1179#elif defined(_ARM64_)
1180
1181 // See StubLinkerCPU::EmitProlog for the layout of the stack
1182 unsigned intRegArgBaseOffset = (argState.numFPRegArgSlots) * sizeof(void*);
1183 unsigned short stackArgBaseOffset = (unsigned short) ((argState.numRegArgs + argState.numFPRegArgSlots) * sizeof(void*));
1184#elif defined(_AMD64_)
1185 unsigned short stackArgBaseOffset = (argState.numRegArgs) * sizeof(void*);
1186#else
1187#error unsupported platform
1188#endif
1189
1190#if defined(_ARM_)
1191 WORD regArgMask = 0;
1192#endif // defined(_ARM_)
1193 // argPerm maps from an index into the argOffsets/argIsReg arrays to
1194 // the order that the arguments are passed.
1195 unsigned* argPermInverse = new unsigned[totalArgs];
1196 for (unsigned t = 0; t < totalArgs; t++)
1197 {
1198 argPermInverse[argPerm[t]] = t;
1199 }
1200
1201 for (unsigned kk = 0; kk < totalArgs; kk++)
1202 {
1203 // Let "k" be the index of the kk'th input in the argOffsets and argIsReg arrays.
1204 // To compute "k" we need to invert argPerm permutation -- determine the "k" such
1205 // that argPerm[k] == kk.
1206 unsigned k = argPermInverse[kk];
1207
1208 assert(k < totalArgs);
1209
1210 if (argState.argIsReg[k] == ArgState::ARS_IntReg)
1211 {
1212 regArgsFound++;
1213 // If any int reg args are used on ARM, we push them all (in ThumbEmitProlog)
1214#if defined(_X86_)
1215 if (regArgsFound == 1)
1216 {
1217 if (!jmpCall) { sl.X86EmitPushReg(kECX); }
1218 argState.argOffsets[k] = (argState.numRegArgs - regArgsFound)*sizeof(void*); // General form, good for general # of reg args.
1219 }
1220 else
1221 {
1222 assert(regArgsFound == 2);
1223 if (!jmpCall) { sl.X86EmitPushReg(kEDX); }
1224 argState.argOffsets[k] = (argState.numRegArgs - regArgsFound)*sizeof(void*);
1225 }
1226#elif defined(_ARM_) || defined(_ARM64_)
1227 argState.argOffsets[k] += intRegArgBaseOffset;
1228#elif defined(_AMD64_)
1229 // First home the register arguments in the stack space allocated by the caller.
1230 // Refer to Stack Allocation on x64 [http://msdn.microsoft.com/en-US/library/ew5tede7(v=vs.80).aspx]
1231 X86Reg argRegs[] = { kECX, kEDX, kR8, kR9 };
1232 if (!jmpCall) { sl.X86EmitIndexRegStoreRSP(regArgsFound * sizeof(void*), argRegs[regArgsFound - 1]); }
1233 argState.argOffsets[k] = (regArgsFound - 1) * sizeof(void*);
1234#else
1235#error unsupported platform
1236#endif
1237 }
1238#if defined(_AMD64_)
1239 else if (argState.argIsReg[k] == ArgState::ARS_FloatReg)
1240 {
1241 // Increment regArgsFound since float/int arguments have overlapping registers.
1242 regArgsFound++;
1243 // Home the float arguments.
1244 X86Reg argRegs[] = { kXMM0, kXMM1, kXMM2, kXMM3 };
1245 if (!jmpCall) { sl.X64EmitMovSDToMem(argRegs[regArgsFound - 1], static_cast<X86Reg>(kESP_Unsafe), regArgsFound * sizeof(void*)); }
1246 argState.argOffsets[k] = (regArgsFound - 1) * sizeof(void*);
1247 }
1248#endif
1249 else if (argState.argIsReg[k] == ArgState::ARS_NotReg)
1250 {
1251 argState.argOffsets[k] += stackArgBaseOffset;
1252 }
1253 // So far, x86 doesn't have any FP reg args, and ARM and ARM64 puts them at offset 0, so no
1254 // adjustment is necessary (yet) for arguments passed in those registers.
1255 }
1256 delete[] argPermInverse;
1257 }
1258 break;
1259
1260 case CORINFO_CALLCONV_C:
1261 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_C");
1262 break;
1263
1264 case CORINFO_CALLCONV_STDCALL:
1265 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_STDCALL");
1266 break;
1267
1268 case CORINFO_CALLCONV_THISCALL:
1269 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_THISCALL");
1270 break;
1271
1272 case CORINFO_CALLCONV_FASTCALL:
1273 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_FASTCALL");
1274 break;
1275
1276 case CORINFO_CALLCONV_FIELD:
1277 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_FIELD");
1278 break;
1279
1280 case CORINFO_CALLCONV_LOCAL_SIG:
1281 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_LOCAL_SIG");
1282 break;
1283
1284 case CORINFO_CALLCONV_PROPERTY:
1285 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_PROPERTY");
1286 break;
1287
1288 case CORINFO_CALLCONV_NATIVEVARARG:
1289 NYI_INTERP("GenerateInterpreterStub -- CORINFO_CALLCONV_NATIVEVARARG");
1290 break;
1291
1292 default:
1293 _ASSERTE_ALL_BUILDS(__FILE__, false); // shouldn't get here
1294 }
1295
1296 delete[] argPerm;
1297
1298 PCODE interpretMethodFunc;
1299 if (!jmpCall)
1300 {
1301 switch (info->args.retType)
1302 {
1303 case CORINFO_TYPE_FLOAT:
1304 interpretMethodFunc = reinterpret_cast<PCODE>(&InterpretMethodFloat);
1305 break;
1306 case CORINFO_TYPE_DOUBLE:
1307 interpretMethodFunc = reinterpret_cast<PCODE>(&InterpretMethodDouble);
1308 break;
1309 default:
1310 interpretMethodFunc = reinterpret_cast<PCODE>(&InterpretMethod);
1311 break;
1312 }
1313 // The argument registers have been pushed by now, so we can use them.
1314#if defined(_X86_)
1315 // First arg is pointer to the base of the ILargs arr -- i.e., the current stack value.
1316 sl.X86EmitMovRegReg(kEDX, static_cast<X86Reg>(kESP_Unsafe));
1317 // InterpretMethod uses F_CALL_CONV == __fastcall; pass 2 args in regs.
1318#if INTERP_ILSTUBS
1319 if (pMD->IsILStub())
1320 {
1321 // Third argument is stubcontext, in eax.
1322 sl.X86EmitPushReg(kEAX);
1323 }
1324 else
1325#endif
1326 {
1327 // For a non-ILStub method, push NULL as the StubContext argument.
1328 sl.X86EmitZeroOutReg(kECX);
1329 sl.X86EmitPushReg(kECX);
1330 }
1331 // sl.X86EmitAddReg(kECX, reinterpret_cast<UINT>(interpMethInfo));
1332 sl.X86EmitRegLoad(kECX, reinterpret_cast<UINT>(interpMethInfo));
1333 sl.X86EmitCall(sl.NewExternalCodeLabel(interpretMethodFunc), 0);
1334 // Now we will deallocate the stack slots we pushed to hold register arguments.
1335 if (argState.numRegArgs > 0)
1336 {
1337 sl.X86EmitAddEsp(argState.numRegArgs * sizeof(void*));
1338 }
1339 sl.X86EmitPopReg(kEBP);
1340 sl.X86EmitReturn(static_cast<WORD>(argState.callerArgStackSlots * sizeof(void*)));
1341#elif defined(_AMD64_)
1342 // Pass "ilArgs", i.e. just the point where registers have been homed, as 2nd arg
1343 sl.X86EmitIndexLeaRSP(ARGUMENT_kREG2, static_cast<X86Reg>(kESP_Unsafe), 8);
1344
1345 // Allocate space for homing callee's (InterpretMethod's) arguments.
1346 // Calling convention requires a default allocation space of 4,
1347 // but to double align the stack frame, we'd allocate 5.
1348 int interpMethodArgSize = 5 * sizeof(void*);
1349 sl.X86EmitSubEsp(interpMethodArgSize);
1350
1351 // If we have IL stubs pass the stub context in R10 or else pass NULL.
1352#if INTERP_ILSTUBS
1353 if (pMD->IsILStub())
1354 {
1355 sl.X86EmitMovRegReg(kR8, kR10);
1356 }
1357 else
1358#endif
1359 {
1360 // For a non-ILStub method, push NULL as the StubContext argument.
1361 sl.X86EmitZeroOutReg(ARGUMENT_kREG1);
1362 sl.X86EmitMovRegReg(kR8, ARGUMENT_kREG1);
1363 }
1364 sl.X86EmitRegLoad(ARGUMENT_kREG1, reinterpret_cast<UINT_PTR>(interpMethInfo));
1365 sl.X86EmitCall(sl.NewExternalCodeLabel(interpretMethodFunc), 0);
1366 sl.X86EmitAddEsp(interpMethodArgSize);
1367 sl.X86EmitReturn(0);
1368#elif defined(_ARM_)
1369
1370 // We have to maintain 8-byte stack alignment. So if the number of
1371 // slots we would normally push is not a multiple of two, add a random
1372 // register. (We will not pop this register, but rather, increment
1373 // sp by an amount that includes it.)
1374 bool oddPushes = (((argState.numRegArgs + NumberOfFixedPushes) % 2) != 0);
1375
1376 UINT stackFrameSize = 0;
1377 if (oddPushes) stackFrameSize = sizeof(void*);
1378 // Now, if any FP regs are used as arguments, we will copy those to the stack; reserve space for that here.
1379 // (We push doubles to keep the stack aligned...)
1380 unsigned short doublesToPush = (argState.numFPRegArgSlots + 1)/2;
1381 stackFrameSize += (doublesToPush*2*sizeof(void*));
1382
1383 // The last argument here causes this to generate code to push all int arg regs.
1384 sl.ThumbEmitProlog(/*cCalleeSavedRegs*/NumberOfCalleeSaveRegsToPush, /*cbStackFrame*/stackFrameSize, /*fPushArgRegs*/TRUE);
1385
1386 // Now we will generate code to copy the floating point registers to the stack frame.
1387 if (doublesToPush > 0)
1388 {
1389 sl.ThumbEmitStoreMultipleVFPDoubleReg(ThumbVFPDoubleReg(0), thumbRegSp, doublesToPush*2);
1390 }
1391
1392#if INTERP_ILSTUBS
1393 if (pMD->IsILStub())
1394 {
1395 // Third argument is stubcontext, in r12.
1396 sl.ThumbEmitMovRegReg(ThumbReg(2), ThumbReg(12));
1397 }
1398 else
1399#endif
1400 {
1401 // For a non-ILStub method, push NULL as the third StubContext argument.
1402 sl.ThumbEmitMovConstant(ThumbReg(2), 0);
1403 }
1404 // Second arg is pointer to the base of the ILargs arr -- i.e., the current stack value.
1405 sl.ThumbEmitMovRegReg(ThumbReg(1), thumbRegSp);
1406
1407 // First arg is the pointer to the interpMethInfo structure.
1408 sl.ThumbEmitMovConstant(ThumbReg(0), reinterpret_cast<int>(interpMethInfo));
1409
1410 // If there's an HFA return, add space for that.
1411 if (HFARetTypeSize > 0)
1412 {
1413 sl.ThumbEmitSubSp(HFARetTypeSize);
1414 }
1415
1416 // Now we can call the right method.
1417 // No "direct call" instruction, so load into register first. Can use R3.
1418 sl.ThumbEmitMovConstant(ThumbReg(3), static_cast<int>(interpretMethodFunc));
1419 sl.ThumbEmitCallRegister(ThumbReg(3));
1420
1421 // If there's an HFA return, copy to FP regs, and deallocate the stack space.
1422 if (HFARetTypeSize > 0)
1423 {
1424 sl.ThumbEmitLoadMultipleVFPDoubleReg(ThumbVFPDoubleReg(0), thumbRegSp, HFARetTypeSize/sizeof(void*));
1425 sl.ThumbEmitAddSp(HFARetTypeSize);
1426 }
1427
1428 sl.ThumbEmitEpilog();
1429
1430#elif defined(_ARM64_)
1431
1432 UINT stackFrameSize = argState.numFPRegArgSlots;
1433
1434 sl.EmitProlog(argState.numRegArgs, argState.numFPRegArgSlots, 0 /*cCalleeSavedRegs*/, static_cast<unsigned short>(cHFAVars*sizeof(void*)));
1435
1436#if INTERP_ILSTUBS
1437 if (pMD->IsILStub())
1438 {
1439 // Third argument is stubcontext, in x12 (METHODDESC_REGISTER)
1440 sl.EmitMovReg(IntReg(2), IntReg(12));
1441 }
1442 else
1443#endif
1444 {
1445 // For a non-ILStub method, push NULL as the third stubContext argument
1446 sl.EmitMovConstant(IntReg(2), 0);
1447 }
1448
1449 // Second arg is pointer to the basei of the ILArgs -- i.e., the current stack value
1450 sl.EmitAddImm(IntReg(1), RegSp, sl.GetSavedRegArgsOffset());
1451
1452 // First arg is the pointer to the interpMethodInfo structure
1453#if INTERP_ILSTUBS
1454 if (!pMD->IsILStub())
1455#endif
1456 {
1457 // interpMethodInfo is already in x8, so copy it from x8
1458 sl.EmitMovReg(IntReg(0), IntReg(8));
1459 }
1460#if INTERP_ILSTUBS
1461 else
1462 {
1463 // We didn't do the short-circuiting, therefore interpMethInfo is
1464 // not stored in a register (x8) before. so do it now.
1465 sl.EmitMovConstant(IntReg(0), reinterpret_cast<UINT64>(interpMethInfo));
1466 }
1467#endif
1468
1469 sl.EmitCallLabel(sl.NewExternalCodeLabel((LPVOID)interpretMethodFunc), FALSE, FALSE);
1470
1471 // If there's an HFA return, copy to FP regs
1472 if (cHFAVars > 0)
1473 {
1474 for (unsigned i=0; i<=(cHFAVars/2)*2;i+=2)
1475 sl.EmitLoadStoreRegPairImm(StubLinkerCPU::eLOAD, VecReg(i), VecReg(i+1), RegSp, i*sizeof(void*));
1476 if ((cHFAVars % 2) == 1)
1477 sl.EmitLoadStoreRegImm(StubLinkerCPU::eLOAD,VecReg(cHFAVars-1), RegSp, cHFAVars*sizeof(void*));
1478
1479 }
1480
1481 sl.EmitEpilog();
1482
1483
1484#else
1485#error unsupported platform
1486#endif
1487 stub = sl.Link(SystemDomain::GetGlobalLoaderAllocator()->GetStubHeap());
1488
1489 *nativeSizeOfCode = static_cast<ULONG>(stub->GetNumCodeBytes());
1490 // TODO: manage reference count of interpreter stubs. Look for examples...
1491 *nativeEntry = dac_cast<BYTE*>(stub->GetEntryPoint());
1492 }
1493
1494 // Initialize the arg offset information.
1495 interpMethInfo->InitArgInfo(comp, info, argState.argOffsets);
1496
1497#ifdef _DEBUG
1498 AddInterpMethInfo(interpMethInfo);
1499#endif // _DEBUG
1500 if (!jmpCall)
1501 {
1502 // Remember the mapping between code address and MethodDesc*.
1503 RecordInterpreterStubForMethodDesc(info->ftn, *nativeEntry);
1504 }
1505
1506 return CORJIT_OK;
1507#undef TRACE_SKIPPED
1508}
1509
1510size_t Interpreter::GetFrameSize(InterpreterMethodInfo* interpMethInfo)
1511{
1512 size_t sz = interpMethInfo->LocalMemSize();
1513#if COMBINE_OPSTACK_VAL_TYPE
1514 sz += (interpMethInfo->m_maxStack * sizeof(OpStackValAndType));
1515#else
1516 sz += (interpMethInfo->m_maxStack * (sizeof(INT64) + sizeof(InterpreterType*)));
1517#endif
1518 return sz;
1519}
1520
1521// static
1522ARG_SLOT Interpreter::ExecuteMethodWrapper(struct InterpreterMethodInfo* interpMethInfo, bool directCall, BYTE* ilArgs, void* stubContext, __out bool* pDoJmpCall, CORINFO_RESOLVED_TOKEN* pResolvedToken)
1523{
1524#define INTERP_DYNAMIC_CONTRACTS 1
1525#if INTERP_DYNAMIC_CONTRACTS
1526 CONTRACTL {
1527 THROWS;
1528 GC_TRIGGERS;
1529 MODE_COOPERATIVE;
1530 } CONTRACTL_END;
1531#else
1532 // Dynamic contract occupies too much stack.
1533 STATIC_CONTRACT_THROWS;
1534 STATIC_CONTRACT_GC_TRIGGERS;
1535 STATIC_CONTRACT_MODE_COOPERATIVE;
1536#endif
1537
1538 size_t sizeWithGS = GetFrameSize(interpMethInfo) + sizeof(GSCookie);
1539 BYTE* frameMemoryGS = static_cast<BYTE*>(_alloca(sizeWithGS));
1540
1541 ARG_SLOT retVal = 0;
1542 unsigned jmpCallToken = 0;
1543
1544 Interpreter interp(interpMethInfo, directCall, ilArgs, stubContext, frameMemoryGS);
1545
1546 // Make sure we can do a GC Scan properly.
1547 FrameWithCookie<InterpreterFrame> interpFrame(&interp);
1548
1549 // Update the interpretation count.
1550 InterlockedIncrement(reinterpret_cast<LONG *>(&interpMethInfo->m_invocations));
1551
1552 // Need to wait until this point to do this JITting, since it may trigger a GC.
1553 JitMethodIfAppropriate(interpMethInfo);
1554
1555 // Pass buffers to get jmpCall flag and the token, if necessary.
1556 interp.ExecuteMethod(&retVal, pDoJmpCall, &jmpCallToken);
1557
1558 if (*pDoJmpCall)
1559 {
1560 GCX_PREEMP();
1561 interp.ResolveToken(pResolvedToken, jmpCallToken, CORINFO_TOKENKIND_Method InterpTracingArg(RTK_Call));
1562 }
1563
1564 interpFrame.Pop();
1565 return retVal;
1566}
1567
1568// TODO: Add GSCookie checks
1569
1570// static
1571inline ARG_SLOT Interpreter::InterpretMethodBody(struct InterpreterMethodInfo* interpMethInfo, bool directCall, BYTE* ilArgs, void* stubContext)
1572{
1573#if INTERP_DYNAMIC_CONTRACTS
1574 CONTRACTL {
1575 THROWS;
1576 GC_TRIGGERS;
1577 MODE_COOPERATIVE;
1578 } CONTRACTL_END;
1579#else
1580 // Dynamic contract occupies too much stack.
1581 STATIC_CONTRACT_THROWS;
1582 STATIC_CONTRACT_GC_TRIGGERS;
1583 STATIC_CONTRACT_MODE_COOPERATIVE;
1584#endif
1585
1586 CEEInfo* jitInfo = NULL;
1587 for (bool doJmpCall = true; doJmpCall; )
1588 {
1589 unsigned jmpCallToken = 0;
1590 CORINFO_RESOLVED_TOKEN methTokPtr;
1591 ARG_SLOT retVal = ExecuteMethodWrapper(interpMethInfo, directCall, ilArgs, stubContext, &doJmpCall, &methTokPtr);
1592 // Clear any allocated jitInfo.
1593 delete jitInfo;
1594
1595 // Nothing to do if the recent method asks not to do a jmpCall.
1596 if (!doJmpCall)
1597 {
1598 return retVal;
1599 }
1600
1601 // The recently executed method wants us to perform a jmpCall.
1602 MethodDesc* pMD = GetMethod(methTokPtr.hMethod);
1603 interpMethInfo = MethodHandleToInterpreterMethInfoPtr(CORINFO_METHOD_HANDLE(pMD));
1604
1605 // Allocate a new jitInfo and also a new interpMethInfo.
1606 if (interpMethInfo == NULL)
1607 {
1608 assert(doJmpCall);
1609 jitInfo = new CEEInfo(pMD, true);
1610
1611 CORINFO_METHOD_INFO methInfo;
1612
1613 GCX_PREEMP();
1614 jitInfo->getMethodInfo(CORINFO_METHOD_HANDLE(pMD), &methInfo);
1615 GenerateInterpreterStub(jitInfo, &methInfo, NULL, 0, &interpMethInfo, true);
1616 }
1617 }
1618 UNREACHABLE();
1619}
1620
1621void Interpreter::JitMethodIfAppropriate(InterpreterMethodInfo* interpMethInfo, bool force)
1622{
1623 CONTRACTL {
1624 THROWS;
1625 GC_TRIGGERS;
1626 MODE_COOPERATIVE;
1627 } CONTRACTL_END;
1628
1629 unsigned int MaxInterpretCount = s_InterpreterJITThreshold.val(CLRConfig::INTERNAL_InterpreterJITThreshold);
1630
1631 if (force || interpMethInfo->m_invocations > MaxInterpretCount)
1632 {
1633 GCX_PREEMP();
1634 MethodDesc *md = reinterpret_cast<MethodDesc *>(interpMethInfo->m_method);
1635 PCODE stub = md->GetNativeCode();
1636
1637 if (InterpretationStubToMethodInfo(stub) == md)
1638 {
1639#if INTERP_TRACING
1640 if (s_TraceInterpreterJITTransitionFlag.val(CLRConfig::INTERNAL_TraceInterpreterJITTransition))
1641 {
1642 fprintf(GetLogFile(), "JITting method %s:%s.\n", md->m_pszDebugClassName, md->m_pszDebugMethodName);
1643 }
1644#endif // INTERP_TRACING
1645 CORJIT_FLAGS jitFlags(CORJIT_FLAGS::CORJIT_FLAG_MAKEFINALCODE);
1646 NewHolder<COR_ILMETHOD_DECODER> pDecoder(NULL);
1647 // Dynamic methods (e.g., IL stubs) do not have an IL decoder but may
1648 // require additional flags. Ordinary methods require the opposite.
1649 if (md->IsDynamicMethod())
1650 {
1651 jitFlags.Add(md->AsDynamicMethodDesc()->GetILStubResolver()->GetJitFlags());
1652 }
1653 else
1654 {
1655 COR_ILMETHOD_DECODER::DecoderStatus status;
1656 pDecoder = new COR_ILMETHOD_DECODER(md->GetILHeader(TRUE),
1657 md->GetMDImport(),
1658 &status);
1659 }
1660 // This used to be a synchronous jit and could be made so again if desired,
1661 // but using ASP.Net MusicStore as an example scenario the performance is
1662 // better doing the JIT asynchronously. Given the not-on-by-default nature of the
1663 // interpreter I didn't wring my hands too much trying to determine the ideal
1664 // policy.
1665#ifdef FEATURE_TIERED_COMPILATION
1666 GetAppDomain()->GetTieredCompilationManager()->AsyncPromoteMethodToTier1(md);
1667#else
1668#error FEATURE_INTERPRETER depends on FEATURE_TIERED_COMPILATION now
1669#endif
1670 }
1671 }
1672}
1673
1674// static
1675HCIMPL3(float, InterpretMethodFloat, struct InterpreterMethodInfo* interpMethInfo, BYTE* ilArgs, void* stubContext)
1676{
1677 FCALL_CONTRACT;
1678
1679 ARG_SLOT retVal = 0;
1680
1681 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
1682 retVal = (ARG_SLOT)Interpreter::InterpretMethodBody(interpMethInfo, false, ilArgs, stubContext);
1683 HELPER_METHOD_FRAME_END();
1684
1685 return *reinterpret_cast<float*>(ArgSlotEndianessFixup(&retVal, sizeof(float)));
1686}
1687HCIMPLEND
1688
1689// static
1690HCIMPL3(double, InterpretMethodDouble, struct InterpreterMethodInfo* interpMethInfo, BYTE* ilArgs, void* stubContext)
1691{
1692 FCALL_CONTRACT;
1693
1694 ARG_SLOT retVal = 0;
1695
1696 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
1697 retVal = Interpreter::InterpretMethodBody(interpMethInfo, false, ilArgs, stubContext);
1698 HELPER_METHOD_FRAME_END();
1699
1700 return *reinterpret_cast<double*>(ArgSlotEndianessFixup(&retVal, sizeof(double)));
1701}
1702HCIMPLEND
1703
1704// static
1705HCIMPL3(INT64, InterpretMethod, struct InterpreterMethodInfo* interpMethInfo, BYTE* ilArgs, void* stubContext)
1706{
1707 FCALL_CONTRACT;
1708
1709 ARG_SLOT retVal = 0;
1710
1711 HELPER_METHOD_FRAME_BEGIN_RET_ATTRIB(Frame::FRAME_ATTR_EXACT_DEPTH|Frame::FRAME_ATTR_CAPTURE_DEPTH_2);
1712 retVal = Interpreter::InterpretMethodBody(interpMethInfo, false, ilArgs, stubContext);
1713 HELPER_METHOD_FRAME_END();
1714
1715 return static_cast<INT64>(retVal);
1716}
1717HCIMPLEND
1718
1719bool Interpreter::IsInCalleesFrames(void* stackPtr)
1720{
1721 // We assume a downwards_growing stack.
1722 return stackPtr < (m_localVarMemory - sizeof(GSCookie));
1723}
1724
1725// I want an enumeration with values for the second byte of 2-byte opcodes.
1726enum OPCODE_2BYTE {
1727#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) TWOBYTE_##c = unsigned(s2),
1728#include "opcode.def"
1729#undef OPDEF
1730};
1731
1732// Optimize the interpreter loop for speed.
1733#ifdef _MSC_VER
1734#pragma optimize("t", on)
1735#endif
1736
1737// Duplicating code from JitHelpers for MonEnter,MonExit,MonEnter_Static,
1738// MonExit_Static because it sets up helper frame for the JIT.
1739static void MonitorEnter(Object* obj, BYTE* pbLockTaken)
1740{
1741
1742 OBJECTREF objRef = ObjectToOBJECTREF(obj);
1743
1744
1745 if (objRef == NULL)
1746 COMPlusThrow(kArgumentNullException);
1747
1748 GCPROTECT_BEGININTERIOR(pbLockTaken);
1749
1750#ifdef _DEBUG
1751 Thread *pThread = GetThread();
1752 DWORD lockCount = pThread->m_dwLockCount;
1753#endif
1754 if (GET_THREAD()->CatchAtSafePointOpportunistic())
1755 {
1756 GET_THREAD()->PulseGCMode();
1757 }
1758 objRef->EnterObjMonitor();
1759 _ASSERTE ((objRef->GetSyncBlock()->GetMonitor()->GetRecursionLevel() == 1 && pThread->m_dwLockCount == lockCount + 1) ||
1760 pThread->m_dwLockCount == lockCount);
1761 if (pbLockTaken != 0) *pbLockTaken = 1;
1762
1763 GCPROTECT_END();
1764}
1765
1766static void MonitorExit(Object* obj, BYTE* pbLockTaken)
1767{
1768 OBJECTREF objRef = ObjectToOBJECTREF(obj);
1769
1770 if (objRef == NULL)
1771 COMPlusThrow(kArgumentNullException);
1772
1773 if (!objRef->LeaveObjMonitor())
1774 COMPlusThrow(kSynchronizationLockException);
1775
1776 if (pbLockTaken != 0) *pbLockTaken = 0;
1777
1778 TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE));
1779
1780 if (GET_THREAD()->IsAbortRequested()) {
1781 GET_THREAD()->HandleThreadAbort();
1782 }
1783}
1784
1785static void MonitorEnterStatic(AwareLock *lock, BYTE* pbLockTaken)
1786{
1787 lock->Enter();
1788 MONHELPER_STATE(*pbLockTaken = 1;)
1789}
1790
1791static void MonitorExitStatic(AwareLock *lock, BYTE* pbLockTaken)
1792{
1793 // Error, yield or contention
1794 if (!lock->Leave())
1795 COMPlusThrow(kSynchronizationLockException);
1796
1797 TESTHOOKCALL(AppDomainCanBeUnloaded(GET_THREAD()->GetDomain()->GetId().m_dwId,FALSE));
1798 if (GET_THREAD()->IsAbortRequested()) {
1799 GET_THREAD()->HandleThreadAbort();
1800 }
1801}
1802
1803
1804AwareLock* Interpreter::GetMonitorForStaticMethod()
1805{
1806 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(m_methInfo->m_method);
1807 CORINFO_LOOKUP_KIND kind;
1808 {
1809 GCX_PREEMP();
1810 kind = m_interpCeeInfo.getLocationOfThisType(m_methInfo->m_method);
1811 }
1812 if (!kind.needsRuntimeLookup)
1813 {
1814 OBJECTREF ref = pMD->GetMethodTable()->GetManagedClassObject();
1815 return (AwareLock*) ref->GetSyncBlock()->GetMonitor();
1816 }
1817 else
1818 {
1819 CORINFO_CLASS_HANDLE classHnd = nullptr;
1820 switch (kind.runtimeLookupKind)
1821 {
1822 case CORINFO_LOOKUP_CLASSPARAM:
1823 {
1824 classHnd = (CORINFO_CLASS_HANDLE) GetPreciseGenericsContext();
1825 }
1826 break;
1827 case CORINFO_LOOKUP_METHODPARAM:
1828 {
1829 MethodDesc* pMD = (MethodDesc*) GetPreciseGenericsContext();
1830 classHnd = (CORINFO_CLASS_HANDLE) pMD->GetMethodTable();
1831 }
1832 break;
1833 default:
1834 NYI_INTERP("Unknown lookup for synchronized methods");
1835 break;
1836 }
1837 MethodTable* pMT = GetMethodTableFromClsHnd(classHnd);
1838 OBJECTREF ref = pMT->GetManagedClassObject();
1839 ASSERT(ref);
1840 return (AwareLock*) ref->GetSyncBlock()->GetMonitor();
1841 }
1842}
1843
1844void Interpreter::DoMonitorEnterWork()
1845{
1846 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(m_methInfo->m_method);
1847 if (pMD->IsSynchronized())
1848 {
1849 if (pMD->IsStatic())
1850 {
1851 AwareLock* lock = GetMonitorForStaticMethod();
1852 MonitorEnterStatic(lock, &m_monAcquired);
1853 }
1854 else
1855 {
1856 MonitorEnter((Object*) m_thisArg, &m_monAcquired);
1857 }
1858 }
1859}
1860
1861void Interpreter::DoMonitorExitWork()
1862{
1863 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(m_methInfo->m_method);
1864 if (pMD->IsSynchronized())
1865 {
1866 if (pMD->IsStatic())
1867 {
1868 AwareLock* lock = GetMonitorForStaticMethod();
1869 MonitorExitStatic(lock, &m_monAcquired);
1870 }
1871 else
1872 {
1873 MonitorExit((Object*) m_thisArg, &m_monAcquired);
1874 }
1875 }
1876}
1877
1878
1879void Interpreter::ExecuteMethod(ARG_SLOT* retVal, __out bool* pDoJmpCall, __out unsigned* pJmpCallToken)
1880{
1881#if INTERP_DYNAMIC_CONTRACTS
1882 CONTRACTL {
1883 THROWS;
1884 GC_TRIGGERS;
1885 MODE_COOPERATIVE;
1886 } CONTRACTL_END;
1887#else
1888 // Dynamic contract occupies too much stack.
1889 STATIC_CONTRACT_THROWS;
1890 STATIC_CONTRACT_GC_TRIGGERS;
1891 STATIC_CONTRACT_MODE_COOPERATIVE;
1892#endif
1893
1894 *pDoJmpCall = false;
1895
1896 // Normally I'd prefer to declare these in small case-block scopes, but most C++ compilers
1897 // do not realize that their lifetimes do not overlap, so that makes for a large stack frame.
1898 // So I avoid that by outside declarations (sigh).
1899 char offsetc, valc;
1900 unsigned char argNumc;
1901 unsigned short argNums;
1902 INT32 vali;
1903 INT64 vall;
1904 InterpreterType it;
1905 size_t sz;
1906
1907 unsigned short ops;
1908
1909 // Make sure that the .cctor for the current method's class has been run.
1910 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(m_methInfo->m_method);
1911 EnsureClassInit(pMD->GetMethodTable());
1912
1913#if INTERP_TRACING
1914 const char* methName = eeGetMethodFullName(m_methInfo->m_method);
1915 unsigned ilOffset = 0;
1916
1917 unsigned curInvocation = InterlockedIncrement(&s_totalInvocations);
1918 if (s_TraceInterpreterEntriesFlag.val(CLRConfig::INTERNAL_TraceInterpreterEntries))
1919 {
1920 fprintf(GetLogFile(), "Entering method #%d (= 0x%x): %s.\n", curInvocation, curInvocation, methName);
1921 fprintf(GetLogFile(), " arguments:\n");
1922 PrintArgs();
1923 }
1924#endif // INTERP_TRACING
1925
1926#if LOOPS_VIA_INSTRS
1927 unsigned instrs = 0;
1928#else
1929#if INTERP_PROFILE
1930 unsigned instrs = 0;
1931#endif
1932#endif
1933
1934EvalLoop:
1935 GCX_ASSERT_COOP();
1936 // Catch any exceptions raised.
1937 EX_TRY {
1938 // Optional features...
1939#define INTERPRETER_CHECK_LARGE_STRUCT_STACK_HEIGHT 1
1940
1941#if INTERP_ILCYCLE_PROFILE
1942 m_instr = CEE_COUNT; // Flag to indicate first instruction.
1943 m_exemptCycles = 0;
1944#endif // INTERP_ILCYCLE_PROFILE
1945
1946 DoMonitorEnterWork();
1947
1948 INTERPLOG("START %d, %s\n", m_methInfo->m_stubNum, methName);
1949 for (;;)
1950 {
1951 // TODO: verify that m_ILCodePtr is legal, and we haven't walked off the end of the IL array? (i.e., bad IL).
1952 // Note that ExecuteBranch() should be called for every branch. That checks that we aren't either before or
1953 // after the IL range. Here, we would only need to check that we haven't gone past the end (not before the beginning)
1954 // because everything that doesn't call ExecuteBranch() should only add to m_ILCodePtr.
1955
1956#if INTERP_TRACING
1957 ilOffset = CurOffset();
1958#endif // _DEBUG
1959#if INTERP_TRACING
1960 if (s_TraceInterpreterOstackFlag.val(CLRConfig::INTERNAL_TraceInterpreterOstack))
1961 {
1962 PrintOStack();
1963 }
1964#if INTERPRETER_CHECK_LARGE_STRUCT_STACK_HEIGHT
1965 _ASSERTE_MSG(LargeStructStackHeightIsValid(), "Large structure stack height invariant violated."); // Check the large struct stack invariant.
1966#endif
1967 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
1968 {
1969 fprintf(GetLogFile(), " %#4x: %s\n", ilOffset, ILOp(m_ILCodePtr));
1970 fflush(GetLogFile());
1971 }
1972#endif // INTERP_TRACING
1973#if LOOPS_VIA_INSTRS
1974 instrs++;
1975#else
1976#if INTERP_PROFILE
1977 instrs++;
1978#endif
1979#endif
1980
1981#if INTERP_ILINSTR_PROFILE
1982#if INTERP_ILCYCLE_PROFILE
1983 UpdateCycleCount();
1984#endif // INTERP_ILCYCLE_PROFILE
1985
1986 InterlockedIncrement(&s_ILInstrExecs[*m_ILCodePtr]);
1987#endif // INTERP_ILINSTR_PROFILE
1988
1989 switch (*m_ILCodePtr)
1990 {
1991 case CEE_NOP:
1992 m_ILCodePtr++;
1993 continue;
1994 case CEE_BREAK: // TODO: interact with the debugger?
1995 m_ILCodePtr++;
1996 continue;
1997 case CEE_LDARG_0:
1998 LdArg(0);
1999 break;
2000 case CEE_LDARG_1:
2001 LdArg(1);
2002 break;
2003 case CEE_LDARG_2:
2004 LdArg(2);
2005 break;
2006 case CEE_LDARG_3:
2007 LdArg(3);
2008 break;
2009 case CEE_LDLOC_0:
2010 LdLoc(0);
2011 m_ILCodePtr++;
2012 continue;
2013 case CEE_LDLOC_1:
2014 LdLoc(1);
2015 break;
2016 case CEE_LDLOC_2:
2017 LdLoc(2);
2018 break;
2019 case CEE_LDLOC_3:
2020 LdLoc(3);
2021 break;
2022 case CEE_STLOC_0:
2023 StLoc(0);
2024 break;
2025 case CEE_STLOC_1:
2026 StLoc(1);
2027 break;
2028 case CEE_STLOC_2:
2029 StLoc(2);
2030 break;
2031 case CEE_STLOC_3:
2032 StLoc(3);
2033 break;
2034 case CEE_LDARG_S:
2035 m_ILCodePtr++;
2036 argNumc = *m_ILCodePtr;
2037 LdArg(argNumc);
2038 break;
2039 case CEE_LDARGA_S:
2040 m_ILCodePtr++;
2041 argNumc = *m_ILCodePtr;
2042 LdArgA(argNumc);
2043 break;
2044 case CEE_STARG_S:
2045 m_ILCodePtr++;
2046 argNumc = *m_ILCodePtr;
2047 StArg(argNumc);
2048 break;
2049 case CEE_LDLOC_S:
2050 argNumc = *(m_ILCodePtr + 1);
2051 LdLoc(argNumc);
2052 m_ILCodePtr += 2;
2053 continue;
2054 case CEE_LDLOCA_S:
2055 m_ILCodePtr++;
2056 argNumc = *m_ILCodePtr;
2057 LdLocA(argNumc);
2058 break;
2059 case CEE_STLOC_S:
2060 argNumc = *(m_ILCodePtr + 1);
2061 StLoc(argNumc);
2062 m_ILCodePtr += 2;
2063 continue;
2064 case CEE_LDNULL:
2065 LdNull();
2066 break;
2067 case CEE_LDC_I4_M1:
2068 LdIcon(-1);
2069 break;
2070 case CEE_LDC_I4_0:
2071 LdIcon(0);
2072 break;
2073 case CEE_LDC_I4_1:
2074 LdIcon(1);
2075 m_ILCodePtr++;
2076 continue;
2077 case CEE_LDC_I4_2:
2078 LdIcon(2);
2079 break;
2080 case CEE_LDC_I4_3:
2081 LdIcon(3);
2082 break;
2083 case CEE_LDC_I4_4:
2084 LdIcon(4);
2085 break;
2086 case CEE_LDC_I4_5:
2087 LdIcon(5);
2088 break;
2089 case CEE_LDC_I4_6:
2090 LdIcon(6);
2091 break;
2092 case CEE_LDC_I4_7:
2093 LdIcon(7);
2094 break;
2095 case CEE_LDC_I4_8:
2096 LdIcon(8);
2097 break;
2098 case CEE_LDC_I4_S:
2099 valc = getI1(m_ILCodePtr + 1);
2100 LdIcon(valc);
2101 m_ILCodePtr += 2;
2102 continue;
2103 case CEE_LDC_I4:
2104 vali = getI4LittleEndian(m_ILCodePtr + 1);
2105 LdIcon(vali);
2106 m_ILCodePtr += 5;
2107 continue;
2108 case CEE_LDC_I8:
2109 vall = getI8LittleEndian(m_ILCodePtr + 1);
2110 LdLcon(vall);
2111 m_ILCodePtr += 9;
2112 continue;
2113 case CEE_LDC_R4:
2114 // We use I4 here because we just care about the bit pattern.
2115 // LdR4Con will push the right InterpreterType.
2116 vali = getI4LittleEndian(m_ILCodePtr + 1);
2117 LdR4con(vali);
2118 m_ILCodePtr += 5;
2119 continue;
2120 case CEE_LDC_R8:
2121 // We use I4 here because we just care about the bit pattern.
2122 // LdR8Con will push the right InterpreterType.
2123 vall = getI8LittleEndian(m_ILCodePtr + 1);
2124 LdR8con(vall);
2125 m_ILCodePtr += 9;
2126 continue;
2127 case CEE_DUP:
2128 assert(m_curStackHt > 0);
2129 it = OpStackTypeGet(m_curStackHt - 1);
2130 OpStackTypeSet(m_curStackHt, it);
2131 if (it.IsLargeStruct(&m_interpCeeInfo))
2132 {
2133 sz = it.Size(&m_interpCeeInfo);
2134 void* dest = LargeStructOperandStackPush(sz);
2135 memcpy(dest, OpStackGet<void*>(m_curStackHt - 1), sz);
2136 OpStackSet<void*>(m_curStackHt, dest);
2137 }
2138 else
2139 {
2140 OpStackSet<INT64>(m_curStackHt, OpStackGet<INT64>(m_curStackHt - 1));
2141 }
2142 m_curStackHt++;
2143 break;
2144 case CEE_POP:
2145 assert(m_curStackHt > 0);
2146 m_curStackHt--;
2147 it = OpStackTypeGet(m_curStackHt);
2148 if (it.IsLargeStruct(&m_interpCeeInfo))
2149 {
2150 LargeStructOperandStackPop(it.Size(&m_interpCeeInfo), OpStackGet<void*>(m_curStackHt));
2151 }
2152 break;
2153
2154 case CEE_JMP:
2155 *pJmpCallToken = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
2156 *pDoJmpCall = true;
2157 goto ExitEvalLoop;
2158
2159 case CEE_CALL:
2160 DoCall(/*virtualCall*/false);
2161#if INTERP_TRACING
2162 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
2163 {
2164 fprintf(GetLogFile(), " Returning to method %s, stub num %d.\n", methName, m_methInfo->m_stubNum);
2165 }
2166#endif // INTERP_TRACING
2167 continue;
2168
2169 case CEE_CALLVIRT:
2170 DoCall(/*virtualCall*/true);
2171#if INTERP_TRACING
2172 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
2173 {
2174 fprintf(GetLogFile(), " Returning to method %s, stub num %d.\n", methName, m_methInfo->m_stubNum);
2175 }
2176#endif // INTERP_TRACING
2177 continue;
2178
2179 // HARD
2180 case CEE_CALLI:
2181 CallI();
2182 continue;
2183
2184 case CEE_RET:
2185 if (m_methInfo->m_returnType == CORINFO_TYPE_VOID)
2186 {
2187 assert(m_curStackHt == 0);
2188 }
2189 else
2190 {
2191 assert(m_curStackHt == 1);
2192 InterpreterType retValIt = OpStackTypeGet(0);
2193 bool looseInt = s_InterpreterLooseRules &&
2194 CorInfoTypeIsIntegral(m_methInfo->m_returnType) &&
2195 (CorInfoTypeIsIntegral(retValIt.ToCorInfoType()) || CorInfoTypeIsPointer(retValIt.ToCorInfoType())) &&
2196 (m_methInfo->m_returnType != retValIt.ToCorInfoType());
2197
2198 bool looseFloat = s_InterpreterLooseRules &&
2199 CorInfoTypeIsFloatingPoint(m_methInfo->m_returnType) &&
2200 CorInfoTypeIsFloatingPoint(retValIt.ToCorInfoType()) &&
2201 (m_methInfo->m_returnType != retValIt.ToCorInfoType());
2202
2203 // Make sure that the return value "matches" (which allows certain relaxations) the declared return type.
2204 assert((m_methInfo->m_returnType == CORINFO_TYPE_VALUECLASS && retValIt.ToCorInfoType() == CORINFO_TYPE_VALUECLASS) ||
2205 (m_methInfo->m_returnType == CORINFO_TYPE_REFANY && retValIt.ToCorInfoType() == CORINFO_TYPE_VALUECLASS) ||
2206 (m_methInfo->m_returnType == CORINFO_TYPE_REFANY && retValIt.ToCorInfoType() == CORINFO_TYPE_REFANY) ||
2207 (looseInt || looseFloat) ||
2208 InterpreterType(m_methInfo->m_returnType).StackNormalize().Matches(retValIt, &m_interpCeeInfo));
2209
2210 size_t sz = retValIt.Size(&m_interpCeeInfo);
2211#if defined(FEATURE_HFA)
2212 CorInfoType cit = CORINFO_TYPE_UNDEF;
2213 {
2214 GCX_PREEMP();
2215 if(m_methInfo->m_returnType == CORINFO_TYPE_VALUECLASS)
2216 cit = m_interpCeeInfo.getHFAType(retValIt.ToClassHandle());
2217 }
2218#endif
2219 if (m_methInfo->GetFlag<InterpreterMethodInfo::Flag_hasRetBuffArg>())
2220 {
2221 assert((m_methInfo->m_returnType == CORINFO_TYPE_VALUECLASS && retValIt.ToCorInfoType() == CORINFO_TYPE_VALUECLASS) ||
2222 (m_methInfo->m_returnType == CORINFO_TYPE_REFANY && retValIt.ToCorInfoType() == CORINFO_TYPE_VALUECLASS) ||
2223 (m_methInfo->m_returnType == CORINFO_TYPE_REFANY && retValIt.ToCorInfoType() == CORINFO_TYPE_REFANY));
2224 if (retValIt.ToCorInfoType() == CORINFO_TYPE_REFANY)
2225 {
2226 InterpreterType typedRefIT = GetTypedRefIT(&m_interpCeeInfo);
2227 TypedByRef* ptr = OpStackGet<TypedByRef*>(0);
2228 *((TypedByRef*) m_retBufArg) = *ptr;
2229 }
2230 else if (retValIt.IsLargeStruct(&m_interpCeeInfo))
2231 {
2232 MethodTable* clsMt = GetMethodTableFromClsHnd(retValIt.ToClassHandle());
2233 // The ostack value is a pointer to the struct value.
2234 CopyValueClassUnchecked(m_retBufArg, OpStackGet<void*>(0), clsMt);
2235 }
2236 else
2237 {
2238 MethodTable* clsMt = GetMethodTableFromClsHnd(retValIt.ToClassHandle());
2239 // The ostack value *is* the struct value.
2240 CopyValueClassUnchecked(m_retBufArg, OpStackGetAddr(0, sz), clsMt);
2241 }
2242 }
2243#if defined(FEATURE_HFA)
2244 // Is it an HFA?
2245 else if (m_methInfo->m_returnType == CORINFO_TYPE_VALUECLASS
2246 && CorInfoTypeIsFloatingPoint(cit)
2247 && (MetaSig(reinterpret_cast<MethodDesc*>(m_methInfo->m_method)).GetCallingConventionInfo() & CORINFO_CALLCONV_VARARG) == 0)
2248 {
2249 if (retValIt.IsLargeStruct(&m_interpCeeInfo))
2250 {
2251 // The ostack value is a pointer to the struct value.
2252 memcpy(GetHFARetBuffAddr(static_cast<unsigned>(sz)), OpStackGet<void*>(0), sz);
2253 }
2254 else
2255 {
2256 // The ostack value *is* the struct value.
2257 memcpy(GetHFARetBuffAddr(static_cast<unsigned>(sz)), OpStackGetAddr(0, sz), sz);
2258 }
2259 }
2260#endif
2261 else if (CorInfoTypeIsFloatingPoint(m_methInfo->m_returnType) &&
2262 CorInfoTypeIsFloatingPoint(retValIt.ToCorInfoType()))
2263 {
2264 double val = (sz <= sizeof(INT32)) ? OpStackGet<float>(0) : OpStackGet<double>(0);
2265 if (m_methInfo->m_returnType == CORINFO_TYPE_DOUBLE)
2266 {
2267 memcpy(retVal, &val, sizeof(double));
2268 }
2269 else
2270 {
2271 float val2 = (float) val;
2272 memcpy(retVal, &val2, sizeof(float));
2273 }
2274 }
2275 else
2276 {
2277 if (sz <= sizeof(INT32))
2278 {
2279 *retVal = OpStackGet<INT32>(0);
2280 }
2281 else
2282 {
2283 // If looseInt is true, we are relying on auto-downcast in case *retVal
2284 // is small (but this is guaranteed not to happen by def'n of ARG_SLOT.)
2285 //
2286 // Note structs of size 5, 6, 7 may be returned as 8 byte ints.
2287 assert(sz <= sizeof(INT64));
2288 *retVal = OpStackGet<INT64>(0);
2289 }
2290 }
2291 }
2292
2293
2294#if INTERP_PROFILE
2295 // We're not capturing instructions executed in a method that terminates via exception,
2296 // but that's OK...
2297 m_methInfo->RecordExecInstrs(instrs);
2298#endif
2299#if INTERP_TRACING
2300 // We keep this live until we leave.
2301 delete methName;
2302#endif // INTERP_TRACING
2303
2304#if INTERP_ILCYCLE_PROFILE
2305 // Finish off accounting for the "RET" before we return
2306 UpdateCycleCount();
2307#endif // INTERP_ILCYCLE_PROFILE
2308
2309 goto ExitEvalLoop;
2310
2311 case CEE_BR_S:
2312 m_ILCodePtr++;
2313 offsetc = *m_ILCodePtr;
2314 // The offset is wrt the beginning of the following instruction, so the +1 is to get to that
2315 // m_ILCodePtr value before adding the offset.
2316 ExecuteBranch(m_ILCodePtr + offsetc + 1);
2317 continue; // Skip the default m_ILCodePtr++ at bottom of loop.
2318
2319 case CEE_LEAVE_S:
2320 // LEAVE empties the operand stack.
2321 m_curStackHt = 0;
2322 m_largeStructOperandStackHt = 0;
2323 offsetc = getI1(m_ILCodePtr + 1);
2324
2325 {
2326 // The offset is wrt the beginning of the following instruction, so the +2 is to get to that
2327 // m_ILCodePtr value before adding the offset.
2328 BYTE* leaveTarget = m_ILCodePtr + offsetc + 2;
2329 unsigned leaveOffset = CurOffset();
2330 m_leaveInfoStack.Push(LeaveInfo(leaveOffset, leaveTarget));
2331 if (!SearchForCoveringFinally())
2332 {
2333 m_leaveInfoStack.Pop();
2334 ExecuteBranch(leaveTarget);
2335 }
2336 }
2337 continue; // Skip the default m_ILCodePtr++ at bottom of loop.
2338
2339 // Abstract the next pair out to something common with templates.
2340 case CEE_BRFALSE_S:
2341 BrOnValue<false, 1>();
2342 continue;
2343
2344 case CEE_BRTRUE_S:
2345 BrOnValue<true, 1>();
2346 continue;
2347
2348 case CEE_BEQ_S:
2349 BrOnComparison<CO_EQ, false, 1>();
2350 continue;
2351 case CEE_BGE_S:
2352 assert(m_curStackHt >= 2);
2353 // ECMA spec gives different semantics for different operand types:
2354 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2355 {
2356 case CORINFO_TYPE_FLOAT:
2357 case CORINFO_TYPE_DOUBLE:
2358 BrOnComparison<CO_LT_UN, true, 1>();
2359 break;
2360 default:
2361 BrOnComparison<CO_LT, true, 1>();
2362 break;
2363 }
2364 continue;
2365 case CEE_BGT_S:
2366 BrOnComparison<CO_GT, false, 1>();
2367 continue;
2368 case CEE_BLE_S:
2369 assert(m_curStackHt >= 2);
2370 // ECMA spec gives different semantics for different operand types:
2371 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2372 {
2373 case CORINFO_TYPE_FLOAT:
2374 case CORINFO_TYPE_DOUBLE:
2375 BrOnComparison<CO_GT_UN, true, 1>();
2376 break;
2377 default:
2378 BrOnComparison<CO_GT, true, 1>();
2379 break;
2380 }
2381 continue;
2382 case CEE_BLT_S:
2383 BrOnComparison<CO_LT, false, 1>();
2384 continue;
2385 case CEE_BNE_UN_S:
2386 BrOnComparison<CO_EQ, true, 1>();
2387 continue;
2388 case CEE_BGE_UN_S:
2389 assert(m_curStackHt >= 2);
2390 // ECMA spec gives different semantics for different operand types:
2391 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2392 {
2393 case CORINFO_TYPE_FLOAT:
2394 case CORINFO_TYPE_DOUBLE:
2395 BrOnComparison<CO_LT, true, 1>();
2396 break;
2397 default:
2398 BrOnComparison<CO_LT_UN, true, 1>();
2399 break;
2400 }
2401 continue;
2402 case CEE_BGT_UN_S:
2403 BrOnComparison<CO_GT_UN, false, 1>();
2404 continue;
2405 case CEE_BLE_UN_S:
2406 assert(m_curStackHt >= 2);
2407 // ECMA spec gives different semantics for different operand types:
2408 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2409 {
2410 case CORINFO_TYPE_FLOAT:
2411 case CORINFO_TYPE_DOUBLE:
2412 BrOnComparison<CO_GT, true, 1>();
2413 break;
2414 default:
2415 BrOnComparison<CO_GT_UN, true, 1>();
2416 break;
2417 }
2418 continue;
2419 case CEE_BLT_UN_S:
2420 BrOnComparison<CO_LT_UN, false, 1>();
2421 continue;
2422
2423 case CEE_BR:
2424 m_ILCodePtr++;
2425 vali = getI4LittleEndian(m_ILCodePtr);
2426 vali += 4; // +4 for the length of the offset.
2427 ExecuteBranch(m_ILCodePtr + vali);
2428 if (vali < 0)
2429 {
2430 // Backwards branch -- enable caching.
2431 BackwardsBranchActions(vali);
2432 }
2433
2434 continue;
2435
2436 case CEE_LEAVE:
2437 // LEAVE empties the operand stack.
2438 m_curStackHt = 0;
2439 m_largeStructOperandStackHt = 0;
2440 vali = getI4LittleEndian(m_ILCodePtr + 1);
2441
2442 {
2443 // The offset is wrt the beginning of the following instruction, so the +5 is to get to that
2444 // m_ILCodePtr value before adding the offset.
2445 BYTE* leaveTarget = m_ILCodePtr + (vali + 5);
2446 unsigned leaveOffset = CurOffset();
2447 m_leaveInfoStack.Push(LeaveInfo(leaveOffset, leaveTarget));
2448 if (!SearchForCoveringFinally())
2449 {
2450 (void)m_leaveInfoStack.Pop();
2451 if (vali < 0)
2452 {
2453 // Backwards branch -- enable caching.
2454 BackwardsBranchActions(vali);
2455 }
2456 ExecuteBranch(leaveTarget);
2457 }
2458 }
2459 continue; // Skip the default m_ILCodePtr++ at bottom of loop.
2460
2461 case CEE_BRFALSE:
2462 BrOnValue<false, 4>();
2463 continue;
2464 case CEE_BRTRUE:
2465 BrOnValue<true, 4>();
2466 continue;
2467
2468 case CEE_BEQ:
2469 BrOnComparison<CO_EQ, false, 4>();
2470 continue;
2471 case CEE_BGE:
2472 assert(m_curStackHt >= 2);
2473 // ECMA spec gives different semantics for different operand types:
2474 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2475 {
2476 case CORINFO_TYPE_FLOAT:
2477 case CORINFO_TYPE_DOUBLE:
2478 BrOnComparison<CO_LT_UN, true, 4>();
2479 break;
2480 default:
2481 BrOnComparison<CO_LT, true, 4>();
2482 break;
2483 }
2484 continue;
2485 case CEE_BGT:
2486 BrOnComparison<CO_GT, false, 4>();
2487 continue;
2488 case CEE_BLE:
2489 assert(m_curStackHt >= 2);
2490 // ECMA spec gives different semantics for different operand types:
2491 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2492 {
2493 case CORINFO_TYPE_FLOAT:
2494 case CORINFO_TYPE_DOUBLE:
2495 BrOnComparison<CO_GT_UN, true, 4>();
2496 break;
2497 default:
2498 BrOnComparison<CO_GT, true, 4>();
2499 break;
2500 }
2501 continue;
2502 case CEE_BLT:
2503 BrOnComparison<CO_LT, false, 4>();
2504 continue;
2505 case CEE_BNE_UN:
2506 BrOnComparison<CO_EQ, true, 4>();
2507 continue;
2508 case CEE_BGE_UN:
2509 assert(m_curStackHt >= 2);
2510 // ECMA spec gives different semantics for different operand types:
2511 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2512 {
2513 case CORINFO_TYPE_FLOAT:
2514 case CORINFO_TYPE_DOUBLE:
2515 BrOnComparison<CO_LT, true, 4>();
2516 break;
2517 default:
2518 BrOnComparison<CO_LT_UN, true, 4>();
2519 break;
2520 }
2521 continue;
2522 case CEE_BGT_UN:
2523 BrOnComparison<CO_GT_UN, false, 4>();
2524 continue;
2525 case CEE_BLE_UN:
2526 assert(m_curStackHt >= 2);
2527 // ECMA spec gives different semantics for different operand types:
2528 switch (OpStackTypeGet(m_curStackHt-1).ToCorInfoType())
2529 {
2530 case CORINFO_TYPE_FLOAT:
2531 case CORINFO_TYPE_DOUBLE:
2532 BrOnComparison<CO_GT, true, 4>();
2533 break;
2534 default:
2535 BrOnComparison<CO_GT_UN, true, 4>();
2536 break;
2537 }
2538 continue;
2539 case CEE_BLT_UN:
2540 BrOnComparison<CO_LT_UN, false, 4>();
2541 continue;
2542
2543 case CEE_SWITCH:
2544 {
2545 assert(m_curStackHt > 0);
2546 m_curStackHt--;
2547#if defined(_DEBUG) || defined(_AMD64_)
2548 CorInfoType cit = OpStackTypeGet(m_curStackHt).ToCorInfoType();
2549#endif // _DEBUG || _AMD64_
2550#ifdef _DEBUG
2551 assert(cit == CORINFO_TYPE_INT || cit == CORINFO_TYPE_UINT || cit == CORINFO_TYPE_NATIVEINT);
2552#endif // _DEBUG
2553#if defined(_AMD64_)
2554 UINT32 val = (cit == CORINFO_TYPE_NATIVEINT) ? (INT32) OpStackGet<NativeInt>(m_curStackHt)
2555 : OpStackGet<INT32>(m_curStackHt);
2556#else
2557 UINT32 val = OpStackGet<INT32>(m_curStackHt);
2558#endif
2559 UINT32 n = getU4LittleEndian(m_ILCodePtr + 1);
2560 UINT32 instrSize = 1 + (n + 1)*4;
2561 if (val < n)
2562 {
2563 vali = getI4LittleEndian(m_ILCodePtr + (5 + val * 4));
2564 ExecuteBranch(m_ILCodePtr + instrSize + vali);
2565 }
2566 else
2567 {
2568 m_ILCodePtr += instrSize;
2569 }
2570 }
2571 continue;
2572
2573 case CEE_LDIND_I1:
2574 LdIndShort<INT8, /*isUnsigned*/false>();
2575 break;
2576 case CEE_LDIND_U1:
2577 LdIndShort<UINT8, /*isUnsigned*/true>();
2578 break;
2579 case CEE_LDIND_I2:
2580 LdIndShort<INT16, /*isUnsigned*/false>();
2581 break;
2582 case CEE_LDIND_U2:
2583 LdIndShort<UINT16, /*isUnsigned*/true>();
2584 break;
2585 case CEE_LDIND_I4:
2586 LdInd<INT32, CORINFO_TYPE_INT>();
2587 break;
2588 case CEE_LDIND_U4:
2589 LdInd<UINT32, CORINFO_TYPE_INT>();
2590 break;
2591 case CEE_LDIND_I8:
2592 LdInd<INT64, CORINFO_TYPE_LONG>();
2593 break;
2594 case CEE_LDIND_I:
2595 LdInd<NativeInt, CORINFO_TYPE_NATIVEINT>();
2596 break;
2597 case CEE_LDIND_R4:
2598 LdInd<float, CORINFO_TYPE_FLOAT>();
2599 break;
2600 case CEE_LDIND_R8:
2601 LdInd<double, CORINFO_TYPE_DOUBLE>();
2602 break;
2603 case CEE_LDIND_REF:
2604 LdInd<Object*, CORINFO_TYPE_CLASS>();
2605 break;
2606 case CEE_STIND_REF:
2607 StInd_Ref();
2608 break;
2609 case CEE_STIND_I1:
2610 StInd<INT8>();
2611 break;
2612 case CEE_STIND_I2:
2613 StInd<INT16>();
2614 break;
2615 case CEE_STIND_I4:
2616 StInd<INT32>();
2617 break;
2618 case CEE_STIND_I8:
2619 StInd<INT64>();
2620 break;
2621 case CEE_STIND_R4:
2622 StInd<float>();
2623 break;
2624 case CEE_STIND_R8:
2625 StInd<double>();
2626 break;
2627 case CEE_ADD:
2628 BinaryArithOp<BA_Add>();
2629 m_ILCodePtr++;
2630 continue;
2631 case CEE_SUB:
2632 BinaryArithOp<BA_Sub>();
2633 break;
2634 case CEE_MUL:
2635 BinaryArithOp<BA_Mul>();
2636 break;
2637 case CEE_DIV:
2638 BinaryArithOp<BA_Div>();
2639 break;
2640 case CEE_DIV_UN:
2641 BinaryIntOp<BIO_DivUn>();
2642 break;
2643 case CEE_REM:
2644 BinaryArithOp<BA_Rem>();
2645 break;
2646 case CEE_REM_UN:
2647 BinaryIntOp<BIO_RemUn>();
2648 break;
2649 case CEE_AND:
2650 BinaryIntOp<BIO_And>();
2651 break;
2652 case CEE_OR:
2653 BinaryIntOp<BIO_Or>();
2654 break;
2655 case CEE_XOR:
2656 BinaryIntOp<BIO_Xor>();
2657 break;
2658 case CEE_SHL:
2659 ShiftOp<CEE_SHL>();
2660 break;
2661 case CEE_SHR:
2662 ShiftOp<CEE_SHR>();
2663 break;
2664 case CEE_SHR_UN:
2665 ShiftOp<CEE_SHR_UN>();
2666 break;
2667 case CEE_NEG:
2668 Neg();
2669 break;
2670 case CEE_NOT:
2671 Not();
2672 break;
2673 case CEE_CONV_I1:
2674 Conv<INT8, /*TIsUnsigned*/false, /*TCanHoldPtr*/false, /*TIsShort*/true, CORINFO_TYPE_INT>();
2675 break;
2676 case CEE_CONV_I2:
2677 Conv<INT16, /*TIsUnsigned*/false, /*TCanHoldPtr*/false, /*TIsShort*/true, CORINFO_TYPE_INT>();
2678 break;
2679 case CEE_CONV_I4:
2680 Conv<INT32, /*TIsUnsigned*/false, /*TCanHoldPtr*/false, /*TIsShort*/false, CORINFO_TYPE_INT>();
2681 break;
2682 case CEE_CONV_I8:
2683 Conv<INT64, /*TIsUnsigned*/false, /*TCanHoldPtr*/true, /*TIsShort*/false, CORINFO_TYPE_LONG>();
2684 break;
2685 case CEE_CONV_R4:
2686 Conv<float, /*TIsUnsigned*/false, /*TCanHoldPtr*/false, /*TIsShort*/false, CORINFO_TYPE_FLOAT>();
2687 break;
2688 case CEE_CONV_R8:
2689 Conv<double, /*TIsUnsigned*/false, /*TCanHoldPtr*/false, /*TIsShort*/false, CORINFO_TYPE_DOUBLE>();
2690 break;
2691 case CEE_CONV_U4:
2692 Conv<UINT32, /*TIsUnsigned*/true, /*TCanHoldPtr*/false, /*TIsShort*/false, CORINFO_TYPE_INT>();
2693 break;
2694 case CEE_CONV_U8:
2695 Conv<UINT64, /*TIsUnsigned*/true, /*TCanHoldPtr*/true, /*TIsShort*/false, CORINFO_TYPE_LONG>();
2696 break;
2697
2698 case CEE_CPOBJ:
2699 CpObj();
2700 continue;
2701 case CEE_LDOBJ:
2702 LdObj();
2703 continue;
2704 case CEE_LDSTR:
2705 LdStr();
2706 continue;
2707 case CEE_NEWOBJ:
2708 NewObj();
2709#if INTERP_TRACING
2710 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
2711 {
2712 fprintf(GetLogFile(), " Returning to method %s, stub num %d.\n", methName, m_methInfo->m_stubNum);
2713 }
2714#endif // INTERP_TRACING
2715 continue;
2716 case CEE_CASTCLASS:
2717 CastClass();
2718 continue;
2719 case CEE_ISINST:
2720 IsInst();
2721 continue;
2722 case CEE_CONV_R_UN:
2723 ConvRUn();
2724 break;
2725 case CEE_UNBOX:
2726 Unbox();
2727 continue;
2728 case CEE_THROW:
2729 Throw();
2730 break;
2731 case CEE_LDFLD:
2732 LdFld();
2733 continue;
2734 case CEE_LDFLDA:
2735 LdFldA();
2736 continue;
2737 case CEE_STFLD:
2738 StFld();
2739 continue;
2740 case CEE_LDSFLD:
2741 LdSFld();
2742 continue;
2743 case CEE_LDSFLDA:
2744 LdSFldA();
2745 continue;
2746 case CEE_STSFLD:
2747 StSFld();
2748 continue;
2749 case CEE_STOBJ:
2750 StObj();
2751 continue;
2752 case CEE_CONV_OVF_I1_UN:
2753 ConvOvfUn<INT8, SCHAR_MIN, SCHAR_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2754 break;
2755 case CEE_CONV_OVF_I2_UN:
2756 ConvOvfUn<INT16, SHRT_MIN, SHRT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2757 break;
2758 case CEE_CONV_OVF_I4_UN:
2759 ConvOvfUn<INT32, INT_MIN, INT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2760 break;
2761 case CEE_CONV_OVF_I8_UN:
2762 ConvOvfUn<INT64, _I64_MIN, _I64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_LONG>();
2763 break;
2764 case CEE_CONV_OVF_U1_UN:
2765 ConvOvfUn<UINT8, 0, UCHAR_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2766 break;
2767 case CEE_CONV_OVF_U2_UN:
2768 ConvOvfUn<UINT16, 0, USHRT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2769 break;
2770 case CEE_CONV_OVF_U4_UN:
2771 ConvOvfUn<UINT32, 0, UINT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2772 break;
2773 case CEE_CONV_OVF_U8_UN:
2774 ConvOvfUn<UINT64, 0, _UI64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_LONG>();
2775 break;
2776 case CEE_CONV_OVF_I_UN:
2777 if (sizeof(NativeInt) == 4)
2778 {
2779 ConvOvfUn<NativeInt, INT_MIN, INT_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2780 }
2781 else
2782 {
2783 assert(sizeof(NativeInt) == 8);
2784 ConvOvfUn<NativeInt, _I64_MIN, _I64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2785 }
2786 break;
2787 case CEE_CONV_OVF_U_UN:
2788 if (sizeof(NativeUInt) == 4)
2789 {
2790 ConvOvfUn<NativeUInt, 0, UINT_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2791 }
2792 else
2793 {
2794 assert(sizeof(NativeUInt) == 8);
2795 ConvOvfUn<NativeUInt, 0, _UI64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2796 }
2797 break;
2798 case CEE_BOX:
2799 Box();
2800 continue;
2801 case CEE_NEWARR:
2802 NewArr();
2803 continue;
2804 case CEE_LDLEN:
2805 LdLen();
2806 break;
2807 case CEE_LDELEMA:
2808 LdElem</*takeAddr*/true>();
2809 continue;
2810 case CEE_LDELEM_I1:
2811 LdElemWithType<INT8, false, CORINFO_TYPE_INT>();
2812 break;
2813 case CEE_LDELEM_U1:
2814 LdElemWithType<UINT8, false, CORINFO_TYPE_INT>();
2815 break;
2816 case CEE_LDELEM_I2:
2817 LdElemWithType<INT16, false, CORINFO_TYPE_INT>();
2818 break;
2819 case CEE_LDELEM_U2:
2820 LdElemWithType<UINT16, false, CORINFO_TYPE_INT>();
2821 break;
2822 case CEE_LDELEM_I4:
2823 LdElemWithType<INT32, false, CORINFO_TYPE_INT>();
2824 break;
2825 case CEE_LDELEM_U4:
2826 LdElemWithType<UINT32, false, CORINFO_TYPE_INT>();
2827 break;
2828 case CEE_LDELEM_I8:
2829 LdElemWithType<INT64, false, CORINFO_TYPE_LONG>();
2830 break;
2831 // Note that the ECMA spec defines a "LDELEM_U8", but it is the same instruction number as LDELEM_I8 (since
2832 // when loading to the widest width, signed/unsigned doesn't matter).
2833 case CEE_LDELEM_I:
2834 LdElemWithType<NativeInt, false, CORINFO_TYPE_NATIVEINT>();
2835 break;
2836 case CEE_LDELEM_R4:
2837 LdElemWithType<float, false, CORINFO_TYPE_FLOAT>();
2838 break;
2839 case CEE_LDELEM_R8:
2840 LdElemWithType<double, false, CORINFO_TYPE_DOUBLE>();
2841 break;
2842 case CEE_LDELEM_REF:
2843 LdElemWithType<Object*, true, CORINFO_TYPE_CLASS>();
2844 break;
2845 case CEE_STELEM_I:
2846 StElemWithType<NativeInt, false>();
2847 break;
2848 case CEE_STELEM_I1:
2849 StElemWithType<INT8, false>();
2850 break;
2851 case CEE_STELEM_I2:
2852 StElemWithType<INT16, false>();
2853 break;
2854 case CEE_STELEM_I4:
2855 StElemWithType<INT32, false>();
2856 break;
2857 case CEE_STELEM_I8:
2858 StElemWithType<INT64, false>();
2859 break;
2860 case CEE_STELEM_R4:
2861 StElemWithType<float, false>();
2862 break;
2863 case CEE_STELEM_R8:
2864 StElemWithType<double, false>();
2865 break;
2866 case CEE_STELEM_REF:
2867 StElemWithType<Object*, true>();
2868 break;
2869 case CEE_LDELEM:
2870 LdElem</*takeAddr*/false>();
2871 continue;
2872 case CEE_STELEM:
2873 StElem();
2874 continue;
2875 case CEE_UNBOX_ANY:
2876 UnboxAny();
2877 continue;
2878 case CEE_CONV_OVF_I1:
2879 ConvOvf<INT8, SCHAR_MIN, SCHAR_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2880 break;
2881 case CEE_CONV_OVF_U1:
2882 ConvOvf<UINT8, 0, UCHAR_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2883 break;
2884 case CEE_CONV_OVF_I2:
2885 ConvOvf<INT16, SHRT_MIN, SHRT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2886 break;
2887 case CEE_CONV_OVF_U2:
2888 ConvOvf<UINT16, 0, USHRT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2889 break;
2890 case CEE_CONV_OVF_I4:
2891 ConvOvf<INT32, INT_MIN, INT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2892 break;
2893 case CEE_CONV_OVF_U4:
2894 ConvOvf<UINT32, 0, UINT_MAX, /*TCanHoldPtr*/false, CORINFO_TYPE_INT>();
2895 break;
2896 case CEE_CONV_OVF_I8:
2897 ConvOvf<INT64, _I64_MIN, _I64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_LONG>();
2898 break;
2899 case CEE_CONV_OVF_U8:
2900 ConvOvf<UINT64, 0, _UI64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_LONG>();
2901 break;
2902 case CEE_REFANYVAL:
2903 RefanyVal();
2904 continue;
2905 case CEE_CKFINITE:
2906 CkFinite();
2907 break;
2908 case CEE_MKREFANY:
2909 MkRefany();
2910 continue;
2911 case CEE_LDTOKEN:
2912 LdToken();
2913 continue;
2914 case CEE_CONV_U2:
2915 Conv<UINT16, /*TIsUnsigned*/true, /*TCanHoldPtr*/false, /*TIsShort*/true, CORINFO_TYPE_INT>();
2916 break;
2917 case CEE_CONV_U1:
2918 Conv<UINT8, /*TIsUnsigned*/true, /*TCanHoldPtr*/false, /*TIsShort*/true, CORINFO_TYPE_INT>();
2919 break;
2920 case CEE_CONV_I:
2921 Conv<NativeInt, /*TIsUnsigned*/false, /*TCanHoldPtr*/true, /*TIsShort*/false, CORINFO_TYPE_NATIVEINT>();
2922 break;
2923 case CEE_CONV_OVF_I:
2924 if (sizeof(NativeInt) == 4)
2925 {
2926 ConvOvf<NativeInt, INT_MIN, INT_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2927 }
2928 else
2929 {
2930 assert(sizeof(NativeInt) == 8);
2931 ConvOvf<NativeInt, _I64_MIN, _I64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2932 }
2933 break;
2934 case CEE_CONV_OVF_U:
2935 if (sizeof(NativeUInt) == 4)
2936 {
2937 ConvOvf<NativeUInt, 0, UINT_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2938 }
2939 else
2940 {
2941 assert(sizeof(NativeUInt) == 8);
2942 ConvOvf<NativeUInt, 0, _UI64_MAX, /*TCanHoldPtr*/true, CORINFO_TYPE_NATIVEINT>();
2943 }
2944 break;
2945 case CEE_ADD_OVF:
2946 BinaryArithOvfOp<BA_Add, /*asUnsigned*/false>();
2947 break;
2948 case CEE_ADD_OVF_UN:
2949 BinaryArithOvfOp<BA_Add, /*asUnsigned*/true>();
2950 break;
2951 case CEE_MUL_OVF:
2952 BinaryArithOvfOp<BA_Mul, /*asUnsigned*/false>();
2953 break;
2954 case CEE_MUL_OVF_UN:
2955 BinaryArithOvfOp<BA_Mul, /*asUnsigned*/true>();
2956 break;
2957 case CEE_SUB_OVF:
2958 BinaryArithOvfOp<BA_Sub, /*asUnsigned*/false>();
2959 break;
2960 case CEE_SUB_OVF_UN:
2961 BinaryArithOvfOp<BA_Sub, /*asUnsigned*/true>();
2962 break;
2963 case CEE_ENDFINALLY:
2964 // We have just ended a finally.
2965 // If we were called during exception dispatch,
2966 // rethrow the exception on our way out.
2967 if (m_leaveInfoStack.IsEmpty())
2968 {
2969 Object* finallyException = NULL;
2970
2971 {
2972 GCX_FORBID();
2973 assert(m_inFlightException != NULL);
2974 finallyException = m_inFlightException;
2975 INTERPLOG("endfinally handling for %s, %p, %p\n", methName, m_methInfo, finallyException);
2976 m_inFlightException = NULL;
2977 }
2978
2979 COMPlusThrow(ObjectToOBJECTREF(finallyException));
2980 UNREACHABLE();
2981 }
2982 // Otherwise, see if there's another finally block to
2983 // execute as part of processing the current LEAVE...
2984 else if (!SearchForCoveringFinally())
2985 {
2986 // No, there isn't -- go to the leave target.
2987 assert(!m_leaveInfoStack.IsEmpty());
2988 LeaveInfo li = m_leaveInfoStack.Pop();
2989 ExecuteBranch(li.m_target);
2990 }
2991 // Yes, there, is, and SearchForCoveringFinally set us up to start executing it.
2992 continue; // Skip the default m_ILCodePtr++ at bottom of loop.
2993
2994 case CEE_STIND_I:
2995 StInd<NativeInt>();
2996 break;
2997 case CEE_CONV_U:
2998 Conv<NativeUInt, /*TIsUnsigned*/true, /*TCanHoldPtr*/true, /*TIsShort*/false, CORINFO_TYPE_NATIVEINT>();
2999 break;
3000 case CEE_PREFIX7:
3001 NYI_INTERP("Unimplemented opcode: CEE_PREFIX7");
3002 break;
3003 case CEE_PREFIX6:
3004 NYI_INTERP("Unimplemented opcode: CEE_PREFIX6");
3005 break;
3006 case CEE_PREFIX5:
3007 NYI_INTERP("Unimplemented opcode: CEE_PREFIX5");
3008 break;
3009 case CEE_PREFIX4:
3010 NYI_INTERP("Unimplemented opcode: CEE_PREFIX4");
3011 break;
3012 case CEE_PREFIX3:
3013 NYI_INTERP("Unimplemented opcode: CEE_PREFIX3");
3014 break;
3015 case CEE_PREFIX2:
3016 NYI_INTERP("Unimplemented opcode: CEE_PREFIX2");
3017 break;
3018 case CEE_PREFIX1:
3019 // This is the prefix for all the 2-byte opcodes.
3020 // Figure out the second byte of the 2-byte opcode.
3021 ops = *(m_ILCodePtr + 1);
3022#if INTERP_ILINSTR_PROFILE
3023 // Take one away from PREFIX1, which we won't count.
3024 InterlockedDecrement(&s_ILInstrExecs[CEE_PREFIX1]);
3025 // Credit instead to the 2-byte instruction index.
3026 InterlockedIncrement(&s_ILInstr2ByteExecs[ops]);
3027#endif // INTERP_ILINSTR_PROFILE
3028 switch (ops)
3029 {
3030 case TWOBYTE_CEE_ARGLIST:
3031 // NYI_INTERP("Unimplemented opcode: TWOBYTE_CEE_ARGLIST");
3032 assert(m_methInfo->m_varArgHandleArgNum != NO_VA_ARGNUM);
3033 LdArgA(m_methInfo->m_varArgHandleArgNum);
3034 m_ILCodePtr += 2;
3035 break;
3036
3037 case TWOBYTE_CEE_CEQ:
3038 CompareOp<CO_EQ>();
3039 m_ILCodePtr += 2;
3040 break;
3041 case TWOBYTE_CEE_CGT:
3042 CompareOp<CO_GT>();
3043 m_ILCodePtr += 2;
3044 break;
3045 case TWOBYTE_CEE_CGT_UN:
3046 CompareOp<CO_GT_UN>();
3047 m_ILCodePtr += 2;
3048 break;
3049 case TWOBYTE_CEE_CLT:
3050 CompareOp<CO_LT>();
3051 m_ILCodePtr += 2;
3052 break;
3053 case TWOBYTE_CEE_CLT_UN:
3054 CompareOp<CO_LT_UN>();
3055 m_ILCodePtr += 2;
3056 break;
3057
3058 case TWOBYTE_CEE_LDARG:
3059 m_ILCodePtr += 2;
3060 argNums = getU2LittleEndian(m_ILCodePtr);
3061 LdArg(argNums);
3062 m_ILCodePtr += 2;
3063 break;
3064 case TWOBYTE_CEE_LDARGA:
3065 m_ILCodePtr += 2;
3066 argNums = getU2LittleEndian(m_ILCodePtr);
3067 LdArgA(argNums);
3068 m_ILCodePtr += 2;
3069 break;
3070 case TWOBYTE_CEE_STARG:
3071 m_ILCodePtr += 2;
3072 argNums = getU2LittleEndian(m_ILCodePtr);
3073 StArg(argNums);
3074 m_ILCodePtr += 2;
3075 break;
3076
3077 case TWOBYTE_CEE_LDLOC:
3078 m_ILCodePtr += 2;
3079 argNums = getU2LittleEndian(m_ILCodePtr);
3080 LdLoc(argNums);
3081 m_ILCodePtr += 2;
3082 break;
3083 case TWOBYTE_CEE_LDLOCA:
3084 m_ILCodePtr += 2;
3085 argNums = getU2LittleEndian(m_ILCodePtr);
3086 LdLocA(argNums);
3087 m_ILCodePtr += 2;
3088 break;
3089 case TWOBYTE_CEE_STLOC:
3090 m_ILCodePtr += 2;
3091 argNums = getU2LittleEndian(m_ILCodePtr);
3092 StLoc(argNums);
3093 m_ILCodePtr += 2;
3094 break;
3095
3096 case TWOBYTE_CEE_CONSTRAINED:
3097 RecordConstrainedCall();
3098 break;
3099
3100 case TWOBYTE_CEE_VOLATILE:
3101 // Set a flag that causes a memory barrier to be associated with the next load or store.
3102 m_volatileFlag = true;
3103 m_ILCodePtr += 2;
3104 break;
3105
3106 case TWOBYTE_CEE_LDFTN:
3107 LdFtn();
3108 break;
3109
3110 case TWOBYTE_CEE_INITOBJ:
3111 InitObj();
3112 break;
3113
3114 case TWOBYTE_CEE_LOCALLOC:
3115 LocAlloc();
3116 m_ILCodePtr += 2;
3117 break;
3118
3119 case TWOBYTE_CEE_LDVIRTFTN:
3120 LdVirtFtn();
3121 break;
3122
3123 case TWOBYTE_CEE_SIZEOF:
3124 Sizeof();
3125 break;
3126
3127 case TWOBYTE_CEE_RETHROW:
3128 Rethrow();
3129 break;
3130
3131 case TWOBYTE_CEE_READONLY:
3132 m_readonlyFlag = true;
3133 m_ILCodePtr += 2;
3134 // A comment in importer.cpp indicates that READONLY may also apply to calls. We'll see.
3135 _ASSERTE_MSG(*m_ILCodePtr == CEE_LDELEMA, "According to the ECMA spec, READONLY may only precede LDELEMA");
3136 break;
3137
3138 case TWOBYTE_CEE_INITBLK:
3139 InitBlk();
3140 break;
3141
3142 case TWOBYTE_CEE_CPBLK:
3143 CpBlk();
3144 break;
3145
3146 case TWOBYTE_CEE_ENDFILTER:
3147 EndFilter();
3148 break;
3149
3150 case TWOBYTE_CEE_UNALIGNED:
3151 // Nothing to do here.
3152 m_ILCodePtr += 3;
3153 break;
3154
3155 case TWOBYTE_CEE_TAILCALL:
3156 // TODO: Needs revisiting when implementing tail call.
3157 // NYI_INTERP("Unimplemented opcode: TWOBYTE_CEE_TAILCALL");
3158 m_ILCodePtr += 2;
3159 break;
3160
3161 case TWOBYTE_CEE_REFANYTYPE:
3162 RefanyType();
3163 break;
3164
3165 default:
3166 UNREACHABLE();
3167 break;
3168 }
3169 continue;
3170
3171 case CEE_PREFIXREF:
3172 NYI_INTERP("Unimplemented opcode: CEE_PREFIXREF");
3173 m_ILCodePtr++;
3174 continue;
3175
3176 default:
3177 UNREACHABLE();
3178 continue;
3179 }
3180 m_ILCodePtr++;
3181 }
3182ExitEvalLoop:;
3183 INTERPLOG("DONE %d, %s\n", m_methInfo->m_stubNum, m_methInfo->m_methName);
3184 }
3185 EX_CATCH
3186 {
3187 INTERPLOG("EXCEPTION %d (throw), %s\n", m_methInfo->m_stubNum, m_methInfo->m_methName);
3188
3189 bool handleException = false;
3190 OBJECTREF orThrowable = NULL;
3191 GCX_COOP_NO_DTOR();
3192
3193 orThrowable = GET_THROWABLE();
3194
3195 if (m_filterNextScan != 0)
3196 {
3197 // We are in the middle of a filter scan and an exception is thrown inside
3198 // a filter. We are supposed to swallow it and assume the filter did not
3199 // handle the exception.
3200 m_curStackHt = 0;
3201 m_largeStructOperandStackHt = 0;
3202 LdIcon(0);
3203 EndFilter();
3204 handleException = true;
3205 }
3206 else
3207 {
3208 // orThrowable must be protected. MethodHandlesException() will place orThrowable
3209 // into the operand stack (a permanently protected area) if it returns true.
3210 GCPROTECT_BEGIN(orThrowable);
3211 handleException = MethodHandlesException(orThrowable);
3212 GCPROTECT_END();
3213 }
3214
3215 if (handleException)
3216 {
3217 GetThread()->SafeSetThrowables(orThrowable
3218 DEBUG_ARG(ThreadExceptionState::STEC_CurrentTrackerEqualNullOkForInterpreter));
3219 goto EvalLoop;
3220 }
3221 else
3222 {
3223 INTERPLOG("EXCEPTION %d (rethrow), %s\n", m_methInfo->m_stubNum, m_methInfo->m_methName);
3224 EX_RETHROW;
3225 }
3226 }
3227 EX_END_CATCH(RethrowTransientExceptions)
3228}
3229
3230#ifdef _MSC_VER
3231#pragma optimize("", on)
3232#endif
3233
3234void Interpreter::EndFilter()
3235{
3236 unsigned handles = OpStackGet<unsigned>(0);
3237 // If the filter decides to handle the exception, then go to the handler offset.
3238 if (handles)
3239 {
3240 // We decided to handle the exception, so give all EH entries a chance to
3241 // handle future exceptions. Clear scan.
3242 m_filterNextScan = 0;
3243 ExecuteBranch(m_methInfo->m_ILCode + m_filterHandlerOffset);
3244 }
3245 // The filter decided not to handle the exception, ask if there is some other filter
3246 // lined up to try to handle it or some other catch/finally handlers will handle it.
3247 // If no one handles the exception, rethrow and be done with it.
3248 else
3249 {
3250 bool handlesEx = false;
3251 {
3252 OBJECTREF orThrowable = ObjectToOBJECTREF(m_inFlightException);
3253 GCPROTECT_BEGIN(orThrowable);
3254 handlesEx = MethodHandlesException(orThrowable);
3255 GCPROTECT_END();
3256 }
3257 if (!handlesEx)
3258 {
3259 // Just clear scan before rethrowing to give any EH entry a chance to handle
3260 // the "rethrow".
3261 m_filterNextScan = 0;
3262 Object* filterException = NULL;
3263 {
3264 GCX_FORBID();
3265 assert(m_inFlightException != NULL);
3266 filterException = m_inFlightException;
3267 INTERPLOG("endfilter handling for %s, %p, %p\n", m_methInfo->m_methName, m_methInfo, filterException);
3268 m_inFlightException = NULL;
3269 }
3270
3271 COMPlusThrow(ObjectToOBJECTREF(filterException));
3272 UNREACHABLE();
3273 }
3274 else
3275 {
3276 // Let it do another round of filter:end-filter or handler block.
3277 // During the next end filter, we will reuse m_filterNextScan and
3278 // continue searching where we left off. Note however, while searching,
3279 // any of the filters could throw an exception. But this is supposed to
3280 // be swallowed and endfilter should be called with a value of 0 on the
3281 // stack.
3282 }
3283 }
3284}
3285
3286bool Interpreter::MethodHandlesException(OBJECTREF orThrowable)
3287{
3288 CONTRACTL {
3289 SO_TOLERANT;
3290 THROWS;
3291 GC_TRIGGERS;
3292 MODE_COOPERATIVE;
3293 } CONTRACTL_END;
3294
3295 bool handlesEx = false;
3296
3297 if (orThrowable != NULL)
3298 {
3299 PTR_Thread pCurThread = GetThread();
3300
3301 // Don't catch ThreadAbort and other uncatchable exceptions
3302 if (!IsUncatchable(&orThrowable))
3303 {
3304 // Does the current method catch this? The clauses are defined by offsets, so get that.
3305 // However, if we are in the middle of a filter scan, make sure we get the offset of the
3306 // excepting code, rather than the offset of the filter body.
3307 DWORD curOffset = (m_filterNextScan != 0) ? m_filterExcILOffset : CurOffset();
3308 TypeHandle orThrowableTH = TypeHandle(orThrowable->GetMethodTable());
3309
3310 GCPROTECT_BEGIN(orThrowable);
3311 GCX_PREEMP();
3312
3313 // Perform a filter scan or regular walk of the EH Table. Filter scan is performed when
3314 // we are evaluating a series of filters to handle the exception until the first handler
3315 // (filter's or otherwise) that will handle the exception.
3316 for (unsigned XTnum = m_filterNextScan; XTnum < m_methInfo->m_ehClauseCount; XTnum++)
3317 {
3318 CORINFO_EH_CLAUSE clause;
3319 m_interpCeeInfo.getEHinfo(m_methInfo->m_method, XTnum, &clause);
3320 assert(clause.HandlerLength != (unsigned)-1); // @DEPRECATED
3321
3322 // First, is the current offset in the try block?
3323 if (clause.TryOffset <= curOffset && curOffset < clause.TryOffset + clause.TryLength)
3324 {
3325 unsigned handlerOffset = 0;
3326 // CORINFO_EH_CLAUSE_NONE represents 'catch' blocks
3327 if (clause.Flags == CORINFO_EH_CLAUSE_NONE)
3328 {
3329 // Now, does the catch block handle the thrown exception type?
3330 CORINFO_CLASS_HANDLE excType = FindClass(clause.ClassToken InterpTracingArg(RTK_CheckHandlesException));
3331 if (ExceptionIsOfRightType(TypeHandle::FromPtr(excType), orThrowableTH))
3332 {
3333 GCX_COOP();
3334 // Push the exception object onto the operand stack.
3335 OpStackSet<OBJECTREF>(0, orThrowable);
3336 OpStackTypeSet(0, InterpreterType(CORINFO_TYPE_CLASS));
3337 m_curStackHt = 1;
3338 m_largeStructOperandStackHt = 0;
3339 handlerOffset = clause.HandlerOffset;
3340 handlesEx = true;
3341 m_filterNextScan = 0;
3342 }
3343 else
3344 {
3345 GCX_COOP();
3346 // Handle a wrapped exception.
3347 OBJECTREF orUnwrapped = PossiblyUnwrapThrowable(orThrowable, GetMethodDesc()->GetAssembly());
3348 if (ExceptionIsOfRightType(TypeHandle::FromPtr(excType), orUnwrapped->GetTrueTypeHandle()))
3349 {
3350 // Push the exception object onto the operand stack.
3351 OpStackSet<OBJECTREF>(0, orUnwrapped);
3352 OpStackTypeSet(0, InterpreterType(CORINFO_TYPE_CLASS));
3353 m_curStackHt = 1;
3354 m_largeStructOperandStackHt = 0;
3355 handlerOffset = clause.HandlerOffset;
3356 handlesEx = true;
3357 m_filterNextScan = 0;
3358 }
3359 }
3360 }
3361 else if (clause.Flags == CORINFO_EH_CLAUSE_FILTER)
3362 {
3363 GCX_COOP();
3364 // Push the exception object onto the operand stack.
3365 OpStackSet<OBJECTREF>(0, orThrowable);
3366 OpStackTypeSet(0, InterpreterType(CORINFO_TYPE_CLASS));
3367 m_curStackHt = 1;
3368 m_largeStructOperandStackHt = 0;
3369 handlerOffset = clause.FilterOffset;
3370 m_inFlightException = OBJECTREFToObject(orThrowable);
3371 handlesEx = true;
3372 m_filterHandlerOffset = clause.HandlerOffset;
3373 m_filterNextScan = XTnum + 1;
3374 m_filterExcILOffset = curOffset;
3375 }
3376 else if (clause.Flags == CORINFO_EH_CLAUSE_FAULT ||
3377 clause.Flags == CORINFO_EH_CLAUSE_FINALLY)
3378 {
3379 GCX_COOP();
3380 // Save the exception object to rethrow.
3381 m_inFlightException = OBJECTREFToObject(orThrowable);
3382 // Empty the operand stack.
3383 m_curStackHt = 0;
3384 m_largeStructOperandStackHt = 0;
3385 handlerOffset = clause.HandlerOffset;
3386 handlesEx = true;
3387 m_filterNextScan = 0;
3388 }
3389
3390 // Reset the interpreter loop in preparation of calling the handler.
3391 if (handlesEx)
3392 {
3393 // Set the IL offset of the handler.
3394 ExecuteBranch(m_methInfo->m_ILCode + handlerOffset);
3395
3396 // If an exception occurs while attempting to leave a protected scope,
3397 // we empty the 'leave' info stack upon entering the handler.
3398 while (!m_leaveInfoStack.IsEmpty())
3399 {
3400 m_leaveInfoStack.Pop();
3401 }
3402
3403 // Some things are set up before a call, and must be cleared on an exception caught be the caller.
3404 // A method that returns a struct allocates local space for the return value, and "registers" that
3405 // space and the type so that it's scanned if a GC happens. "Unregister" it if we throw an exception
3406 // in the call, and handle it in the caller. (If it's not handled by the caller, the Interpreter is
3407 // deallocated, so it's value doesn't matter.)
3408 m_structRetValITPtr = NULL;
3409 m_callThisArg = NULL;
3410 m_argsSize = 0;
3411
3412 break;
3413 }
3414 }
3415 }
3416 GCPROTECT_END();
3417 }
3418 if (!handlesEx)
3419 {
3420 DoMonitorExitWork();
3421 }
3422 }
3423 return handlesEx;
3424}
3425
3426static unsigned OpFormatExtraSize(opcode_format_t format) {
3427 switch (format)
3428 {
3429 case InlineNone:
3430 return 0;
3431 case InlineVar:
3432 return 2;
3433 case InlineI:
3434 case InlineBrTarget:
3435 case InlineMethod:
3436 case InlineField:
3437 case InlineType:
3438 case InlineString:
3439 case InlineSig:
3440 case InlineRVA:
3441 case InlineTok:
3442 case ShortInlineR:
3443 return 4;
3444
3445 case InlineR:
3446 case InlineI8:
3447 return 8;
3448
3449 case InlineSwitch:
3450 return 0; // We'll handle this specially.
3451
3452 case ShortInlineVar:
3453 case ShortInlineI:
3454 case ShortInlineBrTarget:
3455 return 1;
3456
3457 default:
3458 assert(false);
3459 return 0;
3460 }
3461}
3462
3463
3464
3465static unsigned opSizes1Byte[CEE_COUNT];
3466static bool opSizes1ByteInit = false;
3467
3468static void OpSizes1ByteInit()
3469{
3470 if (opSizes1ByteInit) return;
3471#define OPDEF(name, stringname, stackpop, stackpush, params, kind, len, byte1, byte2, ctrl) \
3472 opSizes1Byte[name] = len + OpFormatExtraSize(params);
3473#include "opcode.def"
3474#undef OPDEF
3475 opSizes1ByteInit = true;
3476};
3477
3478// static
3479bool Interpreter::MethodMayHaveLoop(BYTE* ilCode, unsigned codeSize)
3480{
3481 OpSizes1ByteInit();
3482 int delta;
3483 BYTE* ilCodeLim = ilCode + codeSize;
3484 while (ilCode < ilCodeLim)
3485 {
3486 unsigned op = *ilCode;
3487 switch (op)
3488 {
3489 case CEE_BR_S: case CEE_BRFALSE_S: case CEE_BRTRUE_S:
3490 case CEE_BEQ_S: case CEE_BGE_S: case CEE_BGT_S: case CEE_BLE_S: case CEE_BLT_S:
3491 case CEE_BNE_UN_S: case CEE_BGE_UN_S: case CEE_BGT_UN_S: case CEE_BLE_UN_S: case CEE_BLT_UN_S:
3492 case CEE_LEAVE_S:
3493 delta = getI1(ilCode + 1);
3494 if (delta < 0) return true;
3495 ilCode += 2;
3496 break;
3497
3498 case CEE_BR: case CEE_BRFALSE: case CEE_BRTRUE:
3499 case CEE_BEQ: case CEE_BGE: case CEE_BGT: case CEE_BLE: case CEE_BLT:
3500 case CEE_BNE_UN: case CEE_BGE_UN: case CEE_BGT_UN: case CEE_BLE_UN: case CEE_BLT_UN:
3501 case CEE_LEAVE:
3502 delta = getI4LittleEndian(ilCode + 1);
3503 if (delta < 0) return true;
3504 ilCode += 5;
3505 break;
3506
3507 case CEE_SWITCH:
3508 {
3509 UINT32 n = getU4LittleEndian(ilCode + 1);
3510 UINT32 instrSize = 1 + (n + 1)*4;
3511 for (unsigned i = 0; i < n; i++) {
3512 delta = getI4LittleEndian(ilCode + (5 + i * 4));
3513 if (delta < 0) return true;
3514 }
3515 ilCode += instrSize;
3516 break;
3517 }
3518
3519 case CEE_PREFIX1:
3520 op = *(ilCode + 1) + 0x100;
3521 assert(op < CEE_COUNT); // Bounds check for below.
3522 // deliberate fall-through here.
3523 default:
3524 // For the rest of the 1-byte instructions, we'll use a table-driven approach.
3525 ilCode += opSizes1Byte[op];
3526 break;
3527 }
3528 }
3529 return false;
3530
3531}
3532
3533void Interpreter::BackwardsBranchActions(int offset)
3534{
3535 // TODO: Figure out how to do a GC poll.
3536}
3537
3538bool Interpreter::SearchForCoveringFinally()
3539{
3540 CONTRACTL {
3541 SO_TOLERANT;
3542 THROWS;
3543 GC_TRIGGERS;
3544 MODE_ANY;
3545 } CONTRACTL_END;
3546
3547 _ASSERTE_MSG(!m_leaveInfoStack.IsEmpty(), "precondition");
3548
3549 LeaveInfo& li = m_leaveInfoStack.PeekRef();
3550
3551 GCX_PREEMP();
3552
3553 for (unsigned XTnum = li.m_nextEHIndex; XTnum < m_methInfo->m_ehClauseCount; XTnum++)
3554 {
3555 CORINFO_EH_CLAUSE clause;
3556 m_interpCeeInfo.getEHinfo(m_methInfo->m_method, XTnum, &clause);
3557 assert(clause.HandlerLength != (unsigned)-1); // @DEPRECATED
3558
3559 // First, is the offset of the leave instruction in the try block?
3560 unsigned tryEndOffset = clause.TryOffset + clause.TryLength;
3561 if (clause.TryOffset <= li.m_offset && li.m_offset < tryEndOffset)
3562 {
3563 // Yes: is it a finally, and is its target outside the try block?
3564 size_t targOffset = (li.m_target - m_methInfo->m_ILCode);
3565 if (clause.Flags == CORINFO_EH_CLAUSE_FINALLY
3566 && !(clause.TryOffset <= targOffset && targOffset < tryEndOffset))
3567 {
3568 m_ILCodePtr = m_methInfo->m_ILCode + clause.HandlerOffset;
3569 li.m_nextEHIndex = XTnum + 1;
3570 return true;
3571 }
3572 }
3573 }
3574
3575 // Caller will handle popping the leave info stack.
3576 return false;
3577}
3578
3579// static
3580void Interpreter::GCScanRoots(promote_func* pf, ScanContext* sc, void* interp0)
3581{
3582 Interpreter* interp = reinterpret_cast<Interpreter*>(interp0);
3583 interp->GCScanRoots(pf, sc);
3584}
3585
3586void Interpreter::GCScanRoots(promote_func* pf, ScanContext* sc)
3587{
3588 // Report inbound arguments, if the interpreter has not been invoked directly.
3589 // (In the latter case, the arguments are reported by the calling method.)
3590 if (!m_directCall)
3591 {
3592 for (unsigned i = 0; i < m_methInfo->m_numArgs; i++)
3593 {
3594 GCScanRootAtLoc(reinterpret_cast<Object**>(GetArgAddr(i)), GetArgType(i), pf, sc);
3595 }
3596 }
3597
3598 if (m_methInfo->GetFlag<InterpreterMethodInfo::Flag_hasThisArg>())
3599 {
3600 if (m_methInfo->GetFlag<InterpreterMethodInfo::Flag_thisArgIsObjPtr>())
3601 {
3602 GCScanRootAtLoc(&m_thisArg, InterpreterType(CORINFO_TYPE_CLASS), pf, sc);
3603 }
3604 else
3605 {
3606 GCScanRootAtLoc(&m_thisArg, InterpreterType(CORINFO_TYPE_BYREF), pf, sc);
3607 }
3608 }
3609
3610 // This is the "this" argument passed in to DoCallWork. (Note that we treat this as a byref; it
3611 // might be, for a struct instance method, and this covers the object pointer case as well.)
3612 GCScanRootAtLoc(reinterpret_cast<Object**>(&m_callThisArg), InterpreterType(CORINFO_TYPE_BYREF), pf, sc);
3613
3614 // Scan the exception object that we'll rethrow at the end of the finally block.
3615 GCScanRootAtLoc(reinterpret_cast<Object**>(&m_inFlightException), InterpreterType(CORINFO_TYPE_CLASS), pf, sc);
3616
3617 // A retBufArg, may, in some cases, be a byref into the heap.
3618 if (m_retBufArg != NULL)
3619 {
3620 GCScanRootAtLoc(reinterpret_cast<Object**>(&m_retBufArg), InterpreterType(CORINFO_TYPE_BYREF), pf, sc);
3621 }
3622
3623 if (m_structRetValITPtr != NULL)
3624 {
3625 GCScanRootAtLoc(reinterpret_cast<Object**>(m_structRetValTempSpace), *m_structRetValITPtr, pf, sc);
3626 }
3627
3628 // We'll conservatively assume that we might have a security object.
3629 GCScanRootAtLoc(reinterpret_cast<Object**>(&m_securityObject), InterpreterType(CORINFO_TYPE_CLASS), pf, sc);
3630
3631 // Do locals.
3632 for (unsigned i = 0; i < m_methInfo->m_numLocals; i++)
3633 {
3634 InterpreterType it = m_methInfo->m_localDescs[i].m_type;
3635 void* localPtr = NULL;
3636 if (it.IsLargeStruct(&m_interpCeeInfo))
3637 {
3638 void* structPtr = ArgSlotEndianessFixup(reinterpret_cast<ARG_SLOT*>(FixedSizeLocalSlot(i)), sizeof(void**));
3639 localPtr = *reinterpret_cast<void**>(structPtr);
3640 }
3641 else
3642 {
3643 localPtr = ArgSlotEndianessFixup(reinterpret_cast<ARG_SLOT*>(FixedSizeLocalSlot(i)), it.Size(&m_interpCeeInfo));
3644 }
3645 GCScanRootAtLoc(reinterpret_cast<Object**>(localPtr), it, pf, sc, m_methInfo->GetPinningBit(i));
3646 }
3647
3648 // Do current ostack.
3649 for (unsigned i = 0; i < m_curStackHt; i++)
3650 {
3651 InterpreterType it = OpStackTypeGet(i);
3652 if (it.IsLargeStruct(&m_interpCeeInfo))
3653 {
3654 Object** structPtr = reinterpret_cast<Object**>(OpStackGet<void*>(i));
3655 // If the ostack value is a pointer to a local var value, don't scan, since we already
3656 // scanned the variable value above.
3657 if (!IsInLargeStructLocalArea(structPtr))
3658 {
3659 GCScanRootAtLoc(structPtr, it, pf, sc);
3660 }
3661 }
3662 else
3663 {
3664 void* stackPtr = OpStackGetAddr(i, it.Size(&m_interpCeeInfo));
3665 GCScanRootAtLoc(reinterpret_cast<Object**>(stackPtr), it, pf, sc);
3666 }
3667 }
3668
3669 // Any outgoing arguments for a call in progress.
3670 for (unsigned i = 0; i < m_argsSize; i++)
3671 {
3672 // If a call has a large struct argument, we'll have pushed a pointer to the entry for that argument on the
3673 // largeStructStack of the current Interpreter. That will be scanned by the code above, so just skip it.
3674 InterpreterType undef(CORINFO_TYPE_UNDEF);
3675 InterpreterType it = m_argTypes[i];
3676 if (it != undef && !it.IsLargeStruct(&m_interpCeeInfo))
3677 {
3678 BYTE* argPtr = ArgSlotEndianessFixup(&m_args[i], it.Size(&m_interpCeeInfo));
3679 GCScanRootAtLoc(reinterpret_cast<Object**>(argPtr), it, pf, sc);
3680 }
3681 }
3682}
3683
3684void Interpreter::GCScanRootAtLoc(Object** loc, InterpreterType it, promote_func* pf, ScanContext* sc, bool pinningRef)
3685{
3686 switch (it.ToCorInfoType())
3687 {
3688 case CORINFO_TYPE_CLASS:
3689 case CORINFO_TYPE_STRING:
3690 {
3691 DWORD flags = 0;
3692 if (pinningRef) flags |= GC_CALL_PINNED;
3693 (*pf)(loc, sc, flags);
3694 }
3695 break;
3696
3697 case CORINFO_TYPE_BYREF:
3698 case CORINFO_TYPE_REFANY:
3699 {
3700 DWORD flags = GC_CALL_INTERIOR;
3701 if (pinningRef) flags |= GC_CALL_PINNED;
3702 (*pf)(loc, sc, flags);
3703 }
3704 break;
3705
3706 case CORINFO_TYPE_VALUECLASS:
3707 assert(!pinningRef);
3708 GCScanValueClassRootAtLoc(loc, it.ToClassHandle(), pf, sc);
3709 break;
3710
3711 default:
3712 assert(!pinningRef);
3713 break;
3714 }
3715}
3716
3717void Interpreter::GCScanValueClassRootAtLoc(Object** loc, CORINFO_CLASS_HANDLE valueClsHnd, promote_func* pf, ScanContext* sc)
3718{
3719 MethodTable* valClsMT = GetMethodTableFromClsHnd(valueClsHnd);
3720 ReportPointersFromValueType(pf, sc, valClsMT, loc);
3721}
3722
3723// Returns "true" iff "cit" is "stack-normal": all integer types with byte size less than 4
3724// are folded to CORINFO_TYPE_INT; all remaining unsigned types are folded to their signed counterparts.
3725bool IsStackNormalType(CorInfoType cit)
3726{
3727 LIMITED_METHOD_CONTRACT;
3728
3729 switch (cit)
3730 {
3731 case CORINFO_TYPE_UNDEF:
3732 case CORINFO_TYPE_VOID:
3733 case CORINFO_TYPE_BOOL:
3734 case CORINFO_TYPE_CHAR:
3735 case CORINFO_TYPE_BYTE:
3736 case CORINFO_TYPE_UBYTE:
3737 case CORINFO_TYPE_SHORT:
3738 case CORINFO_TYPE_USHORT:
3739 case CORINFO_TYPE_UINT:
3740 case CORINFO_TYPE_NATIVEUINT:
3741 case CORINFO_TYPE_ULONG:
3742 case CORINFO_TYPE_VAR:
3743 case CORINFO_TYPE_STRING:
3744 case CORINFO_TYPE_PTR:
3745 return false;
3746
3747 case CORINFO_TYPE_INT:
3748 case CORINFO_TYPE_NATIVEINT:
3749 case CORINFO_TYPE_BYREF:
3750 case CORINFO_TYPE_CLASS:
3751 case CORINFO_TYPE_LONG:
3752 case CORINFO_TYPE_VALUECLASS:
3753 case CORINFO_TYPE_REFANY:
3754 // I chose to consider both float and double stack-normal; together these comprise
3755 // the "F" type of the ECMA spec. This means I have to consider these to freely
3756 // interconvert.
3757 case CORINFO_TYPE_FLOAT:
3758 case CORINFO_TYPE_DOUBLE:
3759 return true;
3760
3761 default:
3762 UNREACHABLE();
3763 }
3764}
3765
3766CorInfoType CorInfoTypeStackNormalize(CorInfoType cit)
3767{
3768 LIMITED_METHOD_CONTRACT;
3769
3770 switch (cit)
3771 {
3772 case CORINFO_TYPE_UNDEF:
3773 return CORINFO_TYPE_UNDEF;
3774
3775 case CORINFO_TYPE_VOID:
3776 case CORINFO_TYPE_VAR:
3777 _ASSERTE_MSG(false, "Type that cannot be on the ostack.");
3778 return CORINFO_TYPE_UNDEF;
3779
3780 case CORINFO_TYPE_BOOL:
3781 case CORINFO_TYPE_CHAR:
3782 case CORINFO_TYPE_BYTE:
3783 case CORINFO_TYPE_UBYTE:
3784 case CORINFO_TYPE_SHORT:
3785 case CORINFO_TYPE_USHORT:
3786 case CORINFO_TYPE_UINT:
3787 return CORINFO_TYPE_INT;
3788
3789 case CORINFO_TYPE_NATIVEUINT:
3790 case CORINFO_TYPE_PTR:
3791 return CORINFO_TYPE_NATIVEINT;
3792
3793 case CORINFO_TYPE_ULONG:
3794 return CORINFO_TYPE_LONG;
3795
3796 case CORINFO_TYPE_STRING:
3797 return CORINFO_TYPE_CLASS;
3798
3799 case CORINFO_TYPE_INT:
3800 case CORINFO_TYPE_NATIVEINT:
3801 case CORINFO_TYPE_BYREF:
3802 case CORINFO_TYPE_CLASS:
3803 case CORINFO_TYPE_LONG:
3804 case CORINFO_TYPE_VALUECLASS:
3805 case CORINFO_TYPE_REFANY:
3806 // I chose to consider both float and double stack-normal; together these comprise
3807 // the "F" type of the ECMA spec. This means I have to consider these to freely
3808 // interconvert.
3809 case CORINFO_TYPE_FLOAT:
3810 case CORINFO_TYPE_DOUBLE:
3811 assert(IsStackNormalType(cit));
3812 return cit;
3813
3814 default:
3815 UNREACHABLE();
3816 }
3817}
3818
3819InterpreterType InterpreterType::StackNormalize() const
3820{
3821 LIMITED_METHOD_CONTRACT;
3822
3823 switch (ToCorInfoType())
3824 {
3825 case CORINFO_TYPE_BOOL:
3826 case CORINFO_TYPE_CHAR:
3827 case CORINFO_TYPE_BYTE:
3828 case CORINFO_TYPE_UBYTE:
3829 case CORINFO_TYPE_SHORT:
3830 case CORINFO_TYPE_USHORT:
3831 case CORINFO_TYPE_UINT:
3832 return InterpreterType(CORINFO_TYPE_INT);
3833
3834 case CORINFO_TYPE_NATIVEUINT:
3835 case CORINFO_TYPE_PTR:
3836 return InterpreterType(CORINFO_TYPE_NATIVEINT);
3837
3838 case CORINFO_TYPE_ULONG:
3839 return InterpreterType(CORINFO_TYPE_LONG);
3840
3841 case CORINFO_TYPE_STRING:
3842 return InterpreterType(CORINFO_TYPE_CLASS);
3843
3844 case CORINFO_TYPE_INT:
3845 case CORINFO_TYPE_NATIVEINT:
3846 case CORINFO_TYPE_BYREF:
3847 case CORINFO_TYPE_CLASS:
3848 case CORINFO_TYPE_LONG:
3849 case CORINFO_TYPE_VALUECLASS:
3850 case CORINFO_TYPE_REFANY:
3851 case CORINFO_TYPE_FLOAT:
3852 case CORINFO_TYPE_DOUBLE:
3853 return *const_cast<InterpreterType*>(this);
3854
3855 case CORINFO_TYPE_UNDEF:
3856 case CORINFO_TYPE_VOID:
3857 case CORINFO_TYPE_VAR:
3858 default:
3859 _ASSERTE_MSG(false, "should not reach here");
3860 return *const_cast<InterpreterType*>(this);
3861 }
3862}
3863
3864#ifdef _DEBUG
3865bool InterpreterType::MatchesWork(const InterpreterType it2, CEEInfo* info) const
3866{
3867 CONTRACTL {
3868 THROWS;
3869 GC_TRIGGERS;
3870 MODE_COOPERATIVE;
3871 } CONTRACTL_END;
3872
3873 if (*this == it2) return true;
3874
3875 // Otherwise...
3876 CorInfoType cit1 = ToCorInfoType();
3877 CorInfoType cit2 = it2.ToCorInfoType();
3878
3879 GCX_PREEMP();
3880
3881 // An approximation: valueclasses of the same size match.
3882 if (cit1 == CORINFO_TYPE_VALUECLASS &&
3883 cit2 == CORINFO_TYPE_VALUECLASS &&
3884 Size(info) == it2.Size(info))
3885 {
3886 return true;
3887 }
3888
3889 // NativeInt matches byref. (In unsafe code).
3890 if ((cit1 == CORINFO_TYPE_BYREF && cit2 == CORINFO_TYPE_NATIVEINT))
3891 return true;
3892
3893 // apparently the VM may do the optimization of reporting the return type of a method that
3894 // returns a struct of a single nativeint field *as* nativeint; and similarly with at least some other primitive types.
3895 // So weaken this check to allow that.
3896 // (The check is actually a little weaker still, since I don't want to crack the return type and make sure
3897 // that it has only a single nativeint member -- so I just ensure that the total size is correct).
3898 switch (cit1)
3899 {
3900 case CORINFO_TYPE_NATIVEINT:
3901 case CORINFO_TYPE_NATIVEUINT:
3902 assert(sizeof(NativeInt) == sizeof(NativeUInt));
3903 if (it2.Size(info) == sizeof(NativeInt))
3904 return true;
3905 break;
3906
3907 case CORINFO_TYPE_INT:
3908 case CORINFO_TYPE_UINT:
3909 assert(sizeof(INT32) == sizeof(UINT32));
3910 if (it2.Size(info) == sizeof(INT32))
3911 return true;
3912 break;
3913
3914 default:
3915 break;
3916 }
3917
3918 // See if the second is a value type synonym for a primitive.
3919 if (cit2 == CORINFO_TYPE_VALUECLASS)
3920 {
3921 CorInfoType cit2prim = info->getTypeForPrimitiveValueClass(it2.ToClassHandle());
3922 if (cit2prim != CORINFO_TYPE_UNDEF)
3923 {
3924 InterpreterType it2prim(cit2prim);
3925 if (*this == it2prim.StackNormalize())
3926 return true;
3927 }
3928 }
3929
3930 // Otherwise...
3931 return false;
3932}
3933#endif // _DEBUG
3934
3935// Static
3936size_t CorInfoTypeSizeArray[] =
3937{
3938 /*CORINFO_TYPE_UNDEF = 0x0*/0,
3939 /*CORINFO_TYPE_VOID = 0x1*/0,
3940 /*CORINFO_TYPE_BOOL = 0x2*/1,
3941 /*CORINFO_TYPE_CHAR = 0x3*/2,
3942 /*CORINFO_TYPE_BYTE = 0x4*/1,
3943 /*CORINFO_TYPE_UBYTE = 0x5*/1,
3944 /*CORINFO_TYPE_SHORT = 0x6*/2,
3945 /*CORINFO_TYPE_USHORT = 0x7*/2,
3946 /*CORINFO_TYPE_INT = 0x8*/4,
3947 /*CORINFO_TYPE_UINT = 0x9*/4,
3948 /*CORINFO_TYPE_LONG = 0xa*/8,
3949 /*CORINFO_TYPE_ULONG = 0xb*/8,
3950 /*CORINFO_TYPE_NATIVEINT = 0xc*/sizeof(void*),
3951 /*CORINFO_TYPE_NATIVEUINT = 0xd*/sizeof(void*),
3952 /*CORINFO_TYPE_FLOAT = 0xe*/4,
3953 /*CORINFO_TYPE_DOUBLE = 0xf*/8,
3954 /*CORINFO_TYPE_STRING = 0x10*/sizeof(void*),
3955 /*CORINFO_TYPE_PTR = 0x11*/sizeof(void*),
3956 /*CORINFO_TYPE_BYREF = 0x12*/sizeof(void*),
3957 /*CORINFO_TYPE_VALUECLASS = 0x13*/0,
3958 /*CORINFO_TYPE_CLASS = 0x14*/sizeof(void*),
3959 /*CORINFO_TYPE_REFANY = 0x15*/sizeof(void*)*2,
3960 /*CORINFO_TYPE_VAR = 0x16*/0,
3961};
3962
3963bool CorInfoTypeIsUnsigned(CorInfoType cit)
3964{
3965 LIMITED_METHOD_CONTRACT;
3966
3967 switch (cit)
3968 {
3969 case CORINFO_TYPE_UINT:
3970 case CORINFO_TYPE_NATIVEUINT:
3971 case CORINFO_TYPE_ULONG:
3972 case CORINFO_TYPE_UBYTE:
3973 case CORINFO_TYPE_USHORT:
3974 case CORINFO_TYPE_CHAR:
3975 return true;
3976
3977 default:
3978 return false;
3979 }
3980}
3981
3982bool CorInfoTypeIsIntegral(CorInfoType cit)
3983{
3984 LIMITED_METHOD_CONTRACT;
3985
3986 switch (cit)
3987 {
3988 case CORINFO_TYPE_UINT:
3989 case CORINFO_TYPE_NATIVEUINT:
3990 case CORINFO_TYPE_ULONG:
3991 case CORINFO_TYPE_UBYTE:
3992 case CORINFO_TYPE_USHORT:
3993 case CORINFO_TYPE_INT:
3994 case CORINFO_TYPE_NATIVEINT:
3995 case CORINFO_TYPE_LONG:
3996 case CORINFO_TYPE_BYTE:
3997 case CORINFO_TYPE_BOOL:
3998 case CORINFO_TYPE_SHORT:
3999 return true;
4000
4001 default:
4002 return false;
4003 }
4004}
4005
4006bool CorInfoTypeIsFloatingPoint(CorInfoType cit)
4007{
4008 return cit == CORINFO_TYPE_FLOAT || cit == CORINFO_TYPE_DOUBLE;
4009}
4010
4011
4012bool CorElemTypeIsUnsigned(CorElementType cet)
4013{
4014 LIMITED_METHOD_CONTRACT;
4015
4016 switch (cet)
4017 {
4018 case ELEMENT_TYPE_U1:
4019 case ELEMENT_TYPE_U2:
4020 case ELEMENT_TYPE_U4:
4021 case ELEMENT_TYPE_U8:
4022 case ELEMENT_TYPE_U:
4023 return true;
4024
4025 default:
4026 return false;
4027 }
4028}
4029
4030bool CorInfoTypeIsPointer(CorInfoType cit)
4031{
4032 LIMITED_METHOD_CONTRACT;
4033 switch (cit)
4034 {
4035 case CORINFO_TYPE_PTR:
4036 case CORINFO_TYPE_BYREF:
4037 case CORINFO_TYPE_NATIVEINT:
4038 case CORINFO_TYPE_NATIVEUINT:
4039 return true;
4040
4041 // It seems like the ECMA spec doesn't allow this, but (at least) the managed C++
4042 // compiler expects the explicitly-sized pointer type of the platform pointer size to work:
4043 case CORINFO_TYPE_INT:
4044 case CORINFO_TYPE_UINT:
4045 return sizeof(NativeInt) == sizeof(INT32);
4046 case CORINFO_TYPE_LONG:
4047 case CORINFO_TYPE_ULONG:
4048 return sizeof(NativeInt) == sizeof(INT64);
4049
4050 default:
4051 return false;
4052 }
4053}
4054
4055void Interpreter::LdArg(int argNum)
4056{
4057 CONTRACTL {
4058 SO_TOLERANT;
4059 THROWS;
4060 GC_TRIGGERS;
4061 MODE_COOPERATIVE;
4062 } CONTRACTL_END;
4063
4064 LdFromMemAddr(GetArgAddr(argNum), GetArgType(argNum));
4065}
4066
4067void Interpreter::LdArgA(int argNum)
4068{
4069 CONTRACTL {
4070 SO_TOLERANT;
4071 NOTHROW;
4072 GC_NOTRIGGER;
4073 MODE_COOPERATIVE;
4074 } CONTRACTL_END;
4075
4076 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_BYREF));
4077 OpStackSet<void*>(m_curStackHt, reinterpret_cast<void*>(GetArgAddr(argNum)));
4078 m_curStackHt++;
4079}
4080
4081void Interpreter::StArg(int argNum)
4082{
4083 CONTRACTL {
4084 SO_TOLERANT;
4085 THROWS;
4086 GC_TRIGGERS;
4087 MODE_COOPERATIVE;
4088 } CONTRACTL_END;
4089
4090 StToLocalMemAddr(GetArgAddr(argNum), GetArgType(argNum));
4091}
4092
4093
4094void Interpreter::LdLocA(int locNum)
4095{
4096 CONTRACTL {
4097 SO_TOLERANT;
4098 NOTHROW;
4099 GC_NOTRIGGER;
4100 MODE_COOPERATIVE;
4101 } CONTRACTL_END;
4102
4103 InterpreterType tp = m_methInfo->m_localDescs[locNum].m_type;
4104 void* addr;
4105 if (tp.IsLargeStruct(&m_interpCeeInfo))
4106 {
4107 void* structPtr = ArgSlotEndianessFixup(reinterpret_cast<ARG_SLOT*>(FixedSizeLocalSlot(locNum)), sizeof(void**));
4108 addr = *reinterpret_cast<void**>(structPtr);
4109 }
4110 else
4111 {
4112 addr = ArgSlotEndianessFixup(reinterpret_cast<ARG_SLOT*>(FixedSizeLocalSlot(locNum)), tp.Size(&m_interpCeeInfo));
4113 }
4114 // The "addr" above, while a byref, is never a heap pointer, so we're robust if
4115 // any of these were to cause a GC.
4116 OpStackSet<void*>(m_curStackHt, addr);
4117 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_BYREF));
4118 m_curStackHt++;
4119}
4120
4121void Interpreter::LdIcon(INT32 c)
4122{
4123 CONTRACTL {
4124 SO_TOLERANT;
4125 NOTHROW;
4126 GC_NOTRIGGER;
4127 MODE_COOPERATIVE;
4128 } CONTRACTL_END;
4129
4130 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_INT));
4131 OpStackSet<INT32>(m_curStackHt, c);
4132 m_curStackHt++;
4133}
4134
4135void Interpreter::LdR4con(INT32 c)
4136{
4137 CONTRACTL {
4138 SO_TOLERANT;
4139 NOTHROW;
4140 GC_NOTRIGGER;
4141 MODE_COOPERATIVE;
4142 } CONTRACTL_END;
4143
4144 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_FLOAT));
4145 OpStackSet<INT32>(m_curStackHt, c);
4146 m_curStackHt++;
4147}
4148
4149void Interpreter::LdLcon(INT64 c)
4150{
4151 CONTRACTL {
4152 SO_TOLERANT;
4153 NOTHROW;
4154 GC_NOTRIGGER;
4155 MODE_COOPERATIVE;
4156 } CONTRACTL_END;
4157
4158 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_LONG));
4159 OpStackSet<INT64>(m_curStackHt, c);
4160 m_curStackHt++;
4161}
4162
4163void Interpreter::LdR8con(INT64 c)
4164{
4165 CONTRACTL {
4166 SO_TOLERANT;
4167 NOTHROW;
4168 GC_NOTRIGGER;
4169 MODE_COOPERATIVE;
4170 } CONTRACTL_END;
4171
4172 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_DOUBLE));
4173 OpStackSet<INT64>(m_curStackHt, c);
4174 m_curStackHt++;
4175}
4176
4177void Interpreter::LdNull()
4178{
4179 CONTRACTL {
4180 SO_TOLERANT;
4181 NOTHROW;
4182 GC_NOTRIGGER;
4183 MODE_COOPERATIVE;
4184 } CONTRACTL_END;
4185
4186 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_CLASS));
4187 OpStackSet<void*>(m_curStackHt, NULL);
4188 m_curStackHt++;
4189}
4190
4191template<typename T, CorInfoType cit>
4192void Interpreter::LdInd()
4193{
4194 assert(TOSIsPtr());
4195 assert(IsStackNormalType(cit));
4196 unsigned curStackInd = m_curStackHt-1;
4197 T* ptr = OpStackGet<T*>(curStackInd);
4198 ThrowOnInvalidPointer(ptr);
4199 OpStackSet<T>(curStackInd, *ptr);
4200 OpStackTypeSet(curStackInd, InterpreterType(cit));
4201 BarrierIfVolatile();
4202}
4203
4204template<typename T, bool isUnsigned>
4205void Interpreter::LdIndShort()
4206{
4207 assert(TOSIsPtr());
4208 assert(sizeof(T) < 4);
4209 unsigned curStackInd = m_curStackHt-1;
4210 T* ptr = OpStackGet<T*>(curStackInd);
4211 ThrowOnInvalidPointer(ptr);
4212 if (isUnsigned)
4213 {
4214 OpStackSet<UINT32>(curStackInd, *ptr);
4215 }
4216 else
4217 {
4218 OpStackSet<INT32>(curStackInd, *ptr);
4219 }
4220 // All short integers are normalized to INT as their stack type.
4221 OpStackTypeSet(curStackInd, InterpreterType(CORINFO_TYPE_INT));
4222 BarrierIfVolatile();
4223}
4224
4225template<typename T>
4226void Interpreter::StInd()
4227{
4228 assert(m_curStackHt >= 2);
4229 assert(CorInfoTypeIsPointer(OpStackTypeGet(m_curStackHt-2).ToCorInfoType()));
4230 BarrierIfVolatile();
4231 unsigned stackInd0 = m_curStackHt-2;
4232 unsigned stackInd1 = m_curStackHt-1;
4233 T val = OpStackGet<T>(stackInd1);
4234 T* ptr = OpStackGet<T*>(stackInd0);
4235 ThrowOnInvalidPointer(ptr);
4236 *ptr = val;
4237 m_curStackHt -= 2;
4238
4239#if INTERP_TRACING
4240 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL) &&
4241 IsInLocalArea(ptr))
4242 {
4243 PrintLocals();
4244 }
4245#endif // INTERP_TRACING
4246}
4247
4248void Interpreter::StInd_Ref()
4249{
4250 assert(m_curStackHt >= 2);
4251 assert(CorInfoTypeIsPointer(OpStackTypeGet(m_curStackHt-2).ToCorInfoType()));
4252 BarrierIfVolatile();
4253 unsigned stackInd0 = m_curStackHt-2;
4254 unsigned stackInd1 = m_curStackHt-1;
4255 OBJECTREF val = ObjectToOBJECTREF(OpStackGet<Object*>(stackInd1));
4256 OBJECTREF* ptr = OpStackGet<OBJECTREF*>(stackInd0);
4257 ThrowOnInvalidPointer(ptr);
4258 SetObjectReferenceUnchecked(ptr, val);
4259 m_curStackHt -= 2;
4260
4261#if INTERP_TRACING
4262 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL) &&
4263 IsInLocalArea(ptr))
4264 {
4265 PrintLocals();
4266 }
4267#endif // INTERP_TRACING
4268}
4269
4270
4271template<int op>
4272void Interpreter::BinaryArithOp()
4273{
4274 CONTRACTL {
4275 SO_TOLERANT;
4276 THROWS;
4277 GC_TRIGGERS;
4278 MODE_COOPERATIVE;
4279 } CONTRACTL_END;
4280
4281 assert(m_curStackHt >= 2);
4282 unsigned op1idx = m_curStackHt - 2;
4283 unsigned op2idx = m_curStackHt - 1;
4284 InterpreterType t1 = OpStackTypeGet(op1idx);
4285 assert(IsStackNormalType(t1.ToCorInfoType()));
4286 // Looking at the generated code, it does seem to save some instructions to use the "shifted
4287 // types," though the effect on end-to-end time is variable. So I'll leave it set.
4288 InterpreterType t2 = OpStackTypeGet(op2idx);
4289 assert(IsStackNormalType(t2.ToCorInfoType()));
4290
4291 // In all cases belows, since "op" is compile-time constant, "if" chains on it should fold away.
4292 switch (t1.ToCorInfoTypeShifted())
4293 {
4294 case CORINFO_TYPE_SHIFTED_INT:
4295 if (t1 == t2)
4296 {
4297 // Int op Int = Int
4298 INT32 val1 = OpStackGet<INT32>(op1idx);
4299 INT32 val2 = OpStackGet<INT32>(op2idx);
4300 BinaryArithOpWork<op, INT32, /*IsIntType*/true, CORINFO_TYPE_INT, /*TypeIsUnchanged*/true>(val1, val2);
4301 }
4302 else
4303 {
4304 CorInfoTypeShifted cits2 = t2.ToCorInfoTypeShifted();
4305 if (cits2 == CORINFO_TYPE_SHIFTED_NATIVEINT)
4306 {
4307 // Int op NativeInt = NativeInt
4308 NativeInt val1 = static_cast<NativeInt>(OpStackGet<INT32>(op1idx));
4309 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4310 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/false>(val1, val2);
4311 }
4312 else if (s_InterpreterLooseRules && cits2 == CORINFO_TYPE_SHIFTED_LONG)
4313 {
4314 // Int op Long = Long
4315 INT64 val1 = static_cast<INT64>(OpStackGet<INT32>(op1idx));
4316 INT64 val2 = OpStackGet<INT64>(op2idx);
4317 BinaryArithOpWork<op, INT64, /*IsIntType*/true, CORINFO_TYPE_LONG, /*TypeIsUnchanged*/false>(val1, val2);
4318 }
4319 else if (cits2 == CORINFO_TYPE_SHIFTED_BYREF)
4320 {
4321 if (op == BA_Add || (s_InterpreterLooseRules && op == BA_Sub))
4322 {
4323 // Int + ByRef = ByRef
4324 NativeInt val1 = static_cast<NativeInt>(OpStackGet<INT32>(op1idx));
4325 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4326 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/false>(val1, val2);
4327 }
4328 else
4329 {
4330 VerificationError("Operation not permitted on int and managed pointer.");
4331 }
4332 }
4333 else
4334 {
4335 VerificationError("Binary arithmetic operation type mismatch (int and ?)");
4336 }
4337 }
4338 break;
4339
4340 case CORINFO_TYPE_SHIFTED_NATIVEINT:
4341 {
4342 NativeInt val1 = OpStackGet<NativeInt>(op1idx);
4343 if (t1 == t2)
4344 {
4345 // NativeInt op NativeInt = NativeInt
4346 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4347 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4348 }
4349 else
4350 {
4351 CorInfoTypeShifted cits2 = t2.ToCorInfoTypeShifted();
4352 if (cits2 == CORINFO_TYPE_SHIFTED_INT)
4353 {
4354 // NativeInt op Int = NativeInt
4355 NativeInt val2 = static_cast<NativeInt>(OpStackGet<INT32>(op2idx));
4356 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4357 }
4358 // CLI spec does not allow adding a native int and an int64. So use loose rules.
4359 else if (s_InterpreterLooseRules && cits2 == CORINFO_TYPE_SHIFTED_LONG)
4360 {
4361 // NativeInt op Int = NativeInt
4362 NativeInt val2 = static_cast<NativeInt>(OpStackGet<INT64>(op2idx));
4363 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4364 }
4365 else if (cits2 == CORINFO_TYPE_SHIFTED_BYREF)
4366 {
4367 if (op == BA_Add || (s_InterpreterLooseRules && op == BA_Sub))
4368 {
4369 // NativeInt + ByRef = ByRef
4370 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4371 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/false>(val1, val2);
4372 }
4373 else
4374 {
4375 VerificationError("Operation not permitted on native int and managed pointer.");
4376 }
4377 }
4378 else
4379 {
4380 VerificationError("Binary arithmetic operation type mismatch (native int and ?)");
4381 }
4382 }
4383 }
4384 break;
4385
4386 case CORINFO_TYPE_SHIFTED_LONG:
4387 {
4388 bool looseLong = false;
4389#if defined(_AMD64_)
4390 looseLong = (s_InterpreterLooseRules && (t2.ToCorInfoType() == CORINFO_TYPE_NATIVEINT ||
4391 t2.ToCorInfoType() == CORINFO_TYPE_BYREF));
4392#endif
4393 if (t1 == t2 || looseLong)
4394 {
4395 // Long op Long = Long
4396 INT64 val1 = OpStackGet<INT64>(op1idx);
4397 INT64 val2 = OpStackGet<INT64>(op2idx);
4398 BinaryArithOpWork<op, INT64, /*IsIntType*/true, CORINFO_TYPE_LONG, /*TypeIsUnchanged*/true>(val1, val2);
4399 }
4400 else
4401 {
4402 VerificationError("Binary arithmetic operation type mismatch (long and ?)");
4403 }
4404 }
4405 break;
4406
4407 case CORINFO_TYPE_SHIFTED_FLOAT:
4408 {
4409 if (t1 == t2)
4410 {
4411 // Float op Float = Float
4412 float val1 = OpStackGet<float>(op1idx);
4413 float val2 = OpStackGet<float>(op2idx);
4414 BinaryArithOpWork<op, float, /*IsIntType*/false, CORINFO_TYPE_FLOAT, /*TypeIsUnchanged*/true>(val1, val2);
4415 }
4416 else
4417 {
4418 CorInfoTypeShifted cits2 = t2.ToCorInfoTypeShifted();
4419 if (cits2 == CORINFO_TYPE_SHIFTED_DOUBLE)
4420 {
4421 // Float op Double = Double
4422 double val1 = static_cast<double>(OpStackGet<float>(op1idx));
4423 double val2 = OpStackGet<double>(op2idx);
4424 BinaryArithOpWork<op, double, /*IsIntType*/false, CORINFO_TYPE_DOUBLE, /*TypeIsUnchanged*/false>(val1, val2);
4425 }
4426 else
4427 {
4428 VerificationError("Binary arithmetic operation type mismatch (float and ?)");
4429 }
4430 }
4431 }
4432 break;
4433
4434 case CORINFO_TYPE_SHIFTED_DOUBLE:
4435 {
4436 if (t1 == t2)
4437 {
4438 // Double op Double = Double
4439 double val1 = OpStackGet<double>(op1idx);
4440 double val2 = OpStackGet<double>(op2idx);
4441 BinaryArithOpWork<op, double, /*IsIntType*/false, CORINFO_TYPE_DOUBLE, /*TypeIsUnchanged*/true>(val1, val2);
4442 }
4443 else
4444 {
4445 CorInfoTypeShifted cits2 = t2.ToCorInfoTypeShifted();
4446 if (cits2 == CORINFO_TYPE_SHIFTED_FLOAT)
4447 {
4448 // Double op Float = Double
4449 double val1 = OpStackGet<double>(op1idx);
4450 double val2 = static_cast<double>(OpStackGet<float>(op2idx));
4451 BinaryArithOpWork<op, double, /*IsIntType*/false, CORINFO_TYPE_DOUBLE, /*TypeIsUnchanged*/true>(val1, val2);
4452 }
4453 else
4454 {
4455 VerificationError("Binary arithmetic operation type mismatch (double and ?)");
4456 }
4457 }
4458 }
4459 break;
4460
4461 case CORINFO_TYPE_SHIFTED_BYREF:
4462 {
4463 NativeInt val1 = OpStackGet<NativeInt>(op1idx);
4464 CorInfoTypeShifted cits2 = t2.ToCorInfoTypeShifted();
4465 if (cits2 == CORINFO_TYPE_SHIFTED_INT)
4466 {
4467 if (op == BA_Add || op == BA_Sub)
4468 {
4469 // ByRef +- Int = ByRef
4470 NativeInt val2 = static_cast<NativeInt>(OpStackGet<INT32>(op2idx));
4471 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/true>(val1, val2);
4472 }
4473 else
4474 {
4475 VerificationError("May only add/subtract managed pointer and integral value.");
4476 }
4477 }
4478 else if (cits2 == CORINFO_TYPE_SHIFTED_NATIVEINT)
4479 {
4480 if (op == BA_Add || op == BA_Sub)
4481 {
4482 // ByRef +- NativeInt = ByRef
4483 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4484 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/true>(val1, val2);
4485 }
4486 else
4487 {
4488 VerificationError("May only add/subtract managed pointer and integral value.");
4489 }
4490 }
4491 else if (cits2 == CORINFO_TYPE_SHIFTED_BYREF)
4492 {
4493 if (op == BA_Sub)
4494 {
4495 // ByRef - ByRef = NativeInt
4496 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4497 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/false>(val1, val2);
4498 }
4499 else
4500 {
4501 VerificationError("May only subtract managed pointer values.");
4502 }
4503 }
4504 // CLI spec does not allow adding a native int and an int64. So use loose rules.
4505 else if (s_InterpreterLooseRules && cits2 == CORINFO_TYPE_SHIFTED_LONG)
4506 {
4507 // NativeInt op Int = NativeInt
4508 NativeInt val2 = static_cast<NativeInt>(OpStackGet<INT64>(op2idx));
4509 BinaryArithOpWork<op, NativeInt, /*IsIntType*/true, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4510 }
4511 else
4512 {
4513 VerificationError("Binary arithmetic operation not permitted on byref");
4514 }
4515 }
4516 break;
4517
4518 case CORINFO_TYPE_SHIFTED_CLASS:
4519 VerificationError("Can't do binary arithmetic on object references.");
4520 break;
4521
4522 default:
4523 _ASSERTE_MSG(false, "Non-stack-normal type on stack.");
4524 }
4525
4526 // In all cases:
4527 m_curStackHt--;
4528}
4529
4530template<int op, bool asUnsigned>
4531void Interpreter::BinaryArithOvfOp()
4532{
4533 CONTRACTL {
4534 SO_TOLERANT;
4535 THROWS;
4536 GC_TRIGGERS;
4537 MODE_COOPERATIVE;
4538 } CONTRACTL_END;
4539
4540 assert(m_curStackHt >= 2);
4541 unsigned op1idx = m_curStackHt - 2;
4542 unsigned op2idx = m_curStackHt - 1;
4543
4544 InterpreterType t1 = OpStackTypeGet(op1idx);
4545 CorInfoType cit1 = t1.ToCorInfoType();
4546 assert(IsStackNormalType(cit1));
4547
4548 InterpreterType t2 = OpStackTypeGet(op2idx);
4549 CorInfoType cit2 = t2.ToCorInfoType();
4550 assert(IsStackNormalType(cit2));
4551
4552 // In all cases belows, since "op" is compile-time constant, "if" chains on it should fold away.
4553 switch (cit1)
4554 {
4555 case CORINFO_TYPE_INT:
4556 if (cit2 == CORINFO_TYPE_INT)
4557 {
4558 if (asUnsigned)
4559 {
4560 // UnsignedInt op UnsignedInt = UnsignedInt
4561 UINT32 val1 = OpStackGet<UINT32>(op1idx);
4562 UINT32 val2 = OpStackGet<UINT32>(op2idx);
4563 BinaryArithOvfOpWork<op, UINT32, CORINFO_TYPE_INT, /*TypeIsUnchanged*/true>(val1, val2);
4564 }
4565 else
4566 {
4567 // Int op Int = Int
4568 INT32 val1 = OpStackGet<INT32>(op1idx);
4569 INT32 val2 = OpStackGet<INT32>(op2idx);
4570 BinaryArithOvfOpWork<op, INT32, CORINFO_TYPE_INT, /*TypeIsUnchanged*/true>(val1, val2);
4571 }
4572 }
4573 else if (cit2 == CORINFO_TYPE_NATIVEINT)
4574 {
4575 if (asUnsigned)
4576 {
4577 // UnsignedInt op UnsignedNativeInt = UnsignedNativeInt
4578 NativeUInt val1 = static_cast<NativeUInt>(OpStackGet<UINT32>(op1idx));
4579 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4580 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/false>(val1, val2);
4581 }
4582 else
4583 {
4584 // Int op NativeInt = NativeInt
4585 NativeInt val1 = static_cast<NativeInt>(OpStackGet<INT32>(op1idx));
4586 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4587 BinaryArithOvfOpWork<op, NativeInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/false>(val1, val2);
4588 }
4589 }
4590 else if (cit2 == CORINFO_TYPE_BYREF)
4591 {
4592 if (asUnsigned && op == BA_Add)
4593 {
4594 // UnsignedInt + ByRef = ByRef
4595 NativeUInt val1 = static_cast<NativeUInt>(OpStackGet<UINT32>(op1idx));
4596 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4597 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/false>(val1, val2);
4598 }
4599 else
4600 {
4601 VerificationError("Illegal arithmetic overflow operation for int and byref.");
4602 }
4603 }
4604 else
4605 {
4606 VerificationError("Binary arithmetic overflow operation type mismatch (int and ?)");
4607 }
4608 break;
4609
4610 case CORINFO_TYPE_NATIVEINT:
4611 if (cit2 == CORINFO_TYPE_INT)
4612 {
4613 if (asUnsigned)
4614 {
4615 // UnsignedNativeInt op UnsignedInt = UnsignedNativeInt
4616 NativeUInt val1 = OpStackGet<NativeUInt>(op1idx);
4617 NativeUInt val2 = static_cast<NativeUInt>(OpStackGet<UINT32>(op2idx));
4618 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4619 }
4620 else
4621 {
4622 // NativeInt op Int = NativeInt
4623 NativeInt val1 = OpStackGet<NativeInt>(op1idx);
4624 NativeInt val2 = static_cast<NativeInt>(OpStackGet<INT32>(op2idx));
4625 BinaryArithOvfOpWork<op, NativeInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4626 }
4627 }
4628 else if (cit2 == CORINFO_TYPE_NATIVEINT)
4629 {
4630 if (asUnsigned)
4631 {
4632 // UnsignedNativeInt op UnsignedNativeInt = UnsignedNativeInt
4633 NativeUInt val1 = OpStackGet<NativeUInt>(op1idx);
4634 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4635 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4636 }
4637 else
4638 {
4639 // NativeInt op NativeInt = NativeInt
4640 NativeInt val1 = OpStackGet<NativeInt>(op1idx);
4641 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
4642 BinaryArithOvfOpWork<op, NativeInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4643 }
4644 }
4645 else if (cit2 == CORINFO_TYPE_BYREF)
4646 {
4647 if (asUnsigned && op == BA_Add)
4648 {
4649 // UnsignedNativeInt op ByRef = ByRef
4650 NativeUInt val1 = OpStackGet<UINT32>(op1idx);
4651 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4652 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/false>(val1, val2);
4653 }
4654 else
4655 {
4656 VerificationError("Illegal arithmetic overflow operation for native int and byref.");
4657 }
4658 }
4659 else
4660 {
4661 VerificationError("Binary arithmetic overflow operation type mismatch (native int and ?)");
4662 }
4663 break;
4664
4665 case CORINFO_TYPE_LONG:
4666 if (cit2 == CORINFO_TYPE_LONG || (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_NATIVEINT))
4667 {
4668 if (asUnsigned)
4669 {
4670 // UnsignedLong op UnsignedLong = UnsignedLong
4671 UINT64 val1 = OpStackGet<UINT64>(op1idx);
4672 UINT64 val2 = OpStackGet<UINT64>(op2idx);
4673 BinaryArithOvfOpWork<op, UINT64, CORINFO_TYPE_LONG, /*TypeIsUnchanged*/true>(val1, val2);
4674 }
4675 else
4676 {
4677 // Long op Long = Long
4678 INT64 val1 = OpStackGet<INT64>(op1idx);
4679 INT64 val2 = OpStackGet<INT64>(op2idx);
4680 BinaryArithOvfOpWork<op, INT64, CORINFO_TYPE_LONG, /*TypeIsUnchanged*/true>(val1, val2);
4681 }
4682 }
4683 else
4684 {
4685 VerificationError("Binary arithmetic overflow operation type mismatch (long and ?)");
4686 }
4687 break;
4688
4689 case CORINFO_TYPE_BYREF:
4690 if (asUnsigned && (op == BA_Add || op == BA_Sub))
4691 {
4692 NativeUInt val1 = OpStackGet<NativeUInt>(op1idx);
4693 if (cit2 == CORINFO_TYPE_INT)
4694 {
4695 // ByRef +- UnsignedInt = ByRef
4696 NativeUInt val2 = static_cast<NativeUInt>(OpStackGet<INT32>(op2idx));
4697 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/true>(val1, val2);
4698 }
4699 else if (cit2 == CORINFO_TYPE_NATIVEINT)
4700 {
4701 // ByRef +- UnsignedNativeInt = ByRef
4702 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4703 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/true>(val1, val2);
4704 }
4705 else if (cit2 == CORINFO_TYPE_BYREF)
4706 {
4707 if (op == BA_Sub)
4708 {
4709 // ByRef - ByRef = UnsignedNativeInt
4710 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4711 BinaryArithOvfOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/false>(val1, val2);
4712 }
4713 else
4714 {
4715 VerificationError("Illegal arithmetic overflow operation for byref and byref: may only subtract managed pointer values.");
4716 }
4717 }
4718 else
4719 {
4720 VerificationError("Binary arithmetic overflow operation not permitted on byref");
4721 }
4722 }
4723 else
4724 {
4725 if (!asUnsigned)
4726 {
4727 VerificationError("Signed binary arithmetic overflow operation not permitted on managed pointer values.");
4728 }
4729 else
4730 {
4731 _ASSERTE_MSG(op == BA_Mul, "Must be an overflow operation; tested for Add || Sub above.");
4732 VerificationError("Cannot multiply managed pointer values.");
4733 }
4734 }
4735 break;
4736
4737 default:
4738 _ASSERTE_MSG(false, "Non-stack-normal type on stack.");
4739 }
4740
4741 // In all cases:
4742 m_curStackHt--;
4743}
4744
4745template<int op, typename T, CorInfoType cit, bool TypeIsUnchanged>
4746void Interpreter::BinaryArithOvfOpWork(T val1, T val2)
4747{
4748 CONTRACTL {
4749 SO_TOLERANT;
4750 THROWS;
4751 GC_TRIGGERS;
4752 MODE_COOPERATIVE;
4753 } CONTRACTL_END;
4754
4755 ClrSafeInt<T> res;
4756 ClrSafeInt<T> safeV1(val1);
4757 ClrSafeInt<T> safeV2(val2);
4758 if (op == BA_Add)
4759 {
4760 res = safeV1 + safeV2;
4761 }
4762 else if (op == BA_Sub)
4763 {
4764 res = safeV1 - safeV2;
4765 }
4766 else if (op == BA_Mul)
4767 {
4768 res = safeV1 * safeV2;
4769 }
4770 else
4771 {
4772 _ASSERTE_MSG(false, "op should be one of the overflow ops...");
4773 }
4774
4775 if (res.IsOverflow())
4776 {
4777 ThrowOverflowException();
4778 }
4779
4780 unsigned residx = m_curStackHt - 2;
4781 OpStackSet<T>(residx, res.Value());
4782 if (!TypeIsUnchanged)
4783 {
4784 OpStackTypeSet(residx, InterpreterType(cit));
4785 }
4786}
4787
4788template<int op>
4789void Interpreter::BinaryIntOp()
4790{
4791 CONTRACTL {
4792 SO_TOLERANT;
4793 THROWS;
4794 GC_TRIGGERS;
4795 MODE_COOPERATIVE;
4796 } CONTRACTL_END;
4797
4798 assert(m_curStackHt >= 2);
4799 unsigned op1idx = m_curStackHt - 2;
4800 unsigned op2idx = m_curStackHt - 1;
4801
4802 InterpreterType t1 = OpStackTypeGet(op1idx);
4803 CorInfoType cit1 = t1.ToCorInfoType();
4804 assert(IsStackNormalType(cit1));
4805
4806 InterpreterType t2 = OpStackTypeGet(op2idx);
4807 CorInfoType cit2 = t2.ToCorInfoType();
4808 assert(IsStackNormalType(cit2));
4809
4810 // In all cases belows, since "op" is compile-time constant, "if" chains on it should fold away.
4811 switch (cit1)
4812 {
4813 case CORINFO_TYPE_INT:
4814 if (cit2 == CORINFO_TYPE_INT)
4815 {
4816 // Int op Int = Int
4817 UINT32 val1 = OpStackGet<UINT32>(op1idx);
4818 UINT32 val2 = OpStackGet<UINT32>(op2idx);
4819 BinaryIntOpWork<op, UINT32, CORINFO_TYPE_INT, /*TypeIsUnchanged*/true>(val1, val2);
4820 }
4821 else if (cit2 == CORINFO_TYPE_NATIVEINT)
4822 {
4823 // Int op NativeInt = NativeInt
4824 NativeUInt val1 = static_cast<NativeUInt>(OpStackGet<INT32>(op1idx));
4825 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4826 BinaryIntOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/false>(val1, val2);
4827 }
4828 else if (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_BYREF)
4829 {
4830 // Int op NativeUInt = NativeUInt
4831 NativeUInt val1 = static_cast<NativeUInt>(OpStackGet<INT32>(op1idx));
4832 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4833 BinaryIntOpWork<op, NativeUInt, CORINFO_TYPE_BYREF, /*TypeIsUnchanged*/false>(val1, val2);
4834 }
4835 else
4836 {
4837 VerificationError("Binary arithmetic operation type mismatch (int and ?)");
4838 }
4839 break;
4840
4841 case CORINFO_TYPE_NATIVEINT:
4842 if (cit2 == CORINFO_TYPE_NATIVEINT)
4843 {
4844 // NativeInt op NativeInt = NativeInt
4845 NativeUInt val1 = OpStackGet<NativeUInt>(op1idx);
4846 NativeUInt val2 = OpStackGet<NativeUInt>(op2idx);
4847 BinaryIntOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4848 }
4849 else if (cit2 == CORINFO_TYPE_INT)
4850 {
4851 // NativeInt op Int = NativeInt
4852 NativeUInt val1 = OpStackGet<NativeUInt>(op1idx);
4853 NativeUInt val2 = static_cast<NativeUInt>(OpStackGet<INT32>(op2idx));
4854 BinaryIntOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4855 }
4856 // CLI spec does not allow adding a native int and an int64. So use loose rules.
4857 else if (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_LONG)
4858 {
4859 // NativeInt op Int = NativeInt
4860 NativeUInt val1 = OpStackGet<NativeUInt>(op1idx);
4861 NativeUInt val2 = static_cast<NativeUInt>(OpStackGet<INT64>(op2idx));
4862 BinaryIntOpWork<op, NativeUInt, CORINFO_TYPE_NATIVEINT, /*TypeIsUnchanged*/true>(val1, val2);
4863 }
4864 else
4865 {
4866 VerificationError("Binary arithmetic operation type mismatch (native int and ?)");
4867 }
4868 break;
4869
4870 case CORINFO_TYPE_LONG:
4871 if (cit2 == CORINFO_TYPE_LONG || (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_NATIVEINT))
4872 {
4873 // Long op Long = Long
4874 UINT64 val1 = OpStackGet<UINT64>(op1idx);
4875 UINT64 val2 = OpStackGet<UINT64>(op2idx);
4876 BinaryIntOpWork<op, UINT64, CORINFO_TYPE_LONG, /*TypeIsUnchanged*/true>(val1, val2);
4877 }
4878 else
4879 {
4880 VerificationError("Binary arithmetic operation type mismatch (long and ?)");
4881 }
4882 break;
4883
4884 default:
4885 VerificationError("Illegal operation for non-integral data type.");
4886 }
4887
4888 // In all cases:
4889 m_curStackHt--;
4890}
4891
4892template<int op, typename T, CorInfoType cit, bool TypeIsUnchanged>
4893void Interpreter::BinaryIntOpWork(T val1, T val2)
4894{
4895 T res;
4896 if (op == BIO_And)
4897 {
4898 res = val1 & val2;
4899 }
4900 else if (op == BIO_Or)
4901 {
4902 res = val1 | val2;
4903 }
4904 else if (op == BIO_Xor)
4905 {
4906 res = val1 ^ val2;
4907 }
4908 else
4909 {
4910 assert(op == BIO_DivUn || op == BIO_RemUn);
4911 if (val2 == 0)
4912 {
4913 ThrowDivideByZero();
4914 }
4915 else if (val2 == -1 && val1 == static_cast<T>(((UINT64)1) << (sizeof(T)*8 - 1))) // min int / -1 is not representable.
4916 {
4917 ThrowSysArithException();
4918 }
4919 // Otherwise...
4920 if (op == BIO_DivUn)
4921 {
4922 res = val1 / val2;
4923 }
4924 else
4925 {
4926 res = val1 % val2;
4927 }
4928 }
4929
4930 unsigned residx = m_curStackHt - 2;
4931 OpStackSet<T>(residx, res);
4932 if (!TypeIsUnchanged)
4933 {
4934 OpStackTypeSet(residx, InterpreterType(cit));
4935 }
4936}
4937
4938template<int op>
4939void Interpreter::ShiftOp()
4940{
4941 CONTRACTL {
4942 SO_TOLERANT;
4943 NOTHROW;
4944 GC_NOTRIGGER;
4945 MODE_COOPERATIVE;
4946 } CONTRACTL_END;
4947
4948 assert(m_curStackHt >= 2);
4949 unsigned op1idx = m_curStackHt - 2;
4950 unsigned op2idx = m_curStackHt - 1;
4951
4952 InterpreterType t1 = OpStackTypeGet(op1idx);
4953 CorInfoType cit1 = t1.ToCorInfoType();
4954 assert(IsStackNormalType(cit1));
4955
4956 InterpreterType t2 = OpStackTypeGet(op2idx);
4957 CorInfoType cit2 = t2.ToCorInfoType();
4958 assert(IsStackNormalType(cit2));
4959
4960 // In all cases belows, since "op" is compile-time constant, "if" chains on it should fold away.
4961 switch (cit1)
4962 {
4963 case CORINFO_TYPE_INT:
4964 ShiftOpWork<op, INT32, UINT32>(op1idx, cit2);
4965 break;
4966
4967 case CORINFO_TYPE_NATIVEINT:
4968 ShiftOpWork<op, NativeInt, NativeUInt>(op1idx, cit2);
4969 break;
4970
4971 case CORINFO_TYPE_LONG:
4972 ShiftOpWork<op, INT64, UINT64>(op1idx, cit2);
4973 break;
4974
4975 default:
4976 VerificationError("Illegal value type for shift operation.");
4977 break;
4978 }
4979
4980 m_curStackHt--;
4981}
4982
4983template<int op, typename T, typename UT>
4984void Interpreter::ShiftOpWork(unsigned op1idx, CorInfoType cit2)
4985{
4986 T val = OpStackGet<T>(op1idx);
4987 unsigned op2idx = op1idx + 1;
4988 T res = 0;
4989
4990 if (cit2 == CORINFO_TYPE_INT)
4991 {
4992 INT32 shiftAmt = OpStackGet<INT32>(op2idx);
4993 if (op == CEE_SHL)
4994 {
4995 res = val << shiftAmt; // TODO: Check that C++ semantics matches IL.
4996 }
4997 else if (op == CEE_SHR)
4998 {
4999 res = val >> shiftAmt;
5000 }
5001 else
5002 {
5003 assert(op == CEE_SHR_UN);
5004 res = (static_cast<UT>(val)) >> shiftAmt;
5005 }
5006 }
5007 else if (cit2 == CORINFO_TYPE_NATIVEINT)
5008 {
5009 NativeInt shiftAmt = OpStackGet<NativeInt>(op2idx);
5010 if (op == CEE_SHL)
5011 {
5012 res = val << shiftAmt; // TODO: Check that C++ semantics matches IL.
5013 }
5014 else if (op == CEE_SHR)
5015 {
5016 res = val >> shiftAmt;
5017 }
5018 else
5019 {
5020 assert(op == CEE_SHR_UN);
5021 res = (static_cast<UT>(val)) >> shiftAmt;
5022 }
5023 }
5024 else
5025 {
5026 VerificationError("Operand type mismatch for shift operator.");
5027 }
5028 OpStackSet<T>(op1idx, res);
5029}
5030
5031
5032void Interpreter::Neg()
5033{
5034 CONTRACTL {
5035 SO_TOLERANT;
5036 NOTHROW;
5037 GC_NOTRIGGER;
5038 MODE_COOPERATIVE;
5039 } CONTRACTL_END;
5040
5041 assert(m_curStackHt >= 1);
5042 unsigned opidx = m_curStackHt - 1;
5043
5044 InterpreterType t1 = OpStackTypeGet(opidx);
5045 CorInfoType cit1 = t1.ToCorInfoType();
5046 assert(IsStackNormalType(cit1));
5047
5048 switch (cit1)
5049 {
5050 case CORINFO_TYPE_INT:
5051 OpStackSet<INT32>(opidx, -OpStackGet<INT32>(opidx));
5052 break;
5053
5054 case CORINFO_TYPE_NATIVEINT:
5055 OpStackSet<NativeInt>(opidx, -OpStackGet<NativeInt>(opidx));
5056 break;
5057
5058 case CORINFO_TYPE_LONG:
5059 OpStackSet<INT64>(opidx, -OpStackGet<INT64>(opidx));
5060 break;
5061
5062 case CORINFO_TYPE_FLOAT:
5063 OpStackSet<float>(opidx, -OpStackGet<float>(opidx));
5064 break;
5065
5066 case CORINFO_TYPE_DOUBLE:
5067 OpStackSet<double>(opidx, -OpStackGet<double>(opidx));
5068 break;
5069
5070 default:
5071 VerificationError("Illegal operand type for Neg operation.");
5072 }
5073}
5074
5075void Interpreter::Not()
5076{
5077 CONTRACTL {
5078 SO_TOLERANT;
5079 NOTHROW;
5080 GC_NOTRIGGER;
5081 MODE_COOPERATIVE;
5082 } CONTRACTL_END;
5083
5084 assert(m_curStackHt >= 1);
5085 unsigned opidx = m_curStackHt - 1;
5086
5087 InterpreterType t1 = OpStackTypeGet(opidx);
5088 CorInfoType cit1 = t1.ToCorInfoType();
5089 assert(IsStackNormalType(cit1));
5090
5091 switch (cit1)
5092 {
5093 case CORINFO_TYPE_INT:
5094 OpStackSet<INT32>(opidx, ~OpStackGet<INT32>(opidx));
5095 break;
5096
5097 case CORINFO_TYPE_NATIVEINT:
5098 OpStackSet<NativeInt>(opidx, ~OpStackGet<NativeInt>(opidx));
5099 break;
5100
5101 case CORINFO_TYPE_LONG:
5102 OpStackSet<INT64>(opidx, ~OpStackGet<INT64>(opidx));
5103 break;
5104
5105 default:
5106 VerificationError("Illegal operand type for Not operation.");
5107 }
5108}
5109
5110template<typename T, bool TIsUnsigned, bool TCanHoldPtr, bool TIsShort, CorInfoType cit>
5111void Interpreter::Conv()
5112{
5113 CONTRACTL {
5114 SO_TOLERANT;
5115 NOTHROW;
5116 GC_NOTRIGGER;
5117 MODE_COOPERATIVE;
5118 } CONTRACTL_END;
5119
5120 assert(m_curStackHt >= 1);
5121 unsigned opidx = m_curStackHt - 1;
5122
5123 InterpreterType t1 = OpStackTypeGet(opidx);
5124 CorInfoType cit1 = t1.ToCorInfoType();
5125 assert(IsStackNormalType(cit1));
5126
5127 T val;
5128 switch (cit1)
5129 {
5130 case CORINFO_TYPE_INT:
5131 if (TIsUnsigned)
5132 {
5133 // Must convert the 32 bit value to unsigned first, so that we zero-extend if necessary.
5134 val = static_cast<T>(static_cast<UINT32>(OpStackGet<INT32>(opidx)));
5135 }
5136 else
5137 {
5138 val = static_cast<T>(OpStackGet<INT32>(opidx));
5139 }
5140 break;
5141
5142 case CORINFO_TYPE_NATIVEINT:
5143 if (TIsUnsigned)
5144 {
5145 // NativeInt might be 32 bits, so convert to unsigned before possibly widening.
5146 val = static_cast<T>(static_cast<NativeUInt>(OpStackGet<NativeInt>(opidx)));
5147 }
5148 else
5149 {
5150 val = static_cast<T>(OpStackGet<NativeInt>(opidx));
5151 }
5152 break;
5153
5154 case CORINFO_TYPE_LONG:
5155 val = static_cast<T>(OpStackGet<INT64>(opidx));
5156 break;
5157
5158 // TODO: Make sure that the C++ conversions do the right thing (truncate to zero...)
5159 case CORINFO_TYPE_FLOAT:
5160 val = static_cast<T>(OpStackGet<float>(opidx));
5161 break;
5162
5163 case CORINFO_TYPE_DOUBLE:
5164 val = static_cast<T>(OpStackGet<double>(opidx));
5165 break;
5166
5167 case CORINFO_TYPE_BYREF:
5168 case CORINFO_TYPE_CLASS:
5169 case CORINFO_TYPE_STRING:
5170 if (!TCanHoldPtr && !s_InterpreterLooseRules)
5171 {
5172 VerificationError("Conversion of pointer value to type that can't hold its value.");
5173 }
5174
5175 // Otherwise...
5176 // (Must first convert to NativeInt, because the compiler believes this might be applied for T =
5177 // float or double. It won't, by the test above, and the extra cast shouldn't generate any code...)
5178 val = static_cast<T>(reinterpret_cast<NativeInt>(OpStackGet<void*>(opidx)));
5179 break;
5180
5181 default:
5182 VerificationError("Illegal operand type for conv.* operation.");
5183 UNREACHABLE();
5184 }
5185
5186 if (TIsShort)
5187 {
5188 OpStackSet<INT32>(opidx, static_cast<INT32>(val));
5189 }
5190 else
5191 {
5192 OpStackSet<T>(opidx, val);
5193 }
5194
5195 OpStackTypeSet(opidx, InterpreterType(cit));
5196}
5197
5198
5199void Interpreter::ConvRUn()
5200{
5201 CONTRACTL {
5202 SO_TOLERANT;
5203 NOTHROW;
5204 GC_NOTRIGGER;
5205 MODE_COOPERATIVE;
5206 } CONTRACTL_END;
5207
5208 assert(m_curStackHt >= 1);
5209 unsigned opidx = m_curStackHt - 1;
5210
5211 InterpreterType t1 = OpStackTypeGet(opidx);
5212 CorInfoType cit1 = t1.ToCorInfoType();
5213 assert(IsStackNormalType(cit1));
5214
5215 switch (cit1)
5216 {
5217 case CORINFO_TYPE_INT:
5218 OpStackSet<double>(opidx, static_cast<double>(OpStackGet<UINT32>(opidx)));
5219 break;
5220
5221 case CORINFO_TYPE_NATIVEINT:
5222 OpStackSet<double>(opidx, static_cast<double>(OpStackGet<NativeUInt>(opidx)));
5223 break;
5224
5225 case CORINFO_TYPE_LONG:
5226 OpStackSet<double>(opidx, static_cast<double>(OpStackGet<UINT64>(opidx)));
5227 break;
5228
5229 case CORINFO_TYPE_DOUBLE:
5230 return;
5231
5232 default:
5233 VerificationError("Illegal operand type for conv.r.un operation.");
5234 }
5235
5236 OpStackTypeSet(opidx, InterpreterType(CORINFO_TYPE_DOUBLE));
5237}
5238
5239template<typename T, INT64 TMin, UINT64 TMax, bool TCanHoldPtr, CorInfoType cit>
5240void Interpreter::ConvOvf()
5241{
5242 CONTRACTL {
5243 SO_TOLERANT;
5244 THROWS;
5245 GC_TRIGGERS;
5246 MODE_COOPERATIVE;
5247 } CONTRACTL_END;
5248
5249 assert(m_curStackHt >= 1);
5250 unsigned opidx = m_curStackHt - 1;
5251
5252 InterpreterType t1 = OpStackTypeGet(opidx);
5253 CorInfoType cit1 = t1.ToCorInfoType();
5254 assert(IsStackNormalType(cit1));
5255
5256 switch (cit1)
5257 {
5258 case CORINFO_TYPE_INT:
5259 {
5260 INT32 i4 = OpStackGet<INT32>(opidx);
5261 if (!FitsIn<T>(i4))
5262 {
5263 ThrowOverflowException();
5264 }
5265 OpStackSet<T>(opidx, static_cast<T>(i4));
5266 }
5267 break;
5268
5269 case CORINFO_TYPE_NATIVEINT:
5270 {
5271 NativeInt i = OpStackGet<NativeInt>(opidx);
5272 if (!FitsIn<T>(i))
5273 {
5274 ThrowOverflowException();
5275 }
5276 OpStackSet<T>(opidx, static_cast<T>(i));
5277 }
5278 break;
5279
5280 case CORINFO_TYPE_LONG:
5281 {
5282 INT64 i8 = OpStackGet<INT64>(opidx);
5283 if (!FitsIn<T>(i8))
5284 {
5285 ThrowOverflowException();
5286 }
5287 OpStackSet<T>(opidx, static_cast<T>(i8));
5288 }
5289 break;
5290
5291 // Make sure that the C++ conversions do the right thing (truncate to zero...)
5292 case CORINFO_TYPE_FLOAT:
5293 {
5294 float f = OpStackGet<float>(opidx);
5295 if (!FloatFitsInIntType<TMin, TMax>(f))
5296 {
5297 ThrowOverflowException();
5298 }
5299 OpStackSet<T>(opidx, static_cast<T>(f));
5300 }
5301 break;
5302
5303 case CORINFO_TYPE_DOUBLE:
5304 {
5305 double d = OpStackGet<double>(opidx);
5306 if (!DoubleFitsInIntType<TMin, TMax>(d))
5307 {
5308 ThrowOverflowException();
5309 }
5310 OpStackSet<T>(opidx, static_cast<T>(d));
5311 }
5312 break;
5313
5314 case CORINFO_TYPE_BYREF:
5315 case CORINFO_TYPE_CLASS:
5316 case CORINFO_TYPE_STRING:
5317 if (!TCanHoldPtr)
5318 {
5319 VerificationError("Conversion of pointer value to type that can't hold its value.");
5320 }
5321
5322 // Otherwise...
5323 // (Must first convert to NativeInt, because the compiler believes this might be applied for T =
5324 // float or double. It won't, by the test above, and the extra cast shouldn't generate any code...
5325 OpStackSet<T>(opidx, static_cast<T>(reinterpret_cast<NativeInt>(OpStackGet<void*>(opidx))));
5326 break;
5327
5328 default:
5329 VerificationError("Illegal operand type for conv.ovf.* operation.");
5330 }
5331
5332 _ASSERTE_MSG(IsStackNormalType(cit), "Precondition.");
5333 OpStackTypeSet(opidx, InterpreterType(cit));
5334}
5335
5336template<typename T, INT64 TMin, UINT64 TMax, bool TCanHoldPtr, CorInfoType cit>
5337void Interpreter::ConvOvfUn()
5338{
5339 CONTRACTL {
5340 SO_TOLERANT;
5341 THROWS;
5342 GC_TRIGGERS;
5343 MODE_COOPERATIVE;
5344 } CONTRACTL_END;
5345
5346 assert(m_curStackHt >= 1);
5347 unsigned opidx = m_curStackHt - 1;
5348
5349 InterpreterType t1 = OpStackTypeGet(opidx);
5350 CorInfoType cit1 = t1.ToCorInfoType();
5351 assert(IsStackNormalType(cit1));
5352
5353 switch (cit1)
5354 {
5355 case CORINFO_TYPE_INT:
5356 {
5357 UINT32 ui4 = OpStackGet<UINT32>(opidx);
5358 if (!FitsIn<T>(ui4))
5359 {
5360 ThrowOverflowException();
5361 }
5362 OpStackSet<T>(opidx, static_cast<T>(ui4));
5363 }
5364 break;
5365
5366 case CORINFO_TYPE_NATIVEINT:
5367 {
5368 NativeUInt ui = OpStackGet<NativeUInt>(opidx);
5369 if (!FitsIn<T>(ui))
5370 {
5371 ThrowOverflowException();
5372 }
5373 OpStackSet<T>(opidx, static_cast<T>(ui));
5374 }
5375 break;
5376
5377 case CORINFO_TYPE_LONG:
5378 {
5379 UINT64 ui8 = OpStackGet<UINT64>(opidx);
5380 if (!FitsIn<T>(ui8))
5381 {
5382 ThrowOverflowException();
5383 }
5384 OpStackSet<T>(opidx, static_cast<T>(ui8));
5385 }
5386 break;
5387
5388 // Make sure that the C++ conversions do the right thing (truncate to zero...)
5389 case CORINFO_TYPE_FLOAT:
5390 {
5391 float f = OpStackGet<float>(opidx);
5392 if (!FloatFitsInIntType<TMin, TMax>(f))
5393 {
5394 ThrowOverflowException();
5395 }
5396 OpStackSet<T>(opidx, static_cast<T>(f));
5397 }
5398 break;
5399
5400 case CORINFO_TYPE_DOUBLE:
5401 {
5402 double d = OpStackGet<double>(opidx);
5403 if (!DoubleFitsInIntType<TMin, TMax>(d))
5404 {
5405 ThrowOverflowException();
5406 }
5407 OpStackSet<T>(opidx, static_cast<T>(d));
5408 }
5409 break;
5410
5411 case CORINFO_TYPE_BYREF:
5412 case CORINFO_TYPE_CLASS:
5413 case CORINFO_TYPE_STRING:
5414 if (!TCanHoldPtr)
5415 {
5416 VerificationError("Conversion of pointer value to type that can't hold its value.");
5417 }
5418
5419 // Otherwise...
5420 // (Must first convert to NativeInt, because the compiler believes this might be applied for T =
5421 // float or double. It won't, by the test above, and the extra cast shouldn't generate any code...
5422 OpStackSet<T>(opidx, static_cast<T>(reinterpret_cast<NativeInt>(OpStackGet<void*>(opidx))));
5423 break;
5424
5425 default:
5426 VerificationError("Illegal operand type for conv.ovf.*.un operation.");
5427 }
5428
5429 _ASSERTE_MSG(IsStackNormalType(cit), "Precondition.");
5430 OpStackTypeSet(opidx, InterpreterType(cit));
5431}
5432
5433void Interpreter::LdObj()
5434{
5435 CONTRACTL {
5436 SO_TOLERANT;
5437 THROWS;
5438 GC_TRIGGERS;
5439 MODE_COOPERATIVE;
5440 } CONTRACTL_END;
5441
5442 BarrierIfVolatile();
5443
5444 assert(m_curStackHt > 0);
5445 unsigned ind = m_curStackHt - 1;
5446
5447#ifdef _DEBUG
5448 CorInfoType cit = OpStackTypeGet(ind).ToCorInfoType();
5449 _ASSERTE_MSG(IsValidPointerType(cit), "Expect pointer on stack");
5450#endif // _DEBUG
5451
5452#if INTERP_TRACING
5453 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdObj]);
5454#endif // INTERP_TRACING
5455
5456 // TODO: GetTypeFromToken also uses GCX_PREEMP(); can we merge it with the getClassAttribs() block below, and do it just once?
5457 CORINFO_CLASS_HANDLE clsHnd = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_LdObj));
5458 DWORD clsAttribs;
5459 {
5460 GCX_PREEMP();
5461 clsAttribs = m_interpCeeInfo.getClassAttribs(clsHnd);
5462 }
5463
5464 void* src = OpStackGet<void*>(ind);
5465 ThrowOnInvalidPointer(src);
5466
5467 if (clsAttribs & CORINFO_FLG_VALUECLASS)
5468 {
5469 LdObjValueClassWork(clsHnd, ind, src);
5470 }
5471 else
5472 {
5473 OpStackSet<void*>(ind, *reinterpret_cast<void**>(src));
5474 OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_CLASS));
5475 }
5476 m_ILCodePtr += 5;
5477}
5478
5479void Interpreter::LdObjValueClassWork(CORINFO_CLASS_HANDLE valueClsHnd, unsigned ind, void* src)
5480{
5481 CONTRACTL {
5482 SO_TOLERANT;
5483 THROWS;
5484 GC_TRIGGERS;
5485 MODE_COOPERATIVE;
5486 } CONTRACTL_END;
5487
5488 // "src" is a byref, which may be into an object. GCPROTECT for the call below.
5489 GCPROTECT_BEGININTERIOR(src);
5490
5491 InterpreterType it = InterpreterType(&m_interpCeeInfo, valueClsHnd);
5492 size_t sz = it.Size(&m_interpCeeInfo);
5493 // Note that the memcpy's below are permissible because the destination is in the operand stack.
5494 if (sz > sizeof(INT64))
5495 {
5496 void* dest = LargeStructOperandStackPush(sz);
5497 memcpy(dest, src, sz);
5498 OpStackSet<void*>(ind, dest);
5499 }
5500 else
5501 {
5502 OpStackSet<INT64>(ind, GetSmallStructValue(src, sz));
5503 }
5504
5505 OpStackTypeSet(ind, it.StackNormalize());
5506
5507 GCPROTECT_END();
5508}
5509
5510CORINFO_CLASS_HANDLE Interpreter::GetTypeFromToken(BYTE* codePtr, CorInfoTokenKind tokKind InterpTracingArg(ResolveTokenKind rtk))
5511{
5512 CONTRACTL {
5513 SO_TOLERANT;
5514 THROWS;
5515 GC_TRIGGERS;
5516 MODE_COOPERATIVE;
5517 } CONTRACTL_END;
5518
5519 GCX_PREEMP();
5520
5521 CORINFO_RESOLVED_TOKEN typeTok;
5522 ResolveToken(&typeTok, getU4LittleEndian(codePtr), tokKind InterpTracingArg(rtk));
5523 return typeTok.hClass;
5524}
5525
5526bool Interpreter::IsValidPointerType(CorInfoType cit)
5527{
5528 bool isValid = (cit == CORINFO_TYPE_NATIVEINT || cit == CORINFO_TYPE_BYREF);
5529#if defined(_AMD64_)
5530 isValid = isValid || (s_InterpreterLooseRules && cit == CORINFO_TYPE_LONG);
5531#endif
5532 return isValid;
5533}
5534
5535void Interpreter::CpObj()
5536{
5537 CONTRACTL {
5538 SO_TOLERANT;
5539 THROWS;
5540 GC_TRIGGERS;
5541 MODE_COOPERATIVE;
5542 } CONTRACTL_END;
5543
5544 assert(m_curStackHt >= 2);
5545 unsigned destInd = m_curStackHt - 2;
5546 unsigned srcInd = m_curStackHt - 1;
5547
5548#ifdef _DEBUG
5549 // Check that src and dest are both pointer types.
5550 CorInfoType cit = OpStackTypeGet(destInd).ToCorInfoType();
5551 _ASSERTE_MSG(IsValidPointerType(cit), "Expect pointer on stack for dest of cpobj");
5552
5553 cit = OpStackTypeGet(srcInd).ToCorInfoType();
5554 _ASSERTE_MSG(IsValidPointerType(cit), "Expect pointer on stack for src of cpobj");
5555#endif // _DEBUG
5556
5557#if INTERP_TRACING
5558 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_CpObj]);
5559#endif // INTERP_TRACING
5560
5561 CORINFO_CLASS_HANDLE clsHnd = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_CpObj));
5562 DWORD clsAttribs;
5563 {
5564 GCX_PREEMP();
5565 clsAttribs = m_interpCeeInfo.getClassAttribs(clsHnd);
5566 }
5567
5568 void* dest = OpStackGet<void*>(destInd);
5569 void* src = OpStackGet<void*>(srcInd);
5570
5571 ThrowOnInvalidPointer(dest);
5572 ThrowOnInvalidPointer(src);
5573
5574 // dest and src are vulnerable byrefs.
5575 GCX_FORBID();
5576
5577 if (clsAttribs & CORINFO_FLG_VALUECLASS)
5578 {
5579 CopyValueClassUnchecked(dest, src, GetMethodTableFromClsHnd(clsHnd));
5580 }
5581 else
5582 {
5583 OBJECTREF val = *reinterpret_cast<OBJECTREF*>(src);
5584 SetObjectReferenceUnchecked(reinterpret_cast<OBJECTREF*>(dest), val);
5585 }
5586 m_curStackHt -= 2;
5587 m_ILCodePtr += 5;
5588}
5589
5590void Interpreter::StObj()
5591{
5592 CONTRACTL {
5593 SO_TOLERANT;
5594 THROWS;
5595 GC_TRIGGERS;
5596 MODE_COOPERATIVE;
5597 } CONTRACTL_END;
5598
5599 assert(m_curStackHt >= 2);
5600 unsigned destInd = m_curStackHt - 2;
5601 unsigned valInd = m_curStackHt - 1;
5602
5603#ifdef _DEBUG
5604 // Check that dest is a pointer type.
5605 CorInfoType cit = OpStackTypeGet(destInd).ToCorInfoType();
5606 _ASSERTE_MSG(IsValidPointerType(cit), "Expect pointer on stack for dest of stobj");
5607#endif // _DEBUG
5608
5609#if INTERP_TRACING
5610 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_StObj]);
5611#endif // INTERP_TRACING
5612
5613 CORINFO_CLASS_HANDLE clsHnd = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_StObj));
5614 DWORD clsAttribs;
5615 {
5616 GCX_PREEMP();
5617 clsAttribs = m_interpCeeInfo.getClassAttribs(clsHnd);
5618 }
5619
5620 if (clsAttribs & CORINFO_FLG_VALUECLASS)
5621 {
5622 MethodTable* clsMT = GetMethodTableFromClsHnd(clsHnd);
5623 size_t sz;
5624 {
5625 GCX_PREEMP();
5626 sz = getClassSize(clsHnd);
5627 }
5628
5629 // Note that "dest" might be a pointer into the heap. It is therefore important
5630 // to calculate it *after* any PREEMP transitions at which we might do a GC.
5631 void* dest = OpStackGet<void*>(destInd);
5632 ThrowOnInvalidPointer(dest);
5633
5634#ifdef _DEBUG
5635 // Try and validate types
5636 InterpreterType vit = OpStackTypeGet(valInd);
5637 CorInfoType vitc = vit.ToCorInfoType();
5638
5639 if (vitc == CORINFO_TYPE_VALUECLASS)
5640 {
5641 CORINFO_CLASS_HANDLE vClsHnd = vit.ToClassHandle();
5642 const bool isClass = (vClsHnd == clsHnd);
5643 const bool isPrim = (vitc == CorInfoTypeStackNormalize(GetTypeForPrimitiveValueClass(clsHnd)));
5644 bool isShared = false;
5645
5646 // If operand type is shared we need a more complex check;
5647 // the IL type may not be shared
5648 if (!isPrim && !isClass)
5649 {
5650 DWORD vClsAttribs;
5651 {
5652 GCX_PREEMP();
5653 vClsAttribs = m_interpCeeInfo.getClassAttribs(vClsHnd);
5654 }
5655
5656 if ((vClsAttribs & CORINFO_FLG_SHAREDINST) != 0)
5657 {
5658 MethodTable* clsMT2 = clsMT->GetCanonicalMethodTable();
5659 if (((CORINFO_CLASS_HANDLE) clsMT2) == vClsHnd)
5660 {
5661 isShared = true;
5662 }
5663 }
5664 }
5665
5666 assert(isClass || isPrim || isShared);
5667 }
5668 else
5669 {
5670 const bool isSz = s_InterpreterLooseRules && sz <= sizeof(dest);
5671 assert(isSz);
5672 }
5673
5674#endif // _DEBUG
5675
5676 GCX_FORBID();
5677
5678 if (sz > sizeof(INT64))
5679 {
5680 // Large struct case -- ostack entry is pointer.
5681 void* src = OpStackGet<void*>(valInd);
5682 CopyValueClassUnchecked(dest, src, clsMT);
5683 LargeStructOperandStackPop(sz, src);
5684 }
5685 else
5686 {
5687 // The ostack entry contains the struct value.
5688 CopyValueClassUnchecked(dest, OpStackGetAddr(valInd, sz), clsMT);
5689 }
5690 }
5691 else
5692 {
5693 // The ostack entry is an object reference.
5694 assert(OpStackTypeGet(valInd).ToCorInfoType() == CORINFO_TYPE_CLASS);
5695
5696 // Note that "dest" might be a pointer into the heap. It is therefore important
5697 // to calculate it *after* any PREEMP transitions at which we might do a GC. (Thus,
5698 // we have to duplicate this code with the case above.
5699 void* dest = OpStackGet<void*>(destInd);
5700 ThrowOnInvalidPointer(dest);
5701
5702 GCX_FORBID();
5703
5704 OBJECTREF val = ObjectToOBJECTREF(OpStackGet<Object*>(valInd));
5705 SetObjectReferenceUnchecked(reinterpret_cast<OBJECTREF*>(dest), val);
5706 }
5707
5708 m_curStackHt -= 2;
5709 m_ILCodePtr += 5;
5710
5711 BarrierIfVolatile();
5712}
5713
5714void Interpreter::InitObj()
5715{
5716 CONTRACTL {
5717 SO_TOLERANT;
5718 THROWS;
5719 GC_TRIGGERS;
5720 MODE_COOPERATIVE;
5721 } CONTRACTL_END;
5722
5723 assert(m_curStackHt >= 1);
5724 unsigned destInd = m_curStackHt - 1;
5725#ifdef _DEBUG
5726 // Check that src and dest are both pointer types.
5727 CorInfoType cit = OpStackTypeGet(destInd).ToCorInfoType();
5728 _ASSERTE_MSG(IsValidPointerType(cit), "Expect pointer on stack");
5729#endif // _DEBUG
5730
5731#if INTERP_TRACING
5732 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_InitObj]);
5733#endif // INTERP_TRACING
5734
5735 CORINFO_CLASS_HANDLE clsHnd = GetTypeFromToken(m_ILCodePtr + 2, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_InitObj));
5736 size_t valueClassSz = 0;
5737
5738 DWORD clsAttribs;
5739 {
5740 GCX_PREEMP();
5741 clsAttribs = m_interpCeeInfo.getClassAttribs(clsHnd);
5742 if (clsAttribs & CORINFO_FLG_VALUECLASS)
5743 {
5744 valueClassSz = getClassSize(clsHnd);
5745 }
5746 }
5747
5748 void* dest = OpStackGet<void*>(destInd);
5749 ThrowOnInvalidPointer(dest);
5750
5751 // dest is a vulnerable byref.
5752 GCX_FORBID();
5753
5754 if (clsAttribs & CORINFO_FLG_VALUECLASS)
5755 {
5756 memset(dest, 0, valueClassSz);
5757 }
5758 else
5759 {
5760 // The ostack entry is an object reference.
5761 SetObjectReferenceUnchecked(reinterpret_cast<OBJECTREF*>(dest), NULL);
5762 }
5763 m_curStackHt -= 1;
5764 m_ILCodePtr += 6;
5765}
5766
5767void Interpreter::LdStr()
5768{
5769 CONTRACTL {
5770 SO_TOLERANT;
5771 THROWS;
5772 GC_TRIGGERS;
5773 MODE_COOPERATIVE;
5774 } CONTRACTL_END;
5775
5776 OBJECTHANDLE res = ConstructStringLiteral(m_methInfo->m_module, getU4LittleEndian(m_ILCodePtr + 1));
5777 {
5778 GCX_FORBID();
5779 OpStackSet<Object*>(m_curStackHt, *reinterpret_cast<Object**>(res));
5780 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_CLASS)); // Stack-normal type for "string"
5781 m_curStackHt++;
5782 }
5783 m_ILCodePtr += 5;
5784}
5785
5786void Interpreter::NewObj()
5787{
5788#if INTERP_DYNAMIC_CONTRACTS
5789 CONTRACTL {
5790 SO_TOLERANT;
5791 THROWS;
5792 GC_TRIGGERS;
5793 MODE_COOPERATIVE;
5794 } CONTRACTL_END;
5795#else
5796 // Dynamic contract occupies too much stack.
5797 STATIC_CONTRACT_SO_TOLERANT;
5798 STATIC_CONTRACT_THROWS;
5799 STATIC_CONTRACT_GC_TRIGGERS;
5800 STATIC_CONTRACT_MODE_COOPERATIVE;
5801#endif
5802
5803 unsigned ctorTok = getU4LittleEndian(m_ILCodePtr + 1);
5804
5805#if INTERP_TRACING
5806 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_NewObj]);
5807#endif // INTERP_TRACING
5808
5809 CORINFO_CALL_INFO callInfo;
5810 CORINFO_RESOLVED_TOKEN methTok;
5811
5812 {
5813 GCX_PREEMP();
5814 ResolveToken(&methTok, ctorTok, CORINFO_TOKENKIND_Ldtoken InterpTracingArg(RTK_NewObj));
5815 m_interpCeeInfo.getCallInfo(&methTok, NULL,
5816 m_methInfo->m_method,
5817 CORINFO_CALLINFO_FLAGS(0),
5818 &callInfo);
5819 }
5820
5821 unsigned mflags = callInfo.methodFlags;
5822
5823 if ((mflags & (CORINFO_FLG_STATIC|CORINFO_FLG_ABSTRACT)) != 0)
5824 {
5825 VerificationError("newobj on static or abstract method");
5826 }
5827
5828 unsigned clsFlags = callInfo.classFlags;
5829
5830#ifdef _DEBUG
5831 // What class are we allocating?
5832 const char* clsName;
5833
5834 {
5835 GCX_PREEMP();
5836 clsName = m_interpCeeInfo.getClassName(methTok.hClass);
5837 }
5838#endif // _DEBUG
5839
5840 // There are four cases:
5841 // 1) Value types (ordinary constructor, resulting VALUECLASS pushed)
5842 // 2) String (var-args constructor, result automatically pushed)
5843 // 3) MDArray (var-args constructor, resulting OBJECTREF pushed)
5844 // 4) Reference types (ordinary constructor, resulting OBJECTREF pushed)
5845 if (clsFlags & CORINFO_FLG_VALUECLASS)
5846 {
5847 void* tempDest;
5848 INT64 smallTempDest = 0;
5849 size_t sz = 0;
5850 {
5851 GCX_PREEMP();
5852 sz = getClassSize(methTok.hClass);
5853 }
5854 if (sz > sizeof(INT64))
5855 {
5856 // TODO: Make sure this is deleted in the face of exceptions.
5857 tempDest = new BYTE[sz];
5858 }
5859 else
5860 {
5861 tempDest = &smallTempDest;
5862 }
5863 memset(tempDest, 0, sz);
5864 InterpreterType structValRetIT(&m_interpCeeInfo, methTok.hClass);
5865 m_structRetValITPtr = &structValRetIT;
5866 m_structRetValTempSpace = tempDest;
5867
5868 DoCallWork(/*virtCall*/false, tempDest, &methTok, &callInfo);
5869
5870 if (sz > sizeof(INT64))
5871 {
5872 void* dest = LargeStructOperandStackPush(sz);
5873 memcpy(dest, tempDest, sz);
5874 delete[] reinterpret_cast<BYTE*>(tempDest);
5875 OpStackSet<void*>(m_curStackHt, dest);
5876 }
5877 else
5878 {
5879 OpStackSet<INT64>(m_curStackHt, GetSmallStructValue(tempDest, sz));
5880 }
5881 if (m_structRetValITPtr->IsStruct())
5882 {
5883 OpStackTypeSet(m_curStackHt, *m_structRetValITPtr);
5884 }
5885 else
5886 {
5887 // Must stack-normalize primitive types.
5888 OpStackTypeSet(m_curStackHt, m_structRetValITPtr->StackNormalize());
5889 }
5890 // "Unregister" the temp space for GC scanning...
5891 m_structRetValITPtr = NULL;
5892 m_curStackHt++;
5893 }
5894 else if ((clsFlags & CORINFO_FLG_VAROBJSIZE) && !(clsFlags & CORINFO_FLG_ARRAY))
5895 {
5896 // For a VAROBJSIZE class (currently == String), pass NULL as this to "pseudo-constructor."
5897 void* specialFlagArg = reinterpret_cast<void*>(0x1); // Special value for "thisArg" argument of "DoCallWork": push NULL that's not on op stack.
5898 DoCallWork(/*virtCall*/false, specialFlagArg, &methTok, &callInfo); // pushes result automatically
5899 }
5900 else
5901 {
5902 OBJECTREF thisArgObj = NULL;
5903 GCPROTECT_BEGIN(thisArgObj);
5904
5905 if (clsFlags & CORINFO_FLG_ARRAY)
5906 {
5907 assert(clsFlags & CORINFO_FLG_VAROBJSIZE);
5908
5909 MethodDesc* methDesc = GetMethod(methTok.hMethod);
5910
5911 PCCOR_SIGNATURE pSig;
5912 DWORD cbSigSize;
5913 methDesc->GetSig(&pSig, &cbSigSize);
5914 MetaSig msig(pSig, cbSigSize, methDesc->GetModule(), NULL);
5915
5916 unsigned dwNumArgs = msig.NumFixedArgs();
5917 assert(m_curStackHt >= dwNumArgs);
5918 m_curStackHt -= dwNumArgs;
5919
5920 INT32* args = (INT32*)_alloca(dwNumArgs * sizeof(INT32));
5921
5922 unsigned dwArg;
5923 for (dwArg = 0; dwArg < dwNumArgs; dwArg++)
5924 {
5925 unsigned stkInd = m_curStackHt + dwArg;
5926 bool loose = s_InterpreterLooseRules && (OpStackTypeGet(stkInd).ToCorInfoType() == CORINFO_TYPE_NATIVEINT);
5927 if (OpStackTypeGet(stkInd).ToCorInfoType() != CORINFO_TYPE_INT && !loose)
5928 {
5929 VerificationError("MD array dimension bounds and sizes must be int.");
5930 }
5931 args[dwArg] = loose ? (INT32) OpStackGet<NativeInt>(stkInd) : OpStackGet<INT32>(stkInd);
5932 }
5933
5934 thisArgObj = AllocateArrayEx(TypeHandle(methTok.hClass), args, dwNumArgs);
5935 }
5936 else
5937 {
5938 CorInfoHelpFunc newHelper;
5939 {
5940 GCX_PREEMP();
5941 newHelper = m_interpCeeInfo.getNewHelper(&methTok, m_methInfo->m_method);
5942 }
5943
5944 MethodTable * pNewObjMT = GetMethodTableFromClsHnd(methTok.hClass);
5945 switch (newHelper)
5946 {
5947 case CORINFO_HELP_NEWFAST:
5948 default:
5949 thisArgObj = AllocateObject(pNewObjMT);
5950 break;
5951 }
5952
5953 DoCallWork(/*virtCall*/false, OBJECTREFToObject(thisArgObj), &methTok, &callInfo);
5954 }
5955
5956 {
5957 GCX_FORBID();
5958 OpStackSet<Object*>(m_curStackHt, OBJECTREFToObject(thisArgObj));
5959 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_CLASS));
5960 m_curStackHt++;
5961 }
5962 GCPROTECT_END(); // For "thisArgObj"
5963 }
5964
5965 m_ILCodePtr += 5;
5966}
5967
5968void Interpreter::NewArr()
5969{
5970 CONTRACTL {
5971 SO_TOLERANT;
5972 THROWS;
5973 GC_TRIGGERS;
5974 MODE_COOPERATIVE;
5975 } CONTRACTL_END;
5976
5977 assert(m_curStackHt > 0);
5978 unsigned stkInd = m_curStackHt-1;
5979 CorInfoType cit = OpStackTypeGet(stkInd).ToCorInfoType();
5980 NativeInt sz = 0;
5981 switch (cit)
5982 {
5983 case CORINFO_TYPE_INT:
5984 sz = static_cast<NativeInt>(OpStackGet<INT32>(stkInd));
5985 break;
5986 case CORINFO_TYPE_NATIVEINT:
5987 sz = OpStackGet<NativeInt>(stkInd);
5988 break;
5989 default:
5990 VerificationError("Size operand of 'newarr' must be int or native int.");
5991 }
5992
5993 unsigned elemTypeTok = getU4LittleEndian(m_ILCodePtr + 1);
5994
5995 CORINFO_CLASS_HANDLE elemClsHnd;
5996
5997#if INTERP_TRACING
5998 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_NewArr]);
5999#endif // INTERP_TRACING
6000
6001 CORINFO_RESOLVED_TOKEN elemTypeResolvedTok;
6002
6003 {
6004 GCX_PREEMP();
6005 ResolveToken(&elemTypeResolvedTok, elemTypeTok, CORINFO_TOKENKIND_Newarr InterpTracingArg(RTK_NewArr));
6006 elemClsHnd = elemTypeResolvedTok.hClass;
6007 }
6008
6009 {
6010 if (sz < 0)
6011 {
6012 COMPlusThrow(kOverflowException);
6013 }
6014
6015#ifdef _WIN64
6016 // Even though ECMA allows using a native int as the argument to newarr instruction
6017 // (therefore size is INT_PTR), ArrayBase::m_NumComponents is 32-bit, so even on 64-bit
6018 // platforms we can't create an array whose size exceeds 32 bits.
6019 if (sz > INT_MAX)
6020 {
6021 EX_THROW(EEMessageException, (kOverflowException, IDS_EE_ARRAY_DIMENSIONS_EXCEEDED));
6022 }
6023#endif
6024
6025 TypeHandle th(elemClsHnd);
6026 MethodTable* pArrayMT = th.GetMethodTable();
6027 pArrayMT->CheckRunClassInitThrowing();
6028
6029 INT32 size32 = (INT32)sz;
6030 Object* newarray = OBJECTREFToObject(AllocateArrayEx(pArrayMT, &size32, 1));
6031
6032 GCX_FORBID();
6033 OpStackTypeSet(stkInd, InterpreterType(CORINFO_TYPE_CLASS));
6034 OpStackSet<Object*>(stkInd, newarray);
6035 }
6036
6037 m_ILCodePtr += 5;
6038}
6039
6040void Interpreter::IsInst()
6041{
6042 CONTRACTL {
6043 SO_TOLERANT;
6044 THROWS;
6045 GC_TRIGGERS;
6046 MODE_COOPERATIVE;
6047 } CONTRACTL_END;
6048
6049#if INTERP_TRACING
6050 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_IsInst]);
6051#endif // INTERP_TRACING
6052
6053 CORINFO_CLASS_HANDLE cls = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Casting InterpTracingArg(RTK_IsInst));
6054
6055 assert(m_curStackHt >= 1);
6056 unsigned idx = m_curStackHt - 1;
6057#ifdef _DEBUG
6058 CorInfoType cit = OpStackTypeGet(idx).ToCorInfoType();
6059 assert(cit == CORINFO_TYPE_CLASS || cit == CORINFO_TYPE_STRING);
6060#endif // DEBUG
6061
6062 Object * pObj = OpStackGet<Object*>(idx);
6063 if (pObj != NULL)
6064 {
6065 if (!ObjIsInstanceOf(pObj, TypeHandle(cls)))
6066 OpStackSet<Object*>(idx, NULL);
6067 }
6068
6069 // Type stack stays unmodified.
6070
6071 m_ILCodePtr += 5;
6072}
6073
6074void Interpreter::CastClass()
6075{
6076 CONTRACTL {
6077 SO_TOLERANT;
6078 THROWS;
6079 GC_TRIGGERS;
6080 MODE_COOPERATIVE;
6081 } CONTRACTL_END;
6082
6083#if INTERP_TRACING
6084 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_CastClass]);
6085#endif // INTERP_TRACING
6086
6087 CORINFO_CLASS_HANDLE cls = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Casting InterpTracingArg(RTK_CastClass));
6088
6089 assert(m_curStackHt >= 1);
6090 unsigned idx = m_curStackHt - 1;
6091#ifdef _DEBUG
6092 CorInfoType cit = OpStackTypeGet(idx).ToCorInfoType();
6093 assert(cit == CORINFO_TYPE_CLASS || cit == CORINFO_TYPE_STRING);
6094#endif // _DEBUG
6095
6096 Object * pObj = OpStackGet<Object*>(idx);
6097 if (pObj != NULL)
6098 {
6099 if (!ObjIsInstanceOf(pObj, TypeHandle(cls), TRUE))
6100 {
6101 UNREACHABLE(); //ObjIsInstanceOf will throw if cast can't be done
6102 }
6103 }
6104
6105
6106 // Type stack stays unmodified.
6107
6108 m_ILCodePtr += 5;
6109}
6110
6111void Interpreter::LocAlloc()
6112{
6113 CONTRACTL {
6114 SO_TOLERANT;
6115 THROWS;
6116 GC_TRIGGERS;
6117 MODE_COOPERATIVE;
6118 } CONTRACTL_END;
6119
6120 assert(m_curStackHt >= 1);
6121 unsigned idx = m_curStackHt - 1;
6122 CorInfoType cit = OpStackTypeGet(idx).ToCorInfoType();
6123 NativeUInt sz = 0;
6124 if (cit == CORINFO_TYPE_INT || cit == CORINFO_TYPE_UINT)
6125 {
6126 sz = static_cast<NativeUInt>(OpStackGet<UINT32>(idx));
6127 }
6128 else if (cit == CORINFO_TYPE_NATIVEINT || cit == CORINFO_TYPE_NATIVEUINT)
6129 {
6130 sz = OpStackGet<NativeUInt>(idx);
6131 }
6132 else if (s_InterpreterLooseRules && cit == CORINFO_TYPE_LONG)
6133 {
6134 sz = (NativeUInt) OpStackGet<INT64>(idx);
6135 }
6136 else
6137 {
6138 VerificationError("localloc requires int or nativeint argument.");
6139 }
6140 if (sz == 0)
6141 {
6142 OpStackSet<void*>(idx, NULL);
6143 }
6144 else
6145 {
6146 void* res = GetLocAllocData()->Alloc(sz);
6147 if (res == NULL) ThrowStackOverflow();
6148 OpStackSet<void*>(idx, res);
6149 }
6150 OpStackTypeSet(idx, InterpreterType(CORINFO_TYPE_NATIVEINT));
6151}
6152
6153void Interpreter::MkRefany()
6154{
6155 CONTRACTL {
6156 SO_TOLERANT;
6157 THROWS;
6158 GC_TRIGGERS;
6159 MODE_COOPERATIVE;
6160 } CONTRACTL_END;
6161
6162#if INTERP_TRACING
6163 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_MkRefAny]);
6164#endif // INTERP_TRACING
6165
6166 CORINFO_CLASS_HANDLE cls = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_MkRefAny));
6167 assert(m_curStackHt >= 1);
6168 unsigned idx = m_curStackHt - 1;
6169
6170 CorInfoType cit = OpStackTypeGet(idx).ToCorInfoType();
6171 if (!(cit == CORINFO_TYPE_BYREF || cit == CORINFO_TYPE_NATIVEINT))
6172 VerificationError("MkRefany requires byref or native int (pointer) on the stack.");
6173
6174 void* ptr = OpStackGet<void*>(idx);
6175
6176 InterpreterType typedRefIT = GetTypedRefIT(&m_interpCeeInfo);
6177 TypedByRef* tbr;
6178#if defined(_AMD64_)
6179 assert(typedRefIT.IsLargeStruct(&m_interpCeeInfo));
6180 tbr = (TypedByRef*) LargeStructOperandStackPush(GetTypedRefSize(&m_interpCeeInfo));
6181 OpStackSet<void*>(idx, tbr);
6182#elif defined(_X86_) || defined(_ARM_)
6183 assert(!typedRefIT.IsLargeStruct(&m_interpCeeInfo));
6184 tbr = OpStackGetAddr<TypedByRef>(idx);
6185#elif defined(_ARM64_)
6186 tbr = NULL;
6187 NYI_INTERP("Unimplemented code: MkRefAny");
6188#else
6189#error "unsupported platform"
6190#endif
6191 tbr->data = ptr;
6192 tbr->type = TypeHandle(cls);
6193 OpStackTypeSet(idx, typedRefIT);
6194
6195 m_ILCodePtr += 5;
6196}
6197
6198void Interpreter::RefanyType()
6199{
6200 CONTRACTL {
6201 SO_TOLERANT;
6202 THROWS;
6203 GC_TRIGGERS;
6204 MODE_COOPERATIVE;
6205 } CONTRACTL_END;
6206
6207 assert(m_curStackHt > 0);
6208 unsigned idx = m_curStackHt - 1;
6209
6210 if (OpStackTypeGet(idx) != GetTypedRefIT(&m_interpCeeInfo))
6211 VerificationError("RefAnyVal requires a TypedRef on the stack.");
6212
6213 TypedByRef* ptbr = OpStackGet<TypedByRef*>(idx);
6214 LargeStructOperandStackPop(sizeof(TypedByRef), ptbr);
6215
6216 TypeHandle* pth = &ptbr->type;
6217
6218 {
6219 OBJECTREF classobj = TypeHandleToTypeRef(pth);
6220 GCX_FORBID();
6221 OpStackSet<Object*>(idx, OBJECTREFToObject(classobj));
6222 OpStackTypeSet(idx, InterpreterType(CORINFO_TYPE_CLASS));
6223 }
6224 m_ILCodePtr += 2;
6225}
6226
6227// This (unfortunately) duplicates code in JIT_GetRuntimeTypeHandle, which
6228// isn't callable because it sets up a Helper Method Frame.
6229OBJECTREF Interpreter::TypeHandleToTypeRef(TypeHandle* pth)
6230{
6231 OBJECTREF typePtr = NULL;
6232 if (!pth->IsTypeDesc())
6233 {
6234 // Most common... and fastest case
6235 typePtr = pth->AsMethodTable()->GetManagedClassObjectIfExists();
6236 if (typePtr == NULL)
6237 {
6238 typePtr = pth->GetManagedClassObject();
6239 }
6240 }
6241 else
6242 {
6243 typePtr = pth->GetManagedClassObject();
6244 }
6245 return typePtr;
6246}
6247
6248CorInfoType Interpreter::GetTypeForPrimitiveValueClass(CORINFO_CLASS_HANDLE clsHnd)
6249{
6250 CONTRACTL {
6251 SO_TOLERANT;
6252 THROWS;
6253 GC_TRIGGERS;
6254 MODE_COOPERATIVE;
6255 } CONTRACTL_END;
6256
6257 GCX_PREEMP();
6258
6259 return m_interpCeeInfo.getTypeForPrimitiveValueClass(clsHnd);
6260}
6261
6262void Interpreter::RefanyVal()
6263{
6264 CONTRACTL {
6265 SO_TOLERANT;
6266 THROWS;
6267 GC_TRIGGERS;
6268 MODE_COOPERATIVE;
6269 } CONTRACTL_END;
6270
6271 assert(m_curStackHt > 0);
6272 unsigned idx = m_curStackHt - 1;
6273
6274 if (OpStackTypeGet(idx) != GetTypedRefIT(&m_interpCeeInfo))
6275 VerificationError("RefAnyVal requires a TypedRef on the stack.");
6276
6277#if INTERP_TRACING
6278 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_RefAnyVal]);
6279#endif // INTERP_TRACING
6280
6281 CORINFO_CLASS_HANDLE cls = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_RefAnyVal));
6282 TypeHandle expected(cls);
6283
6284 TypedByRef* ptbr = OpStackGet<TypedByRef*>(idx);
6285 LargeStructOperandStackPop(sizeof(TypedByRef), ptbr);
6286 if (expected != ptbr->type) ThrowInvalidCastException();
6287
6288 OpStackSet<void*>(idx, static_cast<void*>(ptbr->data));
6289 OpStackTypeSet(idx, InterpreterType(CORINFO_TYPE_BYREF));
6290
6291 m_ILCodePtr += 5;
6292}
6293
6294void Interpreter::CkFinite()
6295{
6296 CONTRACTL {
6297 SO_TOLERANT;
6298 THROWS;
6299 GC_TRIGGERS;
6300 MODE_COOPERATIVE;
6301 } CONTRACTL_END;
6302
6303 assert(m_curStackHt > 0);
6304 unsigned idx = m_curStackHt - 1;
6305
6306 CorInfoType cit = OpStackTypeGet(idx).ToCorInfoType();
6307 double val = 0.0;
6308
6309 switch (cit)
6310 {
6311 case CORINFO_TYPE_FLOAT:
6312 val = (double)OpStackGet<float>(idx);
6313 break;
6314 case CORINFO_TYPE_DOUBLE:
6315 val = OpStackGet<double>(idx);
6316 break;
6317 default:
6318 VerificationError("CkFinite requires a floating-point value on the stack.");
6319 break;
6320 }
6321
6322 if (!_finite(val))
6323 ThrowSysArithException();
6324}
6325
6326void Interpreter::LdToken()
6327{
6328 CONTRACTL {
6329 SO_TOLERANT;
6330 THROWS;
6331 GC_TRIGGERS;
6332 MODE_COOPERATIVE;
6333 } CONTRACTL_END;
6334
6335 unsigned tokVal = getU4LittleEndian(m_ILCodePtr + 1);
6336
6337#if INTERP_TRACING
6338 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdToken]);
6339#endif // INTERP_TRACING
6340
6341
6342 CORINFO_RESOLVED_TOKEN tok;
6343 {
6344 GCX_PREEMP();
6345 ResolveToken(&tok, tokVal, CORINFO_TOKENKIND_Ldtoken InterpTracingArg(RTK_LdToken));
6346 }
6347
6348 // To save duplication of the factored code at the bottom, I don't do GCX_FORBID for
6349 // these Object* values, but this comment documents the intent.
6350 if (tok.hMethod != NULL)
6351 {
6352 MethodDesc* pMethod = (MethodDesc*)tok.hMethod;
6353 Object* objPtr = OBJECTREFToObject((OBJECTREF)pMethod->GetStubMethodInfo());
6354 OpStackSet<Object*>(m_curStackHt, objPtr);
6355 }
6356 else if (tok.hField != NULL)
6357 {
6358 FieldDesc * pField = (FieldDesc *)tok.hField;
6359 Object* objPtr = OBJECTREFToObject((OBJECTREF)pField->GetStubFieldInfo());
6360 OpStackSet<Object*>(m_curStackHt, objPtr);
6361 }
6362 else
6363 {
6364 TypeHandle th(tok.hClass);
6365 Object* objPtr = OBJECTREFToObject(th.GetManagedClassObject());
6366 OpStackSet<Object*>(m_curStackHt, objPtr);
6367 }
6368
6369 {
6370 GCX_FORBID();
6371 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_CLASS));
6372 m_curStackHt++;
6373 }
6374
6375 m_ILCodePtr += 5;
6376}
6377
6378void Interpreter::LdFtn()
6379{
6380 CONTRACTL {
6381 SO_TOLERANT;
6382 THROWS;
6383 GC_TRIGGERS;
6384 MODE_COOPERATIVE;
6385 } CONTRACTL_END;
6386
6387 unsigned tokVal = getU4LittleEndian(m_ILCodePtr + 2);
6388
6389#if INTERP_TRACING
6390 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdFtn]);
6391#endif // INTERP_TRACING
6392
6393 CORINFO_RESOLVED_TOKEN tok;
6394 CORINFO_CALL_INFO callInfo;
6395 {
6396 GCX_PREEMP();
6397 ResolveToken(&tok, tokVal, CORINFO_TOKENKIND_Method InterpTracingArg(RTK_LdFtn));
6398 m_interpCeeInfo.getCallInfo(&tok, NULL, m_methInfo->m_method,
6399 combine(CORINFO_CALLINFO_SECURITYCHECKS,CORINFO_CALLINFO_LDFTN),
6400 &callInfo);
6401 }
6402
6403 switch (callInfo.kind)
6404 {
6405 case CORINFO_CALL:
6406 {
6407 PCODE pCode = ((MethodDesc *)callInfo.hMethod)->GetMultiCallableAddrOfCode();
6408 OpStackSet<void*>(m_curStackHt, (void *)pCode);
6409 GetFunctionPointerStack()[m_curStackHt] = callInfo.hMethod;
6410 }
6411 break;
6412 case CORINFO_CALL_CODE_POINTER:
6413 NYI_INTERP("Indirect code pointer.");
6414 break;
6415 default:
6416 _ASSERTE_MSG(false, "Should not reach here: unknown call kind.");
6417 break;
6418 }
6419 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT));
6420 m_curStackHt++;
6421 m_ILCodePtr += 6;
6422}
6423
6424void Interpreter::LdVirtFtn()
6425{
6426 CONTRACTL {
6427 SO_TOLERANT;
6428 THROWS;
6429 GC_TRIGGERS;
6430 MODE_COOPERATIVE;
6431 } CONTRACTL_END;
6432
6433 assert(m_curStackHt >= 1);
6434 unsigned ind = m_curStackHt - 1;
6435
6436 unsigned tokVal = getU4LittleEndian(m_ILCodePtr + 2);
6437
6438#if INTERP_TRACING
6439 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdVirtFtn]);
6440#endif // INTERP_TRACING
6441
6442 CORINFO_RESOLVED_TOKEN tok;
6443 CORINFO_CALL_INFO callInfo;
6444 CORINFO_CLASS_HANDLE classHnd;
6445 CORINFO_METHOD_HANDLE methodHnd;
6446 {
6447 GCX_PREEMP();
6448 ResolveToken(&tok, tokVal, CORINFO_TOKENKIND_Method InterpTracingArg(RTK_LdVirtFtn));
6449 m_interpCeeInfo.getCallInfo(&tok, NULL, m_methInfo->m_method,
6450 combine(CORINFO_CALLINFO_SECURITYCHECKS,CORINFO_CALLINFO_LDFTN),
6451 &callInfo);
6452
6453
6454 classHnd = tok.hClass;
6455 methodHnd = tok.hMethod;
6456 }
6457
6458 MethodDesc * pMD = (MethodDesc *)methodHnd;
6459 PCODE pCode;
6460 if (pMD->IsVtableMethod())
6461 {
6462 Object* obj = OpStackGet<Object*>(ind);
6463 ThrowOnInvalidPointer(obj);
6464
6465 OBJECTREF objRef = ObjectToOBJECTREF(obj);
6466 GCPROTECT_BEGIN(objRef);
6467 pCode = pMD->GetMultiCallableAddrOfVirtualizedCode(&objRef, TypeHandle(classHnd));
6468 GCPROTECT_END();
6469
6470 pMD = Entry2MethodDesc(pCode, TypeHandle(classHnd).GetMethodTable());
6471 }
6472 else
6473 {
6474 pCode = pMD->GetMultiCallableAddrOfCode();
6475 }
6476 OpStackSet<void*>(ind, (void *)pCode);
6477 GetFunctionPointerStack()[ind] = (CORINFO_METHOD_HANDLE)pMD;
6478
6479 OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_NATIVEINT));
6480 m_ILCodePtr += 6;
6481}
6482
6483void Interpreter::Sizeof()
6484{
6485 CONTRACTL {
6486 SO_TOLERANT;
6487 THROWS;
6488 GC_TRIGGERS;
6489 MODE_COOPERATIVE;
6490 } CONTRACTL_END;
6491
6492#if INTERP_TRACING
6493 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_Sizeof]);
6494#endif // INTERP_TRACING
6495
6496 CORINFO_CLASS_HANDLE cls = GetTypeFromToken(m_ILCodePtr + 2, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_Sizeof));
6497 unsigned sz;
6498 {
6499 GCX_PREEMP();
6500 CorInfoType cit = ::asCorInfoType(cls);
6501 // For class types, the ECMA spec says to return the size of the object reference, not the referent
6502 // object. Everything else should be a value type, for which we can just return the size as reported
6503 // by the EE.
6504 switch (cit)
6505 {
6506 case CORINFO_TYPE_CLASS:
6507 sz = sizeof(Object*);
6508 break;
6509 default:
6510 sz = getClassSize(cls);
6511 break;
6512 }
6513 }
6514
6515 OpStackSet<UINT32>(m_curStackHt, sz);
6516 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_INT));
6517 m_curStackHt++;
6518 m_ILCodePtr += 6;
6519}
6520
6521
6522// static:
6523bool Interpreter::s_initialized = false;
6524bool Interpreter::s_compilerStaticsInitialized = false;
6525size_t Interpreter::s_TypedRefSize;
6526CORINFO_CLASS_HANDLE Interpreter::s_TypedRefClsHnd;
6527InterpreterType Interpreter::s_TypedRefIT;
6528
6529// Must call GetTypedRefIT
6530size_t Interpreter::GetTypedRefSize(CEEInfo* info)
6531{
6532 _ASSERTE_MSG(s_compilerStaticsInitialized, "Precondition");
6533 return s_TypedRefSize;
6534}
6535
6536InterpreterType Interpreter::GetTypedRefIT(CEEInfo* info)
6537{
6538 _ASSERTE_MSG(s_compilerStaticsInitialized, "Precondition");
6539 return s_TypedRefIT;
6540}
6541
6542CORINFO_CLASS_HANDLE Interpreter::GetTypedRefClsHnd(CEEInfo* info)
6543{
6544 _ASSERTE_MSG(s_compilerStaticsInitialized, "Precondition");
6545 return s_TypedRefClsHnd;
6546}
6547
6548void Interpreter::Initialize()
6549{
6550 assert(!s_initialized);
6551
6552 s_InterpretMeths.ensureInit(CLRConfig::INTERNAL_Interpret);
6553 s_InterpretMethsExclude.ensureInit(CLRConfig::INTERNAL_InterpretExclude);
6554 s_InterpreterUseCaching = (s_InterpreterUseCachingFlag.val(CLRConfig::INTERNAL_InterpreterUseCaching) != 0);
6555 s_InterpreterLooseRules = (s_InterpreterLooseRulesFlag.val(CLRConfig::INTERNAL_InterpreterLooseRules) != 0);
6556 s_InterpreterDoLoopMethods = (s_InterpreterDoLoopMethodsFlag.val(CLRConfig::INTERNAL_InterpreterDoLoopMethods) != 0);
6557
6558 // Initialize the lock used to protect method locks.
6559 // TODO: it would be better if this were a reader/writer lock.
6560 s_methodCacheLock.Init(CrstLeafLock, CRST_DEFAULT);
6561
6562 // Similarly, initialize the lock used to protect the map from
6563 // interpreter stub addresses to their method descs.
6564 s_interpStubToMDMapLock.Init(CrstLeafLock, CRST_DEFAULT);
6565
6566 s_initialized = true;
6567
6568#if INTERP_ILINSTR_PROFILE
6569 SetILInstrCategories();
6570#endif // INTERP_ILINSTR_PROFILE
6571}
6572
6573void Interpreter::InitializeCompilerStatics(CEEInfo* info)
6574{
6575 if (!s_compilerStaticsInitialized)
6576 {
6577 // TODO: I believe I need no synchronization around this on x86, but I do
6578 // on more permissive memory models. (Why it's OK on x86: each thread executes this
6579 // before any access to the initialized static variables; if several threads do
6580 // so, they perform idempotent initializing writes to the statics.
6581 GCX_PREEMP();
6582 s_TypedRefClsHnd = info->getBuiltinClass(CLASSID_TYPED_BYREF);
6583 s_TypedRefIT = InterpreterType(info, s_TypedRefClsHnd);
6584 s_TypedRefSize = getClassSize(s_TypedRefClsHnd);
6585 s_compilerStaticsInitialized = true;
6586 // TODO: Need store-store memory barrier here.
6587 }
6588}
6589
6590void Interpreter::Terminate()
6591{
6592 if (s_initialized)
6593 {
6594 s_methodCacheLock.Destroy();
6595 s_interpStubToMDMapLock.Destroy();
6596 s_initialized = false;
6597 }
6598}
6599
6600#if INTERP_ILINSTR_PROFILE
6601void Interpreter::SetILInstrCategories()
6602{
6603 // Start with the indentity maps
6604 for (unsigned short instr = 0; instr < 512; instr++) s_ILInstrCategories[instr] = instr;
6605 // Now make exceptions.
6606 for (unsigned instr = CEE_LDARG_0; instr <= CEE_LDARG_3; instr++) s_ILInstrCategories[instr] = CEE_LDARG;
6607 s_ILInstrCategories[CEE_LDARG_S] = CEE_LDARG;
6608
6609 for (unsigned instr = CEE_LDLOC_0; instr <= CEE_LDLOC_3; instr++) s_ILInstrCategories[instr] = CEE_LDLOC;
6610 s_ILInstrCategories[CEE_LDLOC_S] = CEE_LDLOC;
6611
6612 for (unsigned instr = CEE_STLOC_0; instr <= CEE_STLOC_3; instr++) s_ILInstrCategories[instr] = CEE_STLOC;
6613 s_ILInstrCategories[CEE_STLOC_S] = CEE_STLOC;
6614
6615 s_ILInstrCategories[CEE_LDLOCA_S] = CEE_LDLOCA;
6616
6617 for (unsigned instr = CEE_LDC_I4_M1; instr <= CEE_LDC_I4_S; instr++) s_ILInstrCategories[instr] = CEE_LDC_I4;
6618
6619 for (unsigned instr = CEE_BR_S; instr <= CEE_BLT_UN; instr++) s_ILInstrCategories[instr] = CEE_BR;
6620
6621 for (unsigned instr = CEE_LDIND_I1; instr <= CEE_LDIND_REF; instr++) s_ILInstrCategories[instr] = CEE_LDIND_I;
6622
6623 for (unsigned instr = CEE_STIND_REF; instr <= CEE_STIND_R8; instr++) s_ILInstrCategories[instr] = CEE_STIND_I;
6624
6625 for (unsigned instr = CEE_ADD; instr <= CEE_REM_UN; instr++) s_ILInstrCategories[instr] = CEE_ADD;
6626
6627 for (unsigned instr = CEE_AND; instr <= CEE_NOT; instr++) s_ILInstrCategories[instr] = CEE_AND;
6628
6629 for (unsigned instr = CEE_CONV_I1; instr <= CEE_CONV_U8; instr++) s_ILInstrCategories[instr] = CEE_CONV_I;
6630 for (unsigned instr = CEE_CONV_OVF_I1_UN; instr <= CEE_CONV_OVF_U_UN; instr++) s_ILInstrCategories[instr] = CEE_CONV_I;
6631
6632 for (unsigned instr = CEE_LDELEM_I1; instr <= CEE_LDELEM_REF; instr++) s_ILInstrCategories[instr] = CEE_LDELEM;
6633 for (unsigned instr = CEE_STELEM_I; instr <= CEE_STELEM_REF; instr++) s_ILInstrCategories[instr] = CEE_STELEM;
6634
6635 for (unsigned instr = CEE_CONV_OVF_I1; instr <= CEE_CONV_OVF_U8; instr++) s_ILInstrCategories[instr] = CEE_CONV_I;
6636 for (unsigned instr = CEE_CONV_U2; instr <= CEE_CONV_U1; instr++) s_ILInstrCategories[instr] = CEE_CONV_I;
6637 for (unsigned instr = CEE_CONV_OVF_I; instr <= CEE_CONV_OVF_U; instr++) s_ILInstrCategories[instr] = CEE_CONV_I;
6638
6639 for (unsigned instr = CEE_ADD_OVF; instr <= CEE_SUB_OVF; instr++) s_ILInstrCategories[instr] = CEE_ADD_OVF;
6640
6641 s_ILInstrCategories[CEE_LEAVE_S] = CEE_LEAVE;
6642 s_ILInstrCategories[CEE_CONV_U] = CEE_CONV_I;
6643}
6644#endif // INTERP_ILINSTR_PROFILE
6645
6646
6647template<int op>
6648void Interpreter::CompareOp()
6649{
6650 CONTRACTL {
6651 SO_TOLERANT;
6652 THROWS;
6653 GC_TRIGGERS;
6654 MODE_COOPERATIVE;
6655 } CONTRACTL_END;
6656
6657 assert(m_curStackHt >= 2);
6658 unsigned op1idx = m_curStackHt - 2;
6659 INT32 res = CompareOpRes<op>(op1idx);
6660 OpStackSet<INT32>(op1idx, res);
6661 OpStackTypeSet(op1idx, InterpreterType(CORINFO_TYPE_INT));
6662 m_curStackHt--;
6663}
6664
6665template<int op>
6666INT32 Interpreter::CompareOpRes(unsigned op1idx)
6667{
6668 CONTRACTL {
6669 SO_TOLERANT;
6670 THROWS;
6671 GC_TRIGGERS;
6672 MODE_COOPERATIVE;
6673 } CONTRACTL_END;
6674
6675 assert(m_curStackHt >= op1idx + 2);
6676 unsigned op2idx = op1idx + 1;
6677 InterpreterType t1 = OpStackTypeGet(op1idx);
6678 CorInfoType cit1 = t1.ToCorInfoType();
6679 assert(IsStackNormalType(cit1));
6680 InterpreterType t2 = OpStackTypeGet(op2idx);
6681 CorInfoType cit2 = t2.ToCorInfoType();
6682 assert(IsStackNormalType(cit2));
6683 INT32 res = 0;
6684
6685 switch (cit1)
6686 {
6687 case CORINFO_TYPE_INT:
6688 if (cit2 == CORINFO_TYPE_INT)
6689 {
6690 INT32 val1 = OpStackGet<INT32>(op1idx);
6691 INT32 val2 = OpStackGet<INT32>(op2idx);
6692 if (op == CO_EQ)
6693 {
6694 if (val1 == val2) res = 1;
6695 }
6696 else if (op == CO_GT)
6697 {
6698 if (val1 > val2) res = 1;
6699 }
6700 else if (op == CO_GT_UN)
6701 {
6702 if (static_cast<UINT32>(val1) > static_cast<UINT32>(val2)) res = 1;
6703 }
6704 else if (op == CO_LT)
6705 {
6706 if (val1 < val2) res = 1;
6707 }
6708 else
6709 {
6710 assert(op == CO_LT_UN);
6711 if (static_cast<UINT32>(val1) < static_cast<UINT32>(val2)) res = 1;
6712 }
6713 }
6714 else if (cit2 == CORINFO_TYPE_NATIVEINT ||
6715 (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_BYREF) ||
6716 (cit2 == CORINFO_TYPE_VALUECLASS
6717 && CorInfoTypeStackNormalize(GetTypeForPrimitiveValueClass(t2.ToClassHandle())) == CORINFO_TYPE_NATIVEINT))
6718 {
6719 NativeInt val1 = OpStackGet<NativeInt>(op1idx);
6720 NativeInt val2 = OpStackGet<NativeInt>(op2idx);
6721 if (op == CO_EQ)
6722 {
6723 if (val1 == val2) res = 1;
6724 }
6725 else if (op == CO_GT)
6726 {
6727 if (val1 > val2) res = 1;
6728 }
6729 else if (op == CO_GT_UN)
6730 {
6731 if (static_cast<NativeUInt>(val1) > static_cast<NativeUInt>(val2)) res = 1;
6732 }
6733 else if (op == CO_LT)
6734 {
6735 if (val1 < val2) res = 1;
6736 }
6737 else
6738 {
6739 assert(op == CO_LT_UN);
6740 if (static_cast<NativeUInt>(val1) < static_cast<NativeUInt>(val2)) res = 1;
6741 }
6742 }
6743 else if (cit2 == CORINFO_TYPE_VALUECLASS)
6744 {
6745 cit2 = GetTypeForPrimitiveValueClass(t2.ToClassHandle());
6746 INT32 val1 = OpStackGet<INT32>(op1idx);
6747 INT32 val2 = 0;
6748 if (CorInfoTypeStackNormalize(cit2) == CORINFO_TYPE_INT)
6749 {
6750
6751 size_t sz = t2.Size(&m_interpCeeInfo);
6752 switch (sz)
6753 {
6754 case 1:
6755 if (CorInfoTypeIsUnsigned(cit2))
6756 {
6757 val2 = OpStackGet<UINT8>(op2idx);
6758 }
6759 else
6760 {
6761 val2 = OpStackGet<INT8>(op2idx);
6762 }
6763 break;
6764 case 2:
6765 if (CorInfoTypeIsUnsigned(cit2))
6766 {
6767 val2 = OpStackGet<UINT16>(op2idx);
6768 }
6769 else
6770 {
6771 val2 = OpStackGet<INT16>(op2idx);
6772 }
6773 break;
6774 case 4:
6775 val2 = OpStackGet<INT32>(op2idx);
6776 break;
6777 default:
6778 UNREACHABLE();
6779 }
6780 }
6781 else
6782 {
6783 VerificationError("Can't compare with struct type.");
6784 }
6785 if (op == CO_EQ)
6786 {
6787 if (val1 == val2) res = 1;
6788 }
6789 else if (op == CO_GT)
6790 {
6791 if (val1 > val2) res = 1;
6792 }
6793 else if (op == CO_GT_UN)
6794 {
6795 if (static_cast<UINT32>(val1) > static_cast<UINT32>(val2)) res = 1;
6796 }
6797 else if (op == CO_LT)
6798 {
6799 if (val1 < val2) res = 1;
6800 }
6801 else
6802 {
6803 assert(op == CO_LT_UN);
6804 if (static_cast<UINT32>(val1) < static_cast<UINT32>(val2)) res = 1;
6805 }
6806 }
6807 else
6808 {
6809 VerificationError("Binary comparision operation: type mismatch.");
6810 }
6811 break;
6812 case CORINFO_TYPE_NATIVEINT:
6813 if (cit2 == CORINFO_TYPE_NATIVEINT || cit2 == CORINFO_TYPE_INT
6814 || (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_LONG)
6815 || (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_BYREF)
6816 || (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_CLASS && OpStackGet<void*>(op2idx) == 0))
6817 {
6818 NativeInt val1 = OpStackGet<NativeInt>(op1idx);
6819 NativeInt val2;
6820 if (cit2 == CORINFO_TYPE_NATIVEINT)
6821 {
6822 val2 = OpStackGet<NativeInt>(op2idx);
6823 }
6824 else if (cit2 == CORINFO_TYPE_INT)
6825 {
6826 val2 = static_cast<NativeInt>(OpStackGet<INT32>(op2idx));
6827 }
6828 else if (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_LONG)
6829 {
6830 val2 = static_cast<NativeInt>(OpStackGet<INT64>(op2idx));
6831 }
6832 else if (cit2 == CORINFO_TYPE_CLASS)
6833 {
6834 assert(OpStackGet<void*>(op2idx) == 0);
6835 val2 = 0;
6836 }
6837 else
6838 {
6839 assert(s_InterpreterLooseRules && cit2 == CORINFO_TYPE_BYREF);
6840 val2 = reinterpret_cast<NativeInt>(OpStackGet<void*>(op2idx));
6841 }
6842 if (op == CO_EQ)
6843 {
6844 if (val1 == val2) res = 1;
6845 }
6846 else if (op == CO_GT)
6847 {
6848 if (val1 > val2) res = 1;
6849 }
6850 else if (op == CO_GT_UN)
6851 {
6852 if (static_cast<NativeUInt>(val1) > static_cast<NativeUInt>(val2)) res = 1;
6853 }
6854 else if (op == CO_LT)
6855 {
6856 if (val1 < val2) res = 1;
6857 }
6858 else
6859 {
6860 assert(op == CO_LT_UN);
6861 if (static_cast<NativeUInt>(val1) < static_cast<NativeUInt>(val2)) res = 1;
6862 }
6863 }
6864 else
6865 {
6866 VerificationError("Binary comparision operation: type mismatch.");
6867 }
6868 break;
6869 case CORINFO_TYPE_LONG:
6870 {
6871 bool looseLong = false;
6872#if defined(_AMD64_)
6873 looseLong = s_InterpreterLooseRules && (cit2 == CORINFO_TYPE_NATIVEINT || cit2 == CORINFO_TYPE_BYREF);
6874#endif
6875 if (cit2 == CORINFO_TYPE_LONG || looseLong)
6876 {
6877 INT64 val1 = OpStackGet<INT64>(op1idx);
6878 INT64 val2 = OpStackGet<INT64>(op2idx);
6879 if (op == CO_EQ)
6880 {
6881 if (val1 == val2) res = 1;
6882 }
6883 else if (op == CO_GT)
6884 {
6885 if (val1 > val2) res = 1;
6886 }
6887 else if (op == CO_GT_UN)
6888 {
6889 if (static_cast<UINT64>(val1) > static_cast<UINT64>(val2)) res = 1;
6890 }
6891 else if (op == CO_LT)
6892 {
6893 if (val1 < val2) res = 1;
6894 }
6895 else
6896 {
6897 assert(op == CO_LT_UN);
6898 if (static_cast<UINT64>(val1) < static_cast<UINT64>(val2)) res = 1;
6899 }
6900 }
6901 else
6902 {
6903 VerificationError("Binary comparision operation: type mismatch.");
6904 }
6905 }
6906 break;
6907
6908 case CORINFO_TYPE_CLASS:
6909 case CORINFO_TYPE_STRING:
6910 if (cit2 == CORINFO_TYPE_CLASS || cit2 == CORINFO_TYPE_STRING)
6911 {
6912 GCX_FORBID();
6913 Object* val1 = OpStackGet<Object*>(op1idx);
6914 Object* val2 = OpStackGet<Object*>(op2idx);
6915 if (op == CO_EQ)
6916 {
6917 if (val1 == val2) res = 1;
6918 }
6919 else if (op == CO_GT_UN)
6920 {
6921 if (val1 != val2) res = 1;
6922 }
6923 else
6924 {
6925 VerificationError("Binary comparision operation: type mismatch.");
6926 }
6927 }
6928 else
6929 {
6930 VerificationError("Binary comparision operation: type mismatch.");
6931 }
6932 break;
6933
6934
6935 case CORINFO_TYPE_FLOAT:
6936 {
6937 bool isDouble = (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_DOUBLE);
6938 if (cit2 == CORINFO_TYPE_FLOAT || isDouble)
6939 {
6940 float val1 = OpStackGet<float>(op1idx);
6941 float val2 = (isDouble) ? (float) OpStackGet<double>(op2idx) : OpStackGet<float>(op2idx);
6942 if (op == CO_EQ)
6943 {
6944 // I'm assuming IEEE math here, so that if at least one is a NAN, the comparison will fail...
6945 if (val1 == val2) res = 1;
6946 }
6947 else if (op == CO_GT)
6948 {
6949 // I'm assuming that C++ arithmetic does the right thing here with infinities and NANs.
6950 if (val1 > val2) res = 1;
6951 }
6952 else if (op == CO_GT_UN)
6953 {
6954 // Check for NAN's here: if either is a NAN, they're unordered, so this comparison returns true.
6955 if (_isnan(val1) || _isnan(val2)) res = 1;
6956 else if (val1 > val2) res = 1;
6957 }
6958 else if (op == CO_LT)
6959 {
6960 if (val1 < val2) res = 1;
6961 }
6962 else
6963 {
6964 assert(op == CO_LT_UN);
6965 // Check for NAN's here: if either is a NAN, they're unordered, so this comparison returns true.
6966 if (_isnan(val1) || _isnan(val2)) res = 1;
6967 else if (val1 < val2) res = 1;
6968 }
6969 }
6970 else
6971 {
6972 VerificationError("Binary comparision operation: type mismatch.");
6973 }
6974 }
6975 break;
6976
6977 case CORINFO_TYPE_DOUBLE:
6978 {
6979 bool isFloat = (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_FLOAT);
6980 if (cit2 == CORINFO_TYPE_DOUBLE || isFloat)
6981 {
6982 double val1 = OpStackGet<double>(op1idx);
6983 double val2 = (isFloat) ? (double) OpStackGet<float>(op2idx) : OpStackGet<double>(op2idx);
6984 if (op == CO_EQ)
6985 {
6986 // I'm assuming IEEE math here, so that if at least one is a NAN, the comparison will fail...
6987 if (val1 == val2) res = 1;
6988 }
6989 else if (op == CO_GT)
6990 {
6991 // I'm assuming that C++ arithmetic does the right thing here with infinities and NANs.
6992 if (val1 > val2) res = 1;
6993 }
6994 else if (op == CO_GT_UN)
6995 {
6996 // Check for NAN's here: if either is a NAN, they're unordered, so this comparison returns true.
6997 if (_isnan(val1) || _isnan(val2)) res = 1;
6998 else if (val1 > val2) res = 1;
6999 }
7000 else if (op == CO_LT)
7001 {
7002 if (val1 < val2) res = 1;
7003 }
7004 else
7005 {
7006 assert(op == CO_LT_UN);
7007 // Check for NAN's here: if either is a NAN, they're unordered, so this comparison returns true.
7008 if (_isnan(val1) || _isnan(val2)) res = 1;
7009 else if (val1 < val2) res = 1;
7010 }
7011 }
7012 else
7013 {
7014 VerificationError("Binary comparision operation: type mismatch.");
7015 }
7016 }
7017 break;
7018
7019 case CORINFO_TYPE_BYREF:
7020 if (cit2 == CORINFO_TYPE_BYREF || (s_InterpreterLooseRules && cit2 == CORINFO_TYPE_NATIVEINT))
7021 {
7022 NativeInt val1 = reinterpret_cast<NativeInt>(OpStackGet<void*>(op1idx));
7023 NativeInt val2;
7024 if (cit2 == CORINFO_TYPE_BYREF)
7025 {
7026 val2 = reinterpret_cast<NativeInt>(OpStackGet<void*>(op2idx));
7027 }
7028 else
7029 {
7030 assert(s_InterpreterLooseRules && cit2 == CORINFO_TYPE_NATIVEINT);
7031 val2 = OpStackGet<NativeInt>(op2idx);
7032 }
7033 if (op == CO_EQ)
7034 {
7035 if (val1 == val2) res = 1;
7036 }
7037 else if (op == CO_GT)
7038 {
7039 if (val1 > val2) res = 1;
7040 }
7041 else if (op == CO_GT_UN)
7042 {
7043 if (static_cast<NativeUInt>(val1) > static_cast<NativeUInt>(val2)) res = 1;
7044 }
7045 else if (op == CO_LT)
7046 {
7047 if (val1 < val2) res = 1;
7048 }
7049 else
7050 {
7051 assert(op == CO_LT_UN);
7052 if (static_cast<NativeUInt>(val1) < static_cast<NativeUInt>(val2)) res = 1;
7053 }
7054 }
7055 else
7056 {
7057 VerificationError("Binary comparision operation: type mismatch.");
7058 }
7059 break;
7060
7061 case CORINFO_TYPE_VALUECLASS:
7062 {
7063 CorInfoType newCit1 = GetTypeForPrimitiveValueClass(t1.ToClassHandle());
7064 if (newCit1 == CORINFO_TYPE_UNDEF)
7065 {
7066 VerificationError("Can't compare a value class.");
7067 }
7068 else
7069 {
7070 NYI_INTERP("Must eliminate 'punning' value classes from the ostack.");
7071 }
7072 }
7073 break;
7074
7075 default:
7076 assert(false); // Should not be here if the type is stack-normal.
7077 }
7078
7079 return res;
7080}
7081
7082template<bool val, int targetLen>
7083void Interpreter::BrOnValue()
7084{
7085 assert(targetLen == 1 || targetLen == 4);
7086 assert(m_curStackHt > 0);
7087 unsigned stackInd = m_curStackHt - 1;
7088 InterpreterType it = OpStackTypeGet(stackInd);
7089
7090 // It shouldn't be a value class, unless it's a punning name for a primitive integral type.
7091 if (it.ToCorInfoType() == CORINFO_TYPE_VALUECLASS)
7092 {
7093 GCX_PREEMP();
7094 CorInfoType cit = m_interpCeeInfo.getTypeForPrimitiveValueClass(it.ToClassHandle());
7095 if (CorInfoTypeIsIntegral(cit))
7096 {
7097 it = InterpreterType(cit);
7098 }
7099 else
7100 {
7101 VerificationError("Can't branch on the value of a value type that is not a primitive type.");
7102 }
7103 }
7104
7105#ifdef _DEBUG
7106 switch (it.ToCorInfoType())
7107 {
7108 case CORINFO_TYPE_FLOAT:
7109 case CORINFO_TYPE_DOUBLE:
7110 VerificationError("Can't branch on the value of a float or double.");
7111 break;
7112 default:
7113 break;
7114 }
7115#endif // _DEBUG
7116
7117 switch (it.SizeNotStruct())
7118 {
7119 case 4:
7120 {
7121 INT32 branchVal = OpStackGet<INT32>(stackInd);
7122 BrOnValueTakeBranch((branchVal != 0) == val, targetLen);
7123 }
7124 break;
7125 case 8:
7126 {
7127 INT64 branchVal = OpStackGet<INT64>(stackInd);
7128 BrOnValueTakeBranch((branchVal != 0) == val, targetLen);
7129 }
7130 break;
7131
7132 // The value-class case handled above makes sizes 1 and 2 possible.
7133 case 1:
7134 {
7135 INT8 branchVal = OpStackGet<INT8>(stackInd);
7136 BrOnValueTakeBranch((branchVal != 0) == val, targetLen);
7137 }
7138 break;
7139 case 2:
7140 {
7141 INT16 branchVal = OpStackGet<INT16>(stackInd);
7142 BrOnValueTakeBranch((branchVal != 0) == val, targetLen);
7143 }
7144 break;
7145 default:
7146 UNREACHABLE();
7147 break;
7148 }
7149 m_curStackHt = stackInd;
7150}
7151
7152// compOp is a member of the BranchComparisonOp enumeration.
7153template<int compOp, bool reverse, int targetLen>
7154void Interpreter::BrOnComparison()
7155{
7156 CONTRACTL {
7157 SO_TOLERANT;
7158 THROWS;
7159 GC_TRIGGERS;
7160 MODE_COOPERATIVE;
7161 } CONTRACTL_END;
7162
7163 assert(targetLen == 1 || targetLen == 4);
7164 assert(m_curStackHt >= 2);
7165 unsigned v1Ind = m_curStackHt - 2;
7166
7167 INT32 res = CompareOpRes<compOp>(v1Ind);
7168 if (reverse)
7169 {
7170 res = (res == 0) ? 1 : 0;
7171 }
7172
7173 if (res)
7174 {
7175 int offset;
7176 if (targetLen == 1)
7177 {
7178 // BYTE is unsigned...
7179 offset = getI1(m_ILCodePtr + 1);
7180 }
7181 else
7182 {
7183 offset = getI4LittleEndian(m_ILCodePtr + 1);
7184 }
7185 // 1 is the size of the current instruction; offset is relative to start of next.
7186 if (offset < 0)
7187 {
7188 // Backwards branch; enable caching.
7189 BackwardsBranchActions(offset);
7190 }
7191 ExecuteBranch(m_ILCodePtr + 1 + targetLen + offset);
7192 }
7193 else
7194 {
7195 m_ILCodePtr += targetLen + 1;
7196 }
7197 m_curStackHt -= 2;
7198}
7199
7200void Interpreter::LdFld(FieldDesc* fldIn)
7201{
7202 CONTRACTL {
7203 SO_TOLERANT;
7204 THROWS;
7205 GC_TRIGGERS;
7206 MODE_COOPERATIVE;
7207 } CONTRACTL_END;
7208
7209 BarrierIfVolatile();
7210
7211 FieldDesc* fld = fldIn;
7212 CORINFO_CLASS_HANDLE valClsHnd = NULL;
7213 DWORD fldOffset;
7214 {
7215 GCX_PREEMP();
7216 unsigned ilOffset = CurOffset();
7217 if (fld == NULL && s_InterpreterUseCaching)
7218 {
7219#if INTERP_TRACING
7220 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdFld]);
7221#endif // INTERP_TRACING
7222 fld = GetCachedInstanceField(ilOffset);
7223 }
7224 if (fld == NULL)
7225 {
7226 unsigned tok = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
7227 fld = FindField(tok InterpTracingArg(RTK_LdFld));
7228 assert(fld != NULL);
7229
7230 fldOffset = fld->GetOffset();
7231 if (s_InterpreterUseCaching && fldOffset < FIELD_OFFSET_LAST_REAL_OFFSET)
7232 CacheInstanceField(ilOffset, fld);
7233 }
7234 else
7235 {
7236 fldOffset = fld->GetOffset();
7237 }
7238 }
7239 CorInfoType valCit = CEEInfo::asCorInfoType(fld->GetFieldType());
7240
7241 // If "fldIn" is non-NULL, it's not a "real" LdFld -- the caller should handle updating the instruction pointer.
7242 if (fldIn == NULL)
7243 m_ILCodePtr += 5; // Last use above, so update now.
7244
7245 // We need to construct the interpreter type for a struct type before we try to do coordinated
7246 // pushes of the value and type on the opstacks -- these must be atomic wrt GC, and constructing
7247 // a struct InterpreterType transitions to preemptive mode.
7248 InterpreterType structValIT;
7249 if (valCit == CORINFO_TYPE_VALUECLASS)
7250 {
7251 GCX_PREEMP();
7252 valCit = m_interpCeeInfo.getFieldType(CORINFO_FIELD_HANDLE(fld), &valClsHnd);
7253 structValIT = InterpreterType(&m_interpCeeInfo, valClsHnd);
7254 }
7255
7256 UINT sz = fld->GetSize();
7257
7258 // Live vars: valCit, structValIt
7259 assert(m_curStackHt > 0);
7260 unsigned stackInd = m_curStackHt - 1;
7261 InterpreterType addrIt = OpStackTypeGet(stackInd);
7262 CorInfoType addrCit = addrIt.ToCorInfoType();
7263 bool isUnsigned;
7264
7265 if (addrCit == CORINFO_TYPE_CLASS)
7266 {
7267 OBJECTREF obj = OBJECTREF(OpStackGet<Object*>(stackInd));
7268 ThrowOnInvalidPointer(OBJECTREFToObject(obj));
7269 if (valCit == CORINFO_TYPE_VALUECLASS)
7270 {
7271 void* srcPtr = fld->GetInstanceAddress(obj);
7272
7273 // srcPtr is now vulnerable.
7274 GCX_FORBID();
7275
7276 MethodTable* valClsMT = GetMethodTableFromClsHnd(valClsHnd);
7277 if (sz > sizeof(INT64))
7278 {
7279 // Large struct case: allocate space on the large struct operand stack.
7280 void* destPtr = LargeStructOperandStackPush(sz);
7281 OpStackSet<void*>(stackInd, destPtr);
7282 CopyValueClass(destPtr, srcPtr, valClsMT, obj->GetAppDomain());
7283 }
7284 else
7285 {
7286 // Small struct case -- is inline in operand stack.
7287 OpStackSet<INT64>(stackInd, GetSmallStructValue(srcPtr, sz));
7288 }
7289 }
7290 else
7291 {
7292 BYTE* fldStart = dac_cast<PTR_BYTE>(OBJECTREFToObject(obj)) + sizeof(Object) + fldOffset;
7293 // fldStart is now a vulnerable byref
7294 GCX_FORBID();
7295
7296 switch (sz)
7297 {
7298 case 1:
7299 isUnsigned = CorInfoTypeIsUnsigned(valCit);
7300 if (isUnsigned)
7301 {
7302 OpStackSet<UINT32>(stackInd, *reinterpret_cast<UINT8*>(fldStart));
7303 }
7304 else
7305 {
7306 OpStackSet<INT32>(stackInd, *reinterpret_cast<INT8*>(fldStart));
7307 }
7308 break;
7309 case 2:
7310 isUnsigned = CorInfoTypeIsUnsigned(valCit);
7311 if (isUnsigned)
7312 {
7313 OpStackSet<UINT32>(stackInd, *reinterpret_cast<UINT16*>(fldStart));
7314 }
7315 else
7316 {
7317 OpStackSet<INT32>(stackInd, *reinterpret_cast<INT16*>(fldStart));
7318 }
7319 break;
7320 case 4:
7321 OpStackSet<INT32>(stackInd, *reinterpret_cast<INT32*>(fldStart));
7322 break;
7323 case 8:
7324 OpStackSet<INT64>(stackInd, *reinterpret_cast<INT64*>(fldStart));
7325 break;
7326 default:
7327 _ASSERTE_MSG(false, "Should not reach here.");
7328 break;
7329 }
7330 }
7331 }
7332 else
7333 {
7334 INT8* ptr = NULL;
7335 if (addrCit == CORINFO_TYPE_VALUECLASS)
7336 {
7337 size_t addrSize = addrIt.Size(&m_interpCeeInfo);
7338 // The ECMA spec allows ldfld to be applied to "an instance of a value type."
7339 // We will take the address of the ostack entry.
7340 if (addrIt.IsLargeStruct(&m_interpCeeInfo))
7341 {
7342 ptr = reinterpret_cast<INT8*>(OpStackGet<void*>(stackInd));
7343 // This is delicate. I'm going to pop the large struct off the large-struct stack
7344 // now, even though the field value we push may go back on the large object stack.
7345 // We rely on the fact that this instruction doesn't do any other pushing, and
7346 // we assume that LargeStructOperandStackPop does not actually deallocate any memory,
7347 // and we rely on memcpy properly handling possibly-overlapping regions being copied.
7348 // Finally (wow, this really *is* delicate), we rely on the property that the large-struct
7349 // stack pop operation doesn't deallocate memory (the size of the allocated memory for the
7350 // large-struct stack only grows in a method execution), and that if we push the field value
7351 // on the large struct stack below, the size of the pushed item is at most the size of the
7352 // popped item, so the stack won't grow (which would allow a dealloc/realloc).
7353 // (All in all, maybe it would be better to just copy the value elsewhere then pop...but
7354 // that wouldn't be very aggressive.)
7355 LargeStructOperandStackPop(addrSize, ptr);
7356 }
7357 else
7358 {
7359 ptr = reinterpret_cast<INT8*>(OpStackGetAddr(stackInd, addrSize));
7360 }
7361 }
7362 else
7363 {
7364 assert(CorInfoTypeIsPointer(addrCit));
7365 ptr = OpStackGet<INT8*>(stackInd);
7366 ThrowOnInvalidPointer(ptr);
7367 }
7368
7369 assert(ptr != NULL);
7370 ptr += fldOffset;
7371
7372 if (valCit == CORINFO_TYPE_VALUECLASS)
7373 {
7374 if (sz > sizeof(INT64))
7375 {
7376 // Large struct case.
7377 void* dstPtr = LargeStructOperandStackPush(sz);
7378 memcpy(dstPtr, ptr, sz);
7379 OpStackSet<void*>(stackInd, dstPtr);
7380 }
7381 else
7382 {
7383 // Small struct case -- is inline in operand stack.
7384 OpStackSet<INT64>(stackInd, GetSmallStructValue(ptr, sz));
7385 }
7386 OpStackTypeSet(stackInd, structValIT.StackNormalize());
7387 return;
7388 }
7389 // Otherwise...
7390 switch (sz)
7391 {
7392 case 1:
7393 isUnsigned = CorInfoTypeIsUnsigned(valCit);
7394 if (isUnsigned)
7395 {
7396 OpStackSet<UINT32>(stackInd, *reinterpret_cast<UINT8*>(ptr));
7397 }
7398 else
7399 {
7400 OpStackSet<INT32>(stackInd, *reinterpret_cast<INT8*>(ptr));
7401 }
7402 break;
7403 case 2:
7404 isUnsigned = CorInfoTypeIsUnsigned(valCit);
7405 if (isUnsigned)
7406 {
7407 OpStackSet<UINT32>(stackInd, *reinterpret_cast<UINT16*>(ptr));
7408 }
7409 else
7410 {
7411 OpStackSet<INT32>(stackInd, *reinterpret_cast<INT16*>(ptr));
7412 }
7413 break;
7414 case 4:
7415 OpStackSet<INT32>(stackInd, *reinterpret_cast<INT32*>(ptr));
7416 break;
7417 case 8:
7418 OpStackSet<INT64>(stackInd, *reinterpret_cast<INT64*>(ptr));
7419 break;
7420 }
7421 }
7422 if (valCit == CORINFO_TYPE_VALUECLASS)
7423 {
7424 OpStackTypeSet(stackInd, structValIT.StackNormalize());
7425 }
7426 else
7427 {
7428 OpStackTypeSet(stackInd, InterpreterType(valCit).StackNormalize());
7429 }
7430}
7431
7432void Interpreter::LdFldA()
7433{
7434 CONTRACTL {
7435 SO_TOLERANT;
7436 THROWS;
7437 GC_TRIGGERS;
7438 MODE_COOPERATIVE;
7439 } CONTRACTL_END;
7440
7441 unsigned tok = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
7442
7443#if INTERP_TRACING
7444 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdFldA]);
7445#endif // INTERP_TRACING
7446
7447 unsigned offset = CurOffset();
7448 m_ILCodePtr += 5; // Last use above, so update now.
7449
7450 FieldDesc* fld = NULL;
7451 if (s_InterpreterUseCaching) fld = GetCachedInstanceField(offset);
7452 if (fld == NULL)
7453 {
7454 GCX_PREEMP();
7455 fld = FindField(tok InterpTracingArg(RTK_LdFldA));
7456 if (s_InterpreterUseCaching) CacheInstanceField(offset, fld);
7457 }
7458 assert(m_curStackHt > 0);
7459 unsigned stackInd = m_curStackHt - 1;
7460 CorInfoType addrCit = OpStackTypeGet(stackInd).ToCorInfoType();
7461 if (addrCit == CORINFO_TYPE_BYREF || addrCit == CORINFO_TYPE_CLASS || addrCit == CORINFO_TYPE_NATIVEINT)
7462 {
7463 NativeInt ptr = OpStackGet<NativeInt>(stackInd);
7464 ThrowOnInvalidPointer((void*)ptr);
7465 // The "offset" below does not include the Object (i.e., the MethodTable pointer) for object pointers, so add that in first.
7466 if (addrCit == CORINFO_TYPE_CLASS) ptr += sizeof(Object);
7467 // Now add the offset.
7468 ptr += fld->GetOffset();
7469 OpStackSet<NativeInt>(stackInd, ptr);
7470 if (addrCit == CORINFO_TYPE_NATIVEINT)
7471 {
7472 OpStackTypeSet(stackInd, InterpreterType(CORINFO_TYPE_NATIVEINT));
7473 }
7474 else
7475 {
7476 OpStackTypeSet(stackInd, InterpreterType(CORINFO_TYPE_BYREF));
7477 }
7478 }
7479 else
7480 {
7481 VerificationError("LdfldA requires object reference, managed or unmanaged pointer type.");
7482 }
7483}
7484
7485void Interpreter::StFld()
7486{
7487 CONTRACTL {
7488 SO_TOLERANT;
7489 THROWS;
7490 GC_TRIGGERS;
7491 MODE_COOPERATIVE;
7492 } CONTRACTL_END;
7493
7494#if INTERP_TRACING
7495 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_StFld]);
7496#endif // INTERP_TRACING
7497
7498 FieldDesc* fld = NULL;
7499 DWORD fldOffset;
7500 {
7501 unsigned ilOffset = CurOffset();
7502 if (s_InterpreterUseCaching) fld = GetCachedInstanceField(ilOffset);
7503 if (fld == NULL)
7504 {
7505 unsigned tok = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
7506 GCX_PREEMP();
7507 fld = FindField(tok InterpTracingArg(RTK_StFld));
7508 assert(fld != NULL);
7509 fldOffset = fld->GetOffset();
7510 if (s_InterpreterUseCaching && fldOffset < FIELD_OFFSET_LAST_REAL_OFFSET)
7511 CacheInstanceField(ilOffset, fld);
7512 }
7513 else
7514 {
7515 fldOffset = fld->GetOffset();
7516 }
7517 }
7518 m_ILCodePtr += 5; // Last use above, so update now.
7519
7520 UINT sz = fld->GetSize();
7521 assert(m_curStackHt >= 2);
7522 unsigned addrInd = m_curStackHt - 2;
7523 CorInfoType addrCit = OpStackTypeGet(addrInd).ToCorInfoType();
7524 unsigned valInd = m_curStackHt - 1;
7525 CorInfoType valCit = OpStackTypeGet(valInd).ToCorInfoType();
7526 assert(IsStackNormalType(addrCit) && IsStackNormalType(valCit));
7527
7528 m_curStackHt -= 2;
7529
7530 if (addrCit == CORINFO_TYPE_CLASS)
7531 {
7532 OBJECTREF obj = OBJECTREF(OpStackGet<Object*>(addrInd));
7533 ThrowOnInvalidPointer(OBJECTREFToObject(obj));
7534
7535 if (valCit == CORINFO_TYPE_CLASS)
7536 {
7537 fld->SetRefValue(obj, ObjectToOBJECTREF(OpStackGet<Object*>(valInd)));
7538 }
7539 else if (valCit == CORINFO_TYPE_VALUECLASS)
7540 {
7541 MethodTable* valClsMT = GetMethodTableFromClsHnd(OpStackTypeGet(valInd).ToClassHandle());
7542 void* destPtr = fld->GetInstanceAddress(obj);
7543
7544 // destPtr is now a vulnerable byref, so can't do GC.
7545 GCX_FORBID();
7546
7547 // I use GCSafeMemCpy below to ensure that write barriers happen for the case in which
7548 // the value class contains GC pointers. We could do better...
7549 if (sz > sizeof(INT64))
7550 {
7551 // Large struct case: stack slot contains pointer...
7552 void* srcPtr = OpStackGet<void*>(valInd);
7553 CopyValueClassUnchecked(destPtr, srcPtr, valClsMT);
7554 LargeStructOperandStackPop(sz, srcPtr);
7555 }
7556 else
7557 {
7558 // Small struct case -- is inline in operand stack.
7559 CopyValueClassUnchecked(destPtr, OpStackGetAddr(valInd, sz), valClsMT);
7560 }
7561 BarrierIfVolatile();
7562 return;
7563 }
7564 else
7565 {
7566 BYTE* fldStart = dac_cast<PTR_BYTE>(OBJECTREFToObject(obj)) + sizeof(Object) + fldOffset;
7567 // fldStart is now a vulnerable byref
7568 GCX_FORBID();
7569
7570 switch (sz)
7571 {
7572 case 1:
7573 *reinterpret_cast<INT8*>(fldStart) = OpStackGet<INT8>(valInd);
7574 break;
7575 case 2:
7576 *reinterpret_cast<INT16*>(fldStart) = OpStackGet<INT16>(valInd);
7577 break;
7578 case 4:
7579 *reinterpret_cast<INT32*>(fldStart) = OpStackGet<INT32>(valInd);
7580 break;
7581 case 8:
7582 *reinterpret_cast<INT64*>(fldStart) = OpStackGet<INT64>(valInd);
7583 break;
7584 }
7585 }
7586 }
7587 else
7588 {
7589 assert(addrCit == CORINFO_TYPE_BYREF || addrCit == CORINFO_TYPE_NATIVEINT);
7590
7591 INT8* destPtr = OpStackGet<INT8*>(addrInd);
7592 ThrowOnInvalidPointer(destPtr);
7593 destPtr += fldOffset;
7594
7595 if (valCit == CORINFO_TYPE_VALUECLASS)
7596 {
7597 MethodTable* valClsMT = GetMethodTableFromClsHnd(OpStackTypeGet(valInd).ToClassHandle());
7598 // I use GCSafeMemCpy below to ensure that write barriers happen for the case in which
7599 // the value class contains GC pointers. We could do better...
7600 if (sz > sizeof(INT64))
7601 {
7602 // Large struct case: stack slot contains pointer...
7603 void* srcPtr = OpStackGet<void*>(valInd);
7604 CopyValueClassUnchecked(destPtr, srcPtr, valClsMT);
7605 LargeStructOperandStackPop(sz, srcPtr);
7606 }
7607 else
7608 {
7609 // Small struct case -- is inline in operand stack.
7610 CopyValueClassUnchecked(destPtr, OpStackGetAddr(valInd, sz), valClsMT);
7611 }
7612 BarrierIfVolatile();
7613 return;
7614 }
7615 else if (valCit == CORINFO_TYPE_CLASS)
7616 {
7617 OBJECTREF val = ObjectToOBJECTREF(OpStackGet<Object*>(valInd));
7618 SetObjectReferenceUnchecked(reinterpret_cast<OBJECTREF*>(destPtr), val);
7619 }
7620 else
7621 {
7622 switch (sz)
7623 {
7624 case 1:
7625 *reinterpret_cast<INT8*>(destPtr) = OpStackGet<INT8>(valInd);
7626 break;
7627 case 2:
7628 *reinterpret_cast<INT16*>(destPtr) = OpStackGet<INT16>(valInd);
7629 break;
7630 case 4:
7631 *reinterpret_cast<INT32*>(destPtr) = OpStackGet<INT32>(valInd);
7632 break;
7633 case 8:
7634 *reinterpret_cast<INT64*>(destPtr) = OpStackGet<INT64>(valInd);
7635 break;
7636 }
7637 }
7638 }
7639 BarrierIfVolatile();
7640}
7641
7642bool Interpreter::StaticFldAddrWork(CORINFO_ACCESS_FLAGS accessFlgs, /*out (byref)*/void** pStaticFieldAddr, /*out*/InterpreterType* pit, /*out*/UINT* pFldSize, /*out*/bool* pManagedMem)
7643{
7644 CONTRACTL {
7645 SO_TOLERANT;
7646 THROWS;
7647 GC_TRIGGERS;
7648 MODE_COOPERATIVE;
7649 } CONTRACTL_END;
7650
7651 bool isCacheable = true;
7652 *pManagedMem = true; // Default result.
7653
7654 unsigned tok = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
7655 m_ILCodePtr += 5; // Above is last use of m_ILCodePtr in this method, so update now.
7656
7657 FieldDesc* fld;
7658 CORINFO_FIELD_INFO fldInfo;
7659 CORINFO_RESOLVED_TOKEN fldTok;
7660
7661 void* pFldAddr = NULL;
7662 {
7663 {
7664 GCX_PREEMP();
7665
7666 ResolveToken(&fldTok, tok, CORINFO_TOKENKIND_Field InterpTracingArg(RTK_SFldAddr));
7667 fld = reinterpret_cast<FieldDesc*>(fldTok.hField);
7668
7669 m_interpCeeInfo.getFieldInfo(&fldTok, m_methInfo->m_method, accessFlgs, &fldInfo);
7670 }
7671
7672 EnsureClassInit(GetMethodTableFromClsHnd(fldTok.hClass));
7673
7674 if (fldInfo.fieldAccessor == CORINFO_FIELD_STATIC_TLS)
7675 {
7676 NYI_INTERP("Thread-local static.");
7677 }
7678 else if (fldInfo.fieldAccessor == CORINFO_FIELD_STATIC_SHARED_STATIC_HELPER
7679 || fldInfo.fieldAccessor == CORINFO_FIELD_STATIC_GENERICS_STATIC_HELPER)
7680 {
7681 *pStaticFieldAddr = fld->GetCurrentStaticAddress();
7682 isCacheable = false;
7683 }
7684 else
7685 {
7686 *pStaticFieldAddr = fld->GetCurrentStaticAddress();
7687 }
7688 }
7689 if (fldInfo.structType != NULL && fldInfo.fieldType != CORINFO_TYPE_CLASS && fldInfo.fieldType != CORINFO_TYPE_PTR)
7690 {
7691 *pit = InterpreterType(&m_interpCeeInfo, fldInfo.structType);
7692
7693 if ((fldInfo.fieldFlags & CORINFO_FLG_FIELD_UNMANAGED) == 0)
7694 {
7695 // For valuetypes in managed memory, the address returned contains a pointer into the heap, to a boxed version of the
7696 // static variable; return a pointer to the boxed struct.
7697 isCacheable = false;
7698 }
7699 else
7700 {
7701 *pManagedMem = false;
7702 }
7703 }
7704 else
7705 {
7706 *pit = InterpreterType(fldInfo.fieldType);
7707 }
7708 *pFldSize = fld->GetSize();
7709
7710 return isCacheable;
7711}
7712
7713void Interpreter::LdSFld()
7714{
7715 CONTRACTL {
7716 SO_TOLERANT;
7717 THROWS;
7718 GC_TRIGGERS;
7719 MODE_COOPERATIVE;
7720 } CONTRACTL_END;
7721
7722 InterpreterType fldIt;
7723 UINT sz;
7724 bool managedMem;
7725 void* srcPtr = NULL;
7726
7727 BarrierIfVolatile();
7728
7729 GCPROTECT_BEGININTERIOR(srcPtr);
7730
7731 StaticFldAddr(CORINFO_ACCESS_GET, &srcPtr, &fldIt, &sz, &managedMem);
7732
7733 bool isUnsigned;
7734
7735 if (fldIt.IsStruct())
7736 {
7737 // Large struct case.
7738 CORINFO_CLASS_HANDLE sh = fldIt.ToClassHandle();
7739 // This call is GC_TRIGGERS, so do it before we copy the value: no GC after this,
7740 // until the op stacks and ht are consistent.
7741 OpStackTypeSet(m_curStackHt, InterpreterType(&m_interpCeeInfo, sh).StackNormalize());
7742 if (fldIt.IsLargeStruct(&m_interpCeeInfo))
7743 {
7744 void* dstPtr = LargeStructOperandStackPush(sz);
7745 memcpy(dstPtr, srcPtr, sz);
7746 OpStackSet<void*>(m_curStackHt, dstPtr);
7747 }
7748 else
7749 {
7750 OpStackSet<INT64>(m_curStackHt, GetSmallStructValue(srcPtr, sz));
7751 }
7752 }
7753 else
7754 {
7755 CorInfoType valCit = fldIt.ToCorInfoType();
7756 switch (sz)
7757 {
7758 case 1:
7759 isUnsigned = CorInfoTypeIsUnsigned(valCit);
7760 if (isUnsigned)
7761 {
7762 OpStackSet<UINT32>(m_curStackHt, *reinterpret_cast<UINT8*>(srcPtr));
7763 }
7764 else
7765 {
7766 OpStackSet<INT32>(m_curStackHt, *reinterpret_cast<INT8*>(srcPtr));
7767 }
7768 break;
7769 case 2:
7770 isUnsigned = CorInfoTypeIsUnsigned(valCit);
7771 if (isUnsigned)
7772 {
7773 OpStackSet<UINT32>(m_curStackHt, *reinterpret_cast<UINT16*>(srcPtr));
7774 }
7775 else
7776 {
7777 OpStackSet<INT32>(m_curStackHt, *reinterpret_cast<INT16*>(srcPtr));
7778 }
7779 break;
7780 case 4:
7781 OpStackSet<INT32>(m_curStackHt, *reinterpret_cast<INT32*>(srcPtr));
7782 break;
7783 case 8:
7784 OpStackSet<INT64>(m_curStackHt, *reinterpret_cast<INT64*>(srcPtr));
7785 break;
7786 default:
7787 _ASSERTE_MSG(false, "LdSFld: this should have exhausted all the possible sizes.");
7788 break;
7789 }
7790 OpStackTypeSet(m_curStackHt, fldIt.StackNormalize());
7791 }
7792 m_curStackHt++;
7793 GCPROTECT_END();
7794}
7795
7796void Interpreter::EnsureClassInit(MethodTable* pMT)
7797{
7798 if (!pMT->IsClassInited())
7799 {
7800 pMT->CheckRestore();
7801 // This is tantamount to a call, so exempt it from the cycle count.
7802#if INTERP_ILCYCLE_PROFILE
7803 unsigned __int64 startCycles;
7804 bool b = CycleTimer::GetThreadCyclesS(&startCycles); assert(b);
7805#endif // INTERP_ILCYCLE_PROFILE
7806
7807 pMT->CheckRunClassInitThrowing();
7808
7809#if INTERP_ILCYCLE_PROFILE
7810 unsigned __int64 endCycles;
7811 b = CycleTimer::GetThreadCyclesS(&endCycles); assert(b);
7812 m_exemptCycles += (endCycles - startCycles);
7813#endif // INTERP_ILCYCLE_PROFILE
7814 }
7815}
7816
7817void Interpreter::LdSFldA()
7818{
7819 CONTRACTL {
7820 SO_TOLERANT;
7821 THROWS;
7822 GC_TRIGGERS;
7823 MODE_COOPERATIVE;
7824 } CONTRACTL_END;
7825
7826 InterpreterType fldIt;
7827 UINT fldSz;
7828 bool managedMem;
7829 void* srcPtr = NULL;
7830 GCPROTECT_BEGININTERIOR(srcPtr);
7831
7832 StaticFldAddr(CORINFO_ACCESS_ADDRESS, &srcPtr, &fldIt, &fldSz, &managedMem);
7833
7834 OpStackSet<void*>(m_curStackHt, srcPtr);
7835 if (managedMem)
7836 {
7837 // Static variable in managed memory...
7838 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_BYREF));
7839 }
7840 else
7841 {
7842 // RVA is in unmanaged memory.
7843 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT));
7844 }
7845 m_curStackHt++;
7846
7847 GCPROTECT_END();
7848}
7849
7850void Interpreter::StSFld()
7851{
7852 CONTRACTL {
7853 SO_TOLERANT;
7854 THROWS;
7855 GC_TRIGGERS;
7856 MODE_COOPERATIVE;
7857 } CONTRACTL_END;
7858 InterpreterType fldIt;
7859 UINT sz;
7860 bool managedMem;
7861 void* dstPtr = NULL;
7862 GCPROTECT_BEGININTERIOR(dstPtr);
7863
7864 StaticFldAddr(CORINFO_ACCESS_SET, &dstPtr, &fldIt, &sz, &managedMem);
7865
7866 m_curStackHt--;
7867 InterpreterType valIt = OpStackTypeGet(m_curStackHt);
7868 CorInfoType valCit = valIt.ToCorInfoType();
7869
7870 if (valCit == CORINFO_TYPE_VALUECLASS)
7871 {
7872 MethodTable* valClsMT = GetMethodTableFromClsHnd(valIt.ToClassHandle());
7873 if (sz > sizeof(INT64))
7874 {
7875 // Large struct case: value in operand stack is indirect pointer.
7876 void* srcPtr = OpStackGet<void*>(m_curStackHt);
7877 CopyValueClassUnchecked(dstPtr, srcPtr, valClsMT);
7878 LargeStructOperandStackPop(sz, srcPtr);
7879 }
7880 else
7881 {
7882 // Struct value is inline in the operand stack.
7883 CopyValueClassUnchecked(dstPtr, OpStackGetAddr(m_curStackHt, sz), valClsMT);
7884 }
7885 }
7886 else if (valCit == CORINFO_TYPE_CLASS)
7887 {
7888 SetObjectReferenceUnchecked(reinterpret_cast<OBJECTREF*>(dstPtr), ObjectToOBJECTREF(OpStackGet<Object*>(m_curStackHt)));
7889 }
7890 else
7891 {
7892 switch (sz)
7893 {
7894 case 1:
7895 *reinterpret_cast<UINT8*>(dstPtr) = OpStackGet<UINT8>(m_curStackHt);
7896 break;
7897 case 2:
7898 *reinterpret_cast<UINT16*>(dstPtr) = OpStackGet<UINT16>(m_curStackHt);
7899 break;
7900 case 4:
7901 *reinterpret_cast<UINT32*>(dstPtr) = OpStackGet<UINT32>(m_curStackHt);
7902 break;
7903 case 8:
7904 *reinterpret_cast<UINT64*>(dstPtr) = OpStackGet<UINT64>(m_curStackHt);
7905 break;
7906 default:
7907 _ASSERTE_MSG(false, "This should have exhausted all the possible sizes.");
7908 break;
7909 }
7910 }
7911 GCPROTECT_END();
7912
7913 BarrierIfVolatile();
7914}
7915
7916template<typename T, bool IsObjType, CorInfoType cit>
7917void Interpreter::LdElemWithType()
7918{
7919 CONTRACTL {
7920 SO_TOLERANT;
7921 THROWS;
7922 GC_TRIGGERS;
7923 MODE_COOPERATIVE;
7924 } CONTRACTL_END;
7925
7926 assert(m_curStackHt >= 2);
7927 unsigned arrInd = m_curStackHt - 2;
7928 unsigned indexInd = m_curStackHt - 1;
7929
7930 assert(OpStackTypeGet(arrInd).ToCorInfoType() == CORINFO_TYPE_CLASS);
7931
7932 ArrayBase* a = OpStackGet<ArrayBase*>(arrInd);
7933 ThrowOnInvalidPointer(a);
7934 int len = a->GetNumComponents();
7935
7936 CorInfoType indexCit = OpStackTypeGet(indexInd).ToCorInfoType();
7937 if (indexCit == CORINFO_TYPE_INT)
7938 {
7939 int index = OpStackGet<INT32>(indexInd);
7940 if (index < 0 || index >= len) ThrowArrayBoundsException();
7941
7942 GCX_FORBID();
7943
7944 if (IsObjType)
7945 {
7946 OBJECTREF res = reinterpret_cast<PtrArray*>(a)->GetAt(index);
7947 OpStackSet<OBJECTREF>(arrInd, res);
7948 }
7949 else
7950 {
7951 T res = reinterpret_cast<Array<T>*>(a)->GetDirectConstPointerToNonObjectElements()[index];
7952 if (cit == CORINFO_TYPE_INT)
7953 {
7954 // Widen narrow types.
7955 int ires = (int)res;
7956 OpStackSet<int>(arrInd, ires);
7957 }
7958 else
7959 {
7960 OpStackSet<T>(arrInd, res);
7961 }
7962 }
7963 }
7964 else
7965 {
7966 assert(indexCit == CORINFO_TYPE_NATIVEINT);
7967 NativeInt index = OpStackGet<NativeInt>(indexInd);
7968 if (index < 0 || index >= NativeInt(len)) ThrowArrayBoundsException();
7969
7970 GCX_FORBID();
7971
7972 if (IsObjType)
7973 {
7974 OBJECTREF res = reinterpret_cast<PtrArray*>(a)->GetAt(index);
7975 OpStackSet<OBJECTREF>(arrInd, res);
7976 }
7977 else
7978 {
7979 T res = reinterpret_cast<Array<T>*>(a)->GetDirectConstPointerToNonObjectElements()[index];
7980 OpStackSet<T>(arrInd, res);
7981 }
7982 }
7983
7984 OpStackTypeSet(arrInd, InterpreterType(cit));
7985 m_curStackHt--;
7986}
7987
7988template<typename T, bool IsObjType>
7989void Interpreter::StElemWithType()
7990{
7991 CONTRACTL {
7992 SO_TOLERANT;
7993 THROWS;
7994 GC_TRIGGERS;
7995 MODE_COOPERATIVE;
7996 } CONTRACTL_END;
7997
7998
7999 assert(m_curStackHt >= 3);
8000 unsigned arrInd = m_curStackHt - 3;
8001 unsigned indexInd = m_curStackHt - 2;
8002 unsigned valInd = m_curStackHt - 1;
8003
8004 assert(OpStackTypeGet(arrInd).ToCorInfoType() == CORINFO_TYPE_CLASS);
8005
8006 ArrayBase* a = OpStackGet<ArrayBase*>(arrInd);
8007 ThrowOnInvalidPointer(a);
8008 int len = a->GetNumComponents();
8009
8010 CorInfoType indexCit = OpStackTypeGet(indexInd).ToCorInfoType();
8011 if (indexCit == CORINFO_TYPE_INT)
8012 {
8013 int index = OpStackGet<INT32>(indexInd);
8014 if (index < 0 || index >= len) ThrowArrayBoundsException();
8015 if (IsObjType)
8016 {
8017 struct _gc {
8018 OBJECTREF val;
8019 OBJECTREF a;
8020 } gc;
8021 gc.val = ObjectToOBJECTREF(OpStackGet<Object*>(valInd));
8022 gc.a = ObjectToOBJECTREF(a);
8023 GCPROTECT_BEGIN(gc);
8024 if (gc.val != NULL &&
8025 !ObjIsInstanceOf(OBJECTREFToObject(gc.val), reinterpret_cast<PtrArray*>(a)->GetArrayElementTypeHandle()))
8026 COMPlusThrow(kArrayTypeMismatchException);
8027 reinterpret_cast<PtrArray*>(OBJECTREFToObject(gc.a))->SetAt(index, gc.val);
8028 GCPROTECT_END();
8029 }
8030 else
8031 {
8032 GCX_FORBID();
8033 T val = OpStackGet<T>(valInd);
8034 reinterpret_cast<Array<T>*>(a)->GetDirectPointerToNonObjectElements()[index] = val;
8035 }
8036 }
8037 else
8038 {
8039 assert(indexCit == CORINFO_TYPE_NATIVEINT);
8040 NativeInt index = OpStackGet<NativeInt>(indexInd);
8041 if (index < 0 || index >= NativeInt(len)) ThrowArrayBoundsException();
8042 if (IsObjType)
8043 {
8044 struct _gc {
8045 OBJECTREF val;
8046 OBJECTREF a;
8047 } gc;
8048 gc.val = ObjectToOBJECTREF(OpStackGet<Object*>(valInd));
8049 gc.a = ObjectToOBJECTREF(a);
8050 GCPROTECT_BEGIN(gc);
8051 if (gc.val != NULL &&
8052 !ObjIsInstanceOf(OBJECTREFToObject(gc.val), reinterpret_cast<PtrArray*>(a)->GetArrayElementTypeHandle()))
8053 COMPlusThrow(kArrayTypeMismatchException);
8054 reinterpret_cast<PtrArray*>(OBJECTREFToObject(gc.a))->SetAt(index, gc.val);
8055 GCPROTECT_END();
8056 }
8057 else
8058 {
8059 GCX_FORBID();
8060 T val = OpStackGet<T>(valInd);
8061 reinterpret_cast<Array<T>*>(a)->GetDirectPointerToNonObjectElements()[index] = val;
8062 }
8063 }
8064
8065 m_curStackHt -= 3;
8066}
8067
8068template<bool takeAddress>
8069void Interpreter::LdElem()
8070{
8071 CONTRACTL {
8072 SO_TOLERANT;
8073 THROWS;
8074 GC_TRIGGERS;
8075 MODE_COOPERATIVE;
8076 } CONTRACTL_END;
8077
8078 assert(m_curStackHt >= 2);
8079 unsigned arrInd = m_curStackHt - 2;
8080 unsigned indexInd = m_curStackHt - 1;
8081
8082 unsigned elemTypeTok = getU4LittleEndian(m_ILCodePtr + 1);
8083
8084#if INTERP_TRACING
8085 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_LdElem]);
8086#endif // INTERP_TRACING
8087
8088 unsigned ilOffset = CurOffset();
8089 CORINFO_CLASS_HANDLE clsHnd = NULL;
8090 if (s_InterpreterUseCaching) clsHnd = GetCachedClassHandle(ilOffset);
8091
8092 if (clsHnd == NULL)
8093 {
8094
8095 CORINFO_RESOLVED_TOKEN elemTypeResolvedTok;
8096 {
8097 GCX_PREEMP();
8098 ResolveToken(&elemTypeResolvedTok, elemTypeTok, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_LdElem));
8099 clsHnd = elemTypeResolvedTok.hClass;
8100 }
8101 if (s_InterpreterUseCaching) CacheClassHandle(ilOffset, clsHnd);
8102 }
8103
8104 CorInfoType elemCit = ::asCorInfoType(clsHnd);
8105
8106 m_ILCodePtr += 5;
8107
8108
8109 InterpreterType elemIt;
8110 if (elemCit == CORINFO_TYPE_VALUECLASS)
8111 {
8112 elemIt = InterpreterType(&m_interpCeeInfo, clsHnd);
8113 }
8114 else
8115 {
8116 elemIt = InterpreterType(elemCit);
8117 }
8118
8119 assert(OpStackTypeGet(arrInd).ToCorInfoType() == CORINFO_TYPE_CLASS);
8120
8121
8122 ArrayBase* a = OpStackGet<ArrayBase*>(arrInd);
8123 ThrowOnInvalidPointer(a);
8124 int len = a->GetNumComponents();
8125
8126 NativeInt index;
8127 {
8128 GCX_FORBID();
8129
8130 CorInfoType indexCit = OpStackTypeGet(indexInd).ToCorInfoType();
8131 if (indexCit == CORINFO_TYPE_INT)
8132 {
8133 index = static_cast<NativeInt>(OpStackGet<INT32>(indexInd));
8134 }
8135 else
8136 {
8137 assert(indexCit == CORINFO_TYPE_NATIVEINT);
8138 index = OpStackGet<NativeInt>(indexInd);
8139 }
8140 }
8141 if (index < 0 || index >= len) ThrowArrayBoundsException();
8142
8143 bool throwTypeMismatch = NULL;
8144 {
8145 void* elemPtr = a->GetDataPtr() + a->GetComponentSize() * index;
8146 // elemPtr is now a vulnerable byref.
8147 GCX_FORBID();
8148
8149 if (takeAddress)
8150 {
8151 // If the element type is a class type, may have to do a type check.
8152 if (elemCit == CORINFO_TYPE_CLASS)
8153 {
8154 // Unless there was a readonly prefix, which removes the need to
8155 // do the (dynamic) type check.
8156 if (m_readonlyFlag)
8157 {
8158 // Consume the readonly prefix, and don't do the type check below.
8159 m_readonlyFlag = false;
8160 }
8161 else
8162 {
8163 PtrArray* pa = reinterpret_cast<PtrArray*>(a);
8164 // The element array type must be exactly the referent type of the managed
8165 // pointer we'll be creating.
8166 if (pa->GetArrayElementTypeHandle() != TypeHandle(clsHnd))
8167 {
8168 throwTypeMismatch = true;
8169 }
8170 }
8171 }
8172 if (!throwTypeMismatch)
8173 {
8174 // If we're not going to throw the exception, we can take the address.
8175 OpStackSet<void*>(arrInd, elemPtr);
8176 OpStackTypeSet(arrInd, InterpreterType(CORINFO_TYPE_BYREF));
8177 m_curStackHt--;
8178 }
8179 }
8180 else
8181 {
8182 m_curStackHt -= 2;
8183 LdFromMemAddr(elemPtr, elemIt);
8184 return;
8185 }
8186 }
8187
8188 // If we're going to throw, we do the throw outside the GCX_FORBID region above, since it requires GC_TRIGGERS.
8189 if (throwTypeMismatch)
8190 {
8191 COMPlusThrow(kArrayTypeMismatchException);
8192 }
8193}
8194
8195void Interpreter::StElem()
8196{
8197 CONTRACTL {
8198 SO_TOLERANT;
8199 THROWS;
8200 GC_TRIGGERS;
8201 MODE_COOPERATIVE;
8202 } CONTRACTL_END;
8203
8204 assert(m_curStackHt >= 3);
8205 unsigned arrInd = m_curStackHt - 3;
8206 unsigned indexInd = m_curStackHt - 2;
8207 unsigned valInd = m_curStackHt - 1;
8208
8209 CorInfoType valCit = OpStackTypeGet(valInd).ToCorInfoType();
8210
8211#if INTERP_TRACING
8212 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_StElem]);
8213#endif // INTERP_TRACING
8214
8215 CORINFO_CLASS_HANDLE typeFromTok = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_StElem));
8216
8217 m_ILCodePtr += 5;
8218
8219 CorInfoType typeFromTokCit;
8220 {
8221 GCX_PREEMP();
8222 typeFromTokCit = ::asCorInfoType(typeFromTok);
8223 }
8224 size_t sz;
8225
8226#ifdef _DEBUG
8227 InterpreterType typeFromTokIt;
8228#endif // _DEBUG
8229
8230 if (typeFromTokCit == CORINFO_TYPE_VALUECLASS)
8231 {
8232 GCX_PREEMP();
8233 sz = getClassSize(typeFromTok);
8234#ifdef _DEBUG
8235 typeFromTokIt = InterpreterType(&m_interpCeeInfo, typeFromTok);
8236#endif // _DEBUG
8237 }
8238 else
8239 {
8240 sz = CorInfoTypeSize(typeFromTokCit);
8241#ifdef _DEBUG
8242 typeFromTokIt = InterpreterType(typeFromTokCit);
8243#endif // _DEBUG
8244 }
8245
8246#ifdef _DEBUG
8247 // Instead of debug, I need to parameterize the interpreter at the top level over whether
8248 // to do checks corresponding to verification.
8249 if (typeFromTokIt.StackNormalize().ToCorInfoType() != valCit)
8250 {
8251 // This is obviously only a partial test of the required condition.
8252 VerificationError("Value in stelem does not have the required type.");
8253 }
8254#endif // _DEBUG
8255
8256 assert(OpStackTypeGet(arrInd).ToCorInfoType() == CORINFO_TYPE_CLASS);
8257
8258 ArrayBase* a = OpStackGet<ArrayBase*>(arrInd);
8259 ThrowOnInvalidPointer(a);
8260 int len = a->GetNumComponents();
8261
8262 CorInfoType indexCit = OpStackTypeGet(indexInd).ToCorInfoType();
8263 NativeInt index = 0;
8264 if (indexCit == CORINFO_TYPE_INT)
8265 {
8266 index = static_cast<NativeInt>(OpStackGet<INT32>(indexInd));
8267 }
8268 else
8269 {
8270 index = OpStackGet<NativeInt>(indexInd);
8271 }
8272
8273 if (index < 0 || index >= len) ThrowArrayBoundsException();
8274
8275 if (typeFromTokCit == CORINFO_TYPE_CLASS)
8276 {
8277 struct _gc {
8278 OBJECTREF val;
8279 OBJECTREF a;
8280 } gc;
8281 gc.val = ObjectToOBJECTREF(OpStackGet<Object*>(valInd));
8282 gc.a = ObjectToOBJECTREF(a);
8283 GCPROTECT_BEGIN(gc);
8284 if (gc.val != NULL &&
8285 !ObjIsInstanceOf(OBJECTREFToObject(gc.val), reinterpret_cast<PtrArray*>(a)->GetArrayElementTypeHandle()))
8286 COMPlusThrow(kArrayTypeMismatchException);
8287 reinterpret_cast<PtrArray*>(OBJECTREFToObject(gc.a))->SetAt(index, gc.val);
8288 GCPROTECT_END();
8289 }
8290 else
8291 {
8292 GCX_FORBID();
8293
8294 void* destPtr = a->GetDataPtr() + index * sz;;
8295
8296 if (typeFromTokCit == CORINFO_TYPE_VALUECLASS)
8297 {
8298 MethodTable* valClsMT = GetMethodTableFromClsHnd(OpStackTypeGet(valInd).ToClassHandle());
8299 // I use GCSafeMemCpy below to ensure that write barriers happen for the case in which
8300 // the value class contains GC pointers. We could do better...
8301 if (sz > sizeof(UINT64))
8302 {
8303 // Large struct case: stack slot contains pointer...
8304 void* src = OpStackGet<void*>(valInd);
8305 CopyValueClassUnchecked(destPtr, src, valClsMT);
8306 LargeStructOperandStackPop(sz, src);
8307 }
8308 else
8309 {
8310 // Small struct case -- is inline in operand stack.
8311 CopyValueClassUnchecked(destPtr, OpStackGetAddr(valInd, sz), valClsMT);
8312 }
8313 }
8314 else
8315 {
8316 switch (sz)
8317 {
8318 case 1:
8319 *reinterpret_cast<INT8*>(destPtr) = OpStackGet<INT8>(valInd);
8320 break;
8321 case 2:
8322 *reinterpret_cast<INT16*>(destPtr) = OpStackGet<INT16>(valInd);
8323 break;
8324 case 4:
8325 *reinterpret_cast<INT32*>(destPtr) = OpStackGet<INT32>(valInd);
8326 break;
8327 case 8:
8328 *reinterpret_cast<INT64*>(destPtr) = OpStackGet<INT64>(valInd);
8329 break;
8330 }
8331 }
8332 }
8333
8334 m_curStackHt -= 3;
8335}
8336
8337void Interpreter::InitBlk()
8338{
8339 CONTRACTL {
8340 SO_TOLERANT;
8341 THROWS;
8342 GC_TRIGGERS;
8343 MODE_COOPERATIVE;
8344 } CONTRACTL_END;
8345
8346 assert(m_curStackHt >= 3);
8347 unsigned addrInd = m_curStackHt - 3;
8348 unsigned valInd = m_curStackHt - 2;
8349 unsigned sizeInd = m_curStackHt - 1;
8350
8351#ifdef _DEBUG
8352 CorInfoType addrCIT = OpStackTypeGet(addrInd).ToCorInfoType();
8353 bool addrValidType = (addrCIT == CORINFO_TYPE_NATIVEINT || addrCIT == CORINFO_TYPE_BYREF);
8354#if defined(_AMD64_)
8355 if (s_InterpreterLooseRules && addrCIT == CORINFO_TYPE_LONG)
8356 addrValidType = true;
8357#endif
8358 if (!addrValidType)
8359 VerificationError("Addr of InitBlk must be native int or &.");
8360
8361 CorInfoType valCIT = OpStackTypeGet(valInd).ToCorInfoType();
8362 if (valCIT != CORINFO_TYPE_INT)
8363 VerificationError("Value of InitBlk must be int");
8364
8365#endif // _DEBUG
8366
8367 CorInfoType sizeCIT = OpStackTypeGet(sizeInd).ToCorInfoType();
8368 bool isLong = s_InterpreterLooseRules && (sizeCIT == CORINFO_TYPE_LONG);
8369
8370#ifdef _DEBUG
8371 if (sizeCIT != CORINFO_TYPE_INT && !isLong)
8372 VerificationError("Size of InitBlk must be int");
8373#endif // _DEBUG
8374
8375 void* addr = OpStackGet<void*>(addrInd);
8376 ThrowOnInvalidPointer(addr);
8377 GCX_FORBID(); // addr is a potentially vulnerable byref.
8378 INT8 val = OpStackGet<INT8>(valInd);
8379 size_t size = (size_t) ((isLong) ? OpStackGet<UINT64>(sizeInd) : OpStackGet<UINT32>(sizeInd));
8380 memset(addr, val, size);
8381
8382 m_curStackHt = addrInd;
8383 m_ILCodePtr += 2;
8384
8385 BarrierIfVolatile();
8386}
8387
8388void Interpreter::CpBlk()
8389{
8390 CONTRACTL {
8391 SO_TOLERANT;
8392 THROWS;
8393 GC_TRIGGERS;
8394 MODE_COOPERATIVE;
8395 } CONTRACTL_END;
8396
8397 assert(m_curStackHt >= 3);
8398 unsigned destInd = m_curStackHt - 3;
8399 unsigned srcInd = m_curStackHt - 2;
8400 unsigned sizeInd = m_curStackHt - 1;
8401
8402#ifdef _DEBUG
8403 CorInfoType destCIT = OpStackTypeGet(destInd).ToCorInfoType();
8404 bool destValidType = (destCIT == CORINFO_TYPE_NATIVEINT || destCIT == CORINFO_TYPE_BYREF);
8405#if defined(_AMD64_)
8406 if (s_InterpreterLooseRules && destCIT == CORINFO_TYPE_LONG)
8407 destValidType = true;
8408#endif
8409 if (!destValidType)
8410 {
8411 VerificationError("Dest addr of CpBlk must be native int or &.");
8412 }
8413 CorInfoType srcCIT = OpStackTypeGet(srcInd).ToCorInfoType();
8414 bool srcValidType = (srcCIT == CORINFO_TYPE_NATIVEINT || srcCIT == CORINFO_TYPE_BYREF);
8415#if defined(_AMD64_)
8416 if (s_InterpreterLooseRules && srcCIT == CORINFO_TYPE_LONG)
8417 srcValidType = true;
8418#endif
8419 if (!srcValidType)
8420 VerificationError("Src addr of CpBlk must be native int or &.");
8421#endif // _DEBUG
8422
8423 CorInfoType sizeCIT = OpStackTypeGet(sizeInd).ToCorInfoType();
8424 bool isLong = s_InterpreterLooseRules && (sizeCIT == CORINFO_TYPE_LONG);
8425
8426#ifdef _DEBUG
8427 if (sizeCIT != CORINFO_TYPE_INT && !isLong)
8428 VerificationError("Size of CpBlk must be int");
8429#endif // _DEBUG
8430
8431
8432 void* destAddr = OpStackGet<void*>(destInd);
8433 void* srcAddr = OpStackGet<void*>(srcInd);
8434 ThrowOnInvalidPointer(destAddr);
8435 ThrowOnInvalidPointer(srcAddr);
8436 GCX_FORBID(); // destAddr & srcAddr are potentially vulnerable byrefs.
8437 size_t size = (size_t)((isLong) ? OpStackGet<UINT64>(sizeInd) : OpStackGet<UINT32>(sizeInd));
8438 memcpyNoGCRefs(destAddr, srcAddr, size);
8439
8440 m_curStackHt = destInd;
8441 m_ILCodePtr += 2;
8442
8443 BarrierIfVolatile();
8444}
8445
8446void Interpreter::Box()
8447{
8448 CONTRACTL {
8449 SO_TOLERANT;
8450 THROWS;
8451 GC_TRIGGERS;
8452 MODE_COOPERATIVE;
8453 } CONTRACTL_END;
8454
8455 assert(m_curStackHt >= 1);
8456 unsigned ind = m_curStackHt - 1;
8457
8458 DWORD boxTypeAttribs = 0;
8459
8460#if INTERP_TRACING
8461 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_Box]);
8462#endif // INTERP_TRACING
8463
8464 CORINFO_CLASS_HANDLE boxTypeClsHnd = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_Box));
8465
8466 {
8467 GCX_PREEMP();
8468 boxTypeAttribs = m_interpCeeInfo.getClassAttribs(boxTypeClsHnd);
8469 }
8470
8471 m_ILCodePtr += 5;
8472
8473 if (boxTypeAttribs & CORINFO_FLG_VALUECLASS)
8474 {
8475 InterpreterType valIt = OpStackTypeGet(ind);
8476
8477 void* valPtr;
8478 if (valIt.IsLargeStruct(&m_interpCeeInfo))
8479 {
8480 // Operand stack entry is pointer to the data.
8481 valPtr = OpStackGet<void*>(ind);
8482 }
8483 else
8484 {
8485 // Operand stack entry *is* the data.
8486 size_t classSize = getClassSize(boxTypeClsHnd);
8487 valPtr = OpStackGetAddr(ind, classSize);
8488 }
8489
8490 TypeHandle th(boxTypeClsHnd);
8491 if (th.IsTypeDesc())
8492 {
8493 COMPlusThrow(kInvalidOperationException, W("InvalidOperation_TypeCannotBeBoxed"));
8494 }
8495
8496 MethodTable* pMT = th.AsMethodTable();
8497
8498 {
8499 Object* res = OBJECTREFToObject(pMT->Box(valPtr));
8500
8501 GCX_FORBID();
8502
8503 // If we're popping a large struct off the operand stack, make sure we clean up.
8504 if (valIt.IsLargeStruct(&m_interpCeeInfo))
8505 {
8506 LargeStructOperandStackPop(valIt.Size(&m_interpCeeInfo), valPtr);
8507 }
8508 OpStackSet<Object*>(ind, res);
8509 OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_CLASS));
8510 }
8511 }
8512}
8513
8514void Interpreter::BoxStructRefAt(unsigned ind, CORINFO_CLASS_HANDLE valCls)
8515{
8516 CONTRACTL {
8517 SO_TOLERANT;
8518 THROWS;
8519 GC_TRIGGERS;
8520 MODE_COOPERATIVE;
8521 } CONTRACTL_END;
8522
8523 _ASSERTE_MSG(ind < m_curStackHt, "Precondition");
8524 {
8525 GCX_PREEMP();
8526 _ASSERTE_MSG(m_interpCeeInfo.getClassAttribs(valCls) & CORINFO_FLG_VALUECLASS, "Precondition");
8527 }
8528 _ASSERTE_MSG(OpStackTypeGet(ind).ToCorInfoType() == CORINFO_TYPE_BYREF, "Precondition");
8529
8530 InterpreterType valIt = InterpreterType(&m_interpCeeInfo, valCls);
8531
8532 void* valPtr = OpStackGet<void*>(ind);
8533
8534 TypeHandle th(valCls);
8535 if (th.IsTypeDesc())
8536 COMPlusThrow(kInvalidOperationException,W("InvalidOperation_TypeCannotBeBoxed"));
8537
8538 MethodTable* pMT = th.AsMethodTable();
8539
8540 {
8541 Object* res = OBJECTREFToObject(pMT->Box(valPtr));
8542
8543 GCX_FORBID();
8544
8545 OpStackSet<Object*>(ind, res);
8546 OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_CLASS));
8547 }
8548}
8549
8550
8551void Interpreter::Unbox()
8552{
8553 CONTRACTL {
8554 SO_TOLERANT;
8555 THROWS;
8556 GC_TRIGGERS;
8557 MODE_COOPERATIVE;
8558 } CONTRACTL_END
8559
8560 assert(m_curStackHt > 0);
8561 unsigned tos = m_curStackHt - 1;
8562
8563#ifdef _DEBUG
8564 CorInfoType tosCIT = OpStackTypeGet(tos).ToCorInfoType();
8565 if (tosCIT != CORINFO_TYPE_CLASS)
8566 VerificationError("Unbox requires that TOS is an object pointer.");
8567#endif // _DEBUG
8568
8569#if INTERP_TRACING
8570 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_Unbox]);
8571#endif // INTERP_TRACING
8572
8573 CORINFO_CLASS_HANDLE boxTypeClsHnd = GetTypeFromToken(m_ILCodePtr + 1, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_Unbox));
8574
8575 CorInfoHelpFunc unboxHelper;
8576
8577 {
8578 GCX_PREEMP();
8579 unboxHelper = m_interpCeeInfo.getUnBoxHelper(boxTypeClsHnd);
8580 }
8581
8582 void* res = NULL;
8583 Object* obj = OpStackGet<Object*>(tos);
8584
8585 switch (unboxHelper)
8586 {
8587 case CORINFO_HELP_UNBOX:
8588 {
8589 ThrowOnInvalidPointer(obj);
8590
8591 MethodTable* pMT1 = (MethodTable*)boxTypeClsHnd;
8592 MethodTable* pMT2 = obj->GetMethodTable();
8593
8594 if (pMT1->IsEquivalentTo(pMT2))
8595 {
8596 res = OpStackGet<Object*>(tos)->UnBox();
8597 }
8598 else
8599 {
8600 CorElementType type1 = pMT1->GetInternalCorElementType();
8601 CorElementType type2 = pMT2->GetInternalCorElementType();
8602
8603 // we allow enums and their primtive type to be interchangable
8604 if (type1 == type2)
8605 {
8606 if ((pMT1->IsEnum() || pMT1->IsTruePrimitive()) &&
8607 (pMT2->IsEnum() || pMT2->IsTruePrimitive()))
8608 {
8609 res = OpStackGet<Object*>(tos)->UnBox();
8610 }
8611 }
8612 }
8613
8614 if (res == NULL)
8615 {
8616 COMPlusThrow(kInvalidCastException);
8617 }
8618 }
8619 break;
8620
8621 case CORINFO_HELP_UNBOX_NULLABLE:
8622 {
8623 // For "unbox Nullable<T>", we need to create a new object (maybe in some temporary local
8624 // space (that we reuse every time we hit this IL instruction?), that gets reported to the GC,
8625 // maybe in the GC heap itself). That object will contain an embedded Nullable<T>. Then, we need to
8626 // get a byref to the data within the object.
8627
8628 NYI_INTERP("Unhandled 'unbox' of Nullable<T>.");
8629 }
8630 break;
8631
8632 default:
8633 NYI_INTERP("Unhandled 'unbox' helper.");
8634 }
8635
8636 {
8637 GCX_FORBID();
8638 OpStackSet<void*>(tos, res);
8639 OpStackTypeSet(tos, InterpreterType(CORINFO_TYPE_BYREF));
8640 }
8641
8642 m_ILCodePtr += 5;
8643}
8644
8645
8646void Interpreter::Throw()
8647{
8648 CONTRACTL {
8649 SO_TOLERANT;
8650 THROWS;
8651 GC_TRIGGERS;
8652 MODE_COOPERATIVE;
8653 } CONTRACTL_END
8654
8655 assert(m_curStackHt >= 1);
8656
8657 // Note that we can't decrement the stack height here, since the operand stack
8658 // protects the thrown object. Nor do we need to, since the ostack will be cleared on
8659 // any catch within this method.
8660 unsigned exInd = m_curStackHt - 1;
8661
8662#ifdef _DEBUG
8663 CorInfoType exCIT = OpStackTypeGet(exInd).ToCorInfoType();
8664 if (exCIT != CORINFO_TYPE_CLASS)
8665 {
8666 VerificationError("Can only throw an object.");
8667 }
8668#endif // _DEBUG
8669
8670 Object* obj = OpStackGet<Object*>(exInd);
8671 ThrowOnInvalidPointer(obj);
8672
8673 OBJECTREF oref = ObjectToOBJECTREF(obj);
8674 if (!IsException(oref->GetMethodTable()))
8675 {
8676 GCPROTECT_BEGIN(oref);
8677 WrapNonCompliantException(&oref);
8678 GCPROTECT_END();
8679 }
8680 COMPlusThrow(oref);
8681}
8682
8683void Interpreter::Rethrow()
8684{
8685 CONTRACTL {
8686 SO_TOLERANT;
8687 THROWS;
8688 GC_TRIGGERS;
8689 MODE_COOPERATIVE;
8690 } CONTRACTL_END
8691
8692 OBJECTREF throwable = GetThread()->LastThrownObject();
8693 COMPlusThrow(throwable);
8694}
8695
8696void Interpreter::UnboxAny()
8697{
8698 CONTRACTL {
8699 SO_TOLERANT;
8700 THROWS;
8701 GC_TRIGGERS;
8702 MODE_COOPERATIVE;
8703 } CONTRACTL_END;
8704
8705 assert(m_curStackHt > 0);
8706 unsigned tos = m_curStackHt - 1;
8707
8708 unsigned boxTypeTok = getU4LittleEndian(m_ILCodePtr + 1);
8709 m_ILCodePtr += 5;
8710
8711#if INTERP_TRACING
8712 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_UnboxAny]);
8713#endif // INTERP_TRACING
8714
8715 CORINFO_RESOLVED_TOKEN boxTypeResolvedTok;
8716 CORINFO_CLASS_HANDLE boxTypeClsHnd;
8717 DWORD boxTypeAttribs = 0;
8718
8719 {
8720 GCX_PREEMP();
8721 ResolveToken(&boxTypeResolvedTok, boxTypeTok, CORINFO_TOKENKIND_Class InterpTracingArg(RTK_UnboxAny));
8722 boxTypeClsHnd = boxTypeResolvedTok.hClass;
8723 boxTypeAttribs = m_interpCeeInfo.getClassAttribs(boxTypeClsHnd);
8724 }
8725
8726 CorInfoType unboxCIT = OpStackTypeGet(tos).ToCorInfoType();
8727 if (unboxCIT != CORINFO_TYPE_CLASS)
8728 VerificationError("Type mismatch in UNBOXANY.");
8729
8730 if ((boxTypeAttribs & CORINFO_FLG_VALUECLASS) == 0)
8731 {
8732 Object* obj = OpStackGet<Object*>(tos);
8733 if (obj != NULL && !ObjIsInstanceOf(obj, TypeHandle(boxTypeClsHnd), TRUE))
8734 {
8735 UNREACHABLE(); //ObjIsInstanceOf will throw if cast can't be done
8736 }
8737 }
8738 else
8739 {
8740 CorInfoHelpFunc unboxHelper;
8741
8742 {
8743 GCX_PREEMP();
8744 unboxHelper = m_interpCeeInfo.getUnBoxHelper(boxTypeClsHnd);
8745 }
8746
8747 // Important that this *not* be factored out with the identical statement in the "if" branch:
8748 // delay read from GC-protected operand stack until after COOP-->PREEMP transition above.
8749 Object* obj = OpStackGet<Object*>(tos);
8750
8751 switch (unboxHelper)
8752 {
8753 case CORINFO_HELP_UNBOX:
8754 {
8755 ThrowOnInvalidPointer(obj);
8756
8757 MethodTable* pMT1 = (MethodTable*)boxTypeClsHnd;
8758 MethodTable* pMT2 = obj->GetMethodTable();
8759
8760 void* res = NULL;
8761 if (pMT1->IsEquivalentTo(pMT2))
8762 {
8763 res = OpStackGet<Object*>(tos)->UnBox();
8764 }
8765 else
8766 {
8767 CorElementType type1 = pMT1->GetInternalCorElementType();
8768 CorElementType type2 = pMT2->GetInternalCorElementType();
8769
8770 // we allow enums and their primtive type to be interchangable
8771 if (type1 == type2)
8772 {
8773 if ((pMT1->IsEnum() || pMT1->IsTruePrimitive()) &&
8774 (pMT2->IsEnum() || pMT2->IsTruePrimitive()))
8775 {
8776 res = OpStackGet<Object*>(tos)->UnBox();
8777 }
8778 }
8779 }
8780
8781 if (res == NULL)
8782 {
8783 COMPlusThrow(kInvalidCastException);
8784 }
8785
8786 // As the ECMA spec says, the rest is like a "ldobj".
8787 LdObjValueClassWork(boxTypeClsHnd, tos, res);
8788 }
8789 break;
8790
8791 case CORINFO_HELP_UNBOX_NULLABLE:
8792 {
8793 InterpreterType it = InterpreterType(&m_interpCeeInfo, boxTypeClsHnd);
8794 size_t sz = it.Size(&m_interpCeeInfo);
8795 if (sz > sizeof(INT64))
8796 {
8797 void* destPtr = LargeStructOperandStackPush(sz);
8798 if (!Nullable::UnBox(destPtr, ObjectToOBJECTREF(obj), (MethodTable*)boxTypeClsHnd))
8799 {
8800 COMPlusThrow(kInvalidCastException);
8801 }
8802 OpStackSet<void*>(tos, destPtr);
8803 }
8804 else
8805 {
8806 INT64 dest = 0;
8807 if (!Nullable::UnBox(&dest, ObjectToOBJECTREF(obj), (MethodTable*)boxTypeClsHnd))
8808 {
8809 COMPlusThrow(kInvalidCastException);
8810 }
8811 OpStackSet<INT64>(tos, dest);
8812 }
8813 OpStackTypeSet(tos, it.StackNormalize());
8814 }
8815 break;
8816
8817 default:
8818 NYI_INTERP("Unhandled 'unbox.any' helper.");
8819 }
8820 }
8821}
8822
8823void Interpreter::LdLen()
8824{
8825 CONTRACTL {
8826 SO_TOLERANT;
8827 THROWS;
8828 GC_TRIGGERS;
8829 MODE_COOPERATIVE;
8830 } CONTRACTL_END;
8831
8832 assert(m_curStackHt >= 1);
8833 unsigned arrInd = m_curStackHt - 1;
8834
8835 assert(OpStackTypeGet(arrInd).ToCorInfoType() == CORINFO_TYPE_CLASS);
8836
8837 GCX_FORBID();
8838
8839 ArrayBase* a = OpStackGet<ArrayBase*>(arrInd);
8840 ThrowOnInvalidPointer(a);
8841 int len = a->GetNumComponents();
8842
8843 OpStackSet<NativeUInt>(arrInd, NativeUInt(len));
8844 // The ECMA spec says that the type of the length value is NATIVEUINT, but this
8845 // doesn't make any sense -- unsigned types are not stack-normalized. So I'm
8846 // using NATIVEINT, to get the width right.
8847 OpStackTypeSet(arrInd, InterpreterType(CORINFO_TYPE_NATIVEINT));
8848}
8849
8850
8851void Interpreter::DoCall(bool virtualCall)
8852{
8853#if INTERP_DYNAMIC_CONTRACTS
8854 CONTRACTL {
8855 SO_TOLERANT;
8856 THROWS;
8857 GC_TRIGGERS;
8858 MODE_COOPERATIVE;
8859 } CONTRACTL_END;
8860#else
8861 // Dynamic contract occupies too much stack.
8862 STATIC_CONTRACT_SO_TOLERANT;
8863 STATIC_CONTRACT_THROWS;
8864 STATIC_CONTRACT_GC_TRIGGERS;
8865 STATIC_CONTRACT_MODE_COOPERATIVE;
8866#endif
8867
8868#if INTERP_TRACING
8869 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_Call]);
8870#endif // INTERP_TRACING
8871
8872 DoCallWork(virtualCall);
8873
8874 m_ILCodePtr += 5;
8875}
8876
8877CORINFO_CONTEXT_HANDLE InterpreterMethodInfo::GetPreciseGenericsContext(Object* thisArg, void* genericsCtxtArg)
8878{
8879 // If the caller has a generic argument, then we need to get the exact methodContext.
8880 // There are several possibilities that lead to a generic argument:
8881 // 1) Static method of generic class: generic argument is the method table of the class.
8882 // 2) generic method of a class: generic argument is the precise MethodDesc* of the method.
8883 if (GetFlag<InterpreterMethodInfo::Flag_hasGenericsContextArg>())
8884 {
8885 assert(GetFlag<InterpreterMethodInfo::Flag_methHasGenericArgs>() || GetFlag<InterpreterMethodInfo::Flag_typeHasGenericArgs>());
8886 if (GetFlag<InterpreterMethodInfo::Flag_methHasGenericArgs>())
8887 {
8888 return MAKE_METHODCONTEXT(reinterpret_cast<CORINFO_METHOD_HANDLE>(genericsCtxtArg));
8889 }
8890 else
8891 {
8892 MethodTable* methodClass = reinterpret_cast<MethodDesc*>(m_method)->GetMethodTable();
8893 MethodTable* contextClass = reinterpret_cast<MethodTable*>(genericsCtxtArg)->GetMethodTableMatchingParentClass(methodClass);
8894 return MAKE_CLASSCONTEXT(contextClass);
8895 }
8896 }
8897 // TODO: This condition isn't quite right. If the actual class is a subtype of the declaring type of the method,
8898 // then it might be in another module, the scope and context won't agree.
8899 else if (GetFlag<InterpreterMethodInfo::Flag_typeHasGenericArgs>()
8900 && !GetFlag<InterpreterMethodInfo::Flag_methHasGenericArgs>()
8901 && GetFlag<InterpreterMethodInfo::Flag_hasThisArg>()
8902 && GetFlag<InterpreterMethodInfo::Flag_thisArgIsObjPtr>() && thisArg != NULL)
8903 {
8904 MethodTable* methodClass = reinterpret_cast<MethodDesc*>(m_method)->GetMethodTable();
8905 MethodTable* contextClass = thisArg->GetMethodTable()->GetMethodTableMatchingParentClass(methodClass);
8906 return MAKE_CLASSCONTEXT(contextClass);
8907 }
8908 else
8909 {
8910 return MAKE_METHODCONTEXT(m_method);
8911 }
8912}
8913
8914void Interpreter::DoCallWork(bool virtualCall, void* thisArg, CORINFO_RESOLVED_TOKEN* methTokPtr, CORINFO_CALL_INFO* callInfoPtr)
8915{
8916#if INTERP_DYNAMIC_CONTRACTS
8917 CONTRACTL {
8918 SO_TOLERANT;
8919 THROWS;
8920 GC_TRIGGERS;
8921 MODE_COOPERATIVE;
8922 } CONTRACTL_END;
8923#else
8924 // Dynamic contract occupies too much stack.
8925 STATIC_CONTRACT_SO_TOLERANT;
8926 STATIC_CONTRACT_THROWS;
8927 STATIC_CONTRACT_GC_TRIGGERS;
8928 STATIC_CONTRACT_MODE_COOPERATIVE;
8929#endif
8930
8931#if INTERP_ILCYCLE_PROFILE
8932#if 0
8933 // XXX
8934 unsigned __int64 callStartCycles;
8935 bool b = CycleTimer::GetThreadCyclesS(&callStartCycles); assert(b);
8936 unsigned __int64 callStartExemptCycles = m_exemptCycles;
8937#endif
8938#endif // INTERP_ILCYCLE_PROFILE
8939
8940#if INTERP_TRACING
8941 InterlockedIncrement(&s_totalInterpCalls);
8942#endif // INTERP_TRACING
8943 unsigned tok = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
8944
8945 // It's possible for an IL method to push a capital-F Frame. If so, we pop it and save it;
8946 // we'll push it back on after our GCPROTECT frame is popped.
8947 Frame* ilPushedFrame = NULL;
8948
8949 // We can't protect "thisArg" with a GCPROTECT, because this pushes a Frame, and there
8950 // exist managed methods that push (and pop) Frames -- so that the Frame chain does not return
8951 // to its original state after a call. Therefore, we can't have a Frame on the stack over the duration
8952 // of a call. (I assume that any method that calls a Frame-pushing IL method performs a matching
8953 // call to pop that Frame before the caller method completes. If this were not true, if one method could push
8954 // a Frame, but defer the pop to its caller, then we could *never* use a Frame in the interpreter, and
8955 // our implementation plan would be doomed.)
8956 assert(m_callThisArg == NULL);
8957 m_callThisArg = thisArg;
8958
8959 // Have we already cached a MethodDescCallSite for this call? (We do this only in loops
8960 // in the current execution).
8961 unsigned iloffset = CurOffset();
8962 CallSiteCacheData* pCscd = NULL;
8963 if (s_InterpreterUseCaching) pCscd = GetCachedCallInfo(iloffset);
8964
8965 // If this is true, then we should not cache this call site.
8966 bool doNotCache;
8967
8968 CORINFO_RESOLVED_TOKEN methTok;
8969 CORINFO_CALL_INFO callInfo;
8970 MethodDesc* methToCall = NULL;
8971 CORINFO_CLASS_HANDLE exactClass = NULL;
8972 CORINFO_SIG_INFO_SMALL sigInfo;
8973 if (pCscd != NULL)
8974 {
8975 GCX_PREEMP();
8976 methToCall = pCscd->m_pMD;
8977 sigInfo = pCscd->m_sigInfo;
8978
8979 doNotCache = true; // We already have a cache entry.
8980 }
8981 else
8982 {
8983 doNotCache = false; // Until we determine otherwise.
8984 if (callInfoPtr == NULL)
8985 {
8986 GCX_PREEMP();
8987
8988 // callInfoPtr and methTokPtr must either both be NULL, or neither.
8989 assert(methTokPtr == NULL);
8990
8991 methTokPtr = &methTok;
8992 ResolveToken(methTokPtr, tok, CORINFO_TOKENKIND_Method InterpTracingArg(RTK_Call));
8993 OPCODE opcode = (OPCODE)(*m_ILCodePtr);
8994
8995 m_interpCeeInfo.getCallInfo(methTokPtr,
8996 m_constrainedFlag ? & m_constrainedResolvedToken : NULL,
8997 m_methInfo->m_method,
8998 //this is how impImportCall invokes getCallInfo
8999 combine(combine(CORINFO_CALLINFO_ALLOWINSTPARAM,
9000 CORINFO_CALLINFO_SECURITYCHECKS),
9001 (opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT
9002 : CORINFO_CALLINFO_NONE),
9003 &callInfo);
9004#if INTERP_ILCYCLE_PROFILE
9005#if 0
9006 if (virtualCall)
9007 {
9008 unsigned __int64 callEndCycles;
9009 b = CycleTimer::GetThreadCyclesS(&callEndCycles); assert(b);
9010 unsigned __int64 delta = (callEndCycles - callStartCycles);
9011 delta -= (m_exemptCycles - callStartExemptCycles);
9012 s_callCycles += delta;
9013 s_calls++;
9014 }
9015#endif
9016#endif // INTERP_ILCYCLE_PROFILE
9017
9018 callInfoPtr = &callInfo;
9019
9020 assert(!callInfoPtr->exactContextNeedsRuntimeLookup);
9021
9022 methToCall = reinterpret_cast<MethodDesc*>(methTok.hMethod);
9023 exactClass = methTok.hClass;
9024 }
9025 else
9026 {
9027 // callInfoPtr and methTokPtr must either both be NULL, or neither.
9028 assert(methTokPtr != NULL);
9029
9030 assert(!callInfoPtr->exactContextNeedsRuntimeLookup);
9031
9032 methToCall = reinterpret_cast<MethodDesc*>(callInfoPtr->hMethod);
9033 exactClass = methTokPtr->hClass;
9034 }
9035
9036 // We used to take the sigInfo from the callInfo here, but that isn't precise, since
9037 // we may have made "methToCall" more precise wrt generics than the method handle in
9038 // the callinfo. So look up th emore precise signature.
9039 GCX_PREEMP();
9040
9041 CORINFO_SIG_INFO sigInfoFull;
9042 m_interpCeeInfo.getMethodSig(CORINFO_METHOD_HANDLE(methToCall), &sigInfoFull);
9043 sigInfo.retTypeClass = sigInfoFull.retTypeClass;
9044 sigInfo.numArgs = sigInfoFull.numArgs;
9045 sigInfo.callConv = sigInfoFull.callConv;
9046 sigInfo.retType = sigInfoFull.retType;
9047 }
9048
9049 // Point A in our cycle count.
9050
9051
9052 // Is the method an intrinsic? If so, and if it's one we've written special-case code for
9053 // handle intrinsically.
9054 CorInfoIntrinsics intrinsicId;
9055 {
9056 GCX_PREEMP();
9057 intrinsicId = m_interpCeeInfo.getIntrinsicID(CORINFO_METHOD_HANDLE(methToCall));
9058 }
9059
9060#if INTERP_TRACING
9061 if (intrinsicId != CORINFO_INTRINSIC_Illegal)
9062 InterlockedIncrement(&s_totalInterpCallsToIntrinsics);
9063#endif // INTERP_TRACING
9064 bool didIntrinsic = false;
9065 if (!m_constrainedFlag)
9066 {
9067 switch (intrinsicId)
9068 {
9069 case CORINFO_INTRINSIC_StringLength:
9070 DoStringLength(); didIntrinsic = true;
9071 break;
9072 case CORINFO_INTRINSIC_StringGetChar:
9073 DoStringGetChar(); didIntrinsic = true;
9074 break;
9075 case CORINFO_INTRINSIC_GetTypeFromHandle:
9076 // This is an identity transformation. (At least until I change LdToken to
9077 // return a RuntimeTypeHandle struct...which is a TODO.)
9078 DoGetTypeFromHandle();
9079 didIntrinsic = true;
9080 break;
9081 case CORINFO_INTRINSIC_ByReference_Ctor:
9082 DoByReferenceCtor();
9083 didIntrinsic = true;
9084 break;
9085 case CORINFO_INTRINSIC_ByReference_Value:
9086 DoByReferenceValue();
9087 didIntrinsic = true;
9088 break;
9089#if INTERP_ILSTUBS
9090 case CORINFO_INTRINSIC_StubHelpers_GetStubContext:
9091 OpStackSet<void*>(m_curStackHt, GetStubContext());
9092 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT));
9093 m_curStackHt++; didIntrinsic = true;
9094 break;
9095 case CORINFO_INTRINSIC_StubHelpers_GetStubContextAddr:
9096 OpStackSet<void*>(m_curStackHt, GetStubContextAddr());
9097 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_NATIVEINT));
9098 m_curStackHt++; didIntrinsic = true;
9099 break;
9100#endif // INTERP_ILSTUBS
9101 default:
9102#if INTERP_TRACING
9103 InterlockedIncrement(&s_totalInterpCallsToIntrinsicsUnhandled);
9104#endif // INTERP_TRACING
9105 break;
9106 }
9107
9108 // Plus some other calls that we're going to treat "like" intrinsics...
9109 if (methToCall == MscorlibBinder::GetMethod(METHOD__STUBHELPERS__SET_LAST_ERROR))
9110 {
9111 // If we're interpreting a method that calls "SetLastError", it's very likely that the call(i) whose
9112 // error we're trying to capture was performed with MethodDescCallSite machinery that itself trashes
9113 // the last error. We solve this by saving the last error in a special interpreter-specific field of
9114 // "Thread" in that case, and essentially implement SetLastError here, taking that field as the
9115 // source for the last error.
9116 Thread* thrd = GetThread();
9117 thrd->m_dwLastError = thrd->m_dwLastErrorInterp;
9118 didIntrinsic = true;
9119 }
9120
9121#if FEATURE_SIMD
9122 if (fFeatureSIMD.val(CLRConfig::EXTERNAL_FeatureSIMD) != 0)
9123 {
9124 // Check for the simd class...
9125 assert(exactClass != NULL);
9126 GCX_PREEMP();
9127 bool isSIMD = m_interpCeeInfo.isInSIMDModule(exactClass);
9128
9129 if (isSIMD)
9130 {
9131 // SIMD intrinsics are recognized by name.
9132 const char* namespaceName = NULL;
9133 const char* className = NULL;
9134 const char* methodName = m_interpCeeInfo.getMethodNameFromMetadata((CORINFO_METHOD_HANDLE)methToCall, &className, &namespaceName, NULL);
9135 if (strcmp(methodName, "get_IsHardwareAccelerated") == 0)
9136 {
9137 GCX_COOP();
9138 DoSIMDHwAccelerated();
9139 didIntrinsic = true;
9140 }
9141 }
9142
9143 if (didIntrinsic)
9144 {
9145 // Must block caching or we lose easy access to the class
9146 doNotCache = true;
9147 }
9148 }
9149#endif // FEATURE_SIMD
9150
9151 }
9152
9153 if (didIntrinsic)
9154 {
9155 if (s_InterpreterUseCaching && !doNotCache)
9156 {
9157 // Cache the token resolution result...
9158 pCscd = new CallSiteCacheData(methToCall, sigInfo);
9159 CacheCallInfo(iloffset, pCscd);
9160 }
9161 // Now we can return.
9162 return;
9163 }
9164
9165 // Handle other simple special cases:
9166
9167#if FEATURE_INTERPRETER_DEADSIMPLE_OPT
9168#ifndef DACCESS_COMPILE
9169 // Dead simple static getters.
9170 InterpreterMethodInfo* calleeInterpMethInfo;
9171 if (GetMethodHandleToInterpMethInfoPtrMap()->Lookup(CORINFO_METHOD_HANDLE(methToCall), &calleeInterpMethInfo))
9172 {
9173 if (calleeInterpMethInfo->GetFlag<InterpreterMethodInfo::Flag_methIsDeadSimpleGetter>())
9174 {
9175 if (methToCall->IsStatic())
9176 {
9177 // TODO
9178 }
9179 else
9180 {
9181 ILOffsetToItemCache* calleeCache;
9182 {
9183 Object* thisArg = OpStackGet<Object*>(m_curStackHt-1);
9184 GCX_FORBID();
9185 // We pass NULL for the generic context arg, because a dead simple getter takes none, by definition.
9186 calleeCache = calleeInterpMethInfo->GetCacheForCall(thisArg, /*genericsContextArg*/NULL);
9187 }
9188 // We've interpreted the getter at least once, so the cache for *some* generics context is populated -- but maybe not
9189 // this one. We're hoping that it usually is.
9190 if (calleeCache != NULL)
9191 {
9192 CachedItem cachedItem;
9193 unsigned offsetOfLd;
9194 if (calleeInterpMethInfo->GetFlag<InterpreterMethodInfo::Flag_methIsDeadSimpleGetterIsDbgForm>())
9195 offsetOfLd = ILOffsetOfLdFldInDeadSimpleInstanceGetterOpt;
9196 else
9197 offsetOfLd = ILOffsetOfLdFldInDeadSimpleInstanceGetterOpt;
9198
9199 bool b = calleeCache->GetItem(offsetOfLd, cachedItem);
9200 _ASSERTE_MSG(b, "If the cache exists for this generic context, it should an entry for the LdFld.");
9201 _ASSERTE_MSG(cachedItem.m_tag == CIK_InstanceField, "If it's there, it should be an instance field cache.");
9202 LdFld(cachedItem.m_value.m_instanceField);
9203#if INTERP_TRACING
9204 InterlockedIncrement(&s_totalInterpCallsToDeadSimpleGetters);
9205 InterlockedIncrement(&s_totalInterpCallsToDeadSimpleGettersShortCircuited);
9206#endif // INTERP_TRACING
9207 return;
9208 }
9209 }
9210 }
9211 }
9212#endif // DACCESS_COMPILE
9213#endif // FEATURE_INTERPRETER_DEADSIMPLE_OPT
9214
9215 unsigned totalSigArgs;
9216 CORINFO_VARARGS_HANDLE vaSigCookie = nullptr;
9217 if ((sigInfo.callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG ||
9218 (sigInfo.callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_NATIVEVARARG)
9219 {
9220 GCX_PREEMP();
9221 CORINFO_SIG_INFO sig;
9222 m_interpCeeInfo.findCallSiteSig(m_methInfo->m_module, methTokPtr->token, MAKE_METHODCONTEXT(m_methInfo->m_method), &sig);
9223 sigInfo.retTypeClass = sig.retTypeClass;
9224 sigInfo.numArgs = sig.numArgs;
9225 sigInfo.callConv = sig.callConv;
9226 sigInfo.retType = sig.retType;
9227 // Adding 'this' pointer because, numArgs doesn't include the this pointer.
9228 totalSigArgs = sigInfo.numArgs + sigInfo.hasThis();
9229
9230 if ((sigInfo.callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG)
9231 {
9232 Module* module = GetModule(sig.scope);
9233 vaSigCookie = CORINFO_VARARGS_HANDLE(module->GetVASigCookie(Signature(sig.pSig, sig.cbSig)));
9234 }
9235 doNotCache = true;
9236 }
9237 else
9238 {
9239 totalSigArgs = sigInfo.totalILArgs();
9240 }
9241
9242 // Note that "totalNativeArgs()" includes space for ret buff arg.
9243 unsigned nSlots = totalSigArgs + 1;
9244 if (sigInfo.hasTypeArg()) nSlots++;
9245 if (sigInfo.isVarArg()) nSlots++;
9246
9247 DelegateCtorArgs ctorData;
9248 // If any of these are non-null, they will be pushed as extra arguments (see the code below).
9249 ctorData.pArg3 = NULL;
9250 ctorData.pArg4 = NULL;
9251 ctorData.pArg5 = NULL;
9252
9253 // Since we make "doNotCache" true below, well never have a non-null "pCscd" for a delegate
9254 // constructor. But we have to check for a cached method first, since callInfoPtr may be null in the cached case.
9255 if (pCscd == NULL && callInfoPtr->classFlags & CORINFO_FLG_DELEGATE && callInfoPtr->methodFlags & CORINFO_FLG_CONSTRUCTOR)
9256 {
9257 // We won't cache this case.
9258 doNotCache = true;
9259
9260 _ASSERTE_MSG(!sigInfo.hasTypeArg(), "I assume that this isn't possible.");
9261 GCX_PREEMP();
9262
9263 ctorData.pMethod = methToCall;
9264
9265 // Second argument to delegate constructor will be code address of the function the delegate wraps.
9266 assert(TOSIsPtr() && OpStackTypeGet(m_curStackHt-1).ToCorInfoType() != CORINFO_TYPE_BYREF);
9267 CORINFO_METHOD_HANDLE targetMethodHnd = GetFunctionPointerStack()[m_curStackHt-1];
9268 assert(targetMethodHnd != NULL);
9269 CORINFO_METHOD_HANDLE alternateCtorHnd = m_interpCeeInfo.GetDelegateCtor(reinterpret_cast<CORINFO_METHOD_HANDLE>(methToCall), methTokPtr->hClass, targetMethodHnd, &ctorData);
9270 MethodDesc* alternateCtor = reinterpret_cast<MethodDesc*>(alternateCtorHnd);
9271 if (alternateCtor != methToCall)
9272 {
9273 methToCall = alternateCtor;
9274
9275 // Translate the method address argument from a method handle to the actual callable code address.
9276 void* val = (void *)((MethodDesc *)targetMethodHnd)->GetMultiCallableAddrOfCode();
9277 // Change the method argument to the code pointer.
9278 OpStackSet<void*>(m_curStackHt-1, val);
9279
9280 // Now if there are extra arguments, add them to the number of slots; we'll push them on the
9281 // arg list later.
9282 if (ctorData.pArg3) nSlots++;
9283 if (ctorData.pArg4) nSlots++;
9284 if (ctorData.pArg5) nSlots++;
9285 }
9286 }
9287
9288 // Make sure that the operand stack has the required number of arguments.
9289 // (Note that this is IL args, not native.)
9290 //
9291
9292 // The total number of arguments on the IL stack. Initially we assume that all the IL arguments
9293 // the callee expects are on the stack, but may be adjusted downwards if the "this" argument
9294 // is provided by an allocation (the call is to a constructor).
9295 unsigned totalArgsOnILStack = totalSigArgs;
9296 if (m_callThisArg != NULL)
9297 {
9298 assert(totalArgsOnILStack > 0);
9299 totalArgsOnILStack--;
9300 }
9301
9302#if defined(FEATURE_HFA)
9303 // Does the callee have an HFA return type?
9304 unsigned HFAReturnArgSlots = 0;
9305 {
9306 GCX_PREEMP();
9307
9308 if (sigInfo.retType == CORINFO_TYPE_VALUECLASS
9309 && CorInfoTypeIsFloatingPoint(m_interpCeeInfo.getHFAType(sigInfo.retTypeClass))
9310 && (sigInfo.getCallConv() & CORINFO_CALLCONV_VARARG) == 0)
9311 {
9312 HFAReturnArgSlots = getClassSize(sigInfo.retTypeClass);
9313 // Round up to a multiple of double size.
9314 HFAReturnArgSlots = (HFAReturnArgSlots + sizeof(ARG_SLOT) - 1) / sizeof(ARG_SLOT);
9315 }
9316 }
9317#endif
9318
9319 // Point B
9320
9321 const unsigned LOCAL_ARG_SLOTS = 8;
9322 ARG_SLOT localArgs[LOCAL_ARG_SLOTS];
9323 InterpreterType localArgTypes[LOCAL_ARG_SLOTS];
9324
9325 ARG_SLOT* args;
9326 InterpreterType* argTypes;
9327#if defined(_X86_)
9328 unsigned totalArgSlots = nSlots;
9329#elif defined(_ARM_) || defined(_ARM64_)
9330 // ARM64TODO: Verify that the following statement is correct for ARM64.
9331 unsigned totalArgSlots = nSlots + HFAReturnArgSlots;
9332#elif defined(_AMD64_)
9333 unsigned totalArgSlots = nSlots;
9334#else
9335#error "unsupported platform"
9336#endif
9337
9338 if (totalArgSlots <= LOCAL_ARG_SLOTS)
9339 {
9340 args = &localArgs[0];
9341 argTypes = &localArgTypes[0];
9342 }
9343 else
9344 {
9345 args = (ARG_SLOT*)_alloca(totalArgSlots * sizeof(ARG_SLOT));
9346#if defined(_ARM_)
9347 // The HFA return buffer, if any, is assumed to be at a negative
9348 // offset from the IL arg pointer, so adjust that pointer upward.
9349 args = args + HFAReturnArgSlots;
9350#endif // defined(_ARM_)
9351 argTypes = (InterpreterType*)_alloca(nSlots * sizeof(InterpreterType));
9352 }
9353 // Make sure that we don't scan any of these until we overwrite them with
9354 // the real types of the arguments.
9355 InterpreterType undefIt(CORINFO_TYPE_UNDEF);
9356 for (unsigned i = 0; i < nSlots; i++) argTypes[i] = undefIt;
9357
9358 // GC-protect the argument array (as byrefs).
9359 m_args = args; m_argsSize = nSlots; m_argTypes = argTypes;
9360
9361 // This is the index into the "args" array (where we copy the value to).
9362 int curArgSlot = 0;
9363
9364 // The operand stack index of the first IL argument.
9365 assert(m_curStackHt >= totalArgsOnILStack);
9366 int argsBase = m_curStackHt - totalArgsOnILStack;
9367
9368 // Current on-stack argument index.
9369 unsigned arg = 0;
9370
9371 // We do "this" -- in the case of a constructor, we "shuffle" the "m_callThisArg" argument in as the first
9372 // argument -- it isn't on the IL operand stack.
9373
9374 if (m_constrainedFlag)
9375 {
9376 _ASSERT(m_callThisArg == NULL); // "m_callThisArg" non-null only for .ctor, which are not callvirts.
9377
9378 CorInfoType argCIT = OpStackTypeGet(argsBase + arg).ToCorInfoType();
9379 if (argCIT != CORINFO_TYPE_BYREF)
9380 VerificationError("This arg of constrained call must be managed pointer.");
9381
9382 // We only cache for the CORINFO_NO_THIS_TRANSFORM case, so we may assume that if we have a cached call site,
9383 // there's no thisTransform to perform.
9384 if (pCscd == NULL)
9385 {
9386 switch (callInfoPtr->thisTransform)
9387 {
9388 case CORINFO_NO_THIS_TRANSFORM:
9389 // It is a constrained call on a method implemented by a value type; this is already the proper managed pointer.
9390 break;
9391
9392 case CORINFO_DEREF_THIS:
9393#ifdef _DEBUG
9394 {
9395 GCX_PREEMP();
9396 DWORD clsAttribs = m_interpCeeInfo.getClassAttribs(m_constrainedResolvedToken.hClass);
9397 assert((clsAttribs & CORINFO_FLG_VALUECLASS) == 0);
9398 }
9399#endif // _DEBUG
9400 {
9401 // As per the spec, dereference the byref to the "this" pointer, and substitute it as the new "this" pointer.
9402 GCX_FORBID();
9403 Object** objPtrPtr = OpStackGet<Object**>(argsBase + arg);
9404 OpStackSet<Object*>(argsBase + arg, *objPtrPtr);
9405 OpStackTypeSet(argsBase + arg, InterpreterType(CORINFO_TYPE_CLASS));
9406 }
9407 doNotCache = true;
9408 break;
9409
9410 case CORINFO_BOX_THIS:
9411 // This is the case where the call is to a virtual method of Object the given
9412 // struct class does not override -- the struct must be boxed, so that the
9413 // method can be invoked as a virtual.
9414 BoxStructRefAt(argsBase + arg, m_constrainedResolvedToken.hClass);
9415 doNotCache = true;
9416 break;
9417 }
9418
9419 exactClass = m_constrainedResolvedToken.hClass;
9420 {
9421 GCX_PREEMP();
9422 DWORD exactClassAttribs = m_interpCeeInfo.getClassAttribs(exactClass);
9423 // If the constraint type is a value class, then it is the exact class (which will be the
9424 // "owner type" in the MDCS below.) If it is not, leave it as the (precise) interface method.
9425 if (exactClassAttribs & CORINFO_FLG_VALUECLASS)
9426 {
9427 MethodTable* exactClassMT = GetMethodTableFromClsHnd(exactClass);
9428 // Find the method on exactClass corresponding to methToCall.
9429 methToCall = MethodDesc::FindOrCreateAssociatedMethodDesc(
9430 reinterpret_cast<MethodDesc*>(callInfoPtr->hMethod), // pPrimaryMD
9431 exactClassMT, // pExactMT
9432 FALSE, // forceBoxedEntryPoint
9433 methToCall->GetMethodInstantiation(), // methodInst
9434 FALSE); // allowInstParam
9435 }
9436 else
9437 {
9438 exactClass = methTokPtr->hClass;
9439 }
9440 }
9441 }
9442
9443 // We've consumed the constraint, so reset the flag.
9444 m_constrainedFlag = false;
9445 }
9446
9447 if (pCscd == NULL)
9448 {
9449 if (callInfoPtr->methodFlags & CORINFO_FLG_STATIC)
9450 {
9451 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(callInfoPtr->hMethod);
9452 EnsureClassInit(pMD->GetMethodTable());
9453 }
9454 }
9455
9456 // Point C
9457
9458 // We must do anything that might make a COOP->PREEMP transition before copying arguments out of the
9459 // operand stack (where they are GC-protected) into the args array (where they are not).
9460#ifdef _DEBUG
9461 const char* clsOfMethToCallName;;
9462 const char* methToCallName = NULL;
9463 {
9464 GCX_PREEMP();
9465 methToCallName = m_interpCeeInfo.getMethodName(CORINFO_METHOD_HANDLE(methToCall), &clsOfMethToCallName);
9466 }
9467#if INTERP_TRACING
9468 if (strncmp(methToCallName, "get_", 4) == 0)
9469 {
9470 InterlockedIncrement(&s_totalInterpCallsToGetters);
9471 size_t offsetOfLd;
9472 if (IsDeadSimpleGetter(&m_interpCeeInfo, methToCall, &offsetOfLd))
9473 {
9474 InterlockedIncrement(&s_totalInterpCallsToDeadSimpleGetters);
9475 }
9476 }
9477 else if (strncmp(methToCallName, "set_", 4) == 0)
9478 {
9479 InterlockedIncrement(&s_totalInterpCallsToSetters);
9480 }
9481#endif // INTERP_TRACING
9482
9483 // Only do this check on the first call, since it should be the same each time.
9484 if (pCscd == NULL)
9485 {
9486 // Ensure that any value types used as argument types are loaded. This property is checked
9487 // by the MethodDescCall site mechanisms. Since enums are freely convertible with their underlying
9488 // integer type, this is at least one case where a caller may push a value convertible to a value type
9489 // without any code having caused the value type to be loaded. This is DEBUG-only because if the callee
9490 // the integer-type value as the enum value type, it will have loaded the value type.
9491 MetaSig ms(methToCall);
9492 CorElementType argType;
9493 while ((argType = ms.NextArg()) != ELEMENT_TYPE_END)
9494 {
9495 if (argType == ELEMENT_TYPE_VALUETYPE)
9496 {
9497 TypeHandle th = ms.GetLastTypeHandleThrowing(ClassLoader::LoadTypes);
9498 CONSISTENCY_CHECK(th.CheckFullyLoaded());
9499 CONSISTENCY_CHECK(th.IsRestored_NoLogging());
9500 }
9501 }
9502 }
9503#endif
9504
9505 // CYCLE PROFILE: BEFORE ARG PROCESSING.
9506
9507 if (sigInfo.hasThis())
9508 {
9509 if (m_callThisArg != NULL)
9510 {
9511 if (size_t(m_callThisArg) == 0x1)
9512 {
9513 args[curArgSlot] = NULL;
9514 }
9515 else
9516 {
9517 args[curArgSlot] = PtrToArgSlot(m_callThisArg);
9518 }
9519 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_BYREF);
9520 }
9521 else
9522 {
9523 args[curArgSlot] = PtrToArgSlot(OpStackGet<void*>(argsBase + arg));
9524 argTypes[curArgSlot] = OpStackTypeGet(argsBase + arg);
9525 arg++;
9526 }
9527 // AV -> NullRef translation is NYI for the interpreter,
9528 // so we should manually check and throw the correct exception.
9529 if (args[curArgSlot] == NULL)
9530 {
9531 // If we're calling a constructor, we bypass this check since the runtime
9532 // should have thrown OOM if it was unable to allocate an instance.
9533 if (m_callThisArg == NULL)
9534 {
9535 assert(!methToCall->IsStatic());
9536 ThrowNullPointerException();
9537 }
9538 // ...except in the case of strings, which are both
9539 // allocated and initialized by their special constructor.
9540 else
9541 {
9542 assert(methToCall->IsCtor() && methToCall->GetMethodTable()->IsString());
9543 }
9544 }
9545 curArgSlot++;
9546 }
9547
9548 // This is the argument slot that will be used to hold the return value.
9549 ARG_SLOT retVal = 0;
9550#if !defined(_ARM_) && !defined(UNIX_AMD64_ABI)
9551 _ASSERTE (NUMBER_RETURNVALUE_SLOTS == 1);
9552#endif
9553
9554 // If the return type is a structure, then these will be initialized.
9555 CORINFO_CLASS_HANDLE retTypeClsHnd = NULL;
9556 InterpreterType retTypeIt;
9557 size_t retTypeSz = 0;
9558
9559 // If non-null, space allocated to hold a large struct return value. Should be deleted later.
9560 // (I could probably optimize this pop all the arguments first, then allocate space for the return value
9561 // on the large structure operand stack, and pass a pointer directly to that space, avoiding the extra
9562 // copy we have below. But this seemed more expedient, and this should be a pretty rare case.)
9563 BYTE* pLargeStructRetVal = NULL;
9564
9565 // If there's a "GetFlag<Flag_hasRetBuffArg>()" struct return value, it will be stored in this variable if it fits,
9566 // otherwise, we'll dynamically allocate memory for it.
9567 ARG_SLOT smallStructRetVal = 0;
9568
9569 // We should have no return buffer temp space registered here...unless this is a constructor, in which
9570 // case it will return void. In particular, if the return type VALUE_CLASS, then this should be NULL.
9571 _ASSERTE_MSG((pCscd != NULL) || sigInfo.retType == CORINFO_TYPE_VOID || m_structRetValITPtr == NULL, "Invariant.");
9572
9573 // Is it the return value a struct with a ret buff?
9574 _ASSERTE_MSG(methToCall != NULL, "assumption");
9575 bool hasRetBuffArg = false;
9576 if (sigInfo.retType == CORINFO_TYPE_VALUECLASS || sigInfo.retType == CORINFO_TYPE_REFANY)
9577 {
9578 hasRetBuffArg = !!methToCall->HasRetBuffArg();
9579 retTypeClsHnd = sigInfo.retTypeClass;
9580
9581 MetaSig ms(methToCall);
9582
9583
9584 // On ARM, if there's an HFA return type, we must also allocate a return buffer, since the
9585 // MDCS calling convention requires it.
9586 if (hasRetBuffArg
9587#if defined(_ARM_)
9588 || HFAReturnArgSlots > 0
9589#endif // defined(_ARM_)
9590 )
9591 {
9592 assert(retTypeClsHnd != NULL);
9593 retTypeIt = InterpreterType(&m_interpCeeInfo, retTypeClsHnd);
9594 retTypeSz = retTypeIt.Size(&m_interpCeeInfo);
9595
9596#if defined(_ARM_)
9597 if (HFAReturnArgSlots > 0)
9598 {
9599 args[curArgSlot] = PtrToArgSlot(args - HFAReturnArgSlots);
9600 }
9601 else
9602#endif // defined(_ARM_)
9603
9604 if (retTypeIt.IsLargeStruct(&m_interpCeeInfo))
9605 {
9606 size_t retBuffSize = retTypeSz;
9607 // If the target architecture can sometimes return a struct in several registers,
9608 // MethodDescCallSite will reserve a return value array big enough to hold the maximum.
9609 // It will then copy *all* of this into the return buffer area we allocate. So make sure
9610 // we allocate at least that much.
9611#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
9612 retBuffSize = max(retTypeSz, ENREGISTERED_RETURNTYPE_MAXSIZE);
9613#endif // ENREGISTERED_RETURNTYPE_MAXSIZE
9614 pLargeStructRetVal = (BYTE*)_alloca(retBuffSize);
9615 // Clear this in case a GC happens.
9616 for (unsigned i = 0; i < retTypeSz; i++) pLargeStructRetVal[i] = 0;
9617 // Register this as location needing GC.
9618 m_structRetValTempSpace = pLargeStructRetVal;
9619 // Set it as the return buffer.
9620 args[curArgSlot] = PtrToArgSlot(pLargeStructRetVal);
9621 }
9622 else
9623 {
9624 // Clear this in case a GC happens.
9625 smallStructRetVal = 0;
9626 // Register this as location needing GC.
9627 m_structRetValTempSpace = &smallStructRetVal;
9628 // Set it as the return buffer.
9629 args[curArgSlot] = PtrToArgSlot(&smallStructRetVal);
9630 }
9631 m_structRetValITPtr = &retTypeIt;
9632 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
9633 curArgSlot++;
9634 }
9635 else
9636 {
9637 // The struct type might "normalize" to a primitive type.
9638 if (retTypeClsHnd == NULL)
9639 {
9640 retTypeIt = InterpreterType(CEEInfo::asCorInfoType(ms.GetReturnTypeNormalized()));
9641 }
9642 else
9643 {
9644 retTypeIt = InterpreterType(&m_interpCeeInfo, retTypeClsHnd);
9645 }
9646 }
9647 }
9648
9649 if (((sigInfo.callConv & CORINFO_CALLCONV_VARARG) != 0) && sigInfo.isVarArg())
9650 {
9651 assert(vaSigCookie != nullptr);
9652 args[curArgSlot] = PtrToArgSlot(vaSigCookie);
9653 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
9654 curArgSlot++;
9655 }
9656
9657 if (pCscd == NULL)
9658 {
9659 if (sigInfo.hasTypeArg())
9660 {
9661 GCX_PREEMP();
9662 // We will find the instantiating stub for the method, and call that instead.
9663 CORINFO_SIG_INFO sigInfoFull;
9664 Instantiation methodInst = methToCall->GetMethodInstantiation();
9665 BOOL fNeedUnboxingStub = virtualCall && TypeHandle(exactClass).IsValueType() && methToCall->IsVirtual();
9666 methToCall = MethodDesc::FindOrCreateAssociatedMethodDesc(methToCall,
9667 TypeHandle(exactClass).GetMethodTable(), fNeedUnboxingStub, methodInst, FALSE, TRUE);
9668 m_interpCeeInfo.getMethodSig(CORINFO_METHOD_HANDLE(methToCall), &sigInfoFull);
9669 sigInfo.retTypeClass = sigInfoFull.retTypeClass;
9670 sigInfo.numArgs = sigInfoFull.numArgs;
9671 sigInfo.callConv = sigInfoFull.callConv;
9672 sigInfo.retType = sigInfoFull.retType;
9673 }
9674
9675 if (sigInfo.hasTypeArg())
9676 {
9677 // If we still have a type argument, we're calling an ArrayOpStub and need to pass the array TypeHandle.
9678 assert(methToCall->IsArray());
9679 doNotCache = true;
9680 args[curArgSlot] = PtrToArgSlot(exactClass);
9681 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
9682 curArgSlot++;
9683 }
9684 }
9685
9686 // Now we do the non-this arguments.
9687 size_t largeStructSpaceToPop = 0;
9688 for (; arg < totalArgsOnILStack; arg++)
9689 {
9690 InterpreterType argIt = OpStackTypeGet(argsBase + arg);
9691 size_t sz = OpStackTypeGet(argsBase + arg).Size(&m_interpCeeInfo);
9692 switch (sz)
9693 {
9694 case 1:
9695 args[curArgSlot] = OpStackGet<INT8>(argsBase + arg);
9696 break;
9697 case 2:
9698 args[curArgSlot] = OpStackGet<INT16>(argsBase + arg);
9699 break;
9700 case 4:
9701 args[curArgSlot] = OpStackGet<INT32>(argsBase + arg);
9702 break;
9703 case 8:
9704 default:
9705 if (sz > 8)
9706 {
9707 void* srcPtr = OpStackGet<void*>(argsBase + arg);
9708 args[curArgSlot] = PtrToArgSlot(srcPtr);
9709 if (!IsInLargeStructLocalArea(srcPtr))
9710 largeStructSpaceToPop += sz;
9711 }
9712 else
9713 {
9714 args[curArgSlot] = OpStackGet<INT64>(argsBase + arg);
9715 }
9716 break;
9717 }
9718 argTypes[curArgSlot] = argIt;
9719 curArgSlot++;
9720 }
9721
9722 if (ctorData.pArg3)
9723 {
9724 args[curArgSlot] = PtrToArgSlot(ctorData.pArg3);
9725 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
9726 curArgSlot++;
9727 }
9728 if (ctorData.pArg4)
9729 {
9730 args[curArgSlot] = PtrToArgSlot(ctorData.pArg4);
9731 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
9732 curArgSlot++;
9733 }
9734 if (ctorData.pArg5)
9735 {
9736 args[curArgSlot] = PtrToArgSlot(ctorData.pArg5);
9737 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
9738 curArgSlot++;
9739 }
9740
9741 // CYCLE PROFILE: AFTER ARG PROCESSING.
9742 {
9743 Thread* thr = GetThread();
9744
9745 Object** thisArgHnd = NULL;
9746 ARG_SLOT nullThisArg = NULL;
9747 if (sigInfo.hasThis())
9748 {
9749 if (m_callThisArg != NULL)
9750 {
9751 if (size_t(m_callThisArg) == 0x1)
9752 {
9753 thisArgHnd = reinterpret_cast<Object**>(&nullThisArg);
9754 }
9755 else
9756 {
9757 thisArgHnd = reinterpret_cast<Object**>(&m_callThisArg);
9758 }
9759 }
9760 else
9761 {
9762 thisArgHnd = OpStackGetAddr<Object*>(argsBase);
9763 }
9764 }
9765
9766 Frame* topFrameBefore = thr->GetFrame();
9767
9768#if INTERP_ILCYCLE_PROFILE
9769 unsigned __int64 startCycles;
9770#endif // INTERP_ILCYCLE_PROFILE
9771
9772 // CYCLE PROFILE: BEFORE MDCS CREATION.
9773
9774 PCODE target = NULL;
9775 MethodDesc *exactMethToCall = methToCall;
9776
9777 // Determine the target of virtual calls.
9778 if (virtualCall && methToCall->IsVtableMethod())
9779 {
9780 PCODE pCode;
9781
9782 assert(thisArgHnd != NULL);
9783 OBJECTREF objRef = ObjectToOBJECTREF(*thisArgHnd);
9784 GCPROTECT_BEGIN(objRef);
9785 pCode = methToCall->GetMultiCallableAddrOfVirtualizedCode(&objRef, methToCall->GetMethodTable());
9786 GCPROTECT_END();
9787
9788 exactMethToCall = Entry2MethodDesc(pCode, objRef->GetMethodTable());
9789 }
9790
9791 // Compile the target in advance of calling.
9792 if (exactMethToCall->IsPointingToPrestub())
9793 {
9794 MethodTable* dispatchingMT = NULL;
9795 if (exactMethToCall->IsVtableMethod())
9796 {
9797 assert(thisArgHnd != NULL);
9798 dispatchingMT = (*thisArgHnd)->GetMethodTable();
9799 }
9800 GCX_PREEMP();
9801 target = exactMethToCall->DoPrestub(dispatchingMT);
9802 }
9803 else
9804 {
9805 target = exactMethToCall->GetMethodEntryPoint();
9806 }
9807
9808 // If we're interpreting the method, simply call it directly.
9809 if (InterpretationStubToMethodInfo(target) == exactMethToCall)
9810 {
9811 assert(!exactMethToCall->IsILStub());
9812 InterpreterMethodInfo* methInfo = MethodHandleToInterpreterMethInfoPtr(CORINFO_METHOD_HANDLE(exactMethToCall));
9813 assert(methInfo != NULL);
9814#if INTERP_ILCYCLE_PROFILE
9815 bool b = CycleTimer::GetThreadCyclesS(&startCycles); assert(b);
9816#endif // INTERP_ILCYCLE_PROFILE
9817 retVal = InterpretMethodBody(methInfo, true, reinterpret_cast<BYTE*>(args), NULL);
9818 pCscd = NULL; // Nothing to cache.
9819 }
9820 else
9821 {
9822 MetaSig msig(exactMethToCall);
9823 // We've already resolved the virtual call target above, so there is no need to do it again.
9824 MethodDescCallSite mdcs(exactMethToCall, &msig, target);
9825#if INTERP_ILCYCLE_PROFILE
9826 bool b = CycleTimer::GetThreadCyclesS(&startCycles); assert(b);
9827#endif // INTERP_ILCYCLE_PROFILE
9828 mdcs.CallTargetWorker(args, &retVal, sizeof(retVal));
9829
9830 if (pCscd != NULL)
9831 {
9832 // We will do a check at the end to determine whether to cache pCscd, to set
9833 // to NULL here to make sure we don't.
9834 pCscd = NULL;
9835 }
9836 else
9837 {
9838 // For now, we won't cache virtual calls to virtual methods.
9839 // TODO: fix this somehow.
9840 if (virtualCall && (callInfoPtr->methodFlags & CORINFO_FLG_VIRTUAL)) doNotCache = true;
9841
9842 if (s_InterpreterUseCaching && !doNotCache)
9843 {
9844 // We will add this to the cache later; the locking provokes a GC,
9845 // and "retVal" is vulnerable.
9846 pCscd = new CallSiteCacheData(exactMethToCall, sigInfo);
9847 }
9848 }
9849 }
9850#if INTERP_ILCYCLE_PROFILE
9851 unsigned __int64 endCycles;
9852 bool b = CycleTimer::GetThreadCyclesS(&endCycles); assert(b);
9853 m_exemptCycles += (endCycles - startCycles);
9854#endif // INTERP_ILCYCLE_PROFILE
9855
9856 // retVal is now vulnerable.
9857 GCX_FORBID();
9858
9859 // Some managed methods, believe it or not, can push capital-F Frames on the Frame chain.
9860 // If this happens, executing the EX_CATCH below will pop it, which is bad.
9861 // So detect that case, pop the explicitly-pushed frame, and push it again after the EX_CATCH.
9862 // (Asserting that there is only 1 such frame!)
9863 if (thr->GetFrame() != topFrameBefore)
9864 {
9865 ilPushedFrame = thr->GetFrame();
9866 if (ilPushedFrame != NULL)
9867 {
9868 ilPushedFrame->Pop(thr);
9869 if (thr->GetFrame() != topFrameBefore)
9870 {
9871 // This wasn't an IL-pushed frame, so restore.
9872 ilPushedFrame->Push(thr);
9873 ilPushedFrame = NULL;
9874 }
9875 }
9876 }
9877 }
9878
9879 // retVal is still vulnerable.
9880 {
9881 GCX_FORBID();
9882 m_argsSize = 0;
9883
9884 // At this point, the call has happened successfully. We can delete the arguments from the operand stack.
9885 m_curStackHt -= totalArgsOnILStack;
9886 // We've already checked that "largeStructSpaceToPop
9887 LargeStructOperandStackPop(largeStructSpaceToPop, NULL);
9888
9889 if (size_t(m_callThisArg) == 0x1)
9890 {
9891 _ASSERTE_MSG(sigInfo.retType == CORINFO_TYPE_VOID, "Constructor for var-sized object becomes factory method that returns result.");
9892 OpStackSet<Object*>(m_curStackHt, reinterpret_cast<Object*>(retVal));
9893 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_CLASS));
9894 m_curStackHt++;
9895 }
9896 else if (sigInfo.retType != CORINFO_TYPE_VOID)
9897 {
9898 switch (sigInfo.retType)
9899 {
9900 case CORINFO_TYPE_BOOL:
9901 case CORINFO_TYPE_BYTE:
9902 OpStackSet<INT32>(m_curStackHt, static_cast<INT8>(retVal));
9903 break;
9904 case CORINFO_TYPE_UBYTE:
9905 OpStackSet<UINT32>(m_curStackHt, static_cast<UINT8>(retVal));
9906 break;
9907 case CORINFO_TYPE_SHORT:
9908 OpStackSet<INT32>(m_curStackHt, static_cast<INT16>(retVal));
9909 break;
9910 case CORINFO_TYPE_USHORT:
9911 case CORINFO_TYPE_CHAR:
9912 OpStackSet<UINT32>(m_curStackHt, static_cast<UINT16>(retVal));
9913 break;
9914 case CORINFO_TYPE_INT:
9915 case CORINFO_TYPE_UINT:
9916 case CORINFO_TYPE_FLOAT:
9917 OpStackSet<INT32>(m_curStackHt, static_cast<INT32>(retVal));
9918 break;
9919 case CORINFO_TYPE_LONG:
9920 case CORINFO_TYPE_ULONG:
9921 case CORINFO_TYPE_DOUBLE:
9922 OpStackSet<INT64>(m_curStackHt, static_cast<INT64>(retVal));
9923 break;
9924 case CORINFO_TYPE_NATIVEINT:
9925 case CORINFO_TYPE_NATIVEUINT:
9926 case CORINFO_TYPE_PTR:
9927 OpStackSet<NativeInt>(m_curStackHt, static_cast<NativeInt>(retVal));
9928 break;
9929 case CORINFO_TYPE_CLASS:
9930 OpStackSet<Object*>(m_curStackHt, reinterpret_cast<Object*>(retVal));
9931 break;
9932 case CORINFO_TYPE_BYREF:
9933 OpStackSet<void*>(m_curStackHt, reinterpret_cast<void*>(retVal));
9934 break;
9935 case CORINFO_TYPE_VALUECLASS:
9936 case CORINFO_TYPE_REFANY:
9937 {
9938 // We must be careful here to write the value, the type, and update the stack height in one
9939 // sequence that has no COOP->PREEMP transitions in it, so no GC's happen until the value
9940 // is protected by being fully "on" the operandStack.
9941#if defined(_ARM_)
9942 // Is the return type an HFA?
9943 if (HFAReturnArgSlots > 0)
9944 {
9945 ARG_SLOT* hfaRetBuff = args - HFAReturnArgSlots;
9946 if (retTypeIt.IsLargeStruct(&m_interpCeeInfo))
9947 {
9948 void* dst = LargeStructOperandStackPush(retTypeSz);
9949 memcpy(dst, hfaRetBuff, retTypeSz);
9950 OpStackSet<void*>(m_curStackHt, dst);
9951 }
9952 else
9953 {
9954 memcpy(OpStackGetAddr<UINT64>(m_curStackHt), hfaRetBuff, retTypeSz);
9955 }
9956 }
9957 else
9958#endif // defined(_ARM_)
9959 if (pLargeStructRetVal != NULL)
9960 {
9961 assert(hasRetBuffArg);
9962 void* dst = LargeStructOperandStackPush(retTypeSz);
9963 CopyValueClassUnchecked(dst, pLargeStructRetVal, GetMethodTableFromClsHnd(retTypeClsHnd));
9964 OpStackSet<void*>(m_curStackHt, dst);
9965 }
9966 else if (hasRetBuffArg)
9967 {
9968 OpStackSet<INT64>(m_curStackHt, GetSmallStructValue(&smallStructRetVal, retTypeSz));
9969 }
9970 else
9971 {
9972 OpStackSet<UINT64>(m_curStackHt, retVal);
9973 }
9974 // We already created this interpreter type, so use it.
9975 OpStackTypeSet(m_curStackHt, retTypeIt.StackNormalize());
9976 m_curStackHt++;
9977
9978
9979 // In the value-class case, the call might have used a ret buff, which we would have registered for GC scanning.
9980 // Make sure it's unregistered.
9981 m_structRetValITPtr = NULL;
9982 }
9983 break;
9984 default:
9985 NYI_INTERP("Unhandled return type");
9986 break;
9987 }
9988 _ASSERTE_MSG(m_structRetValITPtr == NULL, "Invariant.");
9989
9990 // The valueclass case is handled fully in the switch above.
9991 if (sigInfo.retType != CORINFO_TYPE_VALUECLASS &&
9992 sigInfo.retType != CORINFO_TYPE_REFANY)
9993 {
9994 OpStackTypeSet(m_curStackHt, InterpreterType(sigInfo.retType).StackNormalize());
9995 m_curStackHt++;
9996 }
9997 }
9998 }
9999
10000 // Originally, this assertion was in the ValueClass case above, but it does a COOP->PREEMP
10001 // transition, and therefore causes a GC, and we're GCX_FORBIDden from doing a GC while retVal
10002 // is vulnerable. So, for completeness, do it here.
10003 assert(sigInfo.retType != CORINFO_TYPE_VALUECLASS || retTypeIt == InterpreterType(&m_interpCeeInfo, retTypeClsHnd));
10004
10005 // If we created a cached call site, cache it now (when it's safe to take a GC).
10006 if (pCscd != NULL && !doNotCache)
10007 {
10008 CacheCallInfo(iloffset, pCscd);
10009 }
10010
10011 m_callThisArg = NULL;
10012
10013 // If the call we just made pushed a Frame, we popped it above, so re-push it.
10014 if (ilPushedFrame != NULL) ilPushedFrame->Push();
10015}
10016
10017#include "metadata.h"
10018
10019void Interpreter::CallI()
10020{
10021#if INTERP_DYNAMIC_CONTRACTS
10022 CONTRACTL {
10023 SO_TOLERANT;
10024 THROWS;
10025 GC_TRIGGERS;
10026 MODE_COOPERATIVE;
10027 } CONTRACTL_END;
10028#else
10029 // Dynamic contract occupies too much stack.
10030 STATIC_CONTRACT_SO_TOLERANT;
10031 STATIC_CONTRACT_THROWS;
10032 STATIC_CONTRACT_GC_TRIGGERS;
10033 STATIC_CONTRACT_MODE_COOPERATIVE;
10034#endif
10035
10036#if INTERP_TRACING
10037 InterlockedIncrement(&s_totalInterpCalls);
10038#endif // INTERP_TRACING
10039
10040 unsigned tok = getU4LittleEndian(m_ILCodePtr + sizeof(BYTE));
10041
10042 CORINFO_SIG_INFO sigInfo;
10043
10044 {
10045 GCX_PREEMP();
10046 m_interpCeeInfo.findSig(m_methInfo->m_module, tok, GetPreciseGenericsContext(), &sigInfo);
10047 }
10048
10049 // I'm assuming that a calli can't depend on the generics context, so the simple form of type
10050 // context should suffice?
10051 MethodDesc* pMD = reinterpret_cast<MethodDesc*>(m_methInfo->m_method);
10052 SigTypeContext sigTypeCtxt(pMD);
10053 MetaSig mSig(sigInfo.pSig, sigInfo.cbSig, GetModule(sigInfo.scope), &sigTypeCtxt);
10054
10055 unsigned totalSigArgs = sigInfo.totalILArgs();
10056
10057 // Note that "totalNativeArgs()" includes space for ret buff arg.
10058 unsigned nSlots = totalSigArgs + 1;
10059 if ((sigInfo.callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG)
10060 {
10061 nSlots++;
10062 }
10063
10064 // Make sure that the operand stack has the required number of arguments.
10065 // (Note that this is IL args, not native.)
10066 //
10067
10068 // The total number of arguments on the IL stack. Initially we assume that all the IL arguments
10069 // the callee expects are on the stack, but may be adjusted downwards if the "this" argument
10070 // is provided by an allocation (the call is to a constructor).
10071 unsigned totalArgsOnILStack = totalSigArgs;
10072
10073 const unsigned LOCAL_ARG_SLOTS = 8;
10074 ARG_SLOT localArgs[LOCAL_ARG_SLOTS];
10075 InterpreterType localArgTypes[LOCAL_ARG_SLOTS];
10076
10077 ARG_SLOT* args;
10078 InterpreterType* argTypes;
10079 if (nSlots <= LOCAL_ARG_SLOTS)
10080 {
10081 args = &localArgs[0];
10082 argTypes = &localArgTypes[0];
10083 }
10084 else
10085 {
10086 args = (ARG_SLOT*)_alloca(nSlots * sizeof(ARG_SLOT));
10087 argTypes = (InterpreterType*)_alloca(nSlots * sizeof(InterpreterType));
10088 }
10089 // Make sure that we don't scan any of these until we overwrite them with
10090 // the real types of the arguments.
10091 InterpreterType undefIt(CORINFO_TYPE_UNDEF);
10092 for (unsigned i = 0; i < nSlots; i++)
10093 {
10094 argTypes[i] = undefIt;
10095 }
10096
10097 // GC-protect the argument array (as byrefs).
10098 m_args = args;
10099 m_argsSize = nSlots;
10100 m_argTypes = argTypes;
10101
10102 // This is the index into the "args" array (where we copy the value to).
10103 int curArgSlot = 0;
10104
10105 // The operand stack index of the first IL argument.
10106 unsigned totalArgPositions = totalArgsOnILStack + 1; // + 1 for the ftn argument.
10107 assert(m_curStackHt >= totalArgPositions);
10108 int argsBase = m_curStackHt - totalArgPositions;
10109
10110 // Current on-stack argument index.
10111 unsigned arg = 0;
10112
10113 if (sigInfo.hasThis())
10114 {
10115 args[curArgSlot] = PtrToArgSlot(OpStackGet<void*>(argsBase + arg));
10116 argTypes[curArgSlot] = OpStackTypeGet(argsBase + arg);
10117 // AV -> NullRef translation is NYI for the interpreter,
10118 // so we should manually check and throw the correct exception.
10119 ThrowOnInvalidPointer((void*)args[curArgSlot]);
10120 arg++;
10121 curArgSlot++;
10122 }
10123
10124 // This is the argument slot that will be used to hold the return value.
10125 ARG_SLOT retVal = 0;
10126
10127 // If the return type is a structure, then these will be initialized.
10128 CORINFO_CLASS_HANDLE retTypeClsHnd = NULL;
10129 InterpreterType retTypeIt;
10130 size_t retTypeSz = 0;
10131
10132 // If non-null, space allocated to hold a large struct return value. Should be deleted later.
10133 // (I could probably optimize this pop all the arguments first, then allocate space for the return value
10134 // on the large structure operand stack, and pass a pointer directly to that space, avoiding the extra
10135 // copy we have below. But this seemed more expedient, and this should be a pretty rare case.)
10136 BYTE* pLargeStructRetVal = NULL;
10137
10138 // If there's a "GetFlag<Flag_hasRetBuffArg>()" struct return value, it will be stored in this variable if it fits,
10139 // otherwise, we'll dynamically allocate memory for it.
10140 ARG_SLOT smallStructRetVal = 0;
10141
10142 // We should have no return buffer temp space registered here...unless this is a constructor, in which
10143 // case it will return void. In particular, if the return type VALUE_CLASS, then this should be NULL.
10144 _ASSERTE_MSG(sigInfo.retType == CORINFO_TYPE_VOID || m_structRetValITPtr == NULL, "Invariant.");
10145
10146 // Is it the return value a struct with a ret buff?
10147 bool hasRetBuffArg = false;
10148 if (sigInfo.retType == CORINFO_TYPE_VALUECLASS)
10149 {
10150 retTypeClsHnd = sigInfo.retTypeClass;
10151 retTypeIt = InterpreterType(&m_interpCeeInfo, retTypeClsHnd);
10152 retTypeSz = retTypeIt.Size(&m_interpCeeInfo);
10153#if defined(_AMD64_)
10154 // TODO: Investigate why HasRetBuffArg can't be used. pMD is a hacked up MD for the
10155 // calli because it belongs to the current method. Doing what the JIT does.
10156 hasRetBuffArg = (retTypeSz > sizeof(void*)) || ((retTypeSz & (retTypeSz - 1)) != 0);
10157#else
10158 hasRetBuffArg = !!pMD->HasRetBuffArg();
10159#endif
10160 if (hasRetBuffArg)
10161 {
10162 if (retTypeIt.IsLargeStruct(&m_interpCeeInfo))
10163 {
10164 size_t retBuffSize = retTypeSz;
10165 // If the target architecture can sometimes return a struct in several registers,
10166 // MethodDescCallSite will reserve a return value array big enough to hold the maximum.
10167 // It will then copy *all* of this into the return buffer area we allocate. So make sure
10168 // we allocate at least that much.
10169#ifdef ENREGISTERED_RETURNTYPE_MAXSIZE
10170 retBuffSize = max(retTypeSz, ENREGISTERED_RETURNTYPE_MAXSIZE);
10171#endif // ENREGISTERED_RETURNTYPE_MAXSIZE
10172 pLargeStructRetVal = (BYTE*)_alloca(retBuffSize);
10173
10174 // Clear this in case a GC happens.
10175 for (unsigned i = 0; i < retTypeSz; i++)
10176 {
10177 pLargeStructRetVal[i] = 0;
10178 }
10179
10180 // Register this as location needing GC.
10181 m_structRetValTempSpace = pLargeStructRetVal;
10182
10183 // Set it as the return buffer.
10184 args[curArgSlot] = PtrToArgSlot(pLargeStructRetVal);
10185 }
10186 else
10187 {
10188 // Clear this in case a GC happens.
10189 smallStructRetVal = 0;
10190
10191 // Register this as location needing GC.
10192 m_structRetValTempSpace = &smallStructRetVal;
10193
10194 // Set it as the return buffer.
10195 args[curArgSlot] = PtrToArgSlot(&smallStructRetVal);
10196 }
10197 m_structRetValITPtr = &retTypeIt;
10198 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
10199 curArgSlot++;
10200 }
10201 }
10202
10203 if ((sigInfo.callConv & CORINFO_CALLCONV_MASK) == CORINFO_CALLCONV_VARARG)
10204 {
10205 Module* module = GetModule(sigInfo.scope);
10206 CORINFO_VARARGS_HANDLE handle = CORINFO_VARARGS_HANDLE(module->GetVASigCookie(Signature(sigInfo.pSig, sigInfo.cbSig)));
10207 args[curArgSlot] = PtrToArgSlot(handle);
10208 argTypes[curArgSlot] = InterpreterType(CORINFO_TYPE_NATIVEINT);
10209 curArgSlot++;
10210 }
10211
10212 // Now we do the non-this arguments.
10213 size_t largeStructSpaceToPop = 0;
10214 for (; arg < totalArgsOnILStack; arg++)
10215 {
10216 InterpreterType argIt = OpStackTypeGet(argsBase + arg);
10217 size_t sz = OpStackTypeGet(argsBase + arg).Size(&m_interpCeeInfo);
10218 switch (sz)
10219 {
10220 case 1:
10221 args[curArgSlot] = OpStackGet<INT8>(argsBase + arg);
10222 break;
10223 case 2:
10224 args[curArgSlot] = OpStackGet<INT16>(argsBase + arg);
10225 break;
10226 case 4:
10227 args[curArgSlot] = OpStackGet<INT32>(argsBase + arg);
10228 break;
10229 case 8:
10230 default:
10231 if (sz > 8)
10232 {
10233 void* srcPtr = OpStackGet<void*>(argsBase + arg);
10234 args[curArgSlot] = PtrToArgSlot(srcPtr);
10235 if (!IsInLargeStructLocalArea(srcPtr))
10236 {
10237 largeStructSpaceToPop += sz;
10238 }
10239 }
10240 else
10241 {
10242 args[curArgSlot] = OpStackGet<INT64>(argsBase + arg);
10243 }
10244 break;
10245 }
10246 argTypes[curArgSlot] = argIt;
10247 curArgSlot++;
10248 }
10249
10250 // Finally, we get the code pointer.
10251 unsigned ftnInd = m_curStackHt - 1;
10252#ifdef _DEBUG
10253 CorInfoType ftnType = OpStackTypeGet(ftnInd).ToCorInfoType();
10254 assert(ftnType == CORINFO_TYPE_NATIVEINT
10255 || ftnType == CORINFO_TYPE_INT
10256 || ftnType == CORINFO_TYPE_LONG);
10257#endif // DEBUG
10258
10259 PCODE ftnPtr = OpStackGet<PCODE>(ftnInd);
10260
10261 {
10262 MethodDesc* methToCall;
10263 // If we're interpreting the target, simply call it directly.
10264 if ((methToCall = InterpretationStubToMethodInfo((PCODE)ftnPtr)) != NULL)
10265 {
10266 InterpreterMethodInfo* methInfo = MethodHandleToInterpreterMethInfoPtr(CORINFO_METHOD_HANDLE(methToCall));
10267 assert(methInfo != NULL);
10268#if INTERP_ILCYCLE_PROFILE
10269 bool b = CycleTimer::GetThreadCyclesS(&startCycles); assert(b);
10270#endif // INTERP_ILCYCLE_PROFILE
10271 retVal = InterpretMethodBody(methInfo, true, reinterpret_cast<BYTE*>(args), NULL);
10272 }
10273 else
10274 {
10275 // This is not a great workaround. For the most part, we really don't care what method desc we're using, since
10276 // we're providing the signature and function pointer -- other than that it's well-formed and "activated."
10277 // And also, one more thing: whether it is static or not. Which is actually determined by the signature.
10278 // So we query the signature we have to determine whether we need a static or instance MethodDesc, and then
10279 // use one of the appropriate staticness that happens to be sitting around in global variables. For static
10280 // we use "RuntimeHelpers.PrepareConstrainedRegions", for instance we use the default constructor of "Object."
10281 // TODO: make this cleaner -- maybe invent a couple of empty methods with instructive names, just for this purpose.
10282 MethodDesc* pMD;
10283 if (mSig.HasThis())
10284 {
10285 pMD = g_pObjectFinalizerMD;
10286 }
10287 else
10288 {
10289 pMD = g_pExecuteBackoutCodeHelperMethod; // A random static method.
10290 }
10291 MethodDescCallSite mdcs(pMD, &mSig, ftnPtr);
10292#if 0
10293 // If the current method being interpreted is an IL stub, we're calling native code, so
10294 // change the GC mode. (We'll only do this at the call if the calling convention turns out
10295 // to be a managed calling convention.)
10296 MethodDesc* pStubContextMD = reinterpret_cast<MethodDesc*>(m_stubContext);
10297 bool transitionToPreemptive = (pStubContextMD != NULL && !pStubContextMD->IsIL());
10298 mdcs.CallTargetWorker(args, &retVal, sizeof(retVal), transitionToPreemptive);
10299#else
10300 // TODO The code above triggers assertion at threads.cpp:6861:
10301 // _ASSERTE(thread->PreemptiveGCDisabled()); // Should have been in managed code
10302 // The workaround will likely break more things than what it is fixing:
10303 // just do not make transition to preemptive GC for now.
10304 mdcs.CallTargetWorker(args, &retVal, sizeof(retVal));
10305#endif
10306 }
10307 // retVal is now vulnerable.
10308 GCX_FORBID();
10309 }
10310
10311 // retVal is still vulnerable.
10312 {
10313 GCX_FORBID();
10314 m_argsSize = 0;
10315
10316 // At this point, the call has happened successfully. We can delete the arguments from the operand stack.
10317 m_curStackHt -= totalArgPositions;
10318
10319 // We've already checked that "largeStructSpaceToPop
10320 LargeStructOperandStackPop(largeStructSpaceToPop, NULL);
10321
10322 if (size_t(m_callThisArg) == 0x1)
10323 {
10324 _ASSERTE_MSG(sigInfo.retType == CORINFO_TYPE_VOID, "Constructor for var-sized object becomes factory method that returns result.");
10325 OpStackSet<Object*>(m_curStackHt, reinterpret_cast<Object*>(retVal));
10326 OpStackTypeSet(m_curStackHt, InterpreterType(CORINFO_TYPE_CLASS));
10327 m_curStackHt++;
10328 }
10329 else if (sigInfo.retType != CORINFO_TYPE_VOID)
10330 {
10331 switch (sigInfo.retType)
10332 {
10333 case CORINFO_TYPE_BOOL:
10334 case CORINFO_TYPE_BYTE:
10335 OpStackSet<INT32>(m_curStackHt, static_cast<INT8>(retVal));
10336 break;
10337 case CORINFO_TYPE_UBYTE:
10338 OpStackSet<UINT32>(m_curStackHt, static_cast<UINT8>(retVal));
10339 break;
10340 case CORINFO_TYPE_SHORT:
10341 OpStackSet<INT32>(m_curStackHt, static_cast<INT16>(retVal));
10342 break;
10343 case CORINFO_TYPE_USHORT:
10344 case CORINFO_TYPE_CHAR:
10345 OpStackSet<UINT32>(m_curStackHt, static_cast<UINT16>(retVal));
10346 break;
10347 case CORINFO_TYPE_INT:
10348 case CORINFO_TYPE_UINT:
10349 case CORINFO_TYPE_FLOAT:
10350 OpStackSet<INT32>(m_curStackHt, static_cast<INT32>(retVal));
10351 break;
10352 case CORINFO_TYPE_LONG:
10353 case CORINFO_TYPE_ULONG:
10354 case CORINFO_TYPE_DOUBLE:
10355 OpStackSet<INT64>(m_curStackHt, static_cast<INT64>(retVal));
10356 break;
10357 case CORINFO_TYPE_NATIVEINT:
10358 case CORINFO_TYPE_NATIVEUINT:
10359 case CORINFO_TYPE_PTR:
10360 OpStackSet<NativeInt>(m_curStackHt, static_cast<NativeInt>(retVal));
10361 break;
10362 case CORINFO_TYPE_CLASS:
10363 OpStackSet<Object*>(m_curStackHt, reinterpret_cast<Object*>(retVal));
10364 break;
10365 case CORINFO_TYPE_VALUECLASS:
10366 {
10367 // We must be careful here to write the value, the type, and update the stack height in one
10368 // sequence that has no COOP->PREEMP transitions in it, so no GC's happen until the value
10369 // is protected by being fully "on" the operandStack.
10370 if (pLargeStructRetVal != NULL)
10371 {
10372 assert(hasRetBuffArg);
10373 void* dst = LargeStructOperandStackPush(retTypeSz);
10374 CopyValueClassUnchecked(dst, pLargeStructRetVal, GetMethodTableFromClsHnd(retTypeClsHnd));
10375 OpStackSet<void*>(m_curStackHt, dst);
10376 }
10377 else if (hasRetBuffArg)
10378 {
10379 OpStackSet<INT64>(m_curStackHt, GetSmallStructValue(&smallStructRetVal, retTypeSz));
10380 }
10381 else
10382 {
10383 OpStackSet<UINT64>(m_curStackHt, retVal);
10384 }
10385 // We already created this interpreter type, so use it.
10386 OpStackTypeSet(m_curStackHt, retTypeIt.StackNormalize());
10387 m_curStackHt++;
10388
10389 // In the value-class case, the call might have used a ret buff, which we would have registered for GC scanning.
10390 // Make sure it's unregistered.
10391 m_structRetValITPtr = NULL;
10392 }
10393 break;
10394 default:
10395 NYI_INTERP("Unhandled return type");
10396 break;
10397 }
10398 _ASSERTE_MSG(m_structRetValITPtr == NULL, "Invariant.");
10399
10400 // The valueclass case is handled fully in the switch above.
10401 if (sigInfo.retType != CORINFO_TYPE_VALUECLASS)
10402 {
10403 OpStackTypeSet(m_curStackHt, InterpreterType(sigInfo.retType).StackNormalize());
10404 m_curStackHt++;
10405 }
10406 }
10407 }
10408
10409 // Originally, this assertion was in the ValueClass case above, but it does a COOP->PREEMP
10410 // transition, and therefore causes a GC, and we're GCX_FORBIDden from doing a GC while retVal
10411 // is vulnerable. So, for completeness, do it here.
10412 assert(sigInfo.retType != CORINFO_TYPE_VALUECLASS || retTypeIt == InterpreterType(&m_interpCeeInfo, retTypeClsHnd));
10413
10414 m_ILCodePtr += 5;
10415}
10416
10417// static
10418bool Interpreter::IsDeadSimpleGetter(CEEInfo* info, MethodDesc* pMD, size_t* offsetOfLd)
10419{
10420 CONTRACTL {
10421 SO_TOLERANT;
10422 THROWS;
10423 GC_TRIGGERS;
10424 MODE_ANY;
10425 } CONTRACTL_END;
10426
10427 DWORD flags = pMD->GetAttrs();
10428 CORINFO_METHOD_INFO methInfo;
10429 {
10430 GCX_PREEMP();
10431 bool b = info->getMethodInfo(CORINFO_METHOD_HANDLE(pMD), &methInfo);
10432 if (!b) return false;
10433 }
10434
10435 // If the method takes a generic type argument, it's not dead simple...
10436 if (methInfo.args.callConv & CORINFO_CALLCONV_PARAMTYPE) return false;
10437
10438 BYTE* codePtr = methInfo.ILCode;
10439
10440 if (flags & CORINFO_FLG_STATIC)
10441 {
10442 if (methInfo.ILCodeSize != 6)
10443 return false;
10444 if (*codePtr != CEE_LDSFLD)
10445 return false;
10446 assert(ILOffsetOfLdSFldInDeadSimpleStaticGetter == 0);
10447 *offsetOfLd = 0;
10448 codePtr += 5;
10449 return (*codePtr == CEE_RET);
10450 }
10451 else
10452 {
10453 // We handle two forms, one for DBG IL, and one for OPT IL.
10454 bool dbg = false;
10455 if (methInfo.ILCodeSize == 0xc)
10456 dbg = true;
10457 else if (methInfo.ILCodeSize != 7)
10458 return false;
10459
10460 if (dbg)
10461 {
10462 if (*codePtr != CEE_NOP)
10463 return false;
10464 codePtr += 1;
10465 }
10466 if (*codePtr != CEE_LDARG_0)
10467 return false;
10468 codePtr += 1;
10469 if (*codePtr != CEE_LDFLD)
10470 return false;
10471 *offsetOfLd = codePtr - methInfo.ILCode;
10472 assert((dbg && ILOffsetOfLdFldInDeadSimpleInstanceGetterDbg == *offsetOfLd)
10473 || (!dbg && ILOffsetOfLdFldInDeadSimpleInstanceGetterOpt == *offsetOfLd));
10474 codePtr += 5;
10475 if (dbg)
10476 {
10477 if (*codePtr != CEE_STLOC_0)
10478 return false;
10479 codePtr += 1;
10480 if (*codePtr != CEE_BR)
10481 return false;
10482 if (getU4LittleEndian(codePtr + 1) != 0)
10483 return false;
10484 codePtr += 5;
10485 if (*codePtr != CEE_LDLOC_0)
10486 return false;
10487 }
10488 return (*codePtr == CEE_RET);
10489 }
10490}
10491
10492void Interpreter::DoStringLength()
10493{
10494 CONTRACTL {
10495 SO_TOLERANT;
10496 THROWS;
10497 GC_TRIGGERS;
10498 MODE_COOPERATIVE;
10499 } CONTRACTL_END;
10500
10501 assert(m_curStackHt > 0);
10502 unsigned ind = m_curStackHt - 1;
10503
10504#ifdef _DEBUG
10505 CorInfoType stringCIT = OpStackTypeGet(ind).ToCorInfoType();
10506 if (stringCIT != CORINFO_TYPE_CLASS)
10507 {
10508 VerificationError("StringLength called on non-string.");
10509 }
10510#endif // _DEBUG
10511
10512 Object* obj = OpStackGet<Object*>(ind);
10513
10514 if (obj == NULL)
10515 {
10516 ThrowNullPointerException();
10517 }
10518
10519#ifdef _DEBUG
10520 if (obj->GetMethodTable() != g_pStringClass)
10521 {
10522 VerificationError("StringLength called on non-string.");
10523 }
10524#endif // _DEBUG
10525
10526 StringObject* str = reinterpret_cast<StringObject*>(obj);
10527 INT32 len = str->GetStringLength();
10528 OpStackSet<INT32>(ind, len);
10529 OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_INT));
10530}
10531
10532void Interpreter::DoStringGetChar()
10533{
10534 CONTRACTL {
10535 SO_TOLERANT;
10536 THROWS;
10537 GC_TRIGGERS;
10538 MODE_COOPERATIVE;
10539 } CONTRACTL_END;
10540
10541 assert(m_curStackHt >= 2);
10542 unsigned strInd = m_curStackHt - 2;
10543 unsigned indexInd = strInd + 1;
10544
10545#ifdef _DEBUG
10546 CorInfoType stringCIT = OpStackTypeGet(strInd).ToCorInfoType();
10547 if (stringCIT != CORINFO_TYPE_CLASS)
10548 {
10549 VerificationError("StringGetChar called on non-string.");
10550 }
10551#endif // _DEBUG
10552
10553 Object* obj = OpStackGet<Object*>(strInd);
10554
10555 if (obj == NULL)
10556 {
10557 ThrowNullPointerException();
10558 }
10559
10560#ifdef _DEBUG
10561 if (obj->GetMethodTable() != g_pStringClass)
10562 {
10563 VerificationError("StringGetChar called on non-string.");
10564 }
10565#endif // _DEBUG
10566
10567 StringObject* str = reinterpret_cast<StringObject*>(obj);
10568
10569#ifdef _DEBUG
10570 CorInfoType indexCIT = OpStackTypeGet(indexInd).ToCorInfoType();
10571 if (indexCIT != CORINFO_TYPE_INT)
10572 {
10573 VerificationError("StringGetChar needs integer index.");
10574 }
10575#endif // _DEBUG
10576
10577 INT32 ind = OpStackGet<INT32>(indexInd);
10578 if (ind < 0)
10579 ThrowArrayBoundsException();
10580 UINT32 uind = static_cast<UINT32>(ind);
10581 if (uind >= str->GetStringLength())
10582 ThrowArrayBoundsException();
10583
10584 // Otherwise...
10585 GCX_FORBID(); // str is vulnerable.
10586 UINT16* dataPtr = reinterpret_cast<UINT16*>(reinterpret_cast<INT8*>(str) + StringObject::GetBufferOffset());
10587 UINT32 filledChar = dataPtr[ind];
10588 OpStackSet<UINT32>(strInd, filledChar);
10589 OpStackTypeSet(strInd, InterpreterType(CORINFO_TYPE_INT));
10590 m_curStackHt = indexInd;
10591}
10592
10593void Interpreter::DoGetTypeFromHandle()
10594{
10595 CONTRACTL {
10596 SO_TOLERANT;
10597 THROWS;
10598 GC_TRIGGERS;
10599 MODE_COOPERATIVE;
10600 } CONTRACTL_END;
10601
10602 assert(m_curStackHt > 0);
10603 unsigned ind = m_curStackHt - 1;
10604
10605#ifdef _DEBUG
10606 CorInfoType handleCIT = OpStackTypeGet(ind).ToCorInfoType();
10607 if (handleCIT != CORINFO_TYPE_VALUECLASS && handleCIT != CORINFO_TYPE_CLASS)
10608 {
10609 VerificationError("HandleGetTypeFromHandle called on non-RuntimeTypeHandle/non-RuntimeType.");
10610 }
10611 Object* obj = OpStackGet<Object*>(ind);
10612 if (obj->GetMethodTable() != g_pRuntimeTypeClass)
10613 {
10614 VerificationError("HandleGetTypeFromHandle called on non-RuntimeTypeHandle/non-RuntimeType.");
10615 }
10616#endif // _DEBUG
10617
10618 OpStackTypeSet(ind, InterpreterType(CORINFO_TYPE_CLASS));
10619}
10620
10621void Interpreter::DoByReferenceCtor()
10622{
10623 CONTRACTL {
10624 SO_TOLERANT;
10625 THROWS;
10626 GC_TRIGGERS;
10627 MODE_COOPERATIVE;
10628 } CONTRACTL_END;
10629
10630 // Note 'this' is not passed on the operand stack...
10631 assert(m_curStackHt > 0);
10632 assert(m_callThisArg != NULL);
10633 unsigned valInd = m_curStackHt - 1;
10634 CorInfoType valCit = OpStackTypeGet(valInd).ToCorInfoType();
10635
10636#ifdef _DEBUG
10637 if (valCit != CORINFO_TYPE_BYREF)
10638 {
10639 VerificationError("ByReference<T>.ctor called with non-byref value.");
10640 }
10641#endif // _DEBUG
10642
10643#if INTERP_TRACING
10644 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
10645 {
10646 fprintf(GetLogFile(), " ByReference<T>.ctor -- intrinsic\n");
10647 }
10648#endif // INTERP_TRACING
10649
10650 GCX_FORBID();
10651 void** thisPtr = reinterpret_cast<void**>(m_callThisArg);
10652 void* val = OpStackGet<void*>(valInd);
10653 *thisPtr = val;
10654 m_curStackHt--;
10655}
10656
10657void Interpreter::DoByReferenceValue()
10658{
10659 CONTRACTL {
10660 SO_TOLERANT;
10661 THROWS;
10662 GC_TRIGGERS;
10663 MODE_COOPERATIVE;
10664 } CONTRACTL_END;
10665
10666 assert(m_curStackHt > 0);
10667 unsigned slot = m_curStackHt - 1;
10668 CorInfoType thisCit = OpStackTypeGet(slot).ToCorInfoType();
10669
10670#ifdef _DEBUG
10671 if (thisCit != CORINFO_TYPE_BYREF)
10672 {
10673 VerificationError("ByReference<T>.get_Value called with non-byref this");
10674 }
10675#endif // _DEBUG
10676
10677#if INTERP_TRACING
10678 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
10679 {
10680 fprintf(GetLogFile(), " ByReference<T>.getValue -- intrinsic\n");
10681 }
10682#endif // INTERP_TRACING
10683
10684 GCX_FORBID();
10685 void** thisPtr = OpStackGet<void**>(slot);
10686 void* value = *thisPtr;
10687 OpStackSet<void*>(slot, value);
10688 OpStackTypeSet(slot, InterpreterType(CORINFO_TYPE_BYREF));
10689}
10690
10691void Interpreter::DoSIMDHwAccelerated()
10692{
10693 CONTRACTL {
10694 SO_TOLERANT;
10695 THROWS;
10696 GC_TRIGGERS;
10697 MODE_COOPERATIVE;
10698 } CONTRACTL_END;
10699
10700#if INTERP_TRACING
10701 if (s_TraceInterpreterILFlag.val(CLRConfig::INTERNAL_TraceInterpreterIL))
10702 {
10703 fprintf(GetLogFile(), " System.Numerics.Vector.IsHardwareAccelerated -- intrinsic\n");
10704 }
10705#endif // INTERP_TRACING
10706
10707 LdIcon(1);
10708}
10709
10710void Interpreter::RecordConstrainedCall()
10711{
10712 CONTRACTL {
10713 SO_TOLERANT;
10714 THROWS;
10715 GC_TRIGGERS;
10716 MODE_COOPERATIVE;
10717 } CONTRACTL_END;
10718
10719#if INTERP_TRACING
10720 InterlockedIncrement(&s_tokenResolutionOpportunities[RTK_Constrained]);
10721#endif // INTERP_TRACING
10722
10723 {
10724 GCX_PREEMP();
10725 ResolveToken(&m_constrainedResolvedToken, getU4LittleEndian(m_ILCodePtr + 2), CORINFO_TOKENKIND_Constrained InterpTracingArg(RTK_Constrained));
10726 }
10727
10728 m_constrainedFlag = true;
10729
10730 m_ILCodePtr += 6;
10731}
10732
10733void Interpreter::LargeStructOperandStackEnsureCanPush(size_t sz)
10734{
10735 size_t remaining = m_largeStructOperandStackAllocSize - m_largeStructOperandStackHt;
10736 if (remaining < sz)
10737 {
10738 size_t newAllocSize = max(m_largeStructOperandStackAllocSize + sz * 4, m_largeStructOperandStackAllocSize * 2);
10739 BYTE* newStack = new BYTE[newAllocSize];
10740 m_largeStructOperandStackAllocSize = newAllocSize;
10741 if (m_largeStructOperandStack != NULL)
10742 {
10743 memcpy(newStack, m_largeStructOperandStack, m_largeStructOperandStackHt);
10744 delete[] m_largeStructOperandStack;
10745 }
10746 m_largeStructOperandStack = newStack;
10747 }
10748}
10749
10750void* Interpreter::LargeStructOperandStackPush(size_t sz)
10751{
10752 LargeStructOperandStackEnsureCanPush(sz);
10753 assert(m_largeStructOperandStackAllocSize >= m_largeStructOperandStackHt + sz);
10754 void* res = &m_largeStructOperandStack[m_largeStructOperandStackHt];
10755 m_largeStructOperandStackHt += sz;
10756 return res;
10757}
10758
10759void Interpreter::LargeStructOperandStackPop(size_t sz, void* fromAddr)
10760{
10761 if (!IsInLargeStructLocalArea(fromAddr))
10762 {
10763 assert(m_largeStructOperandStackHt >= sz);
10764 m_largeStructOperandStackHt -= sz;
10765 }
10766}
10767
10768#ifdef _DEBUG
10769bool Interpreter::LargeStructStackHeightIsValid()
10770{
10771 size_t sz2 = 0;
10772 for (unsigned k = 0; k < m_curStackHt; k++)
10773 {
10774 if (OpStackTypeGet(k).IsLargeStruct(&m_interpCeeInfo) && !IsInLargeStructLocalArea(OpStackGet<void*>(k)))
10775 {
10776 sz2 += OpStackTypeGet(k).Size(&m_interpCeeInfo);
10777 }
10778 }
10779 assert(sz2 == m_largeStructOperandStackHt);
10780 return sz2 == m_largeStructOperandStackHt;
10781}
10782#endif // _DEBUG
10783
10784void Interpreter::VerificationError(const char* msg)
10785{
10786 // TODO: Should raise an exception eventually; for now:
10787 const char* const msgPrefix = "Verification Error: ";
10788 size_t len = strlen(msgPrefix) + strlen(msg) + 1;
10789 char* msgFinal = (char*)_alloca(len);
10790 strcpy_s(msgFinal, len, msgPrefix);
10791 strcat_s(msgFinal, len, msg);
10792 _ASSERTE_MSG(false, msgFinal);
10793}
10794
10795void Interpreter::ThrowDivideByZero()
10796{
10797 CONTRACTL {
10798 SO_TOLERANT;
10799 THROWS;
10800 GC_TRIGGERS;
10801 MODE_COOPERATIVE;
10802 } CONTRACTL_END;
10803
10804 COMPlusThrow(kDivideByZeroException);
10805}
10806
10807void Interpreter::ThrowSysArithException()
10808{
10809 CONTRACTL {
10810 SO_TOLERANT;
10811 THROWS;
10812 GC_TRIGGERS;
10813 MODE_COOPERATIVE;
10814 } CONTRACTL_END;
10815
10816 // According to the ECMA spec, this should be an ArithmeticException; however,
10817 // the JITs throw an OverflowException and consistency is top priority...
10818 COMPlusThrow(kOverflowException);
10819}
10820
10821void Interpreter::ThrowNullPointerException()
10822{
10823 CONTRACTL {
10824 SO_TOLERANT;
10825 THROWS;
10826 GC_TRIGGERS;
10827 MODE_COOPERATIVE;
10828 } CONTRACTL_END;
10829
10830 COMPlusThrow(kNullReferenceException);
10831}
10832
10833void Interpreter::ThrowOverflowException()
10834{
10835 CONTRACTL {
10836 SO_TOLERANT;
10837 THROWS;
10838 GC_TRIGGERS;
10839 MODE_COOPERATIVE;
10840 } CONTRACTL_END;
10841
10842 COMPlusThrow(kOverflowException);
10843}
10844
10845void Interpreter::ThrowArrayBoundsException()
10846{
10847 CONTRACTL {
10848 SO_TOLERANT;
10849 THROWS;
10850 GC_TRIGGERS;
10851 MODE_COOPERATIVE;
10852 } CONTRACTL_END;
10853
10854 COMPlusThrow(kIndexOutOfRangeException);
10855}
10856
10857void Interpreter::ThrowInvalidCastException()
10858{
10859 CONTRACTL {
10860 SO_TOLERANT;
10861 THROWS;
10862 GC_TRIGGERS;
10863 MODE_COOPERATIVE;
10864 } CONTRACTL_END;
10865
10866 COMPlusThrow(kInvalidCastException);
10867}
10868
10869void Interpreter::ThrowStackOverflow()
10870{
10871 CONTRACTL {
10872 SO_TOLERANT;
10873 THROWS;
10874 GC_TRIGGERS;
10875 MODE_COOPERATIVE;
10876 } CONTRACTL_END;
10877
10878 COMPlusThrow(kStackOverflowException);
10879}
10880
10881float Interpreter::RemFunc(float v1, float v2)
10882{
10883 return fmodf(v1, v2);
10884}
10885
10886double Interpreter::RemFunc(double v1, double v2)
10887{
10888 return fmod(v1, v2);
10889}
10890
10891// Static members and methods.
10892Interpreter::AddrToMDMap* Interpreter::s_addrToMDMap = NULL;
10893
10894unsigned Interpreter::s_interpreterStubNum = 0;
10895
10896// TODO: contracts and synchronization for the AddrToMDMap methods.
10897// Requires caller to hold "s_interpStubToMDMapLock".
10898Interpreter::AddrToMDMap* Interpreter::GetAddrToMdMap()
10899{
10900#if 0
10901 CONTRACTL {
10902 SO_TOLERANT;
10903 THROWS;
10904 GC_NOTRIGGER;
10905 } CONTRACTL_END;
10906#endif
10907
10908 if (s_addrToMDMap == NULL)
10909 {
10910 s_addrToMDMap = new AddrToMDMap();
10911 }
10912 return s_addrToMDMap;
10913}
10914
10915void Interpreter::RecordInterpreterStubForMethodDesc(CORINFO_METHOD_HANDLE md, void* addr)
10916{
10917#if 0
10918 CONTRACTL {
10919 SO_TOLERANT;
10920 NOTHROW;
10921 GC_NOTRIGGER;
10922 } CONTRACTL_END;
10923#endif
10924
10925 CrstHolder ch(&s_interpStubToMDMapLock);
10926
10927 AddrToMDMap* map = Interpreter::GetAddrToMdMap();
10928#ifdef _DEBUG
10929 CORINFO_METHOD_HANDLE dummy;
10930 assert(!map->Lookup(addr, &dummy));
10931#endif // DEBUG
10932 map->AddOrReplace(KeyValuePair<void*,CORINFO_METHOD_HANDLE>(addr, md));
10933}
10934
10935MethodDesc* Interpreter::InterpretationStubToMethodInfo(PCODE addr)
10936{
10937 CONTRACTL {
10938 SO_TOLERANT;
10939 NOTHROW;
10940 GC_NOTRIGGER;
10941 } CONTRACTL_END;
10942
10943
10944 // This query function will never allocate the table...
10945 if (s_addrToMDMap == NULL)
10946 return NULL;
10947
10948 // Otherwise...if we observe s_addrToMdMap non-null, the lock below must be initialized.
10949 // CrstHolder ch(&s_interpStubToMDMapLock);
10950
10951 AddrToMDMap* map = Interpreter::GetAddrToMdMap();
10952 CORINFO_METHOD_HANDLE result = NULL;
10953 (void)map->Lookup((void*)addr, &result);
10954 return (MethodDesc*)result;
10955}
10956
10957Interpreter::MethodHandleToInterpMethInfoPtrMap* Interpreter::s_methodHandleToInterpMethInfoPtrMap = NULL;
10958
10959// Requires caller to hold "s_interpStubToMDMapLock".
10960Interpreter::MethodHandleToInterpMethInfoPtrMap* Interpreter::GetMethodHandleToInterpMethInfoPtrMap()
10961{
10962#if 0
10963 CONTRACTL {
10964 SO_TOLERANT;
10965 THROWS;
10966 GC_NOTRIGGER;
10967 } CONTRACTL_END;
10968#endif
10969
10970 if (s_methodHandleToInterpMethInfoPtrMap == NULL)
10971 {
10972 s_methodHandleToInterpMethInfoPtrMap = new MethodHandleToInterpMethInfoPtrMap();
10973 }
10974 return s_methodHandleToInterpMethInfoPtrMap;
10975}
10976
10977InterpreterMethodInfo* Interpreter::RecordInterpreterMethodInfoForMethodHandle(CORINFO_METHOD_HANDLE md, InterpreterMethodInfo* methInfo)
10978{
10979#if 0
10980 CONTRACTL {
10981 SO_TOLERANT;
10982 NOTHROW;
10983 GC_NOTRIGGER;
10984 } CONTRACTL_END;
10985#endif
10986
10987 CrstHolder ch(&s_interpStubToMDMapLock);
10988
10989 MethodHandleToInterpMethInfoPtrMap* map = Interpreter::GetMethodHandleToInterpMethInfoPtrMap();
10990
10991 MethInfo mi;
10992 if (map->Lookup(md, &mi))
10993 {
10994 // If there's already an entry, make sure it was created by another thread -- the same thread shouldn't create two
10995 // of these.
10996 _ASSERTE_MSG(mi.m_thread != GetThread(), "Two InterpMethInfo's for same meth by same thread.");
10997 // If we were creating an interpreter stub at the same time as another thread, and we lost the race to
10998 // insert it, use the already-existing one, and delete this one.
10999 delete methInfo;
11000 return mi.m_info;
11001 }
11002
11003 mi.m_info = methInfo;
11004#ifdef _DEBUG
11005 mi.m_thread = GetThread();
11006#endif
11007
11008 _ASSERTE_MSG(map->LookupPtr(md) == NULL, "Multiple InterpMethInfos for method desc.");
11009 map->Add(md, mi);
11010 return methInfo;
11011}
11012
11013InterpreterMethodInfo* Interpreter::MethodHandleToInterpreterMethInfoPtr(CORINFO_METHOD_HANDLE md)
11014{
11015 CONTRACTL {
11016 SO_TOLERANT;
11017 NOTHROW;
11018 GC_TRIGGERS;
11019 } CONTRACTL_END;
11020
11021 // This query function will never allocate the table...
11022 if (s_methodHandleToInterpMethInfoPtrMap == NULL)
11023 return NULL;
11024
11025 // Otherwise...if we observe s_addrToMdMap non-null, the lock below must be initialized.
11026 CrstHolder ch(&s_interpStubToMDMapLock);
11027
11028 MethodHandleToInterpMethInfoPtrMap* map = Interpreter::GetMethodHandleToInterpMethInfoPtrMap();
11029
11030 MethInfo mi;
11031 mi.m_info = NULL;
11032 (void)map->Lookup(md, &mi);
11033 return mi.m_info;
11034}
11035
11036
11037#ifndef DACCESS_COMPILE
11038
11039// Requires that the current thread holds "s_methodCacheLock."
11040ILOffsetToItemCache* InterpreterMethodInfo::GetCacheForCall(Object* thisArg, void* genericsCtxtArg, bool alloc)
11041{
11042 // First, does the current method have dynamic generic information, and, if so,
11043 // what kind?
11044 CORINFO_CONTEXT_HANDLE context = GetPreciseGenericsContext(thisArg, genericsCtxtArg);
11045 if (context == MAKE_METHODCONTEXT(m_method))
11046 {
11047 // No dynamic generics context information. The caching field in "m_methInfo" is the
11048 // ILoffset->Item cache directly.
11049 // First, ensure that it's allocated.
11050 if (m_methodCache == NULL && alloc)
11051 {
11052 // Lazy init via compare-exchange.
11053 ILOffsetToItemCache* cache = new ILOffsetToItemCache();
11054 void* prev = InterlockedCompareExchangeT<void*>(&m_methodCache, cache, NULL);
11055 if (prev != NULL) delete cache;
11056 }
11057 return reinterpret_cast<ILOffsetToItemCache*>(m_methodCache);
11058 }
11059 else
11060 {
11061 // Otherwise, it does have generic info, so find the right cache.
11062 // First ensure that the top-level generics-context --> cache cache exists.
11063 GenericContextToInnerCache* outerCache = reinterpret_cast<GenericContextToInnerCache*>(m_methodCache);
11064 if (outerCache == NULL)
11065 {
11066 if (alloc)
11067 {
11068 // Lazy init via compare-exchange.
11069 outerCache = new GenericContextToInnerCache();
11070 void* prev = InterlockedCompareExchangeT<void*>(&m_methodCache, outerCache, NULL);
11071 if (prev != NULL)
11072 {
11073 delete outerCache;
11074 outerCache = reinterpret_cast<GenericContextToInnerCache*>(prev);
11075 }
11076 }
11077 else
11078 {
11079 return NULL;
11080 }
11081 }
11082 // Does the outerCache already have an entry for this instantiation?
11083 ILOffsetToItemCache* innerCache = NULL;
11084 if (!outerCache->GetItem(size_t(context), innerCache) && alloc)
11085 {
11086 innerCache = new ILOffsetToItemCache();
11087 outerCache->AddItem(size_t(context), innerCache);
11088 }
11089 return innerCache;
11090 }
11091}
11092
11093void Interpreter::CacheCallInfo(unsigned iloffset, CallSiteCacheData* callInfo)
11094{
11095 CrstHolder ch(&s_methodCacheLock);
11096
11097 ILOffsetToItemCache* cache = GetThisExecCache(true);
11098 // Insert, but if the item is already there, delete "mdcs" (which would have been owned
11099 // by the cache).
11100 // (Duplicate entries can happen because of recursive calls -- F makes a recursive call to F, and when it
11101 // returns wants to cache it, but the recursive call makes a furher recursive call, and caches that, so the
11102 // first call finds the iloffset already occupied.)
11103 if (!cache->AddItem(iloffset, CachedItem(callInfo)))
11104 {
11105 delete callInfo;
11106 }
11107}
11108
11109CallSiteCacheData* Interpreter::GetCachedCallInfo(unsigned iloffset)
11110{
11111 CrstHolder ch(&s_methodCacheLock);
11112
11113 ILOffsetToItemCache* cache = GetThisExecCache(false);
11114 if (cache == NULL) return NULL;
11115 // Otherwise...
11116 CachedItem item;
11117 if (cache->GetItem(iloffset, item))
11118 {
11119 _ASSERTE_MSG(item.m_tag == CIK_CallSite, "Wrong cached item tag.");
11120 return item.m_value.m_callSiteInfo;
11121 }
11122 else
11123 {
11124 return NULL;
11125 }
11126}
11127
11128void Interpreter::CacheInstanceField(unsigned iloffset, FieldDesc* fld)
11129{
11130 CrstHolder ch(&s_methodCacheLock);
11131
11132 ILOffsetToItemCache* cache = GetThisExecCache(true);
11133 cache->AddItem(iloffset, CachedItem(fld));
11134}
11135
11136FieldDesc* Interpreter::GetCachedInstanceField(unsigned iloffset)
11137{
11138 CrstHolder ch(&s_methodCacheLock);
11139
11140 ILOffsetToItemCache* cache = GetThisExecCache(false);
11141 if (cache == NULL) return NULL;
11142 // Otherwise...
11143 CachedItem item;
11144 if (cache->GetItem(iloffset, item))
11145 {
11146 _ASSERTE_MSG(item.m_tag == CIK_InstanceField, "Wrong cached item tag.");
11147 return item.m_value.m_instanceField;
11148 }
11149 else
11150 {
11151 return NULL;
11152 }
11153}
11154
11155void Interpreter::CacheStaticField(unsigned iloffset, StaticFieldCacheEntry* pEntry)
11156{
11157 CrstHolder ch(&s_methodCacheLock);
11158
11159 ILOffsetToItemCache* cache = GetThisExecCache(true);
11160 // If (say) a concurrent thread has beaten us to this, delete the entry (which otherwise would have
11161 // been owned by the cache).
11162 if (!cache->AddItem(iloffset, CachedItem(pEntry)))
11163 {
11164 delete pEntry;
11165 }
11166}
11167
11168StaticFieldCacheEntry* Interpreter::GetCachedStaticField(unsigned iloffset)
11169{
11170 CrstHolder ch(&s_methodCacheLock);
11171
11172 ILOffsetToItemCache* cache = GetThisExecCache(false);
11173 if (cache == NULL)
11174 return NULL;
11175
11176 // Otherwise...
11177 CachedItem item;
11178 if (cache->GetItem(iloffset, item))
11179 {
11180 _ASSERTE_MSG(item.m_tag == CIK_StaticField, "Wrong cached item tag.");
11181 return item.m_value.m_staticFieldAddr;
11182 }
11183 else
11184 {
11185 return NULL;
11186 }
11187}
11188
11189
11190void Interpreter::CacheClassHandle(unsigned iloffset, CORINFO_CLASS_HANDLE clsHnd)
11191{
11192 CrstHolder ch(&s_methodCacheLock);
11193
11194 ILOffsetToItemCache* cache = GetThisExecCache(true);
11195 cache->AddItem(iloffset, CachedItem(clsHnd));
11196}
11197
11198CORINFO_CLASS_HANDLE Interpreter::GetCachedClassHandle(unsigned iloffset)
11199{
11200 CrstHolder ch(&s_methodCacheLock);
11201
11202 ILOffsetToItemCache* cache = GetThisExecCache(false);
11203 if (cache == NULL)
11204 return NULL;
11205
11206 // Otherwise...
11207 CachedItem item;
11208 if (cache->GetItem(iloffset, item))
11209 {
11210 _ASSERTE_MSG(item.m_tag == CIK_ClassHandle, "Wrong cached item tag.");
11211 return item.m_value.m_clsHnd;
11212 }
11213 else
11214 {
11215 return NULL;
11216 }
11217}
11218#endif // DACCESS_COMPILE
11219
11220// Statics
11221
11222// Theses are not debug-only.
11223ConfigMethodSet Interpreter::s_InterpretMeths;
11224ConfigMethodSet Interpreter::s_InterpretMethsExclude;
11225ConfigDWORD Interpreter::s_InterpretMethHashMin;
11226ConfigDWORD Interpreter::s_InterpretMethHashMax;
11227ConfigDWORD Interpreter::s_InterpreterJITThreshold;
11228ConfigDWORD Interpreter::s_InterpreterDoLoopMethodsFlag;
11229ConfigDWORD Interpreter::s_InterpreterUseCachingFlag;
11230ConfigDWORD Interpreter::s_InterpreterLooseRulesFlag;
11231
11232bool Interpreter::s_InterpreterDoLoopMethods;
11233bool Interpreter::s_InterpreterUseCaching;
11234bool Interpreter::s_InterpreterLooseRules;
11235
11236CrstExplicitInit Interpreter::s_methodCacheLock;
11237CrstExplicitInit Interpreter::s_interpStubToMDMapLock;
11238
11239// The static variables below are debug-only.
11240#if INTERP_TRACING
11241LONG Interpreter::s_totalInvocations = 0;
11242LONG Interpreter::s_totalInterpCalls = 0;
11243LONG Interpreter::s_totalInterpCallsToGetters = 0;
11244LONG Interpreter::s_totalInterpCallsToDeadSimpleGetters = 0;
11245LONG Interpreter::s_totalInterpCallsToDeadSimpleGettersShortCircuited = 0;
11246LONG Interpreter::s_totalInterpCallsToSetters = 0;
11247LONG Interpreter::s_totalInterpCallsToIntrinsics = 0;
11248LONG Interpreter::s_totalInterpCallsToIntrinsicsUnhandled = 0;
11249
11250LONG Interpreter::s_tokenResolutionOpportunities[RTK_Count] = {0, };
11251LONG Interpreter::s_tokenResolutionCalls[RTK_Count] = {0, };
11252const char* Interpreter::s_tokenResolutionKindNames[RTK_Count] =
11253{
11254 "Undefined",
11255 "Constrained",
11256 "NewObj",
11257 "NewArr",
11258 "LdToken",
11259 "LdFtn",
11260 "LdVirtFtn",
11261 "SFldAddr",
11262 "LdElem",
11263 "Call",
11264 "LdObj",
11265 "StObj",
11266 "CpObj",
11267 "InitObj",
11268 "IsInst",
11269 "CastClass",
11270 "MkRefAny",
11271 "RefAnyVal",
11272 "Sizeof",
11273 "StElem",
11274 "Box",
11275 "Unbox",
11276 "UnboxAny",
11277 "LdFld",
11278 "LdFldA",
11279 "StFld",
11280 "FindClass",
11281 "Exception",
11282};
11283
11284FILE* Interpreter::s_InterpreterLogFile = NULL;
11285ConfigDWORD Interpreter::s_DumpInterpreterStubsFlag;
11286ConfigDWORD Interpreter::s_TraceInterpreterEntriesFlag;
11287ConfigDWORD Interpreter::s_TraceInterpreterILFlag;
11288ConfigDWORD Interpreter::s_TraceInterpreterOstackFlag;
11289ConfigDWORD Interpreter::s_TraceInterpreterVerboseFlag;
11290ConfigDWORD Interpreter::s_TraceInterpreterJITTransitionFlag;
11291ConfigDWORD Interpreter::s_InterpreterStubMin;
11292ConfigDWORD Interpreter::s_InterpreterStubMax;
11293#endif // INTERP_TRACING
11294
11295#if INTERP_ILINSTR_PROFILE
11296unsigned short Interpreter::s_ILInstrCategories[512];
11297
11298int Interpreter::s_ILInstrExecs[256] = {0, };
11299int Interpreter::s_ILInstrExecsByCategory[512] = {0, };
11300int Interpreter::s_ILInstr2ByteExecs[Interpreter::CountIlInstr2Byte] = {0, };
11301#if INTERP_ILCYCLE_PROFILE
11302unsigned __int64 Interpreter::s_ILInstrCycles[512] = { 0, };
11303unsigned __int64 Interpreter::s_ILInstrCyclesByCategory[512] = { 0, };
11304// XXX
11305unsigned __int64 Interpreter::s_callCycles = 0;
11306unsigned Interpreter::s_calls = 0;
11307
11308void Interpreter::UpdateCycleCount()
11309{
11310 unsigned __int64 endCycles;
11311 bool b = CycleTimer::GetThreadCyclesS(&endCycles); assert(b);
11312 if (m_instr != CEE_COUNT)
11313 {
11314 unsigned __int64 delta = (endCycles - m_startCycles);
11315 if (m_exemptCycles > 0)
11316 {
11317 delta = delta - m_exemptCycles;
11318 m_exemptCycles = 0;
11319 }
11320 CycleTimer::InterlockedAddU64(&s_ILInstrCycles[m_instr], delta);
11321 }
11322 // In any case, set the instruction to the current one, and record it's start time.
11323 m_instr = (*m_ILCodePtr);
11324 if (m_instr == CEE_PREFIX1) {
11325 m_instr = *(m_ILCodePtr + 1) + 0x100;
11326 }
11327 b = CycleTimer::GetThreadCyclesS(&m_startCycles); assert(b);
11328}
11329
11330#endif // INTERP_ILCYCLE_PROFILE
11331#endif // INTERP_ILINSTR_PROFILE
11332
11333#ifdef _DEBUG
11334InterpreterMethodInfo** Interpreter::s_interpMethInfos = NULL;
11335unsigned Interpreter::s_interpMethInfosAllocSize = 0;
11336unsigned Interpreter::s_interpMethInfosCount = 0;
11337
11338bool Interpreter::TOSIsPtr()
11339{
11340 if (m_curStackHt == 0)
11341 return false;
11342
11343 return CorInfoTypeIsPointer(OpStackTypeGet(m_curStackHt - 1).ToCorInfoType());
11344}
11345#endif // DEBUG
11346
11347ConfigDWORD Interpreter::s_PrintPostMortemFlag;
11348
11349// InterpreterCache.
11350template<typename Key, typename Val>
11351InterpreterCache<Key,Val>::InterpreterCache() : m_pairs(NULL), m_allocSize(0), m_count(0)
11352{
11353#ifdef _DEBUG
11354 AddAllocBytes(sizeof(*this));
11355#endif
11356}
11357
11358#ifdef _DEBUG
11359// static
11360static unsigned InterpreterCacheAllocBytes = 0;
11361const unsigned KBYTE = 1024;
11362const unsigned MBYTE = KBYTE*KBYTE;
11363const unsigned InterpreterCacheAllocBytesIncrement = 16*KBYTE;
11364static unsigned InterpreterCacheAllocBytesNextTarget = InterpreterCacheAllocBytesIncrement;
11365
11366template<typename Key, typename Val>
11367void InterpreterCache<Key,Val>::AddAllocBytes(unsigned bytes)
11368{
11369 // Reinstate this code if you want to track bytes attributable to caching.
11370#if 0
11371 InterpreterCacheAllocBytes += bytes;
11372 if (InterpreterCacheAllocBytes > InterpreterCacheAllocBytesNextTarget)
11373 {
11374 printf("Total cache alloc = %d bytes.\n", InterpreterCacheAllocBytes);
11375 fflush(stdout);
11376 InterpreterCacheAllocBytesNextTarget += InterpreterCacheAllocBytesIncrement;
11377 }
11378#endif
11379}
11380#endif // _DEBUG
11381
11382template<typename Key, typename Val>
11383void InterpreterCache<Key,Val>::EnsureCanInsert()
11384{
11385 if (m_count < m_allocSize)
11386 return;
11387
11388 // Otherwise, must make room.
11389 if (m_allocSize == 0)
11390 {
11391 assert(m_count == 0);
11392 m_pairs = new KeyValPair[InitSize];
11393 m_allocSize = InitSize;
11394#ifdef _DEBUG
11395 AddAllocBytes(m_allocSize * sizeof(KeyValPair));
11396#endif
11397 }
11398 else
11399 {
11400 unsigned short newSize = min(m_allocSize * 2, USHRT_MAX);
11401
11402 KeyValPair* newPairs = new KeyValPair[newSize];
11403 memcpy(newPairs, m_pairs, m_count * sizeof(KeyValPair));
11404 delete[] m_pairs;
11405 m_pairs = newPairs;
11406#ifdef _DEBUG
11407 AddAllocBytes((newSize - m_allocSize) * sizeof(KeyValPair));
11408#endif
11409 m_allocSize = newSize;
11410 }
11411}
11412
11413template<typename Key, typename Val>
11414bool InterpreterCache<Key,Val>::AddItem(Key key, Val val)
11415{
11416 EnsureCanInsert();
11417 // Find the index to insert before.
11418 unsigned firstGreaterOrEqual = 0;
11419 for (; firstGreaterOrEqual < m_count; firstGreaterOrEqual++)
11420 {
11421 if (m_pairs[firstGreaterOrEqual].m_key >= key)
11422 break;
11423 }
11424 if (firstGreaterOrEqual < m_count && m_pairs[firstGreaterOrEqual].m_key == key)
11425 {
11426 assert(m_pairs[firstGreaterOrEqual].m_val == val);
11427 return false;
11428 }
11429 // Move everything starting at firstGreater up one index (if necessary)
11430 if (m_count > 0)
11431 {
11432 for (unsigned k = m_count-1; k >= firstGreaterOrEqual; k--)
11433 {
11434 m_pairs[k + 1] = m_pairs[k];
11435 if (k == 0)
11436 break;
11437 }
11438 }
11439 // Now we can insert the new element.
11440 m_pairs[firstGreaterOrEqual].m_key = key;
11441 m_pairs[firstGreaterOrEqual].m_val = val;
11442 m_count++;
11443 return true;
11444}
11445
11446template<typename Key, typename Val>
11447bool InterpreterCache<Key,Val>::GetItem(Key key, Val& v)
11448{
11449 unsigned lo = 0;
11450 unsigned hi = m_count;
11451 // Invariant: we've determined that the pair for "iloffset", if present,
11452 // is in the index interval [lo, hi).
11453 while (lo < hi)
11454 {
11455 unsigned mid = (hi + lo)/2;
11456 Key midKey = m_pairs[mid].m_key;
11457 if (key == midKey)
11458 {
11459 v = m_pairs[mid].m_val;
11460 return true;
11461 }
11462 else if (key < midKey)
11463 {
11464 hi = mid;
11465 }
11466 else
11467 {
11468 assert(key > midKey);
11469 lo = mid + 1;
11470 }
11471 }
11472 // If we reach here without returning, it's not here.
11473 return false;
11474}
11475
11476// TODO: add a header comment here describing this function.
11477void Interpreter::OpStackNormalize()
11478{
11479 size_t largeStructStackOffset = 0;
11480 // Yes, I've written a quadratic algorithm here. I don't think it will matter in practice.
11481 for (unsigned i = 0; i < m_curStackHt; i++)
11482 {
11483 InterpreterType tp = OpStackTypeGet(i);
11484 if (tp.IsLargeStruct(&m_interpCeeInfo))
11485 {
11486 size_t sz = tp.Size(&m_interpCeeInfo);
11487
11488 void* addr = OpStackGet<void*>(i);
11489 if (IsInLargeStructLocalArea(addr))
11490 {
11491 // We're going to allocate space at the top for the new value, then copy everything above the current slot
11492 // up into that new space, then copy the value into the vacated space.
11493 // How much will we have to copy?
11494 size_t toCopy = m_largeStructOperandStackHt - largeStructStackOffset;
11495
11496 // Allocate space for the new value.
11497 void* dummy = LargeStructOperandStackPush(sz);
11498
11499 // Remember where we're going to write to.
11500 BYTE* fromAddr = m_largeStructOperandStack + largeStructStackOffset;
11501 BYTE* toAddr = fromAddr + sz;
11502 memcpy(toAddr, fromAddr, toCopy);
11503
11504 // Now copy the local variable value.
11505 memcpy(fromAddr, addr, sz);
11506 OpStackSet<void*>(i, fromAddr);
11507 }
11508 largeStructStackOffset += sz;
11509 }
11510 }
11511 // When we've normalized the stack, it contains no pointers to locals.
11512 m_orOfPushedInterpreterTypes = 0;
11513}
11514
11515#if INTERP_TRACING
11516
11517// Code copied from eeinterface.cpp in "compiler". Should be common...
11518
11519static const char* CorInfoTypeNames[] = {
11520 "undef",
11521 "void",
11522 "bool",
11523 "char",
11524 "byte",
11525 "ubyte",
11526 "short",
11527 "ushort",
11528 "int",
11529 "uint",
11530 "long",
11531 "ulong",
11532 "nativeint",
11533 "nativeuint",
11534 "float",
11535 "double",
11536 "string",
11537 "ptr",
11538 "byref",
11539 "valueclass",
11540 "class",
11541 "refany",
11542 "var"
11543};
11544
11545const char* eeGetMethodFullName(CEEInfo* info, CORINFO_METHOD_HANDLE hnd, const char** clsName)
11546{
11547 CONTRACTL {
11548 SO_TOLERANT;
11549 THROWS;
11550 GC_TRIGGERS;
11551 MODE_ANY;
11552 } CONTRACTL_END;
11553
11554 GCX_PREEMP();
11555
11556 const char* returnType = NULL;
11557
11558 const char* className;
11559 const char* methodName = info->getMethodName(hnd, &className);
11560 if (clsName != NULL)
11561 {
11562 *clsName = className;
11563 }
11564
11565 size_t length = 0;
11566 unsigned i;
11567
11568 /* Generating the full signature is a two-pass process. First we have to walk
11569 the components in order to assess the total size, then we allocate the buffer
11570 and copy the elements into it.
11571 */
11572
11573 /* Right now there is a race-condition in the EE, className can be NULL */
11574
11575 /* initialize length with length of className and '.' */
11576
11577 if (className)
11578 {
11579 length = strlen(className) + 1;
11580 }
11581 else
11582 {
11583 assert(strlen("<NULL>.") == 7);
11584 length = 7;
11585 }
11586
11587 /* add length of methodName and opening bracket */
11588 length += strlen(methodName) + 1;
11589
11590 CORINFO_SIG_INFO sig;
11591 info->getMethodSig(hnd, &sig);
11592 CORINFO_ARG_LIST_HANDLE argLst = sig.args;
11593
11594 CORINFO_CLASS_HANDLE dummyCls;
11595 for (i = 0; i < sig.numArgs; i++)
11596 {
11597 CorInfoType type = strip(info->getArgType(&sig, argLst, &dummyCls));
11598
11599 length += strlen(CorInfoTypeNames[type]);
11600 argLst = info->getArgNext(argLst);
11601 }
11602
11603 /* add ',' if there is more than one argument */
11604
11605 if (sig.numArgs > 1)
11606 {
11607 length += (sig.numArgs - 1);
11608 }
11609
11610 if (sig.retType != CORINFO_TYPE_VOID)
11611 {
11612 returnType = CorInfoTypeNames[sig.retType];
11613 length += strlen(returnType) + 1; // don't forget the delimiter ':'
11614 }
11615
11616 /* add closing bracket and null terminator */
11617
11618 length += 2;
11619
11620 char* retName = new char[length];
11621
11622 /* Now generate the full signature string in the allocated buffer */
11623
11624 if (className)
11625 {
11626 strcpy_s(retName, length, className);
11627 strcat_s(retName, length, ":");
11628 }
11629 else
11630 {
11631 strcpy_s(retName, length, "<NULL>.");
11632 }
11633
11634 strcat_s(retName, length, methodName);
11635
11636 // append the signature
11637 strcat_s(retName, length, "(");
11638
11639 argLst = sig.args;
11640
11641 for (i = 0; i < sig.numArgs; i++)
11642 {
11643 CorInfoType type = strip(info->getArgType(&sig, argLst, &dummyCls));
11644 strcat_s(retName, length, CorInfoTypeNames[type]);
11645
11646 argLst = info->getArgNext(argLst);
11647 if (i + 1 < sig.numArgs)
11648 {
11649 strcat_s(retName, length, ",");
11650 }
11651 }
11652
11653 strcat_s(retName, length, ")");
11654
11655 if (returnType)
11656 {
11657 strcat_s(retName, length, ":");
11658 strcat_s(retName, length, returnType);
11659 }
11660
11661 assert(strlen(retName) == length - 1);
11662
11663 return(retName);
11664}
11665
11666const char* Interpreter::eeGetMethodFullName(CORINFO_METHOD_HANDLE hnd)
11667{
11668 return ::eeGetMethodFullName(&m_interpCeeInfo, hnd);
11669}
11670
11671const char* ILOpNames[256*2];
11672bool ILOpNamesInited = false;
11673
11674void InitILOpNames()
11675{
11676 if (!ILOpNamesInited)
11677 {
11678 // Initialize the array.
11679#define OPDEF(c,s,pop,push,args,type,l,s1,s2,ctrl) if (s1 == 0xfe || s1 == 0xff) { int ind ((unsigned(s1) << 8) + unsigned(s2)); ind -= 0xfe00; ILOpNames[ind] = s; }
11680#include "opcode.def"
11681#undef OPDEF
11682 ILOpNamesInited = true;
11683 }
11684};
11685const char* Interpreter::ILOp(BYTE* m_ILCodePtr)
11686{
11687 InitILOpNames();
11688 BYTE b = *m_ILCodePtr;
11689 if (b == 0xfe)
11690 {
11691 return ILOpNames[*(m_ILCodePtr + 1)];
11692 }
11693 else
11694 {
11695 return ILOpNames[(0x1 << 8) + b];
11696 }
11697}
11698const char* Interpreter::ILOp1Byte(unsigned short ilInstrVal)
11699{
11700 InitILOpNames();
11701 return ILOpNames[(0x1 << 8) + ilInstrVal];
11702}
11703const char* Interpreter::ILOp2Byte(unsigned short ilInstrVal)
11704{
11705 InitILOpNames();
11706 return ILOpNames[ilInstrVal];
11707}
11708
11709void Interpreter::PrintOStack()
11710{
11711 if (m_curStackHt == 0)
11712 {
11713 fprintf(GetLogFile(), " <empty>\n");
11714 }
11715 else
11716 {
11717 for (unsigned k = 0; k < m_curStackHt; k++)
11718 {
11719 CorInfoType cit = OpStackTypeGet(k).ToCorInfoType();
11720 assert(IsStackNormalType(cit));
11721 fprintf(GetLogFile(), " %4d: %10s: ", k, CorInfoTypeNames[cit]);
11722 PrintOStackValue(k);
11723 fprintf(GetLogFile(), "\n");
11724 }
11725 }
11726 fflush(GetLogFile());
11727}
11728
11729void Interpreter::PrintOStackValue(unsigned index)
11730{
11731 _ASSERTE_MSG(index < m_curStackHt, "precondition");
11732 InterpreterType it = OpStackTypeGet(index);
11733 if (it.IsLargeStruct(&m_interpCeeInfo))
11734 {
11735 PrintValue(it, OpStackGet<BYTE*>(index));
11736 }
11737 else
11738 {
11739 PrintValue(it, reinterpret_cast<BYTE*>(OpStackGetAddr(index, it.Size(&m_interpCeeInfo))));
11740 }
11741}
11742
11743void Interpreter::PrintLocals()
11744{
11745 if (m_methInfo->m_numLocals == 0)
11746 {
11747 fprintf(GetLogFile(), " <no locals>\n");
11748 }
11749 else
11750 {
11751 for (unsigned i = 0; i < m_methInfo->m_numLocals; i++)
11752 {
11753 InterpreterType it = m_methInfo->m_localDescs[i].m_type;
11754 CorInfoType cit = it.ToCorInfoType();
11755 void* localPtr = NULL;
11756 if (it.IsLargeStruct(&m_interpCeeInfo))
11757 {
11758 void* structPtr = ArgSlotEndianessFixup(reinterpret_cast<ARG_SLOT*>(FixedSizeLocalSlot(i)), sizeof(void**));
11759 localPtr = *reinterpret_cast<void**>(structPtr);
11760 }
11761 else
11762 {
11763 localPtr = ArgSlotEndianessFixup(reinterpret_cast<ARG_SLOT*>(FixedSizeLocalSlot(i)), it.Size(&m_interpCeeInfo));
11764 }
11765 fprintf(GetLogFile(), " loc%-4d: %10s: ", i, CorInfoTypeNames[cit]);
11766 PrintValue(it, reinterpret_cast<BYTE*>(localPtr));
11767 fprintf(GetLogFile(), "\n");
11768 }
11769 }
11770 fflush(GetLogFile());
11771}
11772
11773void Interpreter::PrintArgs()
11774{
11775 for (unsigned k = 0; k < m_methInfo->m_numArgs; k++)
11776 {
11777 CorInfoType cit = GetArgType(k).ToCorInfoType();
11778 fprintf(GetLogFile(), " %4d: %10s: ", k, CorInfoTypeNames[cit]);
11779 PrintArgValue(k);
11780 fprintf(GetLogFile(), "\n");
11781 }
11782 fprintf(GetLogFile(), "\n");
11783 fflush(GetLogFile());
11784}
11785
11786void Interpreter::PrintArgValue(unsigned argNum)
11787{
11788 _ASSERTE_MSG(argNum < m_methInfo->m_numArgs, "precondition");
11789 InterpreterType it = GetArgType(argNum);
11790 PrintValue(it, GetArgAddr(argNum));
11791}
11792
11793// Note that this is used to print non-stack-normal values, so
11794// it must handle all cases.
11795void Interpreter::PrintValue(InterpreterType it, BYTE* valAddr)
11796{
11797 switch (it.ToCorInfoType())
11798 {
11799 case CORINFO_TYPE_BOOL:
11800 fprintf(GetLogFile(), "%s", ((*reinterpret_cast<INT8*>(valAddr)) ? "true" : "false"));
11801 break;
11802 case CORINFO_TYPE_BYTE:
11803 fprintf(GetLogFile(), "%d", *reinterpret_cast<INT8*>(valAddr));
11804 break;
11805 case CORINFO_TYPE_UBYTE:
11806 fprintf(GetLogFile(), "%u", *reinterpret_cast<UINT8*>(valAddr));
11807 break;
11808
11809 case CORINFO_TYPE_SHORT:
11810 fprintf(GetLogFile(), "%d", *reinterpret_cast<INT16*>(valAddr));
11811 break;
11812 case CORINFO_TYPE_USHORT: case CORINFO_TYPE_CHAR:
11813 fprintf(GetLogFile(), "%u", *reinterpret_cast<UINT16*>(valAddr));
11814 break;
11815
11816 case CORINFO_TYPE_INT:
11817 fprintf(GetLogFile(), "%d", *reinterpret_cast<INT32*>(valAddr));
11818 break;
11819 case CORINFO_TYPE_UINT:
11820 fprintf(GetLogFile(), "%u", *reinterpret_cast<UINT32*>(valAddr));
11821 break;
11822
11823 case CORINFO_TYPE_NATIVEINT:
11824 {
11825 INT64 val = static_cast<INT64>(*reinterpret_cast<NativeInt*>(valAddr));
11826 fprintf(GetLogFile(), "%lld (= 0x%llx)", val, val);
11827 }
11828 break;
11829 case CORINFO_TYPE_NATIVEUINT:
11830 {
11831 UINT64 val = static_cast<UINT64>(*reinterpret_cast<NativeUInt*>(valAddr));
11832 fprintf(GetLogFile(), "%lld (= 0x%llx)", val, val);
11833 }
11834 break;
11835
11836 case CORINFO_TYPE_BYREF:
11837 fprintf(GetLogFile(), "0x%p", *reinterpret_cast<void**>(valAddr));
11838 break;
11839
11840 case CORINFO_TYPE_LONG:
11841 {
11842 INT64 val = *reinterpret_cast<INT64*>(valAddr);
11843 fprintf(GetLogFile(), "%lld (= 0x%llx)", val, val);
11844 }
11845 break;
11846 case CORINFO_TYPE_ULONG:
11847 fprintf(GetLogFile(), "%lld", *reinterpret_cast<UINT64*>(valAddr));
11848 break;
11849
11850 case CORINFO_TYPE_CLASS:
11851 {
11852 Object* obj = *reinterpret_cast<Object**>(valAddr);
11853 if (obj == NULL)
11854 {
11855 fprintf(GetLogFile(), "null");
11856 }
11857 else
11858 {
11859#ifdef _DEBUG
11860 fprintf(GetLogFile(), "0x%p (%s) [", obj, obj->GetMethodTable()->GetDebugClassName());
11861#else
11862 fprintf(GetLogFile(), "0x%p (MT=0x%p) [", obj, obj->GetMethodTable());
11863#endif
11864 unsigned sz = obj->GetMethodTable()->GetBaseSize();
11865 BYTE* objBytes = reinterpret_cast<BYTE*>(obj);
11866 for (unsigned i = 0; i < sz; i++)
11867 {
11868 if (i > 0)
11869 {
11870 fprintf(GetLogFile(), " ");
11871 }
11872 fprintf(GetLogFile(), "0x%x", objBytes[i]);
11873 }
11874 fprintf(GetLogFile(), "]");
11875 }
11876 }
11877 break;
11878 case CORINFO_TYPE_VALUECLASS:
11879 {
11880 GCX_PREEMP();
11881 fprintf(GetLogFile(), "<%s>: [", m_interpCeeInfo.getClassName(it.ToClassHandle()));
11882 unsigned sz = getClassSize(it.ToClassHandle());
11883 for (unsigned i = 0; i < sz; i++)
11884 {
11885 if (i > 0)
11886 {
11887 fprintf(GetLogFile(), " ");
11888 }
11889 fprintf(GetLogFile(), "0x%02x", valAddr[i]);
11890 }
11891 fprintf(GetLogFile(), "]");
11892 }
11893 break;
11894 case CORINFO_TYPE_REFANY:
11895 fprintf(GetLogFile(), "<refany>");
11896 break;
11897 case CORINFO_TYPE_FLOAT:
11898 fprintf(GetLogFile(), "%f", *reinterpret_cast<float*>(valAddr));
11899 break;
11900 case CORINFO_TYPE_DOUBLE:
11901 fprintf(GetLogFile(), "%g", *reinterpret_cast<double*>(valAddr));
11902 break;
11903 case CORINFO_TYPE_PTR:
11904 fprintf(GetLogFile(), "0x%p", *reinterpret_cast<void**>(valAddr));
11905 break;
11906 default:
11907 _ASSERTE_MSG(false, "Unknown type in PrintValue.");
11908 break;
11909 }
11910}
11911#endif // INTERP_TRACING
11912
11913#ifdef _DEBUG
11914void Interpreter::AddInterpMethInfo(InterpreterMethodInfo* methInfo)
11915{
11916 typedef InterpreterMethodInfo* InterpreterMethodInfoPtr;
11917 // TODO: this requires synchronization.
11918 const unsigned InitSize = 128;
11919 if (s_interpMethInfos == NULL)
11920 {
11921 s_interpMethInfos = new InterpreterMethodInfoPtr[InitSize];
11922 s_interpMethInfosAllocSize = InitSize;
11923 }
11924 if (s_interpMethInfosAllocSize == s_interpMethInfosCount)
11925 {
11926 unsigned newSize = s_interpMethInfosAllocSize * 2;
11927 InterpreterMethodInfoPtr* tmp = new InterpreterMethodInfoPtr[newSize];
11928 memcpy(tmp, s_interpMethInfos, s_interpMethInfosCount * sizeof(InterpreterMethodInfoPtr));
11929 delete[] s_interpMethInfos;
11930 s_interpMethInfos = tmp;
11931 s_interpMethInfosAllocSize = newSize;
11932 }
11933 s_interpMethInfos[s_interpMethInfosCount] = methInfo;
11934 s_interpMethInfosCount++;
11935}
11936
11937int _cdecl Interpreter::CompareMethInfosByInvocations(const void* mi0in, const void* mi1in)
11938{
11939 const InterpreterMethodInfo* mi0 = *((const InterpreterMethodInfo**)mi0in);
11940 const InterpreterMethodInfo* mi1 = *((const InterpreterMethodInfo**)mi1in);
11941 if (mi0->m_invocations < mi1->m_invocations)
11942 {
11943 return -1;
11944 }
11945 else if (mi0->m_invocations == mi1->m_invocations)
11946 {
11947 return 0;
11948 }
11949 else
11950 {
11951 assert(mi0->m_invocations > mi1->m_invocations);
11952 return 1;
11953 }
11954}
11955
11956#if INTERP_PROFILE
11957int _cdecl Interpreter::CompareMethInfosByILInstrs(const void* mi0in, const void* mi1in)
11958{
11959 const InterpreterMethodInfo* mi0 = *((const InterpreterMethodInfo**)mi0in);
11960 const InterpreterMethodInfo* mi1 = *((const InterpreterMethodInfo**)mi1in);
11961 if (mi0->m_totIlInstructionsExeced < mi1->m_totIlInstructionsExeced) return 1;
11962 else if (mi0->m_totIlInstructionsExeced == mi1->m_totIlInstructionsExeced) return 0;
11963 else
11964 {
11965 assert(mi0->m_totIlInstructionsExeced > mi1->m_totIlInstructionsExeced);
11966 return -1;
11967 }
11968}
11969#endif // INTERP_PROFILE
11970#endif // _DEBUG
11971
11972const int MIL = 1000000;
11973
11974// Leaving this disabled for now.
11975#if 0
11976unsigned __int64 ForceSigWalkCycles = 0;
11977#endif
11978
11979void Interpreter::PrintPostMortemData()
11980{
11981 if (s_PrintPostMortemFlag.val(CLRConfig::INTERNAL_InterpreterPrintPostMortem) == 0)
11982 return;
11983
11984 // Otherwise...
11985
11986#if INTERP_TRACING
11987 // Let's print two things: the number of methods that are 0-10, or more, and
11988 // For each 10% of methods, cumulative % of invocations they represent. By 1% for last 10%.
11989
11990 // First one doesn't require any sorting.
11991 const unsigned HistoMax = 11;
11992 unsigned histo[HistoMax];
11993 unsigned numExecs[HistoMax];
11994 for (unsigned k = 0; k < HistoMax; k++)
11995 {
11996 histo[k] = 0; numExecs[k] = 0;
11997 }
11998 for (unsigned k = 0; k < s_interpMethInfosCount; k++)
11999 {
12000 unsigned invokes = s_interpMethInfos[k]->m_invocations;
12001 if (invokes > HistoMax - 1)
12002 {
12003 invokes = HistoMax - 1;
12004 }
12005 histo[invokes]++;
12006 numExecs[invokes] += s_interpMethInfos[k]->m_invocations;
12007 }
12008
12009 fprintf(GetLogFile(), "Histogram of method executions:\n");
12010 fprintf(GetLogFile(), " # of execs | # meths (%%) | cum %% | %% cum execs\n");
12011 fprintf(GetLogFile(), " -------------------------------------------------------\n");
12012 float fTotMeths = float(s_interpMethInfosCount);
12013 float fTotExecs = float(s_totalInvocations);
12014 float numPct = 0.0f;
12015 float numExecPct = 0.0f;
12016 for (unsigned k = 0; k < HistoMax; k++)
12017 {
12018 fprintf(GetLogFile(), " %10d", k);
12019 if (k == HistoMax)
12020 {
12021 fprintf(GetLogFile(), "+ ");
12022 }
12023 else
12024 {
12025 fprintf(GetLogFile(), " ");
12026 }
12027 float pct = float(histo[k])*100.0f/fTotMeths;
12028 numPct += pct;
12029 float execPct = float(numExecs[k])*100.0f/fTotExecs;
12030 numExecPct += execPct;
12031 fprintf(GetLogFile(), "| %7d (%5.2f%%) | %6.2f%% | %6.2f%%\n", histo[k], pct, numPct, numExecPct);
12032 }
12033
12034 // This sorts them in ascending order of number of invocations.
12035 qsort(&s_interpMethInfos[0], s_interpMethInfosCount, sizeof(InterpreterMethodInfo*), &CompareMethInfosByInvocations);
12036
12037 fprintf(GetLogFile(), "\nFor methods sorted in ascending # of executions order, cumulative %% of executions:\n");
12038 if (s_totalInvocations > 0)
12039 {
12040 fprintf(GetLogFile(), " %% of methods | max execs | cum %% of execs\n");
12041 fprintf(GetLogFile(), " ------------------------------------------\n");
12042 unsigned methNum = 0;
12043 unsigned nNumExecs = 0;
12044 float totExecsF = float(s_totalInvocations);
12045 for (unsigned k = 10; k < 100; k += 10)
12046 {
12047 unsigned targ = unsigned((float(k)/100.0f)*float(s_interpMethInfosCount));
12048 unsigned targLess1 = (targ > 0 ? targ - 1 : 0);
12049 while (methNum < targ)
12050 {
12051 nNumExecs += s_interpMethInfos[methNum]->m_invocations;
12052 methNum++;
12053 }
12054 float pctExecs = float(nNumExecs) * 100.0f / totExecsF;
12055
12056 fprintf(GetLogFile(), " %8d%% | %9d | %8.2f%%\n", k, s_interpMethInfos[targLess1]->m_invocations, pctExecs);
12057
12058 if (k == 90)
12059 {
12060 k++;
12061 for (; k < 100; k++)
12062 {
12063 unsigned targ = unsigned((float(k)/100.0f)*float(s_interpMethInfosCount));
12064 while (methNum < targ)
12065 {
12066 nNumExecs += s_interpMethInfos[methNum]->m_invocations;
12067 methNum++;
12068 }
12069 pctExecs = float(nNumExecs) * 100.0f / totExecsF;
12070
12071 fprintf(GetLogFile(), " %8d%% | %9d | %8.2f%%\n", k, s_interpMethInfos[targLess1]->m_invocations, pctExecs);
12072 }
12073
12074 // Now do 100%.
12075 targ = s_interpMethInfosCount;
12076 while (methNum < targ)
12077 {
12078 nNumExecs += s_interpMethInfos[methNum]->m_invocations;
12079 methNum++;
12080 }
12081 pctExecs = float(nNumExecs) * 100.0f / totExecsF;
12082 fprintf(GetLogFile(), " %8d%% | %9d | %8.2f%%\n", k, s_interpMethInfos[targLess1]->m_invocations, pctExecs);
12083 }
12084 }
12085 }
12086
12087 fprintf(GetLogFile(), "\nTotal number of calls from interpreted code: %d.\n", s_totalInterpCalls);
12088 fprintf(GetLogFile(), " Also, %d are intrinsics; %d of these are not currently handled intrinsically.\n",
12089 s_totalInterpCallsToIntrinsics, s_totalInterpCallsToIntrinsicsUnhandled);
12090 fprintf(GetLogFile(), " Of these, %d to potential property getters (%d of these dead simple), %d to setters.\n",
12091 s_totalInterpCallsToGetters, s_totalInterpCallsToDeadSimpleGetters, s_totalInterpCallsToSetters);
12092 fprintf(GetLogFile(), " Of the dead simple getter calls, %d have been short-circuited.\n",
12093 s_totalInterpCallsToDeadSimpleGettersShortCircuited);
12094
12095 fprintf(GetLogFile(), "\nToken resolutions by category:\n");
12096 fprintf(GetLogFile(), "Category | opportunities | calls | %%\n");
12097 fprintf(GetLogFile(), "---------------------------------------------------\n");
12098 for (unsigned i = RTK_Undefined; i < RTK_Count; i++)
12099 {
12100 float pct = 0.0;
12101 if (s_tokenResolutionOpportunities[i] > 0)
12102 pct = 100.0f * float(s_tokenResolutionCalls[i]) / float(s_tokenResolutionOpportunities[i]);
12103 fprintf(GetLogFile(), "%12s | %15d | %9d | %6.2f%%\n",
12104 s_tokenResolutionKindNames[i], s_tokenResolutionOpportunities[i], s_tokenResolutionCalls[i], pct);
12105 }
12106
12107#if INTERP_PROFILE
12108 fprintf(GetLogFile(), "Information on num of execs:\n");
12109
12110 UINT64 totILInstrs = 0;
12111 for (unsigned i = 0; i < s_interpMethInfosCount; i++) totILInstrs += s_interpMethInfos[i]->m_totIlInstructionsExeced;
12112
12113 float totILInstrsF = float(totILInstrs);
12114
12115 fprintf(GetLogFile(), "\nTotal instructions = %lld.\n", totILInstrs);
12116 fprintf(GetLogFile(), "\nTop <=10 methods by # of IL instructions executed.\n");
12117 fprintf(GetLogFile(), "%10s | %9s | %10s | %10s | %8s | %s\n", "tot execs", "# invokes", "code size", "ratio", "% of tot", "Method");
12118 fprintf(GetLogFile(), "----------------------------------------------------------------------------\n");
12119
12120 qsort(&s_interpMethInfos[0], s_interpMethInfosCount, sizeof(InterpreterMethodInfo*), &CompareMethInfosByILInstrs);
12121
12122 for (unsigned i = 0; i < min(10, s_interpMethInfosCount); i++)
12123 {
12124 unsigned ilCodeSize = unsigned(s_interpMethInfos[i]->m_ILCodeEnd - s_interpMethInfos[i]->m_ILCode);
12125 fprintf(GetLogFile(), "%10lld | %9d | %10d | %10.2f | %8.2f%% | %s:%s\n",
12126 s_interpMethInfos[i]->m_totIlInstructionsExeced,
12127 s_interpMethInfos[i]->m_invocations,
12128 ilCodeSize,
12129 float(s_interpMethInfos[i]->m_totIlInstructionsExeced) / float(ilCodeSize),
12130 float(s_interpMethInfos[i]->m_totIlInstructionsExeced) * 100.0f / totILInstrsF,
12131 s_interpMethInfos[i]->m_clsName,
12132 s_interpMethInfos[i]->m_methName);
12133 }
12134#endif // INTERP_PROFILE
12135#endif // _DEBUG
12136
12137#if INTERP_ILINSTR_PROFILE
12138 fprintf(GetLogFile(), "\nIL instruction profiling:\n");
12139 // First, classify by categories.
12140 unsigned totInstrs = 0;
12141#if INTERP_ILCYCLE_PROFILE
12142 unsigned __int64 totCycles = 0;
12143 unsigned __int64 perMeasurementOverhead = CycleTimer::QueryOverhead();
12144#endif // INTERP_ILCYCLE_PROFILE
12145 for (unsigned i = 0; i < 256; i++)
12146 {
12147 s_ILInstrExecsByCategory[s_ILInstrCategories[i]] += s_ILInstrExecs[i];
12148 totInstrs += s_ILInstrExecs[i];
12149#if INTERP_ILCYCLE_PROFILE
12150 unsigned __int64 cycles = s_ILInstrCycles[i];
12151 if (cycles > s_ILInstrExecs[i] * perMeasurementOverhead) cycles -= s_ILInstrExecs[i] * perMeasurementOverhead;
12152 else cycles = 0;
12153 s_ILInstrCycles[i] = cycles;
12154 s_ILInstrCyclesByCategory[s_ILInstrCategories[i]] += cycles;
12155 totCycles += cycles;
12156#endif // INTERP_ILCYCLE_PROFILE
12157 }
12158 unsigned totInstrs2Byte = 0;
12159#if INTERP_ILCYCLE_PROFILE
12160 unsigned __int64 totCycles2Byte = 0;
12161#endif // INTERP_ILCYCLE_PROFILE
12162 for (unsigned i = 0; i < CountIlInstr2Byte; i++)
12163 {
12164 unsigned ind = 0x100 + i;
12165 s_ILInstrExecsByCategory[s_ILInstrCategories[ind]] += s_ILInstr2ByteExecs[i];
12166 totInstrs += s_ILInstr2ByteExecs[i];
12167 totInstrs2Byte += s_ILInstr2ByteExecs[i];
12168#if INTERP_ILCYCLE_PROFILE
12169 unsigned __int64 cycles = s_ILInstrCycles[ind];
12170 if (cycles > s_ILInstrExecs[ind] * perMeasurementOverhead) cycles -= s_ILInstrExecs[ind] * perMeasurementOverhead;
12171 else cycles = 0;
12172 s_ILInstrCycles[i] = cycles;
12173 s_ILInstrCyclesByCategory[s_ILInstrCategories[ind]] += cycles;
12174 totCycles += cycles;
12175 totCycles2Byte += cycles;
12176#endif // INTERP_ILCYCLE_PROFILE
12177 }
12178
12179 // Now sort the categories by # of occurrences.
12180
12181 InstrExecRecord ieps[256 + CountIlInstr2Byte];
12182 for (unsigned short i = 0; i < 256; i++)
12183 {
12184 ieps[i].m_instr = i; ieps[i].m_is2byte = false; ieps[i].m_execs = s_ILInstrExecs[i];
12185#if INTERP_ILCYCLE_PROFILE
12186 if (i == CEE_BREAK)
12187 {
12188 ieps[i].m_cycles = 0;
12189 continue; // Don't count these if they occur...
12190 }
12191 ieps[i].m_cycles = s_ILInstrCycles[i];
12192 assert((ieps[i].m_execs != 0) || (ieps[i].m_cycles == 0)); // Cycles can be zero for non-zero execs because of measurement correction.
12193#endif // INTERP_ILCYCLE_PROFILE
12194 }
12195 for (unsigned short i = 0; i < CountIlInstr2Byte; i++)
12196 {
12197 int ind = 256 + i;
12198 ieps[ind].m_instr = i; ieps[ind].m_is2byte = true; ieps[ind].m_execs = s_ILInstr2ByteExecs[i];
12199#if INTERP_ILCYCLE_PROFILE
12200 ieps[ind].m_cycles = s_ILInstrCycles[ind];
12201 assert((ieps[i].m_execs != 0) || (ieps[i].m_cycles == 0)); // Cycles can be zero for non-zero execs because of measurement correction.
12202#endif // INTERP_ILCYCLE_PROFILE
12203 }
12204
12205 qsort(&ieps[0], 256 + CountIlInstr2Byte, sizeof(InstrExecRecord), &InstrExecRecord::Compare);
12206
12207 fprintf(GetLogFile(), "\nInstructions (%d total, %d 1-byte):\n", totInstrs, totInstrs - totInstrs2Byte);
12208#if INTERP_ILCYCLE_PROFILE
12209 if (s_callCycles > s_calls * perMeasurementOverhead) s_callCycles -= s_calls * perMeasurementOverhead;
12210 else s_callCycles = 0;
12211 fprintf(GetLogFile(), " MCycles (%lld total, %lld 1-byte, %lld calls (%d calls, %10.2f cyc/call):\n",
12212 totCycles/MIL, (totCycles - totCycles2Byte)/MIL, s_callCycles/MIL, s_calls, float(s_callCycles)/float(s_calls));
12213#if 0
12214 extern unsigned __int64 MetaSigCtor1Cycles;
12215 fprintf(GetLogFile(), " MetaSig(MethodDesc, TypeHandle) ctor: %lld MCycles.\n",
12216 MetaSigCtor1Cycles/MIL);
12217 fprintf(GetLogFile(), " ForceSigWalk: %lld MCycles.\n",
12218 ForceSigWalkCycles/MIL);
12219#endif
12220#endif // INTERP_ILCYCLE_PROFILE
12221
12222 PrintILProfile(&ieps[0], totInstrs
12223#if INTERP_ILCYCLE_PROFILE
12224 , totCycles
12225#endif // INTERP_ILCYCLE_PROFILE
12226 );
12227
12228 fprintf(GetLogFile(), "\nInstructions grouped by category: (%d total, %d 1-byte):\n", totInstrs, totInstrs - totInstrs2Byte);
12229#if INTERP_ILCYCLE_PROFILE
12230 fprintf(GetLogFile(), " MCycles (%lld total, %lld 1-byte):\n",
12231 totCycles/MIL, (totCycles - totCycles2Byte)/MIL);
12232#endif // INTERP_ILCYCLE_PROFILE
12233 for (unsigned short i = 0; i < 256 + CountIlInstr2Byte; i++)
12234 {
12235 if (i < 256)
12236 {
12237 ieps[i].m_instr = i; ieps[i].m_is2byte = false;
12238 }
12239 else
12240 {
12241 ieps[i].m_instr = i - 256; ieps[i].m_is2byte = true;
12242 }
12243 ieps[i].m_execs = s_ILInstrExecsByCategory[i];
12244#if INTERP_ILCYCLE_PROFILE
12245 ieps[i].m_cycles = s_ILInstrCyclesByCategory[i];
12246#endif // INTERP_ILCYCLE_PROFILE
12247 }
12248 qsort(&ieps[0], 256 + CountIlInstr2Byte, sizeof(InstrExecRecord), &InstrExecRecord::Compare);
12249 PrintILProfile(&ieps[0], totInstrs
12250#if INTERP_ILCYCLE_PROFILE
12251 , totCycles
12252#endif // INTERP_ILCYCLE_PROFILE
12253 );
12254
12255#if 0
12256 // Early debugging code.
12257 fprintf(GetLogFile(), "\nInstructions grouped category mapping:\n", totInstrs, totInstrs - totInstrs2Byte);
12258 for (unsigned short i = 0; i < 256; i++)
12259 {
12260 unsigned short cat = s_ILInstrCategories[i];
12261 if (cat < 256) {
12262 fprintf(GetLogFile(), "Instr: %12s ==> %12s.\n", ILOp1Byte(i), ILOp1Byte(cat));
12263 } else {
12264 fprintf(GetLogFile(), "Instr: %12s ==> %12s.\n", ILOp1Byte(i), ILOp2Byte(cat - 256));
12265 }
12266 }
12267 for (unsigned short i = 0; i < CountIlInstr2Byte; i++)
12268 {
12269 unsigned ind = 256 + i;
12270 unsigned short cat = s_ILInstrCategories[ind];
12271 if (cat < 256) {
12272 fprintf(GetLogFile(), "Instr: %12s ==> %12s.\n", ILOp2Byte(i), ILOp1Byte(cat));
12273 } else {
12274 fprintf(GetLogFile(), "Instr: %12s ==> %12s.\n", ILOp2Byte(i), ILOp2Byte(cat - 256));
12275 }
12276 }
12277#endif
12278#endif // INTERP_ILINSTR_PROFILE
12279}
12280
12281#if INTERP_ILINSTR_PROFILE
12282
12283const int K = 1000;
12284
12285// static
12286void Interpreter::PrintILProfile(Interpreter::InstrExecRecord *recs, unsigned int totInstrs
12287#if INTERP_ILCYCLE_PROFILE
12288 , unsigned __int64 totCycles
12289#endif // INTERP_ILCYCLE_PROFILE
12290 )
12291{
12292 float fTotInstrs = float(totInstrs);
12293 fprintf(GetLogFile(), "Instruction | execs | %% | cum %%");
12294#if INTERP_ILCYCLE_PROFILE
12295 float fTotCycles = float(totCycles);
12296 fprintf(GetLogFile(), "| KCycles | %% | cum %% | cyc/inst\n");
12297 fprintf(GetLogFile(), "--------------------------------------------------"
12298 "-----------------------------------------\n");
12299#else
12300 fprintf(GetLogFile(), "\n-------------------------------------------\n");
12301#endif
12302 float numPct = 0.0f;
12303#if INTERP_ILCYCLE_PROFILE
12304 float numCyclePct = 0.0f;
12305#endif // INTERP_ILCYCLE_PROFILE
12306 for (unsigned i = 0; i < 256 + CountIlInstr2Byte; i++)
12307 {
12308 float pct = 0.0f;
12309 if (totInstrs > 0) pct = float(recs[i].m_execs) * 100.0f / fTotInstrs;
12310 numPct += pct;
12311 if (recs[i].m_execs > 0)
12312 {
12313 fprintf(GetLogFile(), "%12s | %9d | %6.2f%% | %6.2f%%",
12314 (recs[i].m_is2byte ? ILOp2Byte(recs[i].m_instr) : ILOp1Byte(recs[i].m_instr)), recs[i].m_execs,
12315 pct, numPct);
12316#if INTERP_ILCYCLE_PROFILE
12317 pct = 0.0f;
12318 if (totCycles > 0) pct = float(recs[i].m_cycles) * 100.0f / fTotCycles;
12319 numCyclePct += pct;
12320 float cyclesPerInst = float(recs[i].m_cycles) / float(recs[i].m_execs);
12321 fprintf(GetLogFile(), "| %12llu | %6.2f%% | %6.2f%% | %11.2f",
12322 recs[i].m_cycles/K, pct, numCyclePct, cyclesPerInst);
12323#endif // INTERP_ILCYCLE_PROFILE
12324 fprintf(GetLogFile(), "\n");
12325 }
12326 }
12327}
12328#endif // INTERP_ILINSTR_PROFILE
12329
12330#endif // FEATURE_INTERPRETER
12331