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.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XX Logic to encode the JIT method header and GC pointer tables XX
12 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
20 #pragma warning(disable : 4244) // loss of data int -> char ..
24 #include "gcinfotypes.h"
26 ReturnKind GCTypeToReturnKind(CorInfoGCType gcType)
37 _ASSERTE(!"TYP_GC_OTHER is unexpected");
42 ReturnKind GCInfo::getReturnKind()
44 switch (compiler->info.compRetType)
52 CORINFO_CLASS_HANDLE structType = compiler->info.compMethodInfo->args.retTypeClass;
53 var_types retType = compiler->getReturnTypeForStruct(structType);
64 if (compiler->IsHfa(structType))
67 _ASSERTE(false && "HFAs not expected for X86");
68 #endif // _TARGET_X86_
75 BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE};
76 compiler->info.compCompHnd->getClassGClayout(structType, gcPtrs);
78 ReturnKind first = GCTypeToReturnKind((CorInfoGCType)gcPtrs[0]);
79 ReturnKind second = GCTypeToReturnKind((CorInfoGCType)gcPtrs[1]);
81 return GetStructReturnKind(first, second);
88 #endif // _TARGET_X86_
98 #endif // _TARGET_X86_
105 #if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
107 // gcMarkFilterVarsPinned - Walk all lifetimes and make it so that anything
108 // live in a filter is marked as pinned (often by splitting the lifetime
109 // so that *only* the filter region is pinned). This should only be
110 // called once (after generating all lifetimes, but before slot ids are
113 // DevDiv 376329 - The VM has to double report filters and their parent frame
114 // because they occur during the 1st pass and the parent frame doesn't go dead
115 // until we start unwinding in the 2nd pass.
117 // Untracked locals will only be reported in non-filter funclets and the
119 // Registers can't be double reported by 2 frames since they're different.
120 // That just leaves stack variables which might be double reported.
122 // Technically double reporting is only a problem when the GC has to relocate a
123 // reference. So we avoid that problem by marking all live tracked stack
124 // variables as pinned inside the filter. Thus if they are double reported, it
125 // won't be a problem since they won't be double relocated.
127 void GCInfo::gcMarkFilterVarsPinned()
129 assert(compiler->ehAnyFunclets());
130 const EHblkDsc* endHBtab = &(compiler->compHndBBtab[compiler->compHndBBtabCount]);
132 for (EHblkDsc* HBtab = compiler->compHndBBtab; HBtab < endHBtab; HBtab++)
134 if (HBtab->HasFilter())
136 const UNATIVE_OFFSET filterBeg = compiler->ehCodeOffset(HBtab->ebdFilter);
137 const UNATIVE_OFFSET filterEnd = compiler->ehCodeOffset(HBtab->ebdHndBeg);
139 for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext)
141 // Get hold of the variable's flags.
142 const unsigned lowBits = varTmp->vpdVarNum & OFFSET_MASK;
144 // Compute the actual lifetime offsets.
145 const unsigned begOffs = varTmp->vpdBegOfs;
146 const unsigned endOffs = varTmp->vpdEndOfs;
148 // Special case: skip any 0-length lifetimes.
149 if (endOffs == begOffs)
154 // Skip lifetimes with no overlap with the filter
155 if ((endOffs <= filterBeg) || (begOffs >= filterEnd))
160 #ifndef JIT32_GCENCODER
161 // Because there is no nesting within filters, nothing
162 // should be already pinned.
163 // For JIT32_GCENCODER, we should not do this check as gcVarPtrList are always sorted by vpdBegOfs
164 // which means that we could see some varPtrDsc that were already pinned by previous splitting.
165 assert((lowBits & pinned_OFFSET_FLAG) == 0);
166 #endif // JIT32_GCENCODER
168 if (begOffs < filterBeg)
170 if (endOffs > filterEnd)
172 // The variable lifetime is starts before AND ends after
173 // the filter, so we need to create 2 new lifetimes:
174 // (1) a pinned one for the filter
175 // (2) a regular one for after the filter
176 // and then adjust the original lifetime to end before
178 CLANG_FORMAT_COMMENT_ANCHOR;
181 if (compiler->verbose)
183 printf("Splitting lifetime for filter: [%04X, %04X).\nOld: ", filterBeg, filterEnd);
184 gcDumpVarPtrDsc(varTmp);
188 varPtrDsc* desc1 = new (compiler, CMK_GC) varPtrDsc;
189 desc1->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
190 desc1->vpdBegOfs = filterBeg;
191 desc1->vpdEndOfs = filterEnd;
193 varPtrDsc* desc2 = new (compiler, CMK_GC) varPtrDsc;
194 desc2->vpdVarNum = varTmp->vpdVarNum;
195 desc2->vpdBegOfs = filterEnd;
196 desc2->vpdEndOfs = endOffs;
198 varTmp->vpdEndOfs = filterBeg;
200 gcInsertVarPtrDscSplit(desc1, varTmp);
201 gcInsertVarPtrDscSplit(desc2, varTmp);
204 if (compiler->verbose)
206 printf("New (1 of 3): ");
207 gcDumpVarPtrDsc(varTmp);
208 printf("New (2 of 3): ");
209 gcDumpVarPtrDsc(desc1);
210 printf("New (3 of 3): ");
211 gcDumpVarPtrDsc(desc2);
217 // The variable lifetime started before the filter and ends
218 // somewhere inside it, so we only create 1 new lifetime,
219 // and then adjust the original lifetime to end before
221 CLANG_FORMAT_COMMENT_ANCHOR;
224 if (compiler->verbose)
226 printf("Splitting lifetime for filter.\nOld: ");
227 gcDumpVarPtrDsc(varTmp);
231 varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc;
232 desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
233 desc->vpdBegOfs = filterBeg;
234 desc->vpdEndOfs = endOffs;
236 varTmp->vpdEndOfs = filterBeg;
238 gcInsertVarPtrDscSplit(desc, varTmp);
241 if (compiler->verbose)
243 printf("New (1 of 2): ");
244 gcDumpVarPtrDsc(varTmp);
245 printf("New (2 of 2): ");
246 gcDumpVarPtrDsc(desc);
253 if (endOffs > filterEnd)
255 // The variable lifetime starts inside the filter and
256 // ends somewhere after it, so we create 1 new
257 // lifetime for the part inside the filter and adjust
258 // the start of the original lifetime to be the end
260 CLANG_FORMAT_COMMENT_ANCHOR;
262 if (compiler->verbose)
264 printf("Splitting lifetime for filter.\nOld: ");
265 gcDumpVarPtrDsc(varTmp);
269 varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc;
270 #ifndef JIT32_GCENCODER
271 desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
272 desc->vpdBegOfs = begOffs;
273 desc->vpdEndOfs = filterEnd;
275 varTmp->vpdBegOfs = filterEnd;
277 // Mark varTmp as pinned and generated use varPtrDsc(desc) as non-pinned
278 // since gcInsertVarPtrDscSplit requires that varTmp->vpdBegOfs must precede desc->vpdBegOfs
279 desc->vpdVarNum = varTmp->vpdVarNum;
280 desc->vpdBegOfs = filterEnd;
281 desc->vpdEndOfs = endOffs;
283 varTmp->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
284 varTmp->vpdEndOfs = filterEnd;
287 gcInsertVarPtrDscSplit(desc, varTmp);
290 if (compiler->verbose)
292 printf("New (1 of 2): ");
293 gcDumpVarPtrDsc(desc);
294 printf("New (2 of 2): ");
295 gcDumpVarPtrDsc(varTmp);
301 // The variable lifetime is completely within the filter,
302 // so just add the pinned flag.
303 CLANG_FORMAT_COMMENT_ANCHOR;
305 if (compiler->verbose)
307 printf("Pinning lifetime for filter.\nOld: ");
308 gcDumpVarPtrDsc(varTmp);
312 varTmp->vpdVarNum |= pinned_OFFSET_FLAG;
314 if (compiler->verbose)
317 gcDumpVarPtrDsc(varTmp);
327 // gcInsertVarPtrDscSplit - Insert varPtrDsc that were created by splitting lifetimes
328 // From gcMarkFilterVarsPinned, we may have created one or two `varPtrDsc`s due to splitting lifetimes
329 // and these newly created `varPtrDsc`s should be inserted in gcVarPtrList.
330 // However the semantics of this call depend on the architecture.
332 // x86-GCInfo requires gcVarPtrList to be sorted by vpdBegOfs.
333 // Every time inserting an entry we should keep the order of entries.
334 // So this function searches for a proper insertion point from "begin" then "desc" gets inserted.
336 // For other architectures(ones that uses GCInfo{En|De}coder), we don't need any sort.
337 // So the argument "begin" is unused and "desc" will be inserted at the front of the list.
339 void GCInfo::gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin)
341 #ifndef JIT32_GCENCODER
343 desc->vpdNext = gcVarPtrList;
345 #else // JIT32_GCENCODER
346 // "desc" and "begin" must not be null
347 assert(desc != nullptr);
348 assert(begin != nullptr);
350 // The caller must guarantee that desc's BegOfs is equal or greater than begin's
351 // since we will search for insertion point from "begin"
352 assert(desc->vpdBegOfs >= begin->vpdBegOfs);
354 varPtrDsc* varTmp = begin->vpdNext;
355 varPtrDsc* varInsert = begin;
357 while (varTmp != nullptr && varTmp->vpdBegOfs < desc->vpdBegOfs)
360 varTmp = varTmp->vpdNext;
363 // Insert point cannot be null
364 assert(varInsert != nullptr);
366 desc->vpdNext = varInsert->vpdNext;
367 varInsert->vpdNext = desc;
368 #endif // JIT32_GCENCODER
373 void GCInfo::gcDumpVarPtrDsc(varPtrDsc* desc)
375 const int offs = (desc->vpdVarNum & ~OFFSET_MASK);
376 const GCtype gcType = (desc->vpdVarNum & byref_OFFSET_FLAG) ? GCT_BYREF : GCT_GCREF;
377 const bool isPin = (desc->vpdVarNum & pinned_OFFSET_FLAG) != 0;
379 printf("[%08X] %s%s var at [%s", dspPtr(desc), GCtypeStr(gcType), isPin ? "pinned-ptr" : "",
380 compiler->isFramePointerUsed() ? STR_FPBASE : STR_SPBASE);
384 printf("-%02XH", -offs);
388 printf("+%02XH", +offs);
391 printf("] live from %04X to %04X\n", desc->vpdBegOfs, desc->vpdEndOfs);
396 #endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
398 #ifdef JIT32_GCENCODER
402 /*****************************************************************************/
403 /*****************************************************************************/
405 /*****************************************************************************/
406 // (see jit.h) #define REGEN_SHORTCUTS 0
407 // To Regenerate the compressed info header shortcuts, define REGEN_SHORTCUTS
408 // and use the following command line pipe/filter to give you the 128
409 // most useful encodings.
411 // find . -name regen.txt | xargs cat | grep InfoHdr | sort | uniq -c | sort -r | head -128
413 // (see jit.h) #define REGEN_CALLPAT 0
414 // To Regenerate the compressed info header shortcuts, define REGEN_CALLPAT
415 // and use the following command line pipe/filter to give you the 80
416 // most useful encodings.
418 // find . -name regen.txt | xargs cat | grep CallSite | sort | uniq -c | sort -r | head -80
420 #if REGEN_SHORTCUTS || REGEN_CALLPAT
421 static FILE* logFile = NULL;
422 CRITICAL_SECTION logFileLock;
426 static void regenLog(unsigned codeDelta,
430 unsigned byrefArgMask,
431 unsigned byrefRegMask,
437 pat.fld.argCnt = (argCnt < 0xff) ? argCnt : 0xff;
438 pat.fld.regMask = (regMask < 0xff) ? regMask : 0xff;
439 pat.fld.argMask = (argMask < 0xff) ? argMask : 0xff;
440 pat.fld.codeDelta = (codeDelta < 0xff) ? codeDelta : 0xff;
444 logFile = fopen("regen.txt", "a");
445 InitializeCriticalSection(&logFileLock);
448 assert(((enSize > 0) && (enSize < 256)) && ((pat.val & 0xffffff) != 0xffffff));
450 EnterCriticalSection(&logFileLock);
452 fprintf(logFile, "CallSite( 0x%08x, 0x%02x%02x, 0x", pat.val, byrefArgMask, byrefRegMask);
456 fprintf(logFile, "%02x", *base++);
459 fprintf(logFile, "),\n");
462 LeaveCriticalSection(&logFileLock);
467 static void regenLog(unsigned encoding, InfoHdr* header, InfoHdr* state)
471 logFile = fopen("regen.txt", "a");
472 InitializeCriticalSection(&logFileLock);
475 EnterCriticalSection(&logFileLock);
477 fprintf(logFile, "InfoHdr( %2d, %2d, %1d, %1d, %1d,"
478 " %1d, %1d, %1d, %1d, %1d,"
479 " %1d, %1d, %1d, %1d, %1d, %1d,"
482 " %2d, %2d, %2d, %2d, %2d, %2d), \n",
483 state->prologSize, state->epilogSize, state->epilogCount, state->epilogAtEnd, state->ediSaved,
484 state->esiSaved, state->ebxSaved, state->ebpSaved, state->ebpFrame, state->interruptible,
485 state->doubleAlign, state->security, state->handlers, state->localloc, state->editNcontinue, state->varargs,
486 state->profCallbacks, state->genericsContext, state->genericsContextIsMethodDesc, state->returnKind,
487 state->argCount, state->frameSize,
488 (state->untrackedCnt <= SET_UNTRACKED_MAX) ? state->untrackedCnt : HAS_UNTRACKED,
489 (state->varPtrTableSize == 0) ? 0 : HAS_VARPTR,
490 (state->gsCookieOffset == INVALID_GS_COOKIE_OFFSET) ? 0 : HAS_GS_COOKIE_OFFSET,
491 (state->syncStartOffset == INVALID_SYNC_OFFSET) ? 0 : HAS_SYNC_OFFSET,
492 (state->syncStartOffset == INVALID_SYNC_OFFSET) ? 0 : HAS_SYNC_OFFSET,
493 (state->revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET) ? 0 : HAS_REV_PINVOKE_FRAME_OFFSET);
497 LeaveCriticalSection(&logFileLock);
501 /*****************************************************************************
503 * Given the four parameters return the index into the callPatternTable[]
504 * that is used to encoding these four items. If an exact match cannot
505 * found then ignore the codeDelta and search the table again for a near
507 * Returns 0..79 for an exact match or
508 * (delta<<8) | (0..79) for a near match.
509 * A near match will be encoded using two bytes, the first byte will
510 * skip the adjustment delta that prevented an exact match and the
511 * rest of the delta plus the other three items are encoded in the
514 int FASTCALL lookupCallPattern(unsigned argCnt, unsigned regMask, unsigned argMask, unsigned codeDelta)
516 if ((argCnt <= CP_MAX_ARG_CNT) && (argMask <= CP_MAX_ARG_MASK))
520 pat.fld.argCnt = argCnt;
521 pat.fld.regMask = regMask; // EBP,EBX,ESI,EDI
522 pat.fld.argMask = argMask;
523 pat.fld.codeDelta = codeDelta;
525 bool codeDeltaOK = (pat.fld.codeDelta == codeDelta);
526 unsigned bestDelta2 = 0xff;
527 unsigned bestPattern = 0xff;
528 unsigned patval = pat.val;
529 assert(sizeof(CallPattern) == sizeof(unsigned));
531 const unsigned* curp = &callPatternTable[0];
532 for (unsigned inx = 0; inx < 80; inx++, curp++)
534 unsigned curval = *curp;
535 if ((patval == curval) && codeDeltaOK)
538 if (((patval ^ curval) & 0xffffff) == 0)
540 unsigned delta2 = codeDelta - (curval >> 24);
541 if (delta2 < bestDelta2)
549 if (bestPattern != 0xff)
551 return (bestDelta2 << 8) | bestPattern;
557 static bool initNeeded3(unsigned cur, unsigned tgt, unsigned max, unsigned* hint)
581 static bool initNeeded4(unsigned cur, unsigned tgt, unsigned max, unsigned* hint)
605 static int bigEncoding3(unsigned cur, unsigned tgt, unsigned max)
624 static int bigEncoding4(unsigned cur, unsigned tgt, unsigned max)
643 BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state, BYTE& codeSet)
645 BYTE encoding = 0xff;
646 codeSet = 1; // codeSet is 1 or 2, depending on whether the returned encoding
647 // corresponds to InfoHdrAdjust, or InfoHdrAdjust2 enumerations.
649 if (state->argCount != header.argCount)
651 // We have one-byte encodings for 0..8
652 if (header.argCount <= SET_ARGCOUNT_MAX)
654 state->argCount = header.argCount;
655 encoding = SET_ARGCOUNT + header.argCount;
661 if (initNeeded4(state->argCount, header.argCount, SET_ARGCOUNT_MAX, &hint))
663 assert(hint <= SET_ARGCOUNT_MAX);
664 state->argCount = hint;
665 encoding = SET_ARGCOUNT + hint;
671 state->argCount <<= 4;
672 state->argCount += hint;
673 encoding = NEXT_FOUR_ARGCOUNT + hint;
679 if (state->frameSize != header.frameSize)
681 // We have one-byte encodings for 0..7
682 if (header.frameSize <= SET_FRAMESIZE_MAX)
684 state->frameSize = header.frameSize;
685 encoding = SET_FRAMESIZE + header.frameSize;
691 if (initNeeded4(state->frameSize, header.frameSize, SET_FRAMESIZE_MAX, &hint))
693 assert(hint <= SET_FRAMESIZE_MAX);
694 state->frameSize = hint;
695 encoding = SET_FRAMESIZE + hint;
701 state->frameSize <<= 4;
702 state->frameSize += hint;
703 encoding = NEXT_FOUR_FRAMESIZE + hint;
709 if ((state->epilogCount != header.epilogCount) || (state->epilogAtEnd != header.epilogAtEnd))
711 if (header.epilogCount > SET_EPILOGCNT_MAX)
712 IMPL_LIMITATION("More than SET_EPILOGCNT_MAX epilogs");
714 state->epilogCount = header.epilogCount;
715 state->epilogAtEnd = header.epilogAtEnd;
716 encoding = SET_EPILOGCNT + header.epilogCount * 2;
717 if (header.epilogAtEnd)
722 if (state->varPtrTableSize != header.varPtrTableSize)
724 assert(state->varPtrTableSize == 0 || state->varPtrTableSize == HAS_VARPTR);
726 if (state->varPtrTableSize == 0)
728 state->varPtrTableSize = HAS_VARPTR;
729 encoding = FLIP_VAR_PTR_TABLE_SZ;
732 else if (header.varPtrTableSize == 0)
734 state->varPtrTableSize = 0;
735 encoding = FLIP_VAR_PTR_TABLE_SZ;
740 if (state->untrackedCnt != header.untrackedCnt)
742 assert(state->untrackedCnt <= SET_UNTRACKED_MAX || state->untrackedCnt == HAS_UNTRACKED);
744 // We have one-byte encodings for 0..3
745 if (header.untrackedCnt <= SET_UNTRACKED_MAX)
747 state->untrackedCnt = header.untrackedCnt;
748 encoding = SET_UNTRACKED + header.untrackedCnt;
751 else if (state->untrackedCnt != HAS_UNTRACKED)
753 state->untrackedCnt = HAS_UNTRACKED;
754 encoding = FFFF_UNTRACKED_CNT;
759 if (state->epilogSize != header.epilogSize)
761 // We have one-byte encodings for 0..10
762 if (header.epilogSize <= SET_EPILOGSIZE_MAX)
764 state->epilogSize = header.epilogSize;
765 encoding = SET_EPILOGSIZE + header.epilogSize;
771 if (initNeeded3(state->epilogSize, header.epilogSize, SET_EPILOGSIZE_MAX, &hint))
773 assert(hint <= SET_EPILOGSIZE_MAX);
774 state->epilogSize = hint;
775 encoding = SET_EPILOGSIZE + hint;
781 state->epilogSize <<= 3;
782 state->epilogSize += hint;
783 encoding = NEXT_THREE_EPILOGSIZE + hint;
789 if (state->prologSize != header.prologSize)
791 // We have one-byte encodings for 0..16
792 if (header.prologSize <= SET_PROLOGSIZE_MAX)
794 state->prologSize = header.prologSize;
795 encoding = SET_PROLOGSIZE + header.prologSize;
801 assert(SET_PROLOGSIZE_MAX > 15);
802 if (initNeeded3(state->prologSize, header.prologSize, 15, &hint))
805 state->prologSize = hint;
806 encoding = SET_PROLOGSIZE + hint;
812 state->prologSize <<= 3;
813 state->prologSize += hint;
814 encoding = NEXT_THREE_PROLOGSIZE + hint;
820 if (state->ediSaved != header.ediSaved)
822 state->ediSaved = header.ediSaved;
823 encoding = FLIP_EDI_SAVED;
827 if (state->esiSaved != header.esiSaved)
829 state->esiSaved = header.esiSaved;
830 encoding = FLIP_ESI_SAVED;
834 if (state->ebxSaved != header.ebxSaved)
836 state->ebxSaved = header.ebxSaved;
837 encoding = FLIP_EBX_SAVED;
841 if (state->ebpSaved != header.ebpSaved)
843 state->ebpSaved = header.ebpSaved;
844 encoding = FLIP_EBP_SAVED;
848 if (state->ebpFrame != header.ebpFrame)
850 state->ebpFrame = header.ebpFrame;
851 encoding = FLIP_EBP_FRAME;
855 if (state->interruptible != header.interruptible)
857 state->interruptible = header.interruptible;
858 encoding = FLIP_INTERRUPTIBLE;
863 if (state->doubleAlign != header.doubleAlign)
865 state->doubleAlign = header.doubleAlign;
866 encoding = FLIP_DOUBLE_ALIGN;
871 if (state->security != header.security)
873 state->security = header.security;
874 encoding = FLIP_SECURITY;
878 if (state->handlers != header.handlers)
880 state->handlers = header.handlers;
881 encoding = FLIP_HANDLERS;
885 if (state->localloc != header.localloc)
887 state->localloc = header.localloc;
888 encoding = FLIP_LOCALLOC;
892 if (state->editNcontinue != header.editNcontinue)
894 state->editNcontinue = header.editNcontinue;
895 encoding = FLIP_EDITnCONTINUE;
899 if (state->varargs != header.varargs)
901 state->varargs = header.varargs;
902 encoding = FLIP_VARARGS;
906 if (state->profCallbacks != header.profCallbacks)
908 state->profCallbacks = header.profCallbacks;
909 encoding = FLIP_PROF_CALLBACKS;
913 if (state->genericsContext != header.genericsContext)
915 state->genericsContext = header.genericsContext;
916 encoding = FLIP_HAS_GENERICS_CONTEXT;
920 if (state->genericsContextIsMethodDesc != header.genericsContextIsMethodDesc)
922 state->genericsContextIsMethodDesc = header.genericsContextIsMethodDesc;
923 encoding = FLIP_GENERICS_CONTEXT_IS_METHODDESC;
927 if (GCInfoEncodesReturnKind() && (state->returnKind != header.returnKind))
929 state->returnKind = header.returnKind;
930 codeSet = 2; // Two byte encoding
931 encoding = header.returnKind;
932 _ASSERTE(encoding < SET_RET_KIND_MAX);
936 if (state->gsCookieOffset != header.gsCookieOffset)
938 assert(state->gsCookieOffset == INVALID_GS_COOKIE_OFFSET || state->gsCookieOffset == HAS_GS_COOKIE_OFFSET);
940 if (state->gsCookieOffset == INVALID_GS_COOKIE_OFFSET)
942 // header.gsCookieOffset is non-zero. We can set it
943 // to zero using FLIP_HAS_GS_COOKIE
944 state->gsCookieOffset = HAS_GS_COOKIE_OFFSET;
945 encoding = FLIP_HAS_GS_COOKIE;
948 else if (header.gsCookieOffset == INVALID_GS_COOKIE_OFFSET)
950 state->gsCookieOffset = INVALID_GS_COOKIE_OFFSET;
951 encoding = FLIP_HAS_GS_COOKIE;
956 if (state->syncStartOffset != header.syncStartOffset)
958 assert(state->syncStartOffset == INVALID_SYNC_OFFSET || state->syncStartOffset == HAS_SYNC_OFFSET);
960 if (state->syncStartOffset == INVALID_SYNC_OFFSET)
962 // header.syncStartOffset is non-zero. We can set it
963 // to zero using FLIP_SYNC
964 state->syncStartOffset = HAS_SYNC_OFFSET;
965 encoding = FLIP_SYNC;
968 else if (header.syncStartOffset == INVALID_SYNC_OFFSET)
970 state->syncStartOffset = INVALID_SYNC_OFFSET;
971 encoding = FLIP_SYNC;
976 if (GCInfoEncodesRevPInvokeFrame() && (state->revPInvokeOffset != header.revPInvokeOffset))
978 assert(state->revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET ||
979 state->revPInvokeOffset == HAS_REV_PINVOKE_FRAME_OFFSET);
981 if (state->revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET)
983 // header.revPInvokeOffset is non-zero.
984 state->revPInvokeOffset = HAS_REV_PINVOKE_FRAME_OFFSET;
985 encoding = FLIP_REV_PINVOKE_FRAME;
988 else if (header.revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET)
990 state->revPInvokeOffset = INVALID_REV_PINVOKE_OFFSET;
991 encoding = FLIP_REV_PINVOKE_FRAME;
997 _ASSERTE(encoding < MORE_BYTES_TO_FOLLOW);
998 if (!state->isHeaderMatch(header))
999 encoding |= MORE_BYTES_TO_FOLLOW;
1004 static int measureDistance(const InfoHdr& header, const InfoHdrSmall* p, int closeness)
1008 if (p->untrackedCnt != header.untrackedCnt)
1010 if (header.untrackedCnt > 3)
1012 if (p->untrackedCnt != HAS_UNTRACKED)
1019 if (distance >= closeness)
1023 if (p->varPtrTableSize != header.varPtrTableSize)
1025 if (header.varPtrTableSize != 0)
1027 if (p->varPtrTableSize != HAS_VARPTR)
1032 assert(p->varPtrTableSize == HAS_VARPTR);
1035 if (distance >= closeness)
1039 if (p->frameSize != header.frameSize)
1042 if (distance >= closeness)
1045 // We have one-byte encodings for 0..7
1046 if (header.frameSize > SET_FRAMESIZE_MAX)
1048 distance += bigEncoding4(p->frameSize, header.frameSize, SET_FRAMESIZE_MAX);
1049 if (distance >= closeness)
1054 if (p->argCount != header.argCount)
1057 if (distance >= closeness)
1060 // We have one-byte encodings for 0..8
1061 if (header.argCount > SET_ARGCOUNT_MAX)
1063 distance += bigEncoding4(p->argCount, header.argCount, SET_ARGCOUNT_MAX);
1064 if (distance >= closeness)
1069 if (p->prologSize != header.prologSize)
1072 if (distance >= closeness)
1075 // We have one-byte encodings for 0..16
1076 if (header.prologSize > SET_PROLOGSIZE_MAX)
1078 assert(SET_PROLOGSIZE_MAX > 15);
1079 distance += bigEncoding3(p->prologSize, header.prologSize, 15);
1080 if (distance >= closeness)
1085 if (p->epilogSize != header.epilogSize)
1088 if (distance >= closeness)
1090 // We have one-byte encodings for 0..10
1091 if (header.epilogSize > SET_EPILOGSIZE_MAX)
1093 distance += bigEncoding3(p->epilogSize, header.epilogSize, SET_EPILOGSIZE_MAX);
1094 if (distance >= closeness)
1099 if ((p->epilogCount != header.epilogCount) || (p->epilogAtEnd != header.epilogAtEnd))
1102 if (distance >= closeness)
1105 if (header.epilogCount > SET_EPILOGCNT_MAX)
1106 IMPL_LIMITATION("More than SET_EPILOGCNT_MAX epilogs");
1109 if (p->ediSaved != header.ediSaved)
1112 if (distance >= closeness)
1116 if (p->esiSaved != header.esiSaved)
1119 if (distance >= closeness)
1123 if (p->ebxSaved != header.ebxSaved)
1126 if (distance >= closeness)
1130 if (p->ebpSaved != header.ebpSaved)
1133 if (distance >= closeness)
1137 if (p->ebpFrame != header.ebpFrame)
1140 if (distance >= closeness)
1144 if (p->interruptible != header.interruptible)
1147 if (distance >= closeness)
1152 if (p->doubleAlign != header.doubleAlign)
1155 if (distance >= closeness)
1160 if (p->security != header.security)
1163 if (distance >= closeness)
1167 if (p->handlers != header.handlers)
1170 if (distance >= closeness)
1174 if (p->localloc != header.localloc)
1177 if (distance >= closeness)
1181 if (p->editNcontinue != header.editNcontinue)
1184 if (distance >= closeness)
1188 if (p->varargs != header.varargs)
1191 if (distance >= closeness)
1195 if (p->profCallbacks != header.profCallbacks)
1198 if (distance >= closeness)
1202 if (p->genericsContext != header.genericsContext)
1205 if (distance >= closeness)
1209 if (p->genericsContextIsMethodDesc != header.genericsContextIsMethodDesc)
1212 if (distance >= closeness)
1216 if (p->returnKind != header.returnKind)
1218 // Setting the ReturnKind requires two bytes of encoding.
1220 if (distance >= closeness)
1224 if (header.gsCookieOffset != INVALID_GS_COOKIE_OFFSET)
1227 if (distance >= closeness)
1231 if (header.syncStartOffset != INVALID_SYNC_OFFSET)
1234 if (distance >= closeness)
1238 if (header.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET)
1241 if (distance >= closeness)
1248 // DllMain calls gcInitEncoderLookupTable to fill in this table
1249 /* extern */ int infoHdrLookup[IH_MAX_PROLOG_SIZE + 2];
1251 /* static */ void GCInfo::gcInitEncoderLookupTable()
1253 const InfoHdrSmall* p = &infoHdrShortcut[0];
1258 for (n = 0; n < 128; n++, p++)
1260 if (p->prologSize != lo)
1262 if (p->prologSize < lo)
1264 assert(p->prologSize == 0);
1265 hi = IH_MAX_PROLOG_SIZE;
1270 assert(hi <= IH_MAX_PROLOG_SIZE);
1273 infoHdrLookup[++lo] = n;
1275 if (lo == IH_MAX_PROLOG_SIZE)
1280 assert(lo == IH_MAX_PROLOG_SIZE);
1281 assert(infoHdrLookup[IH_MAX_PROLOG_SIZE] < 128);
1283 while (p->prologSize == lo)
1291 infoHdrLookup[++lo] = n;
1295 // We do some other DEBUG only validity checks here
1297 assert(callCommonDelta[0] < callCommonDelta[1]);
1298 assert(callCommonDelta[1] < callCommonDelta[2]);
1299 assert(callCommonDelta[2] < callCommonDelta[3]);
1300 assert(sizeof(CallPattern) == sizeof(unsigned));
1301 unsigned maxMarks = 0;
1302 for (unsigned inx = 0; inx < 80; inx++)
1305 pat.val = callPatternTable[inx];
1307 assert(pat.fld.codeDelta <= CP_MAX_CODE_DELTA);
1308 if (pat.fld.codeDelta == CP_MAX_CODE_DELTA)
1311 assert(pat.fld.argCnt <= CP_MAX_ARG_CNT);
1312 if (pat.fld.argCnt == CP_MAX_ARG_CNT)
1315 assert(pat.fld.argMask <= CP_MAX_ARG_MASK);
1316 if (pat.fld.argMask == CP_MAX_ARG_MASK)
1319 assert(maxMarks == 0x07);
1323 const int NO_CACHED_HEADER = -1;
1325 BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int* pCached)
1327 // First try the cached value for an exact match, if there is one
1330 const InfoHdrSmall* p;
1332 if (n != NO_CACHED_HEADER)
1334 p = &infoHdrShortcut[n];
1335 if (p->isHeaderMatch(header))
1337 // exact match found
1338 GetInfoHdr(n, state);
1344 // Next search the table for an exact match
1345 // Only search entries that have a matching prolog size
1346 // Note: lo and hi are saved here as they specify the
1347 // range of entries that have the correct prolog size
1349 unsigned psz = header.prologSize;
1353 if (psz <= IH_MAX_PROLOG_SIZE)
1355 lo = infoHdrLookup[psz];
1356 hi = infoHdrLookup[psz + 1];
1357 p = &infoHdrShortcut[lo];
1358 for (n = lo; n < hi; n++, p++)
1360 assert(psz == p->prologSize);
1361 if (p->isHeaderMatch(header))
1363 // exact match found
1364 GetInfoHdr(n, state);
1365 *pCached = n; // cache the value
1373 // no exact match in infoHdrShortcut[]
1375 // find the nearest entry in the table
1378 int closeness = 255; // (i.e. not very close)
1381 // Calculate the minimum acceptable distance
1382 // if we find an entry that is at least this close
1383 // we will stop the search and use that value
1385 int min_acceptable_distance = 1;
1387 if (header.frameSize > SET_FRAMESIZE_MAX)
1389 ++min_acceptable_distance;
1390 if (header.frameSize > 32)
1391 ++min_acceptable_distance;
1393 if (header.argCount > SET_ARGCOUNT_MAX)
1395 ++min_acceptable_distance;
1396 if (header.argCount > 32)
1397 ++min_acceptable_distance;
1400 // First try the cached value
1401 // and see if it meets the minimum acceptable distance
1403 if (*pCached != NO_CACHED_HEADER)
1405 p = &infoHdrShortcut[*pCached];
1406 int distance = measureDistance(header, p, closeness);
1407 assert(distance > 0);
1408 if (distance <= min_acceptable_distance)
1410 GetInfoHdr(*pCached, state);
1412 return 0x80 | *pCached;
1416 closeness = distance;
1421 // Then try the ones pointed to by [lo..hi),
1422 // (i.e. the ones that have the correct prolog size)
1424 p = &infoHdrShortcut[lo];
1425 for (n = lo; n < hi; n++, p++)
1428 continue; // already tried this one
1429 int distance = measureDistance(header, p, closeness);
1430 assert(distance > 0);
1431 if (distance <= min_acceptable_distance)
1433 GetInfoHdr(n, state);
1434 *pCached = n; // Cache this value
1438 else if (distance < closeness)
1440 closeness = distance;
1445 int last = infoHdrLookup[IH_MAX_PROLOG_SIZE + 1];
1446 assert(last <= 128);
1448 // Then try all the rest [0..last-1]
1449 p = &infoHdrShortcut[0];
1450 for (n = 0; n < last; n++, p++)
1453 continue; // already tried this one
1454 if ((n >= lo) && (n < hi))
1455 continue; // already tried these
1456 int distance = measureDistance(header, p, closeness);
1457 assert(distance > 0);
1458 if (distance <= min_acceptable_distance)
1460 GetInfoHdr(n, state);
1461 *pCached = n; // Cache this value
1465 else if (distance < closeness)
1467 closeness = distance;
1473 // If we reach here then there was no adjacent neighbor
1474 // in infoHdrShortcut[], closeness indicate how many extra
1475 // bytes we will need to encode this item.
1477 assert((nearest >= 0) && (nearest <= 127));
1478 GetInfoHdr(nearest, state);
1479 *pCached = nearest; // Cache this value
1481 return 0x80 | nearest;
1484 /*****************************************************************************
1486 * Write the initial part of the method info block. This is called twice;
1487 * first to compute the size needed for the info (mask=0), the second time
1488 * to actually generate the contents of the table (mask=-1,dest!=NULL).
1491 size_t GCInfo::gcInfoBlockHdrSave(
1492 BYTE* dest, int mask, unsigned methodSize, unsigned prologSize, unsigned epilogSize, InfoHdr* header, int* pCached)
1495 if (compiler->verbose)
1496 printf("*************** In gcInfoBlockHdrSave()\n");
1500 #if VERIFY_GC_TABLES
1501 *castto(dest, unsigned short*)++ = 0xFEEF;
1502 size += sizeof(short);
1505 /* Write the method size first (using between 1 and 5 bytes) */
1506 CLANG_FORMAT_COMMENT_ANCHOR;
1509 if (compiler->verbose)
1512 printf("GCINFO: methodSize = %04X\n", methodSize);
1514 printf("GCINFO: prologSize = %04X\n", prologSize);
1516 printf("GCINFO: epilogSize = %04X\n", epilogSize);
1520 size_t methSz = encodeUnsigned(dest, methodSize);
1522 dest += methSz & mask;
1525 // New style InfoBlk Header
1527 // Typically only uses one-byte to store everything.
1532 memset(header, 0, sizeof(InfoHdr));
1533 *pCached = NO_CACHED_HEADER;
1536 assert(FitsIn<unsigned char>(prologSize));
1537 header->prologSize = static_cast<unsigned char>(prologSize);
1538 assert(FitsIn<unsigned char>(epilogSize));
1539 header->epilogSize = static_cast<unsigned char>(epilogSize);
1540 header->epilogCount = compiler->getEmitter()->emitGetEpilogCnt();
1541 if (header->epilogCount != compiler->getEmitter()->emitGetEpilogCnt())
1542 IMPL_LIMITATION("emitGetEpilogCnt() does not fit in InfoHdr::epilogCount");
1543 header->epilogAtEnd = compiler->getEmitter()->emitHasEpilogEnd();
1545 if (compiler->codeGen->regSet.rsRegsModified(RBM_EDI))
1546 header->ediSaved = 1;
1547 if (compiler->codeGen->regSet.rsRegsModified(RBM_ESI))
1548 header->esiSaved = 1;
1549 if (compiler->codeGen->regSet.rsRegsModified(RBM_EBX))
1550 header->ebxSaved = 1;
1552 header->interruptible = compiler->codeGen->genInterruptible;
1554 if (!compiler->isFramePointerUsed())
1557 if (compiler->genDoubleAlign())
1559 header->ebpSaved = true;
1560 assert(!compiler->codeGen->regSet.rsRegsModified(RBM_EBP));
1563 if (compiler->codeGen->regSet.rsRegsModified(RBM_EBP))
1565 header->ebpSaved = true;
1570 header->ebpSaved = true;
1571 header->ebpFrame = true;
1575 header->doubleAlign = compiler->genDoubleAlign();
1578 header->security = compiler->opts.compNeedSecurityCheck;
1580 header->handlers = compiler->ehHasCallableHandlers();
1581 header->localloc = compiler->compLocallocUsed;
1583 header->varargs = compiler->info.compIsVarArgs;
1584 header->profCallbacks = compiler->info.compProfilerCallback;
1585 header->editNcontinue = compiler->opts.compDbgEnC;
1586 header->genericsContext = compiler->lvaReportParamTypeArg();
1587 header->genericsContextIsMethodDesc =
1588 header->genericsContext && (compiler->info.compMethodInfo->options & (CORINFO_GENERICS_CTXT_FROM_METHODDESC));
1590 if (GCInfoEncodesReturnKind())
1592 ReturnKind returnKind = getReturnKind();
1593 _ASSERTE(IsValidReturnKind(returnKind) && "Return Kind must be valid");
1594 _ASSERTE(!IsStructReturnKind(returnKind) && "Struct Return Kinds Unexpected for JIT32");
1595 _ASSERTE(((int)returnKind < (int)SET_RET_KIND_MAX) && "ReturnKind has no legal encoding");
1596 header->returnKind = returnKind;
1599 header->gsCookieOffset = INVALID_GS_COOKIE_OFFSET;
1600 if (compiler->getNeedsGSSecurityCookie())
1602 assert(compiler->lvaGSSecurityCookie != BAD_VAR_NUM);
1603 int stkOffs = compiler->lvaTable[compiler->lvaGSSecurityCookie].lvStkOffs;
1604 header->gsCookieOffset = compiler->isFramePointerUsed() ? -stkOffs : stkOffs;
1605 assert(header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET);
1608 header->syncStartOffset = INVALID_SYNC_OFFSET;
1609 header->syncEndOffset = INVALID_SYNC_OFFSET;
1610 #ifndef UNIX_X86_ABI
1611 // JIT is responsible for synchronization on funclet-based EH model that x86/Linux uses.
1612 if (compiler->info.compFlags & CORINFO_FLG_SYNCH)
1614 assert(compiler->syncStartEmitCookie != NULL);
1615 header->syncStartOffset = compiler->getEmitter()->emitCodeOffset(compiler->syncStartEmitCookie, 0);
1616 assert(header->syncStartOffset != INVALID_SYNC_OFFSET);
1618 assert(compiler->syncEndEmitCookie != NULL);
1619 header->syncEndOffset = compiler->getEmitter()->emitCodeOffset(compiler->syncEndEmitCookie, 0);
1620 assert(header->syncEndOffset != INVALID_SYNC_OFFSET);
1622 assert(header->syncStartOffset < header->syncEndOffset);
1623 // synchronized methods can't have more than 1 epilog
1624 assert(header->epilogCount <= 1);
1628 header->revPInvokeOffset = INVALID_REV_PINVOKE_OFFSET;
1630 assert((compiler->compArgSize & 0x3) == 0);
1633 (compiler->compArgSize - (compiler->codeGen->intRegState.rsCalleeRegArgCount * REGSIZE_BYTES)) / REGSIZE_BYTES;
1634 assert(argCount <= MAX_USHORT_SIZE_T);
1635 header->argCount = static_cast<unsigned short>(argCount);
1637 header->frameSize = compiler->compLclFrameSize / sizeof(int);
1638 if (header->frameSize != (compiler->compLclFrameSize / sizeof(int)))
1639 IMPL_LIMITATION("compLclFrameSize does not fit in InfoHdr::frameSize");
1643 gcCountForHeader((UNALIGNED unsigned int*)&header->untrackedCnt,
1644 (UNALIGNED unsigned int*)&header->varPtrTableSize);
1648 // If the high-order bit of headerEncoding is set
1649 // then additional bytes will update the InfoHdr state
1650 // until the fully state is encoded
1654 BYTE headerEncoding = encodeHeaderFirst(*header, &state, &more, pCached);
1659 regenLog(headerEncoding, header, &state);
1661 *dest++ = headerEncoding;
1663 BYTE encoding = headerEncoding;
1665 while (encoding & MORE_BYTES_TO_FOLLOW)
1667 encoding = encodeHeaderNext(*header, &state, codeSet);
1670 regenLog(headerEncoding, header, &state);
1672 _ASSERTE(codeSet == 1 || codeSet == 2 && "Encoding must correspond to InfoHdrAdjust or InfoHdrAdjust2");
1675 *dest++ = NEXT_OPCODE | MORE_BYTES_TO_FOLLOW;
1688 if (header->untrackedCnt > SET_UNTRACKED_MAX)
1690 unsigned count = header->untrackedCnt;
1691 unsigned sz = encodeUnsigned(mask ? dest : NULL, count);
1693 dest += (sz & mask);
1696 if (header->varPtrTableSize != 0)
1698 unsigned count = header->varPtrTableSize;
1699 unsigned sz = encodeUnsigned(mask ? dest : NULL, count);
1701 dest += (sz & mask);
1704 if (header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET)
1706 assert(mask == 0 || state.gsCookieOffset == HAS_GS_COOKIE_OFFSET);
1707 unsigned offset = header->gsCookieOffset;
1708 unsigned sz = encodeUnsigned(mask ? dest : NULL, offset);
1710 dest += (sz & mask);
1713 if (header->syncStartOffset != INVALID_SYNC_OFFSET)
1715 assert(mask == 0 || state.syncStartOffset == HAS_SYNC_OFFSET);
1718 unsigned offset = header->syncStartOffset;
1719 unsigned sz = encodeUnsigned(mask ? dest : NULL, offset);
1721 dest += (sz & mask);
1725 unsigned offset = header->syncEndOffset;
1726 unsigned sz = encodeUnsigned(mask ? dest : NULL, offset);
1728 dest += (sz & mask);
1732 if (header->epilogCount)
1734 /* Generate table unless one epilog at the end of the method */
1736 if (header->epilogAtEnd == 0 || header->epilogCount != 1)
1738 #if VERIFY_GC_TABLES
1739 *castto(dest, unsigned short*)++ = 0xFACE;
1740 size += sizeof(short);
1743 /* Simply write a sorted array of offsets using encodeUDelta */
1745 gcEpilogTable = mask ? dest : NULL;
1746 gcEpilogPrevOffset = 0;
1748 size_t sz = compiler->getEmitter()->emitGenEpilogLst(gcRecordEpilog, this);
1750 /* Add the size of the epilog table to the total size */
1753 dest += (sz & mask);
1761 if (compiler->codeGen->genInterruptible)
1771 #endif // DISPLAY_SIZES
1776 /*****************************************************************************
1778 * Return the size of the pointer tracking tables.
1781 size_t GCInfo::gcPtrTableSize(const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
1785 temp[16] = 0xAB; // Set some marker
1788 /* Compute the total size of the tables */
1790 size_t size = gcMakeRegPtrTable(temp, 0, header, codeSize, pArgTabOffset);
1792 assert(temp[16] == 0xAB); // Check that marker didnt get overwritten
1797 /*****************************************************************************
1798 * Encode the callee-saved registers into 3 bits.
1801 unsigned gceEncodeCalleeSavedRegs(unsigned regs)
1803 unsigned encodedRegs = 0;
1806 encodedRegs |= 0x04;
1808 encodedRegs |= 0x02;
1810 encodedRegs |= 0x01;
1815 /*****************************************************************************
1816 * Is the next entry for a byref pointer. If so, emit the prefix for the
1817 * interruptible encoding. Check only for pushes and registers
1820 inline BYTE* gceByrefPrefixI(GCInfo::regPtrDsc* rpd, BYTE* dest)
1822 // For registers, we don't need a prefix if it is going dead.
1823 assert(rpd->rpdArg || rpd->rpdCompiler.rpdDel == 0);
1825 if (!rpd->rpdArg || rpd->rpdArgType == GCInfo::rpdARG_PUSH)
1826 if (rpd->rpdGCtypeGet() == GCT_BYREF)
1832 /*****************************************************************************/
1834 /* These functions are needed to work around a VC5.0 compiler bug */
1835 /* DO NOT REMOVE, unless you are sure that the free build works */
1840 static int (*zeroFunc)() = zeroFN;
1842 /*****************************************************************************
1843 * Modelling of the GC ptrs pushed on the stack
1846 typedef unsigned pasMaskType;
1847 #define BITS_IN_pasMask (BITS_IN_BYTE * sizeof(pasMaskType))
1848 #define HIGHEST_pasMask_BIT (((pasMaskType)0x1) << (BITS_IN_pasMask - 1))
1850 //-----------------------------------------------------------------------------
1852 class PendingArgsStack
1855 PendingArgsStack(unsigned maxDepth, Compiler* pComp);
1857 void pasPush(GCtype gcType);
1858 void pasPop(unsigned count);
1859 void pasKill(unsigned gcCount);
1861 unsigned pasCurDepth()
1865 pasMaskType pasArgMask()
1867 assert(pasDepth <= BITS_IN_pasMask);
1868 return pasBottomMask;
1870 pasMaskType pasByrefArgMask()
1872 assert(pasDepth <= BITS_IN_pasMask);
1873 return pasByrefBottomMask;
1875 bool pasHasGCptrs();
1877 // Use these in the case where there actually are more ptrs than pasArgMask
1878 unsigned pasEnumGCoffsCount();
1879 #define pasENUM_START ((unsigned)-1)
1880 #define pasENUM_LAST ((unsigned)-2)
1881 #define pasENUM_END ((unsigned)-3)
1882 unsigned pasEnumGCoffs(unsigned iter, unsigned* offs);
1885 unsigned pasMaxDepth;
1889 pasMaskType pasBottomMask; // The first 32 args
1890 pasMaskType pasByrefBottomMask; // byref qualifier for pasBottomMask
1892 BYTE* pasTopArray; // More than 32 args are represented here
1893 unsigned pasPtrsInTopArray; // How many GCptrs here
1896 //-----------------------------------------------------------------------------
1898 PendingArgsStack::PendingArgsStack(unsigned maxDepth, Compiler* pComp)
1899 : pasMaxDepth(maxDepth)
1902 , pasByrefBottomMask(0)
1904 , pasPtrsInTopArray(0)
1906 /* Do we need an array as well as the mask ? */
1908 if (pasMaxDepth > BITS_IN_pasMask)
1909 pasTopArray = (BYTE*)pComp->compGetMem(pasMaxDepth - BITS_IN_pasMask);
1912 //-----------------------------------------------------------------------------
1914 void PendingArgsStack::pasPush(GCtype gcType)
1916 assert(pasDepth < pasMaxDepth);
1918 if (pasDepth < BITS_IN_pasMask)
1920 /* Shift the mask */
1922 pasBottomMask <<= 1;
1923 pasByrefBottomMask <<= 1;
1925 if (needsGC(gcType))
1929 if (gcType == GCT_BYREF)
1930 pasByrefBottomMask |= 1;
1937 pasTopArray[pasDepth - BITS_IN_pasMask] = (BYTE)gcType;
1940 pasPtrsInTopArray++;
1946 //-----------------------------------------------------------------------------
1948 void PendingArgsStack::pasPop(unsigned count)
1950 assert(pasDepth >= count);
1952 /* First pop from array (if applicable) */
1954 for (/**/; (pasDepth > BITS_IN_pasMask) && count; pasDepth--, count--)
1956 unsigned topIndex = pasDepth - BITS_IN_pasMask - 1;
1958 GCtype topArg = (GCtype)pasTopArray[topIndex];
1960 if (needsGC(topArg))
1961 pasPtrsInTopArray--;
1966 /* Now un-shift the mask */
1968 assert(pasPtrsInTopArray == 0);
1969 assert(count <= BITS_IN_pasMask);
1971 if (count == BITS_IN_pasMask) // (x>>32) is a nop on x86. So special-case it
1973 pasBottomMask = pasByrefBottomMask = 0;
1978 pasBottomMask >>= count;
1979 pasByrefBottomMask >>= count;
1984 //-----------------------------------------------------------------------------
1985 // Kill (but don't pop) the top 'gcCount' args
1987 void PendingArgsStack::pasKill(unsigned gcCount)
1989 assert(gcCount != 0);
1991 /* First kill args in array (if any) */
1993 for (unsigned curPos = pasDepth; (curPos > BITS_IN_pasMask) && gcCount; curPos--)
1995 unsigned curIndex = curPos - BITS_IN_pasMask - 1;
1997 GCtype curArg = (GCtype)pasTopArray[curIndex];
1999 if (needsGC(curArg))
2001 pasTopArray[curIndex] = GCT_NONE;
2002 pasPtrsInTopArray--;
2007 /* Now kill bits from the mask */
2009 assert(pasPtrsInTopArray == 0);
2010 assert(gcCount <= BITS_IN_pasMask);
2012 for (unsigned bitPos = 1; gcCount; bitPos <<= 1)
2014 assert(pasBottomMask != 0);
2016 if (pasBottomMask & bitPos)
2018 pasBottomMask &= ~bitPos;
2019 pasByrefBottomMask &= ~bitPos;
2024 assert(bitPos != HIGHEST_pasMask_BIT);
2029 //-----------------------------------------------------------------------------
2030 // Used for the case where there are more than BITS_IN_pasMask args on stack,
2031 // but none are any pointers. May avoid reporting anything to GCinfo
2033 bool PendingArgsStack::pasHasGCptrs()
2035 if (pasDepth <= BITS_IN_pasMask)
2036 return pasBottomMask != 0;
2038 return pasBottomMask != 0 || pasPtrsInTopArray != 0;
2041 //-----------------------------------------------------------------------------
2042 // Iterates over mask and array to return total count.
2043 // Use only when you are going to emit a table of the offsets
2045 unsigned PendingArgsStack::pasEnumGCoffsCount()
2047 /* Should only be used in the worst case, when just the mask can't be used */
2049 assert(pasDepth > BITS_IN_pasMask && pasHasGCptrs());
2051 /* Count number of set bits in mask */
2055 for (pasMaskType mask = 0x1, i = 0; i < BITS_IN_pasMask; mask <<= 1, i++)
2057 if (mask & pasBottomMask)
2061 return count + pasPtrsInTopArray;
2064 //-----------------------------------------------------------------------------
2065 // Initalize enumeration by passing in iter=pasENUM_START.
2066 // Continue by passing in the return value as the new value of iter
2067 // End of enumeration when pasENUM_END is returned
2068 // If return value != pasENUM_END, *offs is set to the offset for GCinfo
2070 unsigned PendingArgsStack::pasEnumGCoffs(unsigned iter, unsigned* offs)
2072 if (iter == pasENUM_LAST)
2075 unsigned i = (iter == pasENUM_START) ? pasDepth : iter;
2077 for (/**/; i > BITS_IN_pasMask; i--)
2079 GCtype curArg = (GCtype)pasTopArray[i - BITS_IN_pasMask - 1];
2080 if (needsGC(curArg))
2084 offset = (pasDepth - i) * TARGET_POINTER_SIZE;
2085 if (curArg == GCT_BYREF)
2086 offset |= byref_OFFSET_FLAG;
2096 // Have we already processed some of the bits in pasBottomMask ?
2098 i = (iter == pasENUM_START || iter >= BITS_IN_pasMask) ? 0 // no
2101 for (pasMaskType mask = 0x1 << i; mask; i++, mask <<= 1)
2103 if (mask & pasBottomMask)
2105 unsigned lvl = (pasDepth > BITS_IN_pasMask) ? (pasDepth - BITS_IN_pasMask) : 0; // How many in pasTopArray[]
2109 offset = lvl * TARGET_POINTER_SIZE;
2110 if (mask & pasByrefBottomMask)
2111 offset |= byref_OFFSET_FLAG;
2115 unsigned remMask = -int(mask << 1);
2116 return ((pasBottomMask & remMask) ? (i + 1) : pasENUM_LAST);
2120 assert(!"Shouldnt reach here");
2124 /*****************************************************************************
2126 * Generate the register pointer map, and return its total size in bytes. If
2127 * 'mask' is 0, we don't actually store any data in 'dest' (except for one
2128 * entry, which is never more than 10 bytes), so this can be used to merely
2129 * compute the size of the table.
2133 #pragma warning(push)
2134 #pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
2136 size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
2145 size_t totalSize = 0;
2146 unsigned lastOffset;
2148 bool thisKeptAliveIsInUntracked = false;
2150 /* The mask should be all 0's or all 1's */
2152 assert(mask == 0 || mask == -1);
2154 /* Start computing the total size of the table */
2156 BOOL emitArgTabOffset = (header.varPtrTableSize != 0 || header.untrackedCnt > SET_UNTRACKED_MAX);
2157 if (mask != 0 && emitArgTabOffset)
2159 assert(*pArgTabOffset <= MAX_UNSIGNED_SIZE_T);
2160 unsigned sz = encodeUnsigned(dest, static_cast<unsigned>(*pArgTabOffset));
2165 #if VERIFY_GC_TABLES
2168 *(short*)dest = (short)0xBEEF;
2169 dest += sizeof(short);
2171 totalSize += sizeof(short);
2174 /**************************************************************************
2176 * Untracked ptr variables
2178 **************************************************************************
2182 for (pass = 0; pass < 2; pass++)
2184 /* If pass==0, generate the count
2185 * If pass==1, write the table of untracked pointer variables.
2191 assert(count == header.untrackedCnt);
2192 if (header.untrackedCnt == 0)
2193 break; // No entries, break exits the loop since pass==1
2196 /* Count&Write untracked locals and non-enregistered args */
2198 for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
2200 if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
2202 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
2203 // reported through its parent local
2207 if (varTypeIsGC(varDsc->TypeGet()))
2209 /* Do we have an argument or local variable? */
2210 if (!varDsc->lvIsParam)
2212 // If is is pinned, it must be an untracked local
2213 assert(!varDsc->lvPinned || !varDsc->lvTracked);
2215 if (varDsc->lvTracked || !varDsc->lvOnFrame)
2220 /* Stack-passed arguments which are not enregistered
2221 * are always reported in this "untracked stack
2222 * pointers" section of the GC info even if lvTracked==true
2225 /* Has this argument been enregistered? */
2226 #ifndef LEGACY_BACKEND
2227 if (!varDsc->lvOnFrame)
2228 #else // LEGACY_BACKEND
2229 if (varDsc->lvRegister)
2230 #endif // LEGACY_BACKEND
2232 /* if a CEE_JMP has been used, then we need to report all the arguments
2233 even if they are enregistered, since we will be using this value
2234 in JMP call. Note that this is subtle as we require that
2235 argument offsets are always fixed up properly even if lvRegister
2237 if (!compiler->compJmpOpUsed)
2242 if (!varDsc->lvOnFrame)
2244 /* If this non-enregistered pointer arg is never
2245 * used, we don't need to report it
2247 assert(varDsc->lvRefCnt == 0); // This assert is currently a known issue for X86-RyuJit
2250 else if (varDsc->lvIsRegArg && varDsc->lvTracked)
2252 /* If this register-passed arg is tracked, then
2253 * it has been allocated space near the other
2254 * pointer variables and we have accurate life-
2255 * time info. It will be reported with
2256 * gcVarPtrList in the "tracked-pointer" section
2264 #ifndef WIN64EXCEPTIONS
2265 // For WIN64EXCEPTIONS, "this" must always be in untracked variables
2266 // so we cannot have "this" in variable lifetimes
2267 if (compiler->lvaIsOriginalThisArg(varNum) && compiler->lvaKeepAliveAndReportThis())
2270 // Encoding of untracked variables does not support reporting
2271 // "this". So report it as a tracked variable with a liveness
2272 // extending over the entire method.
2274 thisKeptAliveIsInUntracked = true;
2286 offset = varDsc->lvStkOffs;
2288 // For genDoubleAlign(), locals are addressed relative to ESP and
2289 // arguments are addressed relative to EBP.
2291 if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg)
2292 offset += compiler->codeGen->genTotalFrameSize();
2295 // The lower bits of the offset encode properties of the stk ptr
2297 assert(~OFFSET_MASK % sizeof(offset) == 0);
2299 if (varDsc->TypeGet() == TYP_BYREF)
2301 // Or in byref_OFFSET_FLAG for 'byref' pointer tracking
2302 offset |= byref_OFFSET_FLAG;
2305 if (varDsc->lvPinned)
2307 // Or in pinned_OFFSET_FLAG for 'pinned' pointer tracking
2308 offset |= pinned_OFFSET_FLAG;
2311 int encodedoffset = lastoffset - offset;
2312 lastoffset = offset;
2315 totalSize += encodeSigned(NULL, encodedoffset);
2318 unsigned sz = encodeSigned(dest, encodedoffset);
2325 // A struct will have gcSlots only if it is at least TARGET_POINTER_SIZE.
2326 if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
2328 unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
2329 BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum);
2331 // walk each member of the array
2332 for (unsigned i = 0; i < slots; i++)
2334 if (gcPtrs[i] == TYPE_GC_NONE) // skip non-gc slots
2343 unsigned offset = varDsc->lvStkOffs + i * TARGET_POINTER_SIZE;
2345 // For genDoubleAlign(), locals are addressed relative to ESP and
2346 // arguments are addressed relative to EBP.
2348 if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg)
2349 offset += compiler->codeGen->genTotalFrameSize();
2351 if (gcPtrs[i] == TYPE_GC_BYREF)
2352 offset |= byref_OFFSET_FLAG; // indicate it is a byref GC pointer
2354 int encodedoffset = lastoffset - offset;
2355 lastoffset = offset;
2358 totalSize += encodeSigned(NULL, encodedoffset);
2361 unsigned sz = encodeSigned(dest, encodedoffset);
2370 /* Count&Write spill temps that hold pointers */
2372 assert(compiler->tmpAllFree());
2373 for (TempDsc* tempItem = compiler->tmpListBeg(); tempItem != nullptr; tempItem = compiler->tmpListNxt(tempItem))
2375 if (varTypeIsGC(tempItem->tdTempType()))
2384 offset = tempItem->tdTempOffs();
2386 if (tempItem->tdTempType() == TYP_BYREF)
2388 offset |= byref_OFFSET_FLAG;
2391 int encodedoffset = lastoffset - offset;
2392 lastoffset = offset;
2396 totalSize += encodeSigned(NULL, encodedoffset);
2400 unsigned sz = encodeSigned(dest, encodedoffset);
2409 #if VERIFY_GC_TABLES
2412 *(short*)dest = (short)0xCAFE;
2413 dest += sizeof(short);
2415 totalSize += sizeof(short);
2418 /**************************************************************************
2420 * Generate the table of stack pointer variable lifetimes.
2422 * In the first pass we'll count the lifetime entries and note
2423 * whether there are any that don't fit in a small encoding. In
2424 * the second pass we actually generate the table contents.
2426 **************************************************************************
2429 // First we check for the most common case - no lifetimes at all.
2431 if (header.varPtrTableSize == 0)
2437 #ifndef WIN64EXCEPTIONS
2438 if (thisKeptAliveIsInUntracked)
2442 // Encoding of untracked variables does not support reporting
2443 // "this". So report it as a tracked variable with a liveness
2444 // extending over the entire method.
2446 assert(compiler->lvaTable[compiler->info.compThisArg].TypeGet() == TYP_REF);
2448 unsigned varOffs = compiler->lvaTable[compiler->info.compThisArg].lvStkOffs;
2450 /* For negative stack offsets we must reset the low bits,
2451 * take abs and then set them back */
2453 varOffs = abs(static_cast<int>(varOffs));
2454 varOffs |= this_OFFSET_FLAG;
2457 sz = encodeUnsigned(mask ? (dest + sz) : NULL, varOffs);
2458 sz += encodeUDelta(mask ? (dest + sz) : NULL, 0, 0);
2459 sz += encodeUDelta(mask ? (dest + sz) : NULL, codeSize, 0);
2461 dest += (sz & mask);
2466 for (pass = 0; pass < 2; pass++)
2468 /* If second pass, generate the count */
2472 assert(header.varPtrTableSize > 0);
2473 assert(header.varPtrTableSize == count);
2476 /* We'll use a delta encoding for the lifetime offsets */
2480 for (varTmp = gcVarPtrList; varTmp; varTmp = varTmp->vpdNext)
2488 assert(~OFFSET_MASK % TARGET_POINTER_SIZE == 0);
2490 /* Get hold of the variable's stack offset */
2492 lowBits = varTmp->vpdVarNum & OFFSET_MASK;
2494 /* For negative stack offsets we must reset the low bits,
2495 * take abs and then set them back */
2497 varOffs = abs(static_cast<int>(varTmp->vpdVarNum & ~OFFSET_MASK));
2500 /* Compute the actual lifetime offsets */
2502 begOffs = varTmp->vpdBegOfs;
2503 endOffs = varTmp->vpdEndOfs;
2505 /* Special case: skip any 0-length lifetimes */
2507 if (endOffs == begOffs)
2510 /* Are we counting or generating? */
2519 sz = encodeUnsigned(mask ? (dest + sz) : NULL, varOffs);
2520 sz += encodeUDelta(mask ? (dest + sz) : NULL, begOffs, lastOffset);
2521 sz += encodeUDelta(mask ? (dest + sz) : NULL, endOffs, begOffs);
2523 dest += (sz & mask);
2527 /* The next entry will be relative to the one we just processed */
2529 lastOffset = begOffs;
2535 if (pArgTabOffset != NULL)
2536 *pArgTabOffset = totalSize;
2538 #if VERIFY_GC_TABLES
2541 *(short*)dest = (short)0xBABE;
2542 dest += sizeof(short);
2544 totalSize += sizeof(short);
2547 if (!mask && emitArgTabOffset)
2549 assert(*pArgTabOffset <= MAX_UNSIGNED_SIZE_T);
2550 totalSize += encodeUnsigned(NULL, static_cast<unsigned>(*pArgTabOffset));
2553 /**************************************************************************
2555 * Prepare to generate the pointer register/argument map
2557 **************************************************************************
2562 if (compiler->codeGen->genInterruptible)
2565 assert(compiler->genFullPtrRegMap);
2567 unsigned ptrRegs = 0;
2569 regPtrDsc* genRegPtrTemp;
2571 /* Walk the list of pointer register/argument entries */
2573 for (genRegPtrTemp = gcRegPtrList; genRegPtrTemp; genRegPtrTemp = genRegPtrTemp->rpdNext)
2577 unsigned nextOffset;
2580 nextOffset = genRegPtrTemp->rpdOffs;
2583 Encoding table for methods that are fully interruptible
2585 The encoding used is as follows:
2587 ptr reg dead 00RRRDDD [RRR != 100]
2588 ptr reg live 01RRRDDD [RRR != 100]
2590 non-ptr arg push 10110DDD [SSS == 110]
2591 ptr arg push 10SSSDDD [SSS != 110] && [SSS != 111]
2592 ptr arg pop 11CCCDDD [CCC != 000] && [CCC != 110] && [CCC != 111]
2593 little skip 11000DDD [CCC == 000]
2594 bigger skip 11110BBB [CCC == 110]
2596 The values used in the above encodings are as follows:
2598 DDD code offset delta from previous entry (0-7)
2599 BBB bigger delta 000=8,001=16,010=24,...,111=64
2600 RRR register number (EAX=000,ECX=001,EDX=010,EBX=011,
2601 EBP=101,ESI=110,EDI=111), ESP=100 is reserved
2602 SSS argument offset from base of stack. This is
2603 redundant for frameless methods as we can
2604 infer it from the previous pushes+pops. However,
2605 for EBP-methods, we only report GC pushes, and
2607 CCC argument count being popped (includes only ptrs for EBP methods)
2609 The following are the 'large' versions:
2611 large delta skip 10111000 [0xB8] , encodeUnsigned(delta)
2613 large ptr arg push 11111000 [0xF8] , encodeUnsigned(pushCount)
2614 large non-ptr arg push 11111001 [0xF9] , encodeUnsigned(pushCount)
2615 large ptr arg pop 11111100 [0xFC] , encodeUnsigned(popCount)
2616 large arg dead 11111101 [0xFD] , encodeUnsigned(popCount) for caller-pop args.
2617 Any GC args go dead after the call,
2618 but are still sitting on the stack
2620 this pointer prefix 10111100 [0xBC] the next encoding is a ptr live
2622 and contains the this pointer
2624 interior or by-ref 10111111 [0xBF] the next encoding is a ptr live
2625 pointer prefix or a ptr arg push
2626 and contains an interior
2630 The value 11111111 [0xFF] indicates the end of the table.
2633 codeDelta = nextOffset - lastOffset;
2634 assert((int)codeDelta >= 0);
2636 // If the code delta is between 8 and (64+7),
2637 // generate a 'bigger delta' encoding
2639 if ((codeDelta >= 8) && (codeDelta <= (64 + 7)))
2641 unsigned biggerDelta = ((codeDelta - 8) & 0x38) + 8;
2642 *dest++ = 0xF0 | ((biggerDelta - 8) >> 3);
2643 lastOffset += biggerDelta;
2647 // If the code delta is still bigger than 7,
2648 // generate a 'large code delta' encoding
2653 dest += encodeUnsigned(dest, codeDelta);
2656 /* Remember the new 'last' offset */
2658 lastOffset = nextOffset;
2661 /* Is this a pointer argument or register entry? */
2663 if (genRegPtrTemp->rpdArg)
2665 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL)
2670 Use the small encoding:
2671 little delta skip 11000DDD [0xC0]
2674 assert((codeDelta & 0x7) == codeDelta);
2675 *dest++ = 0xC0 | (BYTE)codeDelta;
2677 /* Remember the new 'last' offset */
2679 lastOffset = nextOffset;
2682 /* Caller-pop arguments are dead after call but are still
2683 sitting on the stack */
2686 assert(genRegPtrTemp->rpdPtrArg != 0);
2687 dest += encodeUnsigned(dest, genRegPtrTemp->rpdPtrArg);
2689 else if (genRegPtrTemp->rpdPtrArg < 6 && genRegPtrTemp->rpdGCtypeGet())
2691 /* Is the argument offset/count smaller than 6 ? */
2693 dest = gceByrefPrefixI(genRegPtrTemp, dest);
2695 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH || (genRegPtrTemp->rpdPtrArg != 0))
2698 Use the small encoding:
2700 ptr arg push 10SSSDDD [SSS != 110] && [SSS != 111]
2701 ptr arg pop 11CCCDDD [CCC != 110] && [CCC != 111]
2704 bool isPop = genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP;
2706 *dest++ = 0x80 | (BYTE)codeDelta | genRegPtrTemp->rpdPtrArg << 3 | isPop << 6;
2708 /* Remember the new 'last' offset */
2710 lastOffset = nextOffset;
2714 assert(!"Check this");
2717 else if (genRegPtrTemp->rpdGCtypeGet() == GCT_NONE)
2720 Use the small encoding:
2721 ` non-ptr arg push 10110DDD [0xB0] (push of sizeof(int))
2724 assert((codeDelta & 0x7) == codeDelta);
2725 *dest++ = 0xB0 | (BYTE)codeDelta;
2726 #ifndef UNIX_X86_ABI
2727 assert(!compiler->isFramePointerUsed());
2730 /* Remember the new 'last' offset */
2732 lastOffset = nextOffset;
2736 /* Will have to use large encoding;
2737 * first do the code delta
2743 Use the small encoding:
2744 little delta skip 11000DDD [0xC0]
2747 assert((codeDelta & 0x7) == codeDelta);
2748 *dest++ = 0xC0 | (BYTE)codeDelta;
2752 Now append a large argument record:
2754 large ptr arg push 11111000 [0xF8]
2755 large ptr arg pop 11111100 [0xFC]
2758 bool isPop = genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP;
2760 dest = gceByrefPrefixI(genRegPtrTemp, dest);
2762 *dest++ = 0xF8 | (isPop << 2);
2763 dest += encodeUnsigned(dest, genRegPtrTemp->rpdPtrArg);
2765 /* Remember the new 'last' offset */
2767 lastOffset = nextOffset;
2774 /* Record any registers that are becoming dead */
2776 regMask = genRegPtrTemp->rpdCompiler.rpdDel & ptrRegs;
2778 while (regMask) // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2783 /* Get hold of the next register bit */
2785 tmpMask = genFindLowestReg(regMask);
2788 /* Remember the new state of this register */
2790 ptrRegs &= ~tmpMask;
2792 /* Figure out which register the next bit corresponds to */
2794 regNum = genRegNumFromMask(tmpMask);
2795 assert(regNum <= 7);
2797 /* Reserve ESP, regNum==4 for future use */
2799 assert(regNum != 4);
2802 Generate a small encoding:
2804 ptr reg dead 00RRRDDD
2807 assert((codeDelta & 0x7) == codeDelta);
2808 *dest++ = 0x00 | regNum << 3 | (BYTE)codeDelta;
2810 /* Turn the bit we've just generated off and continue */
2812 regMask -= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2814 /* Remember the new 'last' offset */
2816 lastOffset = nextOffset;
2818 /* Any entries that follow will be at the same offset */
2820 codeDelta = zeroFunc(); /* DO NOT REMOVE */
2823 /* Record any registers that are becoming live */
2825 regMask = genRegPtrTemp->rpdCompiler.rpdAdd & ~ptrRegs;
2827 while (regMask) // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2832 /* Get hold of the next register bit */
2834 tmpMask = genFindLowestReg(regMask);
2837 /* Remember the new state of this register */
2841 /* Figure out which register the next bit corresponds to */
2843 regNum = genRegNumFromMask(tmpMask);
2844 assert(regNum <= 7);
2847 Generate a small encoding:
2849 ptr reg live 01RRRDDD
2852 dest = gceByrefPrefixI(genRegPtrTemp, dest);
2854 if (!thisKeptAliveIsInUntracked && genRegPtrTemp->rpdIsThis)
2856 // Mark with 'this' pointer prefix
2858 // Can only have one bit set in regMask
2859 assert(regMask == tmpMask);
2862 assert((codeDelta & 0x7) == codeDelta);
2863 *dest++ = 0x40 | (regNum << 3) | (BYTE)codeDelta;
2865 /* Turn the bit we've just generated off and continue */
2867 regMask -= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2869 /* Remember the new 'last' offset */
2871 lastOffset = nextOffset;
2873 /* Any entries that follow will be at the same offset */
2875 codeDelta = zeroFunc(); /* DO NOT REMOVE */
2879 /* Keep track of the total amount of generated stuff */
2881 totalSize += dest - base;
2883 /* Go back to the buffer start if we're not generating a table */
2888 #endif // _TARGET_X86_
2890 /* Terminate the table with 0xFF */
2896 else if (compiler->isFramePointerUsed()) // genInterruptible is false
2900 Encoding table for methods with an EBP frame and
2901 that are not fully interruptible
2903 The encoding used is as follows:
2905 this pointer encodings:
2907 01000000 this pointer in EBX
2908 00100000 this pointer in ESI
2909 00010000 this pointer in EDI
2914 requires code delta > 0 & delta < 16 (4-bits)
2915 requires pushed argmask == 0
2917 where DDDD is code delta
2918 b indicates that register EBX is a live pointer
2919 s indicates that register ESI is a live pointer
2920 d indicates that register EDI is a live pointer
2927 requires code delta < 120 (7-bits)
2928 requires pushed argmask < 64 (5-bits)
2930 where DDDDDDD is code delta
2931 AAAAA is the pushed args mask
2932 b indicates that register EBX is a live pointer
2933 s indicates that register ESI is a live pointer
2934 d indicates that register EDI is a live pointer
2938 0xFD aaaaaaaa AAAAdddd bseDDDDD
2940 requires code delta < 512 (9-bits)
2941 requires pushed argmask < 2048 (12-bits)
2943 where DDDDD is the upper 5-bits of the code delta
2944 dddd is the low 4-bits of the code delta
2945 AAAA is the upper 4-bits of the pushed arg mask
2946 aaaaaaaa is the low 8-bits of the pushed arg mask
2947 b indicates that register EBX is a live pointer
2948 s indicates that register ESI is a live pointer
2949 e indicates that register EDI is a live pointer
2951 medium encoding with interior pointers
2953 0xF9 DDDDDDDD bsdAAAAAA iiiIIIII
2955 requires code delta < 256 (8-bits)
2956 requires pushed argmask < 64 (5-bits)
2958 where DDDDDDD is the code delta
2959 b indicates that register EBX is a live pointer
2960 s indicates that register ESI is a live pointer
2961 d indicates that register EDI is a live pointer
2962 AAAAA is the pushed arg mask
2963 iii indicates that EBX,EDI,ESI are interior pointers
2964 IIIII indicates that bits in the arg mask are interior
2969 0xFE [0BSD0bsd][32-bit code delta][32-bit argMask]
2971 b indicates that register EBX is a live pointer
2972 s indicates that register ESI is a live pointer
2973 d indicates that register EDI is a live pointer
2974 B indicates that register EBX is an interior pointer
2975 S indicates that register ESI is an interior pointer
2976 D indicates that register EDI is an interior pointer
2977 requires pushed argmask < 32-bits
2979 large encoding with interior pointers
2981 0xFA [0BSD0bsd][32-bit code delta][32-bit argMask][32-bit interior pointer mask]
2984 b indicates that register EBX is a live pointer
2985 s indicates that register ESI is a live pointer
2986 d indicates that register EDI is a live pointer
2987 B indicates that register EBX is an interior pointer
2988 S indicates that register ESI is an interior pointer
2989 D indicates that register EDI is an interior pointer
2990 requires pushed argmask < 32-bits
2991 requires pushed iArgmask < 32-bits
2994 huge encoding This is the only encoding that supports
2995 a pushed argmask which is greater than
2998 0xFB [0BSD0bsd][32-bit code delta]
2999 [32-bit table count][32-bit table size]
3000 [pushed ptr offsets table...]
3002 b indicates that register EBX is a live pointer
3003 s indicates that register ESI is a live pointer
3004 d indicates that register EDI is a live pointer
3005 B indicates that register EBX is an interior pointer
3006 S indicates that register ESI is an interior pointer
3007 D indicates that register EDI is an interior pointer
3008 the list count is the number of entries in the list
3009 the list size gives the byte-length of the list
3010 the offsets in the list are variable-length
3013 /* If "this" is enregistered, note it. We do this explicitly here as
3014 genFullPtrRegMap==false, and so we don't have any regPtrDsc's. */
3016 if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvRegister)
3018 unsigned thisRegMask = genRegMask(compiler->lvaTable[compiler->info.compThisArg].lvRegNum);
3019 unsigned thisPtrRegEnc = gceEncodeCalleeSavedRegs(thisRegMask) << 4;
3025 *dest++ = thisPtrRegEnc;
3031 assert(compiler->genFullPtrRegMap == false);
3033 /* Walk the list of pointer register/argument entries */
3035 for (call = gcCallDescList; call; call = call->cdNext)
3038 unsigned nextOffset;
3040 /* Figure out the code offset of this entry */
3042 nextOffset = call->cdOffs;
3044 /* Compute the distance from the previous call */
3046 DWORD codeDelta = nextOffset - lastOffset;
3048 assert((int)codeDelta >= 0);
3050 /* Remember the new 'last' offset */
3052 lastOffset = nextOffset;
3054 /* Compute the register mask */
3056 unsigned gcrefRegMask = 0;
3057 unsigned byrefRegMask = 0;
3059 gcrefRegMask |= gceEncodeCalleeSavedRegs(call->cdGCrefRegs);
3060 byrefRegMask |= gceEncodeCalleeSavedRegs(call->cdByrefRegs);
3062 assert((gcrefRegMask & byrefRegMask) == 0);
3064 unsigned regMask = gcrefRegMask | byrefRegMask;
3066 bool byref = (byrefRegMask | call->u1.cdByrefArgMask) != 0;
3068 /* Check for the really large argument offset case */
3069 /* The very rare Huge encodings */
3074 DWORD argCnt = call->cdArgCnt;
3076 BYTE* pArgBytes = DUMMY_INIT(NULL);
3081 *dest++ = (byrefRegMask << 4) | regMask;
3082 *(DWORD*)dest = codeDelta;
3083 dest += sizeof(DWORD);
3084 *(DWORD*)dest = argCnt;
3085 dest += sizeof(DWORD);
3086 // skip the byte-size for now. Just note where it will go
3088 dest += sizeof(DWORD);
3091 for (argNum = 0; argNum < argCnt; argNum++)
3094 eltSize = encodeUnsigned(dest, call->cdArgTable[argNum]);
3095 argBytes += eltSize;
3102 dest = base + 1 + 1 + 3 * sizeof(DWORD) + argBytes;
3106 assert(dest == pArgBytes + sizeof(argBytes) + argBytes);
3107 *(DWORD*)pArgBytes = argBytes;
3111 /* Check if we can use a tiny encoding */
3112 else if ((codeDelta < 16) && (codeDelta != 0) && (call->u1.cdArgMask == 0) && !byref)
3114 *dest++ = (regMask << 4) | (BYTE)codeDelta;
3117 /* Check if we can use the small encoding */
3118 else if ((codeDelta < 0x79) && (call->u1.cdArgMask <= 0x1F) && !byref)
3120 *dest++ = 0x80 | (BYTE)codeDelta;
3121 *dest++ = call->u1.cdArgMask | (regMask << 5);
3124 /* Check if we can use the medium encoding */
3125 else if (codeDelta <= 0x01FF && call->u1.cdArgMask <= 0x0FFF && !byref)
3128 *dest++ = call->u1.cdArgMask;
3129 *dest++ = ((call->u1.cdArgMask >> 4) & 0xF0) | ((BYTE)codeDelta & 0x0F);
3130 *dest++ = (regMask << 5) | (BYTE)((codeDelta >> 4) & 0x1F);
3133 /* Check if we can use the medium encoding with byrefs */
3134 else if (codeDelta <= 0x0FF && call->u1.cdArgMask <= 0x01F)
3137 *dest++ = (BYTE)codeDelta;
3138 *dest++ = (regMask << 5) | call->u1.cdArgMask;
3139 *dest++ = (byrefRegMask << 5) | call->u1.cdByrefArgMask;
3142 /* We'll use the large encoding */
3146 *dest++ = (byrefRegMask << 4) | regMask;
3147 *(DWORD*)dest = codeDelta;
3148 dest += sizeof(DWORD);
3149 *(DWORD*)dest = call->u1.cdArgMask;
3150 dest += sizeof(DWORD);
3153 /* We'll use the large encoding with byrefs */
3157 *dest++ = (byrefRegMask << 4) | regMask;
3158 *(DWORD*)dest = codeDelta;
3159 dest += sizeof(DWORD);
3160 *(DWORD*)dest = call->u1.cdArgMask;
3161 dest += sizeof(DWORD);
3162 *(DWORD*)dest = call->u1.cdByrefArgMask;
3163 dest += sizeof(DWORD);
3166 /* Keep track of the total amount of generated stuff */
3168 totalSize += dest - base;
3170 /* Go back to the buffer start if we're not generating a table */
3175 #endif // _TARGET_X86_
3177 /* Terminate the table with 0xFF */
3183 else // genInterruptible is false and we have an EBP-less frame
3185 assert(compiler->genFullPtrRegMap);
3189 regPtrDsc* genRegPtrTemp;
3190 regNumber thisRegNum = regNumber(0);
3191 PendingArgsStack pasStk(compiler->getEmitter()->emitMaxStackDepth, compiler);
3193 /* Walk the list of pointer register/argument entries */
3195 for (genRegPtrTemp = gcRegPtrList; genRegPtrTemp; genRegPtrTemp = genRegPtrTemp->rpdNext)
3199 * Encoding table for methods without an EBP frame and
3200 * that are not fully interruptible
3202 * The encoding used is as follows:
3204 * push 000DDDDD ESP push one item with 5-bit delta
3205 * push 00100000 [pushCount] ESP push multiple items
3206 * reserved 0010xxxx xxxx != 0000
3208 * skip 01000000 [Delta] Skip Delta, arbitrary sized delta
3209 * skip 0100DDDD Skip small Delta, for call (DDDD != 0)
3210 * pop 01CCDDDD ESP pop CC items with 4-bit delta (CC != 00)
3211 * call 1PPPPPPP Call Pattern, P=[0..79]
3212 * call 1101pbsd DDCCCMMM Call RegMask=pbsd,ArgCnt=CCC,
3213 * ArgMask=MMM Delta=commonDelta[DD]
3214 * call 1110pbsd [ArgCnt] [ArgMask] Call ArgCnt,RegMask=pbsd,ArgMask
3215 * call 11111000 [PBSDpbsd][32-bit delta][32-bit ArgCnt]
3216 * [32-bit PndCnt][32-bit PndSize][PndOffs...]
3217 * iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask
3218 * thisptr 111101RR This pointer is in Register RR
3219 * 00=EDI,01=ESI,10=EBX,11=EBP
3220 * reserved 111100xx xx != 00
3221 * reserved 111110xx xx != 00
3222 * reserved 11111xxx xxx != 000 && xxx != 111(EOT)
3224 * The value 11111111 [0xFF] indicates the end of the table. (EOT)
3226 * An offset (at which stack-walking is performed) without an explicit encoding
3227 * is assumed to be a trivial call-site (no GC registers, stack empty before and
3228 * after) to avoid having to encode all trivial calls.
3230 * Note on the encoding used for interior pointers
3232 * The iptr encoding must immediately precede a call encoding. It is used
3233 * to transform a normal GC pointer addresses into an interior pointers for
3234 * GC purposes. The mask supplied to the iptr encoding is read from the
3235 * least signicant bit to the most signicant bit. (i.e the lowest bit is
3238 * p indicates that register EBP is a live pointer
3239 * b indicates that register EBX is a live pointer
3240 * s indicates that register ESI is a live pointer
3241 * d indicates that register EDI is a live pointer
3242 * P indicates that register EBP is an interior pointer
3243 * B indicates that register EBX is an interior pointer
3244 * S indicates that register ESI is an interior pointer
3245 * D indicates that register EDI is an interior pointer
3247 * As an example the following sequence indicates that EDI.ESI and the
3248 * second pushed pointer in ArgMask are really interior pointers. The
3249 * pointer in ESI in a normal pointer:
3251 * iptr 11110000 00010011 => read Interior Ptr, Interior Ptr,
3252 * Normal Ptr, Normal Ptr, Interior Ptr
3254 * call 11010011 DDCCC011 RRRR=1011 => read EDI is a GC-pointer,
3255 * ESI is a GC-pointer.
3256 * EBP is a GC-pointer
3257 * MMM=0011 => read two GC-pointers arguments
3258 * on the stack (nested call)
3260 * Since the call instruction mentions 5 GC-pointers we list them in
3261 * the required order: EDI, ESI, EBP, 1st-pushed pointer, 2nd-pushed pointer
3263 * And we apply the Interior Pointer mask mmmm=10011 to the five GC-pointers
3264 * we learn that EDI and ESI are interior GC-pointers and that
3265 * the second push arg is an interior GC-pointer.
3270 bool usePopEncoding;
3273 unsigned byrefRegMask;
3274 unsigned byrefArgMask;
3277 unsigned nextOffset;
3280 nextOffset = genRegPtrTemp->rpdOffs;
3282 /* Compute the distance from the previous call */
3284 codeDelta = nextOffset - lastOffset;
3285 assert((int)codeDelta >= 0);
3288 // Must initialize this flag to true when REGEN_CALLPAT is on
3289 usePopEncoding = true;
3290 unsigned origCodeDelta = codeDelta;
3293 if (!thisKeptAliveIsInUntracked && genRegPtrTemp->rpdIsThis)
3295 unsigned tmpMask = genRegPtrTemp->rpdCompiler.rpdAdd;
3297 /* tmpMask must have exactly one bit set */
3299 assert(tmpMask && ((tmpMask & (tmpMask - 1)) == 0));
3301 thisRegNum = genRegNumFromMask(tmpMask);
3310 *dest++ = 0xF4; /* 11110100 This pointer is in EDI */
3313 *dest++ = 0xF5; /* 11110100 This pointer is in ESI */
3316 *dest++ = 0xF6; /* 11110100 This pointer is in EBX */
3319 *dest++ = 0xF7; /* 11110100 This pointer is in EBP */
3326 /* Is this a stack pointer change or call? */
3328 if (genRegPtrTemp->rpdArg)
3330 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL)
3332 // kill 'rpdPtrArg' number of pointer variables in pasStk
3333 pasStk.pasKill(genRegPtrTemp->rpdPtrArg);
3335 /* Is this a call site? */
3336 else if (genRegPtrTemp->rpdCall)
3338 /* This is a true call site */
3340 /* Remember the new 'last' offset */
3342 lastOffset = nextOffset;
3344 callArgCnt = genRegPtrTemp->rpdPtrArg;
3346 unsigned gcrefRegMask = genRegPtrTemp->rpdCallGCrefRegs;
3348 byrefRegMask = genRegPtrTemp->rpdCallByrefRegs;
3350 assert((gcrefRegMask & byrefRegMask) == 0);
3352 regMask = gcrefRegMask | byrefRegMask;
3354 /* adjust argMask for this call-site */
3355 pasStk.pasPop(callArgCnt);
3357 /* Do we have to use the fat encoding */
3359 if (pasStk.pasCurDepth() > BITS_IN_pasMask && pasStk.pasHasGCptrs())
3361 /* use fat encoding:
3362 * 11111000 [PBSDpbsd][32-bit delta][32-bit ArgCnt]
3363 * [32-bit PndCnt][32-bit PndSize][PndOffs...]
3366 DWORD pndCount = pasStk.pasEnumGCoffsCount();
3368 BYTE* pPndSize = DUMMY_INIT(NULL);
3373 *dest++ = (byrefRegMask << 4) | regMask;
3374 *(DWORD*)dest = codeDelta;
3375 dest += sizeof(DWORD);
3376 *(DWORD*)dest = callArgCnt;
3377 dest += sizeof(DWORD);
3378 *(DWORD*)dest = pndCount;
3379 dest += sizeof(DWORD);
3381 dest += sizeof(DWORD); // Leave space for pndSize
3384 unsigned offs, iter;
3386 for (iter = pasStk.pasEnumGCoffs(pasENUM_START, &offs); pndCount;
3387 iter = pasStk.pasEnumGCoffs(iter, &offs), pndCount--)
3389 unsigned eltSize = encodeUnsigned(dest, offs);
3395 assert(iter == pasENUM_END);
3399 dest = base + 2 + 4 * sizeof(DWORD) + pndSize;
3403 assert(pPndSize + sizeof(pndSize) + pndSize == dest);
3404 *(DWORD*)pPndSize = pndSize;
3410 argMask = byrefArgMask = 0;
3412 if (pasStk.pasHasGCptrs())
3414 assert(pasStk.pasCurDepth() <= BITS_IN_pasMask);
3416 argMask = pasStk.pasArgMask();
3417 byrefArgMask = pasStk.pasByrefArgMask();
3420 /* Shouldn't be reporting trivial call-sites */
3422 assert(regMask || argMask || callArgCnt || pasStk.pasCurDepth());
3424 // Emit IPtrMask if needed
3426 #define CHK_NON_INTRPT_ESP_IPtrMask \
3428 if (byrefRegMask || byrefArgMask) \
3431 unsigned imask = (byrefArgMask << 4) | byrefRegMask; \
3432 dest += encodeUnsigned(dest, imask); \
3435 /* When usePopEncoding is true:
3436 * this is not an interesting call site
3437 * because nothing is live here.
3439 usePopEncoding = ((callArgCnt < 4) && (regMask == 0) && (argMask == 0));
3441 if (!usePopEncoding)
3443 int pattern = lookupCallPattern(callArgCnt, regMask, argMask, codeDelta);
3448 codeDelta = pattern >> 8;
3450 if (codeDelta >= 16)
3453 /* skip 01000000 [Delta] */
3455 dest += encodeUnsigned(dest, codeDelta);
3461 /* skip 0100DDDD small delta=DDDD */
3462 *dest++ = 0x40 | (BYTE)codeDelta;
3466 // Emit IPtrMask if needed
3467 CHK_NON_INTRPT_ESP_IPtrMask;
3469 assert((pattern >= 0) && (pattern < 80));
3470 *dest++ = 0x80 | pattern;
3474 /* See if we can use 2nd call encoding
3475 * 1101RRRR DDCCCMMM encoding */
3477 if ((callArgCnt <= 7) && (argMask <= 7))
3479 unsigned inx; // callCommonDelta[] index
3480 unsigned maxCommonDelta = callCommonDelta[3];
3482 if (codeDelta > maxCommonDelta)
3484 if (codeDelta > maxCommonDelta + 15)
3487 /* skip 01000000 [Delta] */
3489 dest += encodeUnsigned(dest, codeDelta - maxCommonDelta);
3494 /* skip 0100DDDD small delta=DDDD */
3495 *dest++ = 0x40 | (BYTE)(codeDelta - maxCommonDelta);
3498 codeDelta = maxCommonDelta;
3500 goto EMIT_2ND_CALL_ENCODING;
3503 for (inx = 0; inx < 4; inx++)
3505 if (codeDelta == callCommonDelta[inx])
3507 EMIT_2ND_CALL_ENCODING:
3508 // Emit IPtrMask if needed
3509 CHK_NON_INTRPT_ESP_IPtrMask;
3511 *dest++ = 0xD0 | regMask;
3512 *dest++ = (inx << 6) | (callArgCnt << 3) | argMask;
3517 unsigned minCommonDelta = callCommonDelta[0];
3519 if ((codeDelta > minCommonDelta) && (codeDelta < maxCommonDelta))
3521 assert((minCommonDelta + 16) > maxCommonDelta);
3523 /* skip 0100DDDD small delta=DDDD */
3524 *dest++ = 0x40 | (BYTE)(codeDelta - minCommonDelta);
3526 codeDelta = minCommonDelta;
3528 goto EMIT_2ND_CALL_ENCODING;
3533 if (codeDelta >= 16)
3535 unsigned i = (usePopEncoding ? 15 : 0);
3537 /* skip 01000000 [Delta] arbitrary sized delta */
3539 dest += encodeUnsigned(dest, codeDelta - i);
3543 if ((codeDelta > 0) || usePopEncoding)
3548 /* pop 01CCDDDD ESP pop CC items, 4-bit delta */
3549 if (callArgCnt || codeDelta)
3550 *dest++ = (BYTE)(0x40 | (callArgCnt << 4) | codeDelta);
3556 /* skip 0100DDDD small delta=DDDD */
3557 *dest++ = 0x40 | (BYTE)codeDelta;
3561 // Emit IPtrMask if needed
3562 CHK_NON_INTRPT_ESP_IPtrMask;
3565 /* call 1110RRRR [ArgCnt] [ArgMask] */
3567 *dest++ = 0xE0 | regMask;
3568 dest += encodeUnsigned(dest, callArgCnt);
3570 dest += encodeUnsigned(dest, argMask);
3574 /* This is a push or a pop site */
3576 /* Remember the new 'last' offset */
3578 lastOffset = nextOffset;
3580 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP)
3582 /* This must be a gcArgPopSingle */
3584 assert(genRegPtrTemp->rpdPtrArg == 1);
3586 if (codeDelta >= 16)
3589 /* skip 01000000 [Delta] */
3591 dest += encodeUnsigned(dest, codeDelta - 15);
3596 /* pop1 0101DDDD ESP pop one item, 4-bit delta */
3598 *dest++ = 0x50 | (BYTE)codeDelta;
3600 /* adjust argMask for this pop */
3605 /* This is a push */
3607 if (codeDelta >= 32)
3610 /* skip 01000000 [Delta] */
3612 dest += encodeUnsigned(dest, codeDelta - 31);
3616 assert(codeDelta < 32);
3619 /* push 000DDDDD ESP push one item, 5-bit delta */
3621 *dest++ = (BYTE)codeDelta;
3623 /* adjust argMask for this push */
3624 pasStk.pasPush(genRegPtrTemp->rpdGCtypeGet());
3629 /* We ignore the register live/dead information, since the
3630 * rpdCallRegMask contains all the liveness information
3635 totalSize += dest - base;
3637 /* Go back to the buffer start if we're not generating a table */
3643 if ((mask == -1) && (usePopEncoding == false) && ((dest - base) > 0))
3644 regenLog(origCodeDelta, argMask, regMask, callArgCnt, byrefArgMask, byrefRegMask, base, (dest - base));
3648 /* Verify that we pop every arg that was pushed and that argMask is 0 */
3650 assert(pasStk.pasCurDepth() == 0);
3652 #endif // _TARGET_X86_
3654 /* Terminate the table with 0xFF */
3661 #if VERIFY_GC_TABLES
3664 *(short*)dest = (short)0xBEEB;
3665 dest += sizeof(short);
3667 totalSize += sizeof(short);
3670 #if MEASURE_PTRTAB_SIZE
3673 s_gcTotalPtrTabSize += totalSize;
3680 #pragma warning(pop)
3683 /*****************************************************************************/
3685 /*****************************************************************************
3687 * Dump the contents of a GC pointer table.
3692 #if VERIFY_GC_TABLES
3693 const bool verifyGCTables = true;
3695 const bool verifyGCTables = false;
3698 /*****************************************************************************
3700 * Dump the info block header.
3703 unsigned GCInfo::gcInfoBlockHdrDump(const BYTE* table, InfoHdr* header, unsigned* methodSize)
3705 GCDump gcDump(GCINFO_VERSION);
3707 gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
3708 printf("Method info block:\n");
3710 return gcDump.DumpInfoHdr(table, header, methodSize, verifyGCTables);
3713 /*****************************************************************************/
3715 unsigned GCInfo::gcDumpPtrTable(const BYTE* table, const InfoHdr& header, unsigned methodSize)
3717 printf("Pointer table:\n");
3719 GCDump gcDump(GCINFO_VERSION);
3720 gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
3722 return gcDump.DumpGCTable(table, header, methodSize, verifyGCTables);
3725 /*****************************************************************************
3727 * Find all the live pointers in a stack frame.
3730 void GCInfo::gcFindPtrsInFrame(const void* infoBlock, const void* codeBlock, unsigned offs)
3732 GCDump gcDump(GCINFO_VERSION);
3733 gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
3735 gcDump.DumpPtrsInFrame((PTR_CBYTE)infoBlock, (const BYTE*)codeBlock, offs, verifyGCTables);
3738 #endif // DUMP_GC_TABLES
3740 #else // !JIT32_GCENCODER
3742 #include "gcinfoencoder.h"
3744 // Do explicit instantiation.
3745 template class JitHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId>;
3746 template class JitHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId>;
3750 static const char* const GcSlotFlagsNames[] = {"",
3755 "(byref, untracked) ",
3756 "(pinned, untracked) ",
3757 "(byref, pinned, untracked) "};
3759 // I'm making a local wrapper class for GcInfoEncoder so that can add logging of my own (DLD).
3760 class GcInfoEncoderWithLogging
3762 GcInfoEncoder* m_gcInfoEncoder;
3766 GcInfoEncoderWithLogging(GcInfoEncoder* gcInfoEncoder, bool verbose)
3767 : m_gcInfoEncoder(gcInfoEncoder), m_doLogging(verbose || JitConfig.JitGCInfoLogging() != 0)
3771 GcSlotId GetStackSlotId(INT32 spOffset, GcSlotFlags flags, GcStackSlotBase spBase = GC_CALLER_SP_REL)
3773 GcSlotId newSlotId = m_gcInfoEncoder->GetStackSlotId(spOffset, flags, spBase);
3776 printf("Stack slot id for offset %d (0x%x) (%s) %s= %d.\n", spOffset, spOffset,
3777 GcStackSlotBaseNames[spBase], GcSlotFlagsNames[flags & 7], newSlotId);
3782 GcSlotId GetRegisterSlotId(UINT32 regNum, GcSlotFlags flags)
3784 GcSlotId newSlotId = m_gcInfoEncoder->GetRegisterSlotId(regNum, flags);
3787 printf("Register slot id for reg %s %s= %d.\n", getRegName(regNum), GcSlotFlagsNames[flags & 7], newSlotId);
3792 void SetSlotState(UINT32 instructionOffset, GcSlotId slotId, GcSlotState slotState)
3794 m_gcInfoEncoder->SetSlotState(instructionOffset, slotId, slotState);
3797 printf("Set state of slot %d at instr offset 0x%x to %s.\n", slotId, instructionOffset,
3798 (slotState == GC_SLOT_LIVE ? "Live" : "Dead"));
3802 void DefineCallSites(UINT32* pCallSites, BYTE* pCallSiteSizes, UINT32 numCallSites)
3804 m_gcInfoEncoder->DefineCallSites(pCallSites, pCallSiteSizes, numCallSites);
3807 printf("Defining %d call sites:\n", numCallSites);
3808 for (UINT32 k = 0; k < numCallSites; k++)
3810 printf(" Offset 0x%x, size %d.\n", pCallSites[k], pCallSiteSizes[k]);
3815 void DefineInterruptibleRange(UINT32 startInstructionOffset, UINT32 length)
3817 m_gcInfoEncoder->DefineInterruptibleRange(startInstructionOffset, length);
3820 printf("Defining interruptible range: [0x%x, 0x%x).\n", startInstructionOffset,
3821 startInstructionOffset + length);
3825 void SetCodeLength(UINT32 length)
3827 m_gcInfoEncoder->SetCodeLength(length);
3830 printf("Set code length to %d.\n", length);
3834 void SetReturnKind(ReturnKind returnKind)
3836 m_gcInfoEncoder->SetReturnKind(returnKind);
3839 printf("Set ReturnKind to %s.\n", ReturnKindToString(returnKind));
3843 void SetStackBaseRegister(UINT32 registerNumber)
3845 m_gcInfoEncoder->SetStackBaseRegister(registerNumber);
3848 printf("Set stack base register to %s.\n", getRegName(registerNumber));
3852 void SetPrologSize(UINT32 prologSize)
3854 m_gcInfoEncoder->SetPrologSize(prologSize);
3857 printf("Set prolog size 0x%x.\n", prologSize);
3861 void SetGSCookieStackSlot(INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd)
3863 m_gcInfoEncoder->SetGSCookieStackSlot(spOffsetGSCookie, validRangeStart, validRangeEnd);
3866 printf("Set GS Cookie stack slot to %d, valid from 0x%x to 0x%x.\n", spOffsetGSCookie, validRangeStart,
3871 void SetPSPSymStackSlot(INT32 spOffsetPSPSym)
3873 m_gcInfoEncoder->SetPSPSymStackSlot(spOffsetPSPSym);
3876 printf("Set PSPSym stack slot to %d.\n", spOffsetPSPSym);
3880 void SetGenericsInstContextStackSlot(INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type)
3882 m_gcInfoEncoder->SetGenericsInstContextStackSlot(spOffsetGenericsContext, type);
3885 printf("Set generic instantiation context stack slot to %d, type is %s.\n", spOffsetGenericsContext,
3886 (type == GENERIC_CONTEXTPARAM_THIS
3888 : (type == GENERIC_CONTEXTPARAM_MT ? "MT"
3889 : (type == GENERIC_CONTEXTPARAM_MD ? "MD" : "UNKNOWN!"))));
3893 void SetSecurityObjectStackSlot(INT32 spOffset)
3895 m_gcInfoEncoder->SetSecurityObjectStackSlot(spOffset);
3898 printf("Set security object stack slot to %d.\n", spOffset);
3904 m_gcInfoEncoder->SetIsVarArg();
3907 printf("SetIsVarArg.\n");
3911 #ifdef _TARGET_AMD64_
3912 void SetWantsReportOnlyLeaf()
3914 m_gcInfoEncoder->SetWantsReportOnlyLeaf();
3917 printf("Set WantsReportOnlyLeaf.\n");
3920 #elif defined(_TARGET_ARMARCH_)
3921 void SetHasTailCalls()
3923 m_gcInfoEncoder->SetHasTailCalls();
3926 printf("Set HasTailCalls.\n");
3929 #endif // _TARGET_AMD64_
3931 void SetSizeOfStackOutgoingAndScratchArea(UINT32 size)
3933 m_gcInfoEncoder->SetSizeOfStackOutgoingAndScratchArea(size);
3936 printf("Set Outgoing stack arg area size to %d.\n", size);
3941 #define GCENCODER_WITH_LOGGING(withLog, realEncoder) \
3942 GcInfoEncoderWithLogging withLog##Var(realEncoder, compiler->verbose || compiler->opts.dspGCtbls); \
3943 GcInfoEncoderWithLogging* withLog = &withLog##Var;
3947 #define GCENCODER_WITH_LOGGING(withLog, realEncoder) GcInfoEncoder* withLog = realEncoder;
3951 void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize)
3954 if (compiler->verbose)
3956 printf("*************** In gcInfoBlockHdrSave()\n");
3960 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
3962 // Can't create tables if we've not saved code.
3964 gcInfoEncoderWithLog->SetCodeLength(methodSize);
3966 gcInfoEncoderWithLog->SetReturnKind(getReturnKind());
3968 if (compiler->isFramePointerUsed())
3970 gcInfoEncoderWithLog->SetStackBaseRegister(REG_FPBASE);
3973 if (compiler->info.compIsVarArgs)
3975 gcInfoEncoderWithLog->SetIsVarArg();
3978 // header->profCallbacks = compiler->info.compProfilerCallback;
3979 // header->editNcontinue = compiler->opts.compDbgEnC;
3981 if (compiler->lvaReportParamTypeArg())
3983 // The predicate above is true only if there is an extra generic context parameter, not for
3984 // the case where the generic context is provided by "this."
3985 assert(compiler->info.compTypeCtxtArg != BAD_VAR_NUM);
3986 GENERIC_CONTEXTPARAM_TYPE ctxtParamType = GENERIC_CONTEXTPARAM_NONE;
3987 switch (compiler->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_MASK)
3989 case CORINFO_GENERICS_CTXT_FROM_METHODDESC:
3990 ctxtParamType = GENERIC_CONTEXTPARAM_MD;
3992 case CORINFO_GENERICS_CTXT_FROM_METHODTABLE:
3993 ctxtParamType = GENERIC_CONTEXTPARAM_MT;
3996 case CORINFO_GENERICS_CTXT_FROM_THIS: // See comment above.
3998 // If we have a generic context parameter, then we should have
3999 // one of the two options flags handled above.
4003 gcInfoEncoderWithLog->SetGenericsInstContextStackSlot(
4004 compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(),
4005 compiler->isFramePointerUsed()),
4008 // As discussed above, handle the case where the generics context is obtained via
4009 // the method table of "this".
4010 else if (compiler->lvaKeepAliveAndReportThis())
4012 assert(compiler->info.compThisArg != BAD_VAR_NUM);
4013 gcInfoEncoderWithLog->SetGenericsInstContextStackSlot(
4014 compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(),
4015 compiler->isFramePointerUsed()),
4016 GENERIC_CONTEXTPARAM_THIS);
4019 if (compiler->getNeedsGSSecurityCookie())
4021 assert(compiler->lvaGSSecurityCookie != BAD_VAR_NUM);
4023 // The lv offset is FP-relative, and the using code expects caller-sp relative, so translate.
4024 // The code offset ranges assume that the GS Cookie slot is initialized in the prolog, and is valid
4025 // through the remainder of the method. We will not query for the GS Cookie while we're in an epilog,
4026 // so the question of where in the epilog it becomes invalid is moot.
4027 gcInfoEncoderWithLog->SetGSCookieStackSlot(compiler->lvaGetCallerSPRelativeOffset(
4028 compiler->lvaGSSecurityCookie),
4029 prologSize, methodSize);
4031 else if (compiler->opts.compNeedSecurityCheck || compiler->lvaReportParamTypeArg() ||
4032 compiler->lvaKeepAliveAndReportThis())
4034 gcInfoEncoderWithLog->SetPrologSize(prologSize);
4037 if (compiler->opts.compNeedSecurityCheck)
4039 assert(compiler->lvaSecurityObject != BAD_VAR_NUM);
4041 // A VM requirement due to how the decoder works (it ignores partially interruptible frames when
4042 // an exception has escaped, but the VM requires the security object to live on).
4043 assert(compiler->codeGen->genInterruptible);
4045 // The lv offset is FP-relative, and the using code expects caller-sp relative, so translate.
4046 // The normal GC lifetime reporting mechanisms will report a proper lifetime to the GC.
4047 // The security subsystem can safely assume that anywhere it might walk the stack, it will be
4048 // valid (null or a live GC ref).
4049 gcInfoEncoderWithLog->SetSecurityObjectStackSlot(
4050 compiler->lvaGetCallerSPRelativeOffset(compiler->lvaSecurityObject));
4053 #if FEATURE_EH_FUNCLETS
4054 if (compiler->lvaPSPSym != BAD_VAR_NUM)
4056 #ifdef _TARGET_AMD64_
4057 // The PSPSym is relative to InitialSP on X64 and CallerSP on other platforms.
4058 gcInfoEncoderWithLog->SetPSPSymStackSlot(compiler->lvaGetInitialSPRelativeOffset(compiler->lvaPSPSym));
4059 #else // !_TARGET_AMD64_
4060 gcInfoEncoderWithLog->SetPSPSymStackSlot(compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym));
4061 #endif // !_TARGET_AMD64_
4064 #ifdef _TARGET_AMD64_
4065 if (compiler->ehAnyFunclets())
4067 // Set this to avoid double-reporting the parent frame (unlike JIT64)
4068 gcInfoEncoderWithLog->SetWantsReportOnlyLeaf();
4070 #endif // _TARGET_AMD64_
4072 #endif // FEATURE_EH_FUNCLETS
4074 #ifdef _TARGET_ARMARCH_
4075 if (compiler->codeGen->hasTailCalls)
4077 gcInfoEncoderWithLog->SetHasTailCalls();
4079 #endif // _TARGET_ARMARCH_
4081 #if FEATURE_FIXED_OUT_ARGS
4082 // outgoing stack area size
4083 gcInfoEncoderWithLog->SetSizeOfStackOutgoingAndScratchArea(compiler->lvaOutgoingArgSpaceSize);
4084 #endif // FEATURE_FIXED_OUT_ARGS
4088 if (compiler->codeGen->genInterruptible)
4097 #endif // DISPLAY_SIZES
4101 #define Encoder GcInfoEncoderWithLogging
4103 #define Encoder GcInfoEncoder
4106 // Small helper class to handle the No-GC-Interrupt callbacks
4107 // when reporting interruptible ranges.
4109 // Encoder should be either GcInfoEncoder or GcInfoEncoderWithLogging
4111 struct InterruptibleRangeReporter
4114 Encoder* gcInfoEncoderWithLog;
4116 InterruptibleRangeReporter(unsigned _prevStart, Encoder* _gcInfo)
4117 : prevStart(_prevStart), gcInfoEncoderWithLog(_gcInfo)
4121 // This callback is called for each insGroup marked with
4122 // IGF_NOGCINTERRUPT (currently just prologs and epilogs).
4123 // Report everything between the previous region and the current
4124 // region as interruptible.
4126 bool operator()(unsigned igFuncIdx, unsigned igOffs, unsigned igSize)
4128 if (igOffs < prevStart)
4130 // We're still in the main method prolog, which has already
4131 // had it's interruptible range reported.
4132 assert(igFuncIdx == 0);
4133 assert(igOffs + igSize <= prevStart);
4137 assert(igOffs >= prevStart);
4138 if (igOffs > prevStart)
4140 gcInfoEncoderWithLog->DefineInterruptibleRange(prevStart, igOffs - prevStart);
4142 prevStart = igOffs + igSize;
4147 void GCInfo::gcMakeRegPtrTable(
4148 GcInfoEncoder* gcInfoEncoder, unsigned codeSize, unsigned prologSize, MakeRegPtrMode mode, unsigned* callCntRef)
4150 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4152 const bool noTrackedGCSlots =
4153 (compiler->opts.MinOpts() && !compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT)
4154 #if !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND)
4155 && !JitConfig.JitMinOptsTrackGCrefs()
4156 #endif // !defined(JIT32_GCENCODER) || !defined(LEGACY_BACKEND)
4159 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4161 m_regSlotMap = new (compiler->getAllocator()) RegSlotMap(compiler->getAllocator());
4162 m_stackSlotMap = new (compiler->getAllocator()) StackSlotMap(compiler->getAllocator());
4165 /**************************************************************************
4167 * Untracked ptr variables
4169 **************************************************************************
4176 /* Count&Write untracked locals and non-enregistered args */
4180 for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
4182 if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
4184 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
4185 // reported through its parent local.
4189 if (varTypeIsGC(varDsc->TypeGet()))
4191 // Do we have an argument or local variable?
4192 if (!varDsc->lvIsParam)
4194 // If is is pinned, it must be an untracked local.
4195 assert(!varDsc->lvPinned || !varDsc->lvTracked);
4197 if (varDsc->lvTracked || !varDsc->lvOnFrame)
4204 // Stack-passed arguments which are not enregistered
4205 // are always reported in this "untracked stack
4206 // pointers" section of the GC info even if lvTracked==true
4208 // Has this argument been fully enregistered?
4209 CLANG_FORMAT_COMMENT_ANCHOR;
4211 #ifndef LEGACY_BACKEND
4212 if (!varDsc->lvOnFrame)
4213 #else // LEGACY_BACKEND
4214 if (varDsc->lvRegister)
4215 #endif // LEGACY_BACKEND
4217 // If a CEE_JMP has been used, then we need to report all the arguments
4218 // even if they are enregistered, since we will be using this value
4219 // in a JMP call. Note that this is subtle as we require that
4220 // argument offsets are always fixed up properly even if lvRegister
4222 if (!compiler->compJmpOpUsed)
4229 if (!varDsc->lvOnFrame)
4231 // If this non-enregistered pointer arg is never
4232 // used, we don't need to report it.
4233 assert(varDsc->lvRefCnt == 0);
4236 else if (varDsc->lvIsRegArg && varDsc->lvTracked)
4238 // If this register-passed arg is tracked, then
4239 // it has been allocated space near the other
4240 // pointer variables and we have accurate life-
4241 // time info. It will be reported with
4242 // gcVarPtrList in the "tracked-pointer" section.
4248 // If we haven't continued to the next variable, we should report this as an untracked local.
4249 CLANG_FORMAT_COMMENT_ANCHOR;
4251 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4253 if (varDsc->TypeGet() == TYP_BYREF)
4255 // Or in byref_OFFSET_FLAG for 'byref' pointer tracking
4256 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4259 if (varDsc->lvPinned)
4261 // Or in pinned_OFFSET_FLAG for 'pinned' pointer tracking
4262 flags = (GcSlotFlags)(flags | GC_SLOT_PINNED);
4264 GcStackSlotBase stackSlotBase = GC_SP_REL;
4265 if (varDsc->lvFramePointerBased)
4267 stackSlotBase = GC_FRAMEREG_REL;
4269 if (noTrackedGCSlots)
4271 // No need to hash/lookup untracked GC refs; just grab a new Slot Id.
4272 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4274 gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
4279 StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
4281 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4283 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4285 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
4286 m_stackSlotMap->Set(sskey, varSlotId);
4292 // If this is a TYP_STRUCT, handle its GC pointers.
4293 // Note that the enregisterable struct types cannot have GC pointers in them.
4294 if ((varDsc->lvType == TYP_STRUCT) && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
4296 unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
4297 BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum);
4299 // walk each member of the array
4300 for (unsigned i = 0; i < slots; i++)
4302 if (gcPtrs[i] == TYPE_GC_NONE)
4303 { // skip non-gc slots
4307 int offset = varDsc->lvStkOffs + i * TARGET_POINTER_SIZE;
4309 // For genDoubleAlign(), locals are addressed relative to ESP and
4310 // arguments are addressed relative to EBP.
4312 if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg)
4313 offset += compiler->codeGen->genTotalFrameSize();
4315 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4316 if (gcPtrs[i] == TYPE_GC_BYREF)
4318 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4321 GcStackSlotBase stackSlotBase = GC_SP_REL;
4322 if (varDsc->lvFramePointerBased)
4324 stackSlotBase = GC_FRAMEREG_REL;
4326 StackSlotIdKey sskey(offset, (stackSlotBase == GC_FRAMEREG_REL), flags);
4328 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4330 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4332 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(offset, flags, stackSlotBase);
4333 m_stackSlotMap->Set(sskey, varSlotId);
4340 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4342 // Count&Write spill temps that hold pointers.
4344 assert(compiler->tmpAllFree());
4345 for (TempDsc* tempItem = compiler->tmpListBeg(); tempItem != nullptr; tempItem = compiler->tmpListNxt(tempItem))
4347 if (varTypeIsGC(tempItem->tdTempType()))
4349 int offset = tempItem->tdTempOffs();
4351 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4352 if (tempItem->tdTempType() == TYP_BYREF)
4354 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4357 GcStackSlotBase stackSlotBase = GC_SP_REL;
4358 if (compiler->isFramePointerUsed())
4360 stackSlotBase = GC_FRAMEREG_REL;
4362 StackSlotIdKey sskey(offset, (stackSlotBase == GC_FRAMEREG_REL), flags);
4364 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4366 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(offset, flags, stackSlotBase);
4367 m_stackSlotMap->Set(sskey, varSlotId);
4372 if (compiler->lvaKeepAliveAndReportThis())
4374 // We need to report the cached copy as an untracked pointer
4375 assert(compiler->info.compThisArg != BAD_VAR_NUM);
4376 assert(!compiler->lvaReportParamTypeArg());
4377 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4379 if (compiler->lvaTable[compiler->info.compThisArg].TypeGet() == TYP_BYREF)
4381 // Or in GC_SLOT_INTERIOR for 'byref' pointer tracking
4382 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4385 GcStackSlotBase stackSlotBase = compiler->isFramePointerUsed() ? GC_FRAMEREG_REL : GC_SP_REL;
4387 gcInfoEncoderWithLog->GetStackSlotId(compiler->lvaCachedGenericContextArgOffset(), flags, stackSlotBase);
4391 // Generate the table of tracked stack pointer variable lifetimes.
4392 gcMakeVarPtrTable(gcInfoEncoder, mode);
4394 /**************************************************************************
4396 * Prepare to generate the pointer register/argument map
4398 **************************************************************************
4401 if (compiler->codeGen->genInterruptible)
4403 assert(compiler->genFullPtrRegMap);
4405 regMaskSmall ptrRegs = 0;
4406 regPtrDsc* regStackArgFirst = nullptr;
4408 // Walk the list of pointer register/argument entries.
4410 for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr; genRegPtrTemp = genRegPtrTemp->rpdNext)
4412 int nextOffset = genRegPtrTemp->rpdOffs;
4414 if (genRegPtrTemp->rpdArg)
4416 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL)
4418 // Kill all arguments for a call
4419 if ((mode == MAKE_REG_PTR_MODE_DO_WORK) && (regStackArgFirst != nullptr))
4421 // Record any outgoing arguments as becoming dead
4422 gcInfoRecordGCStackArgsDead(gcInfoEncoder, genRegPtrTemp->rpdOffs, regStackArgFirst,
4425 regStackArgFirst = nullptr;
4427 else if (genRegPtrTemp->rpdGCtypeGet() != GCT_NONE)
4429 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH || (genRegPtrTemp->rpdPtrArg != 0))
4431 bool isPop = genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP;
4433 gcInfoRecordGCStackArgLive(gcInfoEncoder, mode, genRegPtrTemp);
4434 if (regStackArgFirst == nullptr)
4436 regStackArgFirst = genRegPtrTemp;
4441 // We know it's a POP. Sometimes we'll record a POP for a call, just to make sure
4442 // the call site is recorded.
4443 // This is just the negation of the condition:
4444 assert(genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP && genRegPtrTemp->rpdPtrArg == 0);
4445 // This asserts that we only get here when we're recording a call site.
4446 assert(genRegPtrTemp->rpdArg && genRegPtrTemp->rpdIsCallInstr());
4448 // Kill all arguments for a call
4449 if ((mode == MAKE_REG_PTR_MODE_DO_WORK) && (regStackArgFirst != nullptr))
4451 // Record any outgoing arguments as becoming dead
4452 gcInfoRecordGCStackArgsDead(gcInfoEncoder, genRegPtrTemp->rpdOffs, regStackArgFirst,
4455 regStackArgFirst = nullptr;
4461 // Record any registers that are becoming dead.
4463 regMaskSmall regMask = genRegPtrTemp->rpdCompiler.rpdDel & ptrRegs;
4464 regMaskSmall byRefMask = 0;
4465 if (genRegPtrTemp->rpdGCtypeGet() == GCT_BYREF)
4467 byRefMask = regMask;
4469 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, genRegPtrTemp->rpdOffs, regMask, GC_SLOT_DEAD,
4470 byRefMask, &ptrRegs);
4472 // Record any registers that are becoming live.
4473 regMask = genRegPtrTemp->rpdCompiler.rpdAdd & ~ptrRegs;
4475 // As far as I (DLD, 2010) can tell, there's one GCtype for the entire genRegPtrTemp, so if
4476 // it says byref then all the registers in "regMask" contain byrefs.
4477 if (genRegPtrTemp->rpdGCtypeGet() == GCT_BYREF)
4479 byRefMask = regMask;
4481 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, genRegPtrTemp->rpdOffs, regMask, GC_SLOT_LIVE,
4482 byRefMask, &ptrRegs);
4486 // Now we can declare the entire method body fully interruptible.
4487 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4489 assert(prologSize <= codeSize);
4491 // Now exempt any other region marked as IGF_NOGCINTERRUPT
4492 // Currently just prologs and epilogs.
4494 InterruptibleRangeReporter reporter(prologSize, gcInfoEncoderWithLog);
4495 compiler->getEmitter()->emitGenNoGCLst(reporter);
4496 prologSize = reporter.prevStart;
4498 // Report any remainder
4499 if (prologSize < codeSize)
4501 gcInfoEncoderWithLog->DefineInterruptibleRange(prologSize, codeSize - prologSize);
4505 else if (compiler->isFramePointerUsed()) // genInterruptible is false, and we're using EBP as a frame pointer.
4507 assert(compiler->genFullPtrRegMap == false);
4509 // Walk the list of pointer register/argument entries.
4511 // First count them.
4512 unsigned numCallSites = 0;
4514 // Now we can allocate the information.
4515 unsigned* pCallSites = nullptr;
4516 BYTE* pCallSiteSizes = nullptr;
4517 unsigned callSiteNum = 0;
4519 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4521 if (gcCallDescList != nullptr)
4523 if (noTrackedGCSlots)
4525 // We have the call count from the previous run.
4526 numCallSites = *callCntRef;
4528 // If there are no calls, tell the world and bail.
4529 if (numCallSites == 0)
4531 gcInfoEncoderWithLog->DefineCallSites(nullptr, nullptr, 0);
4537 for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
4542 pCallSites = new (compiler, CMK_GC) unsigned[numCallSites];
4543 pCallSiteSizes = new (compiler, CMK_GC) BYTE[numCallSites];
4547 // Now consider every call.
4548 for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
4550 // Figure out the code offset of this entry.
4551 unsigned nextOffset = call->cdOffs;
4553 // As far as I (DLD, 2010) can determine by asking around, the "call->u1.cdArgMask"
4554 // and "cdArgCnt" cases are to handle x86 situations in which a call expression is nested as an
4555 // argument to an outer call. The "natural" (evaluation-order-preserving) thing to do is to
4556 // evaluate the outer call's arguments, pushing those that are not enregistered, until you
4557 // encounter the nested call. These parts of the call description, then, describe the "pending"
4558 // pushed arguments. This situation does not exist outside of x86, where we're going to use a
4559 // fixed-size stack frame: in situations like this nested call, we would evaluate the pending
4560 // arguments to temporaries, and only "push" them (really, write them to the outgoing argument section
4561 // of the stack frame) when it's the outer call's "turn." So we can assert that these
4562 // situations never occur.
4563 assert(call->u1.cdArgMask == 0 && call->cdArgCnt == 0);
4565 // Other than that, we just have to deal with the regmasks.
4566 regMaskSmall gcrefRegMask = call->cdGCrefRegs & RBM_CALLEE_SAVED;
4567 regMaskSmall byrefRegMask = call->cdByrefRegs & RBM_CALLEE_SAVED;
4569 assert((gcrefRegMask & byrefRegMask) == 0);
4571 regMaskSmall regMask = gcrefRegMask | byrefRegMask;
4573 assert(call->cdOffs >= call->cdCallInstrSize);
4574 // call->cdOffs is actually the offset of the instruction *following* the call, so subtract
4575 // the call instruction size to get the offset of the actual call instruction...
4576 unsigned callOffset = nextOffset - call->cdCallInstrSize;
4578 if (noTrackedGCSlots && regMask == 0)
4580 // No live GC refs in regs at the call -> don't record the call.
4584 // Append an entry for the call if doing the real thing.
4585 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4587 pCallSites[callSiteNum] = callOffset;
4588 pCallSiteSizes[callSiteNum] = call->cdCallInstrSize;
4592 // Record that these registers are live before the call...
4593 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask,
4595 // ...and dead after.
4596 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, nextOffset, regMask, GC_SLOT_DEAD, byrefRegMask,
4600 // Make sure we've recorded the expected number of calls
4601 assert(mode != MAKE_REG_PTR_MODE_DO_WORK || numCallSites == callSiteNum);
4602 // Return the actual recorded call count to the caller
4603 *callCntRef = callSiteNum;
4605 // OK, define the call sites.
4606 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4608 gcInfoEncoderWithLog->DefineCallSites(pCallSites, pCallSiteSizes, numCallSites);
4611 else // genInterruptible is false and we have an EBP-less frame
4613 assert(compiler->genFullPtrRegMap);
4615 // Walk the list of pointer register/argument entries */
4616 // First count them.
4617 unsigned numCallSites = 0;
4619 // Now we can allocate the information (if we're in the "DO_WORK" pass...)
4620 unsigned* pCallSites = nullptr;
4621 BYTE* pCallSiteSizes = nullptr;
4622 unsigned callSiteNum = 0;
4624 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4626 for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr;
4627 genRegPtrTemp = genRegPtrTemp->rpdNext)
4629 if (genRegPtrTemp->rpdArg && genRegPtrTemp->rpdIsCallInstr())
4635 if (numCallSites > 0)
4637 pCallSites = new (compiler, CMK_GC) unsigned[numCallSites];
4638 pCallSiteSizes = new (compiler, CMK_GC) BYTE[numCallSites];
4642 for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr; genRegPtrTemp = genRegPtrTemp->rpdNext)
4644 if (genRegPtrTemp->rpdArg)
4646 // Is this a call site?
4647 if (genRegPtrTemp->rpdIsCallInstr())
4649 // This is a true call site.
4651 regMaskSmall gcrefRegMask = genRegMaskFromCalleeSavedMask(genRegPtrTemp->rpdCallGCrefRegs);
4653 regMaskSmall byrefRegMask = genRegMaskFromCalleeSavedMask(genRegPtrTemp->rpdCallByrefRegs);
4655 assert((gcrefRegMask & byrefRegMask) == 0);
4657 regMaskSmall regMask = gcrefRegMask | byrefRegMask;
4659 // The "rpdOffs" is (apparently) the offset of the following instruction already.
4660 // GcInfoEncoder wants the call instruction, so subtract the width of the call instruction.
4661 assert(genRegPtrTemp->rpdOffs >= genRegPtrTemp->rpdCallInstrSize);
4662 unsigned callOffset = genRegPtrTemp->rpdOffs - genRegPtrTemp->rpdCallInstrSize;
4664 // Tell the GCInfo encoder about these registers. We say that the registers become live
4665 // before the call instruction, and dead after.
4666 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask,
4668 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, genRegPtrTemp->rpdOffs, regMask, GC_SLOT_DEAD,
4669 byrefRegMask, nullptr);
4671 // Also remember the call site.
4672 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4674 assert(pCallSites != nullptr && pCallSiteSizes != nullptr);
4675 pCallSites[callSiteNum] = callOffset;
4676 pCallSiteSizes[callSiteNum] = genRegPtrTemp->rpdCallInstrSize;
4682 // These are reporting outgoing stack arguments, but we don't need to report anything
4683 // for partially interruptible
4684 assert(genRegPtrTemp->rpdGCtypeGet() != GCT_NONE);
4685 assert(genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH);
4689 // The routine is fully interruptible.
4690 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4692 gcInfoEncoderWithLog->DefineCallSites(pCallSites, pCallSiteSizes, numCallSites);
4697 void GCInfo::gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder,
4698 MakeRegPtrMode mode,
4699 unsigned instrOffset,
4700 regMaskSmall regMask,
4701 GcSlotState newState,
4702 regMaskSmall byRefMask,
4703 regMaskSmall* pPtrRegs)
4705 // Precondition: byRefMask is a subset of regMask.
4706 assert((byRefMask & ~regMask) == 0);
4708 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4712 // Get hold of the next register bit.
4713 regMaskTP tmpMask = genFindLowestReg(regMask);
4716 // Remember the new state of this register.
4717 if (pPtrRegs != nullptr)
4719 if (newState == GC_SLOT_DEAD)
4721 *pPtrRegs &= ~tmpMask;
4725 *pPtrRegs |= tmpMask;
4729 // Figure out which register the next bit corresponds to.
4730 regNumber regNum = genRegNumFromMask(tmpMask);
4732 /* Reserve SP future use */
4733 assert(regNum != REG_SPBASE);
4735 GcSlotFlags regFlags = GC_SLOT_BASE;
4736 if ((tmpMask & byRefMask) != 0)
4738 regFlags = (GcSlotFlags)(regFlags | GC_SLOT_INTERIOR);
4741 RegSlotIdKey rskey(regNum, regFlags);
4743 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4745 if (!m_regSlotMap->Lookup(rskey, ®SlotId))
4747 regSlotId = gcInfoEncoderWithLog->GetRegisterSlotId(regNum, regFlags);
4748 m_regSlotMap->Set(rskey, regSlotId);
4753 BOOL b = m_regSlotMap->Lookup(rskey, ®SlotId);
4754 assert(b); // Should have been added in the first pass.
4755 gcInfoEncoderWithLog->SetSlotState(instrOffset, regSlotId, newState);
4758 // Turn the bit we've just generated off and continue.
4759 regMask -= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
4763 /**************************************************************************
4765 * gcMakeVarPtrTable - Generate the table of tracked stack pointer
4766 * variable lifetimes.
4768 * In the first pass we'll allocate slot Ids
4769 * In the second pass we actually generate the lifetimes.
4771 **************************************************************************
4774 void GCInfo::gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode)
4776 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4778 // Make sure any flags we hide in the offset are in the bits guaranteed
4779 // unused by alignment
4780 C_ASSERT((OFFSET_MASK + 1) <= sizeof(int));
4783 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4785 // Tracked variables can't be pinned, and the encoding takes
4786 // advantage of that by using the same bit for 'pinned' and 'this'
4787 // Since we don't track 'this', we should never see either flag here.
4788 // Check it now before we potentially add some pinned flags.
4789 for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext)
4791 const unsigned flags = varTmp->vpdVarNum & OFFSET_MASK;
4792 assert((flags & pinned_OFFSET_FLAG) == 0);
4793 assert((flags & this_OFFSET_FLAG) == 0);
4798 // Only need to do this once, and only if we have EH.
4799 if ((mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS) && compiler->ehAnyFunclets())
4801 gcMarkFilterVarsPinned();
4804 for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext)
4806 C_ASSERT((OFFSET_MASK + 1) <= sizeof(int));
4808 // Get hold of the variable's stack offset.
4810 unsigned lowBits = varTmp->vpdVarNum & OFFSET_MASK;
4812 // For negative stack offsets we must reset the low bits
4813 int varOffs = static_cast<int>(varTmp->vpdVarNum & ~OFFSET_MASK);
4815 // Compute the actual lifetime offsets.
4816 unsigned begOffs = varTmp->vpdBegOfs;
4817 unsigned endOffs = varTmp->vpdEndOfs;
4819 // Special case: skip any 0-length lifetimes.
4820 if (endOffs == begOffs)
4825 GcSlotFlags flags = GC_SLOT_BASE;
4826 if ((lowBits & byref_OFFSET_FLAG) != 0)
4828 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4830 if ((lowBits & pinned_OFFSET_FLAG) != 0)
4832 flags = (GcSlotFlags)(flags | GC_SLOT_PINNED);
4835 GcStackSlotBase stackSlotBase = GC_SP_REL;
4836 if (compiler->isFramePointerUsed())
4838 stackSlotBase = GC_FRAMEREG_REL;
4840 StackSlotIdKey sskey(varOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
4842 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4844 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4846 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varOffs, flags, stackSlotBase);
4847 m_stackSlotMap->Set(sskey, varSlotId);
4852 BOOL b = m_stackSlotMap->Lookup(sskey, &varSlotId);
4853 assert(b); // Should have been added in the first pass.
4854 // Live from the beginning to the end.
4855 gcInfoEncoderWithLog->SetSlotState(begOffs, varSlotId, GC_SLOT_LIVE);
4856 gcInfoEncoderWithLog->SetSlotState(endOffs, varSlotId, GC_SLOT_DEAD);
4861 void GCInfo::gcInfoRecordGCStackArgLive(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode, regPtrDsc* genStackPtr)
4863 // On non-x86 platforms, don't have pointer argument push/pop/kill declarations.
4864 // But we use the same mechanism to record writes into the outgoing argument space...
4865 assert(genStackPtr->rpdGCtypeGet() != GCT_NONE);
4866 assert(genStackPtr->rpdArg);
4867 assert(genStackPtr->rpdArgTypeGet() == rpdARG_PUSH);
4869 // We only need to report these when we're doing fuly-interruptible
4870 assert(compiler->codeGen->genInterruptible);
4872 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4874 StackSlotIdKey sskey(genStackPtr->rpdPtrArg, FALSE,
4875 GcSlotFlags(genStackPtr->rpdGCtypeGet() == GCT_BYREF ? GC_SLOT_INTERIOR : GC_SLOT_BASE));
4877 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4879 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4881 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(sskey.m_offset, (GcSlotFlags)sskey.m_flags, GC_SP_REL);
4882 m_stackSlotMap->Set(sskey, varSlotId);
4887 BOOL b = m_stackSlotMap->Lookup(sskey, &varSlotId);
4888 assert(b); // Should have been added in the first pass.
4889 // Live until the call.
4890 gcInfoEncoderWithLog->SetSlotState(genStackPtr->rpdOffs, varSlotId, GC_SLOT_LIVE);
4894 void GCInfo::gcInfoRecordGCStackArgsDead(GcInfoEncoder* gcInfoEncoder,
4895 unsigned instrOffset,
4896 regPtrDsc* genStackPtrFirst,
4897 regPtrDsc* genStackPtrLast)
4899 // After a call all of the outgoing arguments are marked as dead.
4900 // The calling loop keeps track of the first argument pushed for this call
4901 // and passes it in as genStackPtrFirst.
4902 // genStackPtrLast is the call.
4903 // Re-walk that list and mark all outgoing arguments that we're marked as live
4904 // earlier, as going dead after the call.
4906 // We only need to report these when we're doing fuly-interruptible
4907 assert(compiler->codeGen->genInterruptible);
4909 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4911 for (regPtrDsc* genRegPtrTemp = genStackPtrFirst; genRegPtrTemp != genStackPtrLast;
4912 genRegPtrTemp = genRegPtrTemp->rpdNext)
4914 if (!genRegPtrTemp->rpdArg)
4919 assert(genRegPtrTemp->rpdGCtypeGet() != GCT_NONE);
4920 assert(genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH);
4922 StackSlotIdKey sskey(genRegPtrTemp->rpdPtrArg, FALSE,
4923 genRegPtrTemp->rpdGCtypeGet() == GCT_BYREF ? GC_SLOT_INTERIOR : GC_SLOT_BASE);
4925 BOOL b = m_stackSlotMap->Lookup(sskey, &varSlotId);
4926 assert(b); // Should have been added in the first pass.
4927 // Live until the call.
4928 gcInfoEncoderWithLog->SetSlotState(instrOffset, varSlotId, GC_SLOT_DEAD);
4932 #undef GCENCODER_WITH_LOGGING
4934 #endif // !JIT32_GCENCODER
4936 /*****************************************************************************/
4937 /*****************************************************************************/