1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX GCEncode XX
9XX XX
10XX Logic to encode the JIT method header and GC pointer tables XX
11XX XX
12XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
13XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
14*/
15
16#include "jitpch.h"
17#ifdef _MSC_VER
18#pragma hdrstop
19
20#pragma warning(disable : 4244) // loss of data int -> char ..
21
22#endif
23
24#include "gcinfotypes.h"
25
26ReturnKind GCTypeToReturnKind(CorInfoGCType gcType)
27{
28 switch (gcType)
29 {
30 case TYPE_GC_NONE:
31 return RT_Scalar;
32 case TYPE_GC_REF:
33 return RT_Object;
34 case TYPE_GC_BYREF:
35 return RT_ByRef;
36 default:
37 _ASSERTE(!"TYP_GC_OTHER is unexpected");
38 return RT_Illegal;
39 }
40}
41
42ReturnKind GCInfo::getReturnKind()
43{
44 switch (compiler->info.compRetType)
45 {
46 case TYP_REF:
47 return RT_Object;
48 case TYP_BYREF:
49 return RT_ByRef;
50 case TYP_STRUCT:
51 {
52 CORINFO_CLASS_HANDLE structType = compiler->info.compMethodInfo->args.retTypeClass;
53 var_types retType = compiler->getReturnTypeForStruct(structType);
54
55 switch (retType)
56 {
57 case TYP_REF:
58 return RT_Object;
59
60 case TYP_BYREF:
61 return RT_ByRef;
62
63 case TYP_STRUCT:
64 if (compiler->IsHfa(structType))
65 {
66#ifdef _TARGET_X86_
67 _ASSERTE(false && "HFAs not expected for X86");
68#endif // _TARGET_X86_
69
70 return RT_Scalar;
71 }
72 else
73 {
74 // Multi-reg return
75 BYTE gcPtrs[2] = {TYPE_GC_NONE, TYPE_GC_NONE};
76 compiler->info.compCompHnd->getClassGClayout(structType, gcPtrs);
77
78 ReturnKind first = GCTypeToReturnKind((CorInfoGCType)gcPtrs[0]);
79 ReturnKind second = GCTypeToReturnKind((CorInfoGCType)gcPtrs[1]);
80
81 return GetStructReturnKind(first, second);
82 }
83
84#ifdef _TARGET_X86_
85 case TYP_FLOAT:
86 case TYP_DOUBLE:
87 return RT_Float;
88#endif // _TARGET_X86_
89 default:
90 return RT_Scalar;
91 }
92 }
93
94#ifdef _TARGET_X86_
95 case TYP_FLOAT:
96 case TYP_DOUBLE:
97 return RT_Float;
98#endif // _TARGET_X86_
99
100 default:
101 return RT_Scalar;
102 }
103}
104
105#if !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
106
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
111// finalized.
112//
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.
116//
117// Untracked locals will only be reported in non-filter funclets and the
118// parent.
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.
121//
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.
126//
127void GCInfo::gcMarkFilterVarsPinned()
128{
129 assert(compiler->ehAnyFunclets());
130 const EHblkDsc* endHBtab = &(compiler->compHndBBtab[compiler->compHndBBtabCount]);
131
132 for (EHblkDsc* HBtab = compiler->compHndBBtab; HBtab < endHBtab; HBtab++)
133 {
134 if (HBtab->HasFilter())
135 {
136 const UNATIVE_OFFSET filterBeg = compiler->ehCodeOffset(HBtab->ebdFilter);
137 const UNATIVE_OFFSET filterEnd = compiler->ehCodeOffset(HBtab->ebdHndBeg);
138
139 for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext)
140 {
141 // Get hold of the variable's flags.
142 const unsigned lowBits = varTmp->vpdVarNum & OFFSET_MASK;
143
144 // Compute the actual lifetime offsets.
145 const unsigned begOffs = varTmp->vpdBegOfs;
146 const unsigned endOffs = varTmp->vpdEndOfs;
147
148 // Special case: skip any 0-length lifetimes.
149 if (endOffs == begOffs)
150 {
151 continue;
152 }
153
154 // Skip lifetimes with no overlap with the filter
155 if ((endOffs <= filterBeg) || (begOffs >= filterEnd))
156 {
157 continue;
158 }
159
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
167
168 if (begOffs < filterBeg)
169 {
170 if (endOffs > filterEnd)
171 {
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
177 // the filter.
178 CLANG_FORMAT_COMMENT_ANCHOR;
179
180#ifdef DEBUG
181 if (compiler->verbose)
182 {
183 printf("Splitting lifetime for filter: [%04X, %04X).\nOld: ", filterBeg, filterEnd);
184 gcDumpVarPtrDsc(varTmp);
185 }
186#endif // DEBUG
187
188 varPtrDsc* desc1 = new (compiler, CMK_GC) varPtrDsc;
189 desc1->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
190 desc1->vpdBegOfs = filterBeg;
191 desc1->vpdEndOfs = filterEnd;
192
193 varPtrDsc* desc2 = new (compiler, CMK_GC) varPtrDsc;
194 desc2->vpdVarNum = varTmp->vpdVarNum;
195 desc2->vpdBegOfs = filterEnd;
196 desc2->vpdEndOfs = endOffs;
197
198 varTmp->vpdEndOfs = filterBeg;
199
200 gcInsertVarPtrDscSplit(desc1, varTmp);
201 gcInsertVarPtrDscSplit(desc2, varTmp);
202
203#ifdef DEBUG
204 if (compiler->verbose)
205 {
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);
212 }
213#endif // DEBUG
214 }
215 else
216 {
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
220 // the filter.
221 CLANG_FORMAT_COMMENT_ANCHOR;
222
223#ifdef DEBUG
224 if (compiler->verbose)
225 {
226 printf("Splitting lifetime for filter.\nOld: ");
227 gcDumpVarPtrDsc(varTmp);
228 }
229#endif // DEBUG
230
231 varPtrDsc* desc = new (compiler, CMK_GC) varPtrDsc;
232 desc->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
233 desc->vpdBegOfs = filterBeg;
234 desc->vpdEndOfs = endOffs;
235
236 varTmp->vpdEndOfs = filterBeg;
237
238 gcInsertVarPtrDscSplit(desc, varTmp);
239
240#ifdef DEBUG
241 if (compiler->verbose)
242 {
243 printf("New (1 of 2): ");
244 gcDumpVarPtrDsc(varTmp);
245 printf("New (2 of 2): ");
246 gcDumpVarPtrDsc(desc);
247 }
248#endif // DEBUG
249 }
250 }
251 else
252 {
253 if (endOffs > filterEnd)
254 {
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
259 // of the filter
260 CLANG_FORMAT_COMMENT_ANCHOR;
261#ifdef DEBUG
262 if (compiler->verbose)
263 {
264 printf("Splitting lifetime for filter.\nOld: ");
265 gcDumpVarPtrDsc(varTmp);
266 }
267#endif // DEBUG
268
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;
274
275 varTmp->vpdBegOfs = filterEnd;
276#else
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;
282
283 varTmp->vpdVarNum = varTmp->vpdVarNum | pinned_OFFSET_FLAG;
284 varTmp->vpdEndOfs = filterEnd;
285#endif
286
287 gcInsertVarPtrDscSplit(desc, varTmp);
288
289#ifdef DEBUG
290 if (compiler->verbose)
291 {
292 printf("New (1 of 2): ");
293 gcDumpVarPtrDsc(desc);
294 printf("New (2 of 2): ");
295 gcDumpVarPtrDsc(varTmp);
296 }
297#endif // DEBUG
298 }
299 else
300 {
301 // The variable lifetime is completely within the filter,
302 // so just add the pinned flag.
303 CLANG_FORMAT_COMMENT_ANCHOR;
304#ifdef DEBUG
305 if (compiler->verbose)
306 {
307 printf("Pinning lifetime for filter.\nOld: ");
308 gcDumpVarPtrDsc(varTmp);
309 }
310#endif // DEBUG
311
312 varTmp->vpdVarNum |= pinned_OFFSET_FLAG;
313#ifdef DEBUG
314 if (compiler->verbose)
315 {
316 printf("New : ");
317 gcDumpVarPtrDsc(varTmp);
318 }
319#endif // DEBUG
320 }
321 }
322 }
323 } // HasFilter
324 } // Foreach EH
325}
326
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.
331//
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.
335//
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.
338
339void GCInfo::gcInsertVarPtrDscSplit(varPtrDsc* desc, varPtrDsc* begin)
340{
341#ifndef JIT32_GCENCODER
342 (void)begin;
343 desc->vpdNext = gcVarPtrList;
344 gcVarPtrList = desc;
345#else // JIT32_GCENCODER
346 // "desc" and "begin" must not be null
347 assert(desc != nullptr);
348 assert(begin != nullptr);
349
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);
353
354 varPtrDsc* varTmp = begin->vpdNext;
355 varPtrDsc* varInsert = begin;
356
357 while (varTmp != nullptr && varTmp->vpdBegOfs < desc->vpdBegOfs)
358 {
359 varInsert = varTmp;
360 varTmp = varTmp->vpdNext;
361 }
362
363 // Insert point cannot be null
364 assert(varInsert != nullptr);
365
366 desc->vpdNext = varInsert->vpdNext;
367 varInsert->vpdNext = desc;
368#endif // JIT32_GCENCODER
369}
370
371#ifdef DEBUG
372
373void GCInfo::gcDumpVarPtrDsc(varPtrDsc* desc)
374{
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;
378
379 printf("[%08X] %s%s var at [%s", dspPtr(desc), GCtypeStr(gcType), isPin ? "pinned-ptr" : "",
380 compiler->isFramePointerUsed() ? STR_FPBASE : STR_SPBASE);
381
382 if (offs < 0)
383 {
384 printf("-%02XH", -offs);
385 }
386 else if (offs > 0)
387 {
388 printf("+%02XH", +offs);
389 }
390
391 printf("] live from %04X to %04X\n", desc->vpdBegOfs, desc->vpdEndOfs);
392}
393
394#endif // DEBUG
395
396#endif // !defined(JIT32_GCENCODER) || defined(WIN64EXCEPTIONS)
397
398#ifdef JIT32_GCENCODER
399
400#include "emit.h"
401
402/*****************************************************************************/
403/*****************************************************************************/
404
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.
410//
411// find . -name regen.txt | xargs cat | grep InfoHdr | sort | uniq -c | sort -r | head -128
412
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.
417//
418// find . -name regen.txt | xargs cat | grep CallSite | sort | uniq -c | sort -r | head -80
419
420#if REGEN_SHORTCUTS || REGEN_CALLPAT
421static FILE* logFile = NULL;
422CRITICAL_SECTION logFileLock;
423#endif
424
425#if REGEN_CALLPAT
426static void regenLog(unsigned codeDelta,
427 unsigned argMask,
428 unsigned regMask,
429 unsigned argCnt,
430 unsigned byrefArgMask,
431 unsigned byrefRegMask,
432 BYTE* base,
433 unsigned enSize)
434{
435 CallPattern pat;
436
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;
441
442 if (logFile == NULL)
443 {
444 logFile = fopen("regen.txt", "a");
445 InitializeCriticalSection(&logFileLock);
446 }
447
448 assert(((enSize > 0) && (enSize < 256)) && ((pat.val & 0xffffff) != 0xffffff));
449
450 EnterCriticalSection(&logFileLock);
451
452 fprintf(logFile, "CallSite( 0x%08x, 0x%02x%02x, 0x", pat.val, byrefArgMask, byrefRegMask);
453
454 while (enSize > 0)
455 {
456 fprintf(logFile, "%02x", *base++);
457 enSize--;
458 }
459 fprintf(logFile, "),\n");
460 fflush(logFile);
461
462 LeaveCriticalSection(&logFileLock);
463}
464#endif
465
466#if REGEN_SHORTCUTS
467static void regenLog(unsigned encoding, InfoHdr* header, InfoHdr* state)
468{
469 if (logFile == NULL)
470 {
471 logFile = fopen("regen.txt", "a");
472 InitializeCriticalSection(&logFileLock);
473 }
474
475 EnterCriticalSection(&logFileLock);
476
477 fprintf(logFile, "InfoHdr( %2d, %2d, %1d, %1d, %1d,"
478 " %1d, %1d, %1d, %1d, %1d,"
479 " %1d, %1d, %1d, %1d, %1d, %1d,"
480 " %1d, %1d, %1d,"
481 " %1d, %2d, %2d,"
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);
494
495 fflush(logFile);
496
497 LeaveCriticalSection(&logFileLock);
498}
499#endif
500
501/*****************************************************************************
502 *
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
506 * match.
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
512 * second byte.
513 */
514int FASTCALL lookupCallPattern(unsigned argCnt, unsigned regMask, unsigned argMask, unsigned codeDelta)
515{
516 if ((argCnt <= CP_MAX_ARG_CNT) && (argMask <= CP_MAX_ARG_MASK))
517 {
518 CallPattern pat;
519
520 pat.fld.argCnt = argCnt;
521 pat.fld.regMask = regMask; // EBP,EBX,ESI,EDI
522 pat.fld.argMask = argMask;
523 pat.fld.codeDelta = codeDelta;
524
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));
530
531 const unsigned* curp = &callPatternTable[0];
532 for (unsigned inx = 0; inx < 80; inx++, curp++)
533 {
534 unsigned curval = *curp;
535 if ((patval == curval) && codeDeltaOK)
536 return inx;
537
538 if (((patval ^ curval) & 0xffffff) == 0)
539 {
540 unsigned delta2 = codeDelta - (curval >> 24);
541 if (delta2 < bestDelta2)
542 {
543 bestDelta2 = delta2;
544 bestPattern = inx;
545 }
546 }
547 }
548
549 if (bestPattern != 0xff)
550 {
551 return (bestDelta2 << 8) | bestPattern;
552 }
553 }
554 return -1;
555}
556
557static bool initNeeded3(unsigned cur, unsigned tgt, unsigned max, unsigned* hint)
558{
559 assert(cur != tgt);
560
561 unsigned tmp = tgt;
562 unsigned nib = 0;
563 unsigned cnt = 0;
564
565 while (tmp > max)
566 {
567 nib = tmp & 0x07;
568 tmp >>= 3;
569 if (tmp == cur)
570 {
571 *hint = nib;
572 return false;
573 }
574 cnt++;
575 }
576
577 *hint = tmp;
578 return true;
579}
580
581static bool initNeeded4(unsigned cur, unsigned tgt, unsigned max, unsigned* hint)
582{
583 assert(cur != tgt);
584
585 unsigned tmp = tgt;
586 unsigned nib = 0;
587 unsigned cnt = 0;
588
589 while (tmp > max)
590 {
591 nib = tmp & 0x0f;
592 tmp >>= 4;
593 if (tmp == cur)
594 {
595 *hint = nib;
596 return false;
597 }
598 cnt++;
599 }
600
601 *hint = tmp;
602 return true;
603}
604
605static int bigEncoding3(unsigned cur, unsigned tgt, unsigned max)
606{
607 assert(cur != tgt);
608
609 unsigned tmp = tgt;
610 unsigned nib = 0;
611 unsigned cnt = 0;
612
613 while (tmp > max)
614 {
615 nib = tmp & 0x07;
616 tmp >>= 3;
617 if (tmp == cur)
618 break;
619 cnt++;
620 }
621 return cnt;
622}
623
624static int bigEncoding4(unsigned cur, unsigned tgt, unsigned max)
625{
626 assert(cur != tgt);
627
628 unsigned tmp = tgt;
629 unsigned nib = 0;
630 unsigned cnt = 0;
631
632 while (tmp > max)
633 {
634 nib = tmp & 0x0f;
635 tmp >>= 4;
636 if (tmp == cur)
637 break;
638 cnt++;
639 }
640 return cnt;
641}
642
643BYTE FASTCALL encodeHeaderNext(const InfoHdr& header, InfoHdr* state, BYTE& codeSet)
644{
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.
648
649 if (state->argCount != header.argCount)
650 {
651 // We have one-byte encodings for 0..8
652 if (header.argCount <= SET_ARGCOUNT_MAX)
653 {
654 state->argCount = header.argCount;
655 encoding = SET_ARGCOUNT + header.argCount;
656 goto DO_RETURN;
657 }
658 else
659 {
660 unsigned hint;
661 if (initNeeded4(state->argCount, header.argCount, SET_ARGCOUNT_MAX, &hint))
662 {
663 assert(hint <= SET_ARGCOUNT_MAX);
664 state->argCount = hint;
665 encoding = SET_ARGCOUNT + hint;
666 goto DO_RETURN;
667 }
668 else
669 {
670 assert(hint <= 0xf);
671 state->argCount <<= 4;
672 state->argCount += hint;
673 encoding = NEXT_FOUR_ARGCOUNT + hint;
674 goto DO_RETURN;
675 }
676 }
677 }
678
679 if (state->frameSize != header.frameSize)
680 {
681 // We have one-byte encodings for 0..7
682 if (header.frameSize <= SET_FRAMESIZE_MAX)
683 {
684 state->frameSize = header.frameSize;
685 encoding = SET_FRAMESIZE + header.frameSize;
686 goto DO_RETURN;
687 }
688 else
689 {
690 unsigned hint;
691 if (initNeeded4(state->frameSize, header.frameSize, SET_FRAMESIZE_MAX, &hint))
692 {
693 assert(hint <= SET_FRAMESIZE_MAX);
694 state->frameSize = hint;
695 encoding = SET_FRAMESIZE + hint;
696 goto DO_RETURN;
697 }
698 else
699 {
700 assert(hint <= 0xf);
701 state->frameSize <<= 4;
702 state->frameSize += hint;
703 encoding = NEXT_FOUR_FRAMESIZE + hint;
704 goto DO_RETURN;
705 }
706 }
707 }
708
709 if ((state->epilogCount != header.epilogCount) || (state->epilogAtEnd != header.epilogAtEnd))
710 {
711 if (header.epilogCount > SET_EPILOGCNT_MAX)
712 IMPL_LIMITATION("More than SET_EPILOGCNT_MAX epilogs");
713
714 state->epilogCount = header.epilogCount;
715 state->epilogAtEnd = header.epilogAtEnd;
716 encoding = SET_EPILOGCNT + header.epilogCount * 2;
717 if (header.epilogAtEnd)
718 encoding++;
719 goto DO_RETURN;
720 }
721
722 if (state->varPtrTableSize != header.varPtrTableSize)
723 {
724 assert(state->varPtrTableSize == 0 || state->varPtrTableSize == HAS_VARPTR);
725
726 if (state->varPtrTableSize == 0)
727 {
728 state->varPtrTableSize = HAS_VARPTR;
729 encoding = FLIP_VAR_PTR_TABLE_SZ;
730 goto DO_RETURN;
731 }
732 else if (header.varPtrTableSize == 0)
733 {
734 state->varPtrTableSize = 0;
735 encoding = FLIP_VAR_PTR_TABLE_SZ;
736 goto DO_RETURN;
737 }
738 }
739
740 if (state->untrackedCnt != header.untrackedCnt)
741 {
742 assert(state->untrackedCnt <= SET_UNTRACKED_MAX || state->untrackedCnt == HAS_UNTRACKED);
743
744 // We have one-byte encodings for 0..3
745 if (header.untrackedCnt <= SET_UNTRACKED_MAX)
746 {
747 state->untrackedCnt = header.untrackedCnt;
748 encoding = SET_UNTRACKED + header.untrackedCnt;
749 goto DO_RETURN;
750 }
751 else if (state->untrackedCnt != HAS_UNTRACKED)
752 {
753 state->untrackedCnt = HAS_UNTRACKED;
754 encoding = FFFF_UNTRACKED_CNT;
755 goto DO_RETURN;
756 }
757 }
758
759 if (state->epilogSize != header.epilogSize)
760 {
761 // We have one-byte encodings for 0..10
762 if (header.epilogSize <= SET_EPILOGSIZE_MAX)
763 {
764 state->epilogSize = header.epilogSize;
765 encoding = SET_EPILOGSIZE + header.epilogSize;
766 goto DO_RETURN;
767 }
768 else
769 {
770 unsigned hint;
771 if (initNeeded3(state->epilogSize, header.epilogSize, SET_EPILOGSIZE_MAX, &hint))
772 {
773 assert(hint <= SET_EPILOGSIZE_MAX);
774 state->epilogSize = hint;
775 encoding = SET_EPILOGSIZE + hint;
776 goto DO_RETURN;
777 }
778 else
779 {
780 assert(hint <= 0x7);
781 state->epilogSize <<= 3;
782 state->epilogSize += hint;
783 encoding = NEXT_THREE_EPILOGSIZE + hint;
784 goto DO_RETURN;
785 }
786 }
787 }
788
789 if (state->prologSize != header.prologSize)
790 {
791 // We have one-byte encodings for 0..16
792 if (header.prologSize <= SET_PROLOGSIZE_MAX)
793 {
794 state->prologSize = header.prologSize;
795 encoding = SET_PROLOGSIZE + header.prologSize;
796 goto DO_RETURN;
797 }
798 else
799 {
800 unsigned hint;
801 assert(SET_PROLOGSIZE_MAX > 15);
802 if (initNeeded3(state->prologSize, header.prologSize, 15, &hint))
803 {
804 assert(hint <= 15);
805 state->prologSize = hint;
806 encoding = SET_PROLOGSIZE + hint;
807 goto DO_RETURN;
808 }
809 else
810 {
811 assert(hint <= 0x7);
812 state->prologSize <<= 3;
813 state->prologSize += hint;
814 encoding = NEXT_THREE_PROLOGSIZE + hint;
815 goto DO_RETURN;
816 }
817 }
818 }
819
820 if (state->ediSaved != header.ediSaved)
821 {
822 state->ediSaved = header.ediSaved;
823 encoding = FLIP_EDI_SAVED;
824 goto DO_RETURN;
825 }
826
827 if (state->esiSaved != header.esiSaved)
828 {
829 state->esiSaved = header.esiSaved;
830 encoding = FLIP_ESI_SAVED;
831 goto DO_RETURN;
832 }
833
834 if (state->ebxSaved != header.ebxSaved)
835 {
836 state->ebxSaved = header.ebxSaved;
837 encoding = FLIP_EBX_SAVED;
838 goto DO_RETURN;
839 }
840
841 if (state->ebpSaved != header.ebpSaved)
842 {
843 state->ebpSaved = header.ebpSaved;
844 encoding = FLIP_EBP_SAVED;
845 goto DO_RETURN;
846 }
847
848 if (state->ebpFrame != header.ebpFrame)
849 {
850 state->ebpFrame = header.ebpFrame;
851 encoding = FLIP_EBP_FRAME;
852 goto DO_RETURN;
853 }
854
855 if (state->interruptible != header.interruptible)
856 {
857 state->interruptible = header.interruptible;
858 encoding = FLIP_INTERRUPTIBLE;
859 goto DO_RETURN;
860 }
861
862#if DOUBLE_ALIGN
863 if (state->doubleAlign != header.doubleAlign)
864 {
865 state->doubleAlign = header.doubleAlign;
866 encoding = FLIP_DOUBLE_ALIGN;
867 goto DO_RETURN;
868 }
869#endif
870
871 if (state->security != header.security)
872 {
873 state->security = header.security;
874 encoding = FLIP_SECURITY;
875 goto DO_RETURN;
876 }
877
878 if (state->handlers != header.handlers)
879 {
880 state->handlers = header.handlers;
881 encoding = FLIP_HANDLERS;
882 goto DO_RETURN;
883 }
884
885 if (state->localloc != header.localloc)
886 {
887 state->localloc = header.localloc;
888 encoding = FLIP_LOCALLOC;
889 goto DO_RETURN;
890 }
891
892 if (state->editNcontinue != header.editNcontinue)
893 {
894 state->editNcontinue = header.editNcontinue;
895 encoding = FLIP_EDITnCONTINUE;
896 goto DO_RETURN;
897 }
898
899 if (state->varargs != header.varargs)
900 {
901 state->varargs = header.varargs;
902 encoding = FLIP_VARARGS;
903 goto DO_RETURN;
904 }
905
906 if (state->profCallbacks != header.profCallbacks)
907 {
908 state->profCallbacks = header.profCallbacks;
909 encoding = FLIP_PROF_CALLBACKS;
910 goto DO_RETURN;
911 }
912
913 if (state->genericsContext != header.genericsContext)
914 {
915 state->genericsContext = header.genericsContext;
916 encoding = FLIP_HAS_GENERICS_CONTEXT;
917 goto DO_RETURN;
918 }
919
920 if (state->genericsContextIsMethodDesc != header.genericsContextIsMethodDesc)
921 {
922 state->genericsContextIsMethodDesc = header.genericsContextIsMethodDesc;
923 encoding = FLIP_GENERICS_CONTEXT_IS_METHODDESC;
924 goto DO_RETURN;
925 }
926
927 if (GCInfoEncodesReturnKind() && (state->returnKind != header.returnKind))
928 {
929 state->returnKind = header.returnKind;
930 codeSet = 2; // Two byte encoding
931 encoding = header.returnKind;
932 _ASSERTE(encoding < SET_RET_KIND_MAX);
933 goto DO_RETURN;
934 }
935
936 if (state->gsCookieOffset != header.gsCookieOffset)
937 {
938 assert(state->gsCookieOffset == INVALID_GS_COOKIE_OFFSET || state->gsCookieOffset == HAS_GS_COOKIE_OFFSET);
939
940 if (state->gsCookieOffset == INVALID_GS_COOKIE_OFFSET)
941 {
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;
946 goto DO_RETURN;
947 }
948 else if (header.gsCookieOffset == INVALID_GS_COOKIE_OFFSET)
949 {
950 state->gsCookieOffset = INVALID_GS_COOKIE_OFFSET;
951 encoding = FLIP_HAS_GS_COOKIE;
952 goto DO_RETURN;
953 }
954 }
955
956 if (state->syncStartOffset != header.syncStartOffset)
957 {
958 assert(state->syncStartOffset == INVALID_SYNC_OFFSET || state->syncStartOffset == HAS_SYNC_OFFSET);
959
960 if (state->syncStartOffset == INVALID_SYNC_OFFSET)
961 {
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;
966 goto DO_RETURN;
967 }
968 else if (header.syncStartOffset == INVALID_SYNC_OFFSET)
969 {
970 state->syncStartOffset = INVALID_SYNC_OFFSET;
971 encoding = FLIP_SYNC;
972 goto DO_RETURN;
973 }
974 }
975
976 if (GCInfoEncodesRevPInvokeFrame() && (state->revPInvokeOffset != header.revPInvokeOffset))
977 {
978 assert(state->revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET ||
979 state->revPInvokeOffset == HAS_REV_PINVOKE_FRAME_OFFSET);
980
981 if (state->revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET)
982 {
983 // header.revPInvokeOffset is non-zero.
984 state->revPInvokeOffset = HAS_REV_PINVOKE_FRAME_OFFSET;
985 encoding = FLIP_REV_PINVOKE_FRAME;
986 goto DO_RETURN;
987 }
988 else if (header.revPInvokeOffset == INVALID_REV_PINVOKE_OFFSET)
989 {
990 state->revPInvokeOffset = INVALID_REV_PINVOKE_OFFSET;
991 encoding = FLIP_REV_PINVOKE_FRAME;
992 goto DO_RETURN;
993 }
994 }
995
996DO_RETURN:
997 _ASSERTE(encoding < MORE_BYTES_TO_FOLLOW);
998 if (!state->isHeaderMatch(header))
999 encoding |= MORE_BYTES_TO_FOLLOW;
1000
1001 return encoding;
1002}
1003
1004static int measureDistance(const InfoHdr& header, const InfoHdrSmall* p, int closeness)
1005{
1006 int distance = 0;
1007
1008 if (p->untrackedCnt != header.untrackedCnt)
1009 {
1010 if (header.untrackedCnt > 3)
1011 {
1012 if (p->untrackedCnt != HAS_UNTRACKED)
1013 distance += 1;
1014 }
1015 else
1016 {
1017 distance += 1;
1018 }
1019 if (distance >= closeness)
1020 return distance;
1021 }
1022
1023 if (p->varPtrTableSize != header.varPtrTableSize)
1024 {
1025 if (header.varPtrTableSize != 0)
1026 {
1027 if (p->varPtrTableSize != HAS_VARPTR)
1028 distance += 1;
1029 }
1030 else
1031 {
1032 assert(p->varPtrTableSize == HAS_VARPTR);
1033 distance += 1;
1034 }
1035 if (distance >= closeness)
1036 return distance;
1037 }
1038
1039 if (p->frameSize != header.frameSize)
1040 {
1041 distance += 1;
1042 if (distance >= closeness)
1043 return distance;
1044
1045 // We have one-byte encodings for 0..7
1046 if (header.frameSize > SET_FRAMESIZE_MAX)
1047 {
1048 distance += bigEncoding4(p->frameSize, header.frameSize, SET_FRAMESIZE_MAX);
1049 if (distance >= closeness)
1050 return distance;
1051 }
1052 }
1053
1054 if (p->argCount != header.argCount)
1055 {
1056 distance += 1;
1057 if (distance >= closeness)
1058 return distance;
1059
1060 // We have one-byte encodings for 0..8
1061 if (header.argCount > SET_ARGCOUNT_MAX)
1062 {
1063 distance += bigEncoding4(p->argCount, header.argCount, SET_ARGCOUNT_MAX);
1064 if (distance >= closeness)
1065 return distance;
1066 }
1067 }
1068
1069 if (p->prologSize != header.prologSize)
1070 {
1071 distance += 1;
1072 if (distance >= closeness)
1073 return distance;
1074
1075 // We have one-byte encodings for 0..16
1076 if (header.prologSize > SET_PROLOGSIZE_MAX)
1077 {
1078 assert(SET_PROLOGSIZE_MAX > 15);
1079 distance += bigEncoding3(p->prologSize, header.prologSize, 15);
1080 if (distance >= closeness)
1081 return distance;
1082 }
1083 }
1084
1085 if (p->epilogSize != header.epilogSize)
1086 {
1087 distance += 1;
1088 if (distance >= closeness)
1089 return distance;
1090 // We have one-byte encodings for 0..10
1091 if (header.epilogSize > SET_EPILOGSIZE_MAX)
1092 {
1093 distance += bigEncoding3(p->epilogSize, header.epilogSize, SET_EPILOGSIZE_MAX);
1094 if (distance >= closeness)
1095 return distance;
1096 }
1097 }
1098
1099 if ((p->epilogCount != header.epilogCount) || (p->epilogAtEnd != header.epilogAtEnd))
1100 {
1101 distance += 1;
1102 if (distance >= closeness)
1103 return distance;
1104
1105 if (header.epilogCount > SET_EPILOGCNT_MAX)
1106 IMPL_LIMITATION("More than SET_EPILOGCNT_MAX epilogs");
1107 }
1108
1109 if (p->ediSaved != header.ediSaved)
1110 {
1111 distance += 1;
1112 if (distance >= closeness)
1113 return distance;
1114 }
1115
1116 if (p->esiSaved != header.esiSaved)
1117 {
1118 distance += 1;
1119 if (distance >= closeness)
1120 return distance;
1121 }
1122
1123 if (p->ebxSaved != header.ebxSaved)
1124 {
1125 distance += 1;
1126 if (distance >= closeness)
1127 return distance;
1128 }
1129
1130 if (p->ebpSaved != header.ebpSaved)
1131 {
1132 distance += 1;
1133 if (distance >= closeness)
1134 return distance;
1135 }
1136
1137 if (p->ebpFrame != header.ebpFrame)
1138 {
1139 distance += 1;
1140 if (distance >= closeness)
1141 return distance;
1142 }
1143
1144 if (p->interruptible != header.interruptible)
1145 {
1146 distance += 1;
1147 if (distance >= closeness)
1148 return distance;
1149 }
1150
1151#if DOUBLE_ALIGN
1152 if (p->doubleAlign != header.doubleAlign)
1153 {
1154 distance += 1;
1155 if (distance >= closeness)
1156 return distance;
1157 }
1158#endif
1159
1160 if (p->security != header.security)
1161 {
1162 distance += 1;
1163 if (distance >= closeness)
1164 return distance;
1165 }
1166
1167 if (p->handlers != header.handlers)
1168 {
1169 distance += 1;
1170 if (distance >= closeness)
1171 return distance;
1172 }
1173
1174 if (p->localloc != header.localloc)
1175 {
1176 distance += 1;
1177 if (distance >= closeness)
1178 return distance;
1179 }
1180
1181 if (p->editNcontinue != header.editNcontinue)
1182 {
1183 distance += 1;
1184 if (distance >= closeness)
1185 return distance;
1186 }
1187
1188 if (p->varargs != header.varargs)
1189 {
1190 distance += 1;
1191 if (distance >= closeness)
1192 return distance;
1193 }
1194
1195 if (p->profCallbacks != header.profCallbacks)
1196 {
1197 distance += 1;
1198 if (distance >= closeness)
1199 return distance;
1200 }
1201
1202 if (p->genericsContext != header.genericsContext)
1203 {
1204 distance += 1;
1205 if (distance >= closeness)
1206 return distance;
1207 }
1208
1209 if (p->genericsContextIsMethodDesc != header.genericsContextIsMethodDesc)
1210 {
1211 distance += 1;
1212 if (distance >= closeness)
1213 return distance;
1214 }
1215
1216 if (p->returnKind != header.returnKind)
1217 {
1218 // Setting the ReturnKind requires two bytes of encoding.
1219 distance += 2;
1220 if (distance >= closeness)
1221 return distance;
1222 }
1223
1224 if (header.gsCookieOffset != INVALID_GS_COOKIE_OFFSET)
1225 {
1226 distance += 1;
1227 if (distance >= closeness)
1228 return distance;
1229 }
1230
1231 if (header.syncStartOffset != INVALID_SYNC_OFFSET)
1232 {
1233 distance += 1;
1234 if (distance >= closeness)
1235 return distance;
1236 }
1237
1238 if (header.revPInvokeOffset != INVALID_REV_PINVOKE_OFFSET)
1239 {
1240 distance += 1;
1241 if (distance >= closeness)
1242 return distance;
1243 }
1244
1245 return distance;
1246}
1247
1248// DllMain calls gcInitEncoderLookupTable to fill in this table
1249/* extern */ int infoHdrLookup[IH_MAX_PROLOG_SIZE + 2];
1250
1251/* static */ void GCInfo::gcInitEncoderLookupTable()
1252{
1253 const InfoHdrSmall* p = &infoHdrShortcut[0];
1254 int lo = -1;
1255 int hi = 0;
1256 int n;
1257
1258 for (n = 0; n < 128; n++, p++)
1259 {
1260 if (p->prologSize != lo)
1261 {
1262 if (p->prologSize < lo)
1263 {
1264 assert(p->prologSize == 0);
1265 hi = IH_MAX_PROLOG_SIZE;
1266 }
1267 else
1268 hi = p->prologSize;
1269
1270 assert(hi <= IH_MAX_PROLOG_SIZE);
1271
1272 while (lo < hi)
1273 infoHdrLookup[++lo] = n;
1274
1275 if (lo == IH_MAX_PROLOG_SIZE)
1276 break;
1277 }
1278 }
1279
1280 assert(lo == IH_MAX_PROLOG_SIZE);
1281 assert(infoHdrLookup[IH_MAX_PROLOG_SIZE] < 128);
1282
1283 while (p->prologSize == lo)
1284 {
1285 n++;
1286 if (n >= 128)
1287 break;
1288 p++;
1289 }
1290
1291 infoHdrLookup[++lo] = n;
1292
1293#ifdef DEBUG
1294 //
1295 // We do some other DEBUG only validity checks here
1296 //
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++)
1303 {
1304 CallPattern pat;
1305 pat.val = callPatternTable[inx];
1306
1307 assert(pat.fld.codeDelta <= CP_MAX_CODE_DELTA);
1308 if (pat.fld.codeDelta == CP_MAX_CODE_DELTA)
1309 maxMarks |= 0x01;
1310
1311 assert(pat.fld.argCnt <= CP_MAX_ARG_CNT);
1312 if (pat.fld.argCnt == CP_MAX_ARG_CNT)
1313 maxMarks |= 0x02;
1314
1315 assert(pat.fld.argMask <= CP_MAX_ARG_MASK);
1316 if (pat.fld.argMask == CP_MAX_ARG_MASK)
1317 maxMarks |= 0x04;
1318 }
1319 assert(maxMarks == 0x07);
1320#endif
1321}
1322
1323const int NO_CACHED_HEADER = -1;
1324
1325BYTE FASTCALL encodeHeaderFirst(const InfoHdr& header, InfoHdr* state, int* more, int* pCached)
1326{
1327 // First try the cached value for an exact match, if there is one
1328 //
1329 int n = *pCached;
1330 const InfoHdrSmall* p;
1331
1332 if (n != NO_CACHED_HEADER)
1333 {
1334 p = &infoHdrShortcut[n];
1335 if (p->isHeaderMatch(header))
1336 {
1337 // exact match found
1338 GetInfoHdr(n, state);
1339 *more = 0;
1340 return n;
1341 }
1342 }
1343
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
1348 //
1349 unsigned psz = header.prologSize;
1350 int lo = 0;
1351 int hi = 0;
1352
1353 if (psz <= IH_MAX_PROLOG_SIZE)
1354 {
1355 lo = infoHdrLookup[psz];
1356 hi = infoHdrLookup[psz + 1];
1357 p = &infoHdrShortcut[lo];
1358 for (n = lo; n < hi; n++, p++)
1359 {
1360 assert(psz == p->prologSize);
1361 if (p->isHeaderMatch(header))
1362 {
1363 // exact match found
1364 GetInfoHdr(n, state);
1365 *pCached = n; // cache the value
1366 *more = 0;
1367 return n;
1368 }
1369 }
1370 }
1371
1372 //
1373 // no exact match in infoHdrShortcut[]
1374 //
1375 // find the nearest entry in the table
1376 //
1377 int nearest = -1;
1378 int closeness = 255; // (i.e. not very close)
1379
1380 //
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
1384 //
1385 int min_acceptable_distance = 1;
1386
1387 if (header.frameSize > SET_FRAMESIZE_MAX)
1388 {
1389 ++min_acceptable_distance;
1390 if (header.frameSize > 32)
1391 ++min_acceptable_distance;
1392 }
1393 if (header.argCount > SET_ARGCOUNT_MAX)
1394 {
1395 ++min_acceptable_distance;
1396 if (header.argCount > 32)
1397 ++min_acceptable_distance;
1398 }
1399
1400 // First try the cached value
1401 // and see if it meets the minimum acceptable distance
1402 //
1403 if (*pCached != NO_CACHED_HEADER)
1404 {
1405 p = &infoHdrShortcut[*pCached];
1406 int distance = measureDistance(header, p, closeness);
1407 assert(distance > 0);
1408 if (distance <= min_acceptable_distance)
1409 {
1410 GetInfoHdr(*pCached, state);
1411 *more = distance;
1412 return 0x80 | *pCached;
1413 }
1414 else
1415 {
1416 closeness = distance;
1417 nearest = *pCached;
1418 }
1419 }
1420
1421 // Then try the ones pointed to by [lo..hi),
1422 // (i.e. the ones that have the correct prolog size)
1423 //
1424 p = &infoHdrShortcut[lo];
1425 for (n = lo; n < hi; n++, p++)
1426 {
1427 if (n == *pCached)
1428 continue; // already tried this one
1429 int distance = measureDistance(header, p, closeness);
1430 assert(distance > 0);
1431 if (distance <= min_acceptable_distance)
1432 {
1433 GetInfoHdr(n, state);
1434 *pCached = n; // Cache this value
1435 *more = distance;
1436 return 0x80 | n;
1437 }
1438 else if (distance < closeness)
1439 {
1440 closeness = distance;
1441 nearest = n;
1442 }
1443 }
1444
1445 int last = infoHdrLookup[IH_MAX_PROLOG_SIZE + 1];
1446 assert(last <= 128);
1447
1448 // Then try all the rest [0..last-1]
1449 p = &infoHdrShortcut[0];
1450 for (n = 0; n < last; n++, p++)
1451 {
1452 if (n == *pCached)
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)
1459 {
1460 GetInfoHdr(n, state);
1461 *pCached = n; // Cache this value
1462 *more = distance;
1463 return 0x80 | n;
1464 }
1465 else if (distance < closeness)
1466 {
1467 closeness = distance;
1468 nearest = n;
1469 }
1470 }
1471
1472 //
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.
1476 //
1477 assert((nearest >= 0) && (nearest <= 127));
1478 GetInfoHdr(nearest, state);
1479 *pCached = nearest; // Cache this value
1480 *more = closeness;
1481 return 0x80 | nearest;
1482}
1483
1484/*****************************************************************************
1485 *
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).
1489 */
1490
1491size_t GCInfo::gcInfoBlockHdrSave(
1492 BYTE* dest, int mask, unsigned methodSize, unsigned prologSize, unsigned epilogSize, InfoHdr* header, int* pCached)
1493{
1494#ifdef DEBUG
1495 if (compiler->verbose)
1496 printf("*************** In gcInfoBlockHdrSave()\n");
1497#endif
1498 size_t size = 0;
1499
1500#if VERIFY_GC_TABLES
1501 *castto(dest, unsigned short*)++ = 0xFEEF;
1502 size += sizeof(short);
1503#endif
1504
1505 /* Write the method size first (using between 1 and 5 bytes) */
1506 CLANG_FORMAT_COMMENT_ANCHOR;
1507
1508#ifdef DEBUG
1509 if (compiler->verbose)
1510 {
1511 if (mask)
1512 printf("GCINFO: methodSize = %04X\n", methodSize);
1513 if (mask)
1514 printf("GCINFO: prologSize = %04X\n", prologSize);
1515 if (mask)
1516 printf("GCINFO: epilogSize = %04X\n", epilogSize);
1517 }
1518#endif
1519
1520 size_t methSz = encodeUnsigned(dest, methodSize);
1521 size += methSz;
1522 dest += methSz & mask;
1523
1524 //
1525 // New style InfoBlk Header
1526 //
1527 // Typically only uses one-byte to store everything.
1528 //
1529
1530 if (mask == 0)
1531 {
1532 memset(header, 0, sizeof(InfoHdr));
1533 *pCached = NO_CACHED_HEADER;
1534 }
1535
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();
1544
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;
1551
1552 header->interruptible = compiler->codeGen->genInterruptible;
1553
1554 if (!compiler->isFramePointerUsed())
1555 {
1556#if DOUBLE_ALIGN
1557 if (compiler->genDoubleAlign())
1558 {
1559 header->ebpSaved = true;
1560 assert(!compiler->codeGen->regSet.rsRegsModified(RBM_EBP));
1561 }
1562#endif
1563 if (compiler->codeGen->regSet.rsRegsModified(RBM_EBP))
1564 {
1565 header->ebpSaved = true;
1566 }
1567 }
1568 else
1569 {
1570 header->ebpSaved = true;
1571 header->ebpFrame = true;
1572 }
1573
1574#if DOUBLE_ALIGN
1575 header->doubleAlign = compiler->genDoubleAlign();
1576#endif
1577
1578 header->security = compiler->opts.compNeedSecurityCheck;
1579
1580 header->handlers = compiler->ehHasCallableHandlers();
1581 header->localloc = compiler->compLocallocUsed;
1582
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));
1589
1590 if (GCInfoEncodesReturnKind())
1591 {
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;
1597 }
1598
1599 header->gsCookieOffset = INVALID_GS_COOKIE_OFFSET;
1600 if (compiler->getNeedsGSSecurityCookie())
1601 {
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);
1606 }
1607
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)
1613 {
1614 assert(compiler->syncStartEmitCookie != NULL);
1615 header->syncStartOffset = compiler->getEmitter()->emitCodeOffset(compiler->syncStartEmitCookie, 0);
1616 assert(header->syncStartOffset != INVALID_SYNC_OFFSET);
1617
1618 assert(compiler->syncEndEmitCookie != NULL);
1619 header->syncEndOffset = compiler->getEmitter()->emitCodeOffset(compiler->syncEndEmitCookie, 0);
1620 assert(header->syncEndOffset != INVALID_SYNC_OFFSET);
1621
1622 assert(header->syncStartOffset < header->syncEndOffset);
1623 // synchronized methods can't have more than 1 epilog
1624 assert(header->epilogCount <= 1);
1625 }
1626#endif
1627
1628 header->revPInvokeOffset = INVALID_REV_PINVOKE_OFFSET;
1629
1630 assert((compiler->compArgSize & 0x3) == 0);
1631
1632 size_t argCount =
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);
1636
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");
1640
1641 if (mask == 0)
1642 {
1643 gcCountForHeader((UNALIGNED unsigned int*)&header->untrackedCnt,
1644 (UNALIGNED unsigned int*)&header->varPtrTableSize);
1645 }
1646
1647 //
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
1651 //
1652 InfoHdr state;
1653 int more = 0;
1654 BYTE headerEncoding = encodeHeaderFirst(*header, &state, &more, pCached);
1655 ++size;
1656 if (mask)
1657 {
1658#if REGEN_SHORTCUTS
1659 regenLog(headerEncoding, header, &state);
1660#endif
1661 *dest++ = headerEncoding;
1662
1663 BYTE encoding = headerEncoding;
1664 BYTE codeSet = 1;
1665 while (encoding & MORE_BYTES_TO_FOLLOW)
1666 {
1667 encoding = encodeHeaderNext(*header, &state, codeSet);
1668
1669#if REGEN_SHORTCUTS
1670 regenLog(headerEncoding, header, &state);
1671#endif
1672 _ASSERTE(codeSet == 1 || codeSet == 2 && "Encoding must correspond to InfoHdrAdjust or InfoHdrAdjust2");
1673 if (codeSet == 2)
1674 {
1675 *dest++ = NEXT_OPCODE | MORE_BYTES_TO_FOLLOW;
1676 ++size;
1677 }
1678
1679 *dest++ = encoding;
1680 ++size;
1681 }
1682 }
1683 else
1684 {
1685 size += more;
1686 }
1687
1688 if (header->untrackedCnt > SET_UNTRACKED_MAX)
1689 {
1690 unsigned count = header->untrackedCnt;
1691 unsigned sz = encodeUnsigned(mask ? dest : NULL, count);
1692 size += sz;
1693 dest += (sz & mask);
1694 }
1695
1696 if (header->varPtrTableSize != 0)
1697 {
1698 unsigned count = header->varPtrTableSize;
1699 unsigned sz = encodeUnsigned(mask ? dest : NULL, count);
1700 size += sz;
1701 dest += (sz & mask);
1702 }
1703
1704 if (header->gsCookieOffset != INVALID_GS_COOKIE_OFFSET)
1705 {
1706 assert(mask == 0 || state.gsCookieOffset == HAS_GS_COOKIE_OFFSET);
1707 unsigned offset = header->gsCookieOffset;
1708 unsigned sz = encodeUnsigned(mask ? dest : NULL, offset);
1709 size += sz;
1710 dest += (sz & mask);
1711 }
1712
1713 if (header->syncStartOffset != INVALID_SYNC_OFFSET)
1714 {
1715 assert(mask == 0 || state.syncStartOffset == HAS_SYNC_OFFSET);
1716
1717 {
1718 unsigned offset = header->syncStartOffset;
1719 unsigned sz = encodeUnsigned(mask ? dest : NULL, offset);
1720 size += sz;
1721 dest += (sz & mask);
1722 }
1723
1724 {
1725 unsigned offset = header->syncEndOffset;
1726 unsigned sz = encodeUnsigned(mask ? dest : NULL, offset);
1727 size += sz;
1728 dest += (sz & mask);
1729 }
1730 }
1731
1732 if (header->epilogCount)
1733 {
1734 /* Generate table unless one epilog at the end of the method */
1735
1736 if (header->epilogAtEnd == 0 || header->epilogCount != 1)
1737 {
1738#if VERIFY_GC_TABLES
1739 *castto(dest, unsigned short*)++ = 0xFACE;
1740 size += sizeof(short);
1741#endif
1742
1743 /* Simply write a sorted array of offsets using encodeUDelta */
1744
1745 gcEpilogTable = mask ? dest : NULL;
1746 gcEpilogPrevOffset = 0;
1747
1748 size_t sz = compiler->getEmitter()->emitGenEpilogLst(gcRecordEpilog, this);
1749
1750 /* Add the size of the epilog table to the total size */
1751
1752 size += sz;
1753 dest += (sz & mask);
1754 }
1755 }
1756
1757#if DISPLAY_SIZES
1758
1759 if (mask)
1760 {
1761 if (compiler->codeGen->genInterruptible)
1762 {
1763 genMethodICnt++;
1764 }
1765 else
1766 {
1767 genMethodNCnt++;
1768 }
1769 }
1770
1771#endif // DISPLAY_SIZES
1772
1773 return size;
1774}
1775
1776/*****************************************************************************
1777 *
1778 * Return the size of the pointer tracking tables.
1779 */
1780
1781size_t GCInfo::gcPtrTableSize(const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
1782{
1783 BYTE temp[16 + 1];
1784#ifdef DEBUG
1785 temp[16] = 0xAB; // Set some marker
1786#endif
1787
1788 /* Compute the total size of the tables */
1789
1790 size_t size = gcMakeRegPtrTable(temp, 0, header, codeSize, pArgTabOffset);
1791
1792 assert(temp[16] == 0xAB); // Check that marker didnt get overwritten
1793
1794 return size;
1795}
1796
1797/*****************************************************************************
1798 * Encode the callee-saved registers into 3 bits.
1799 */
1800
1801unsigned gceEncodeCalleeSavedRegs(unsigned regs)
1802{
1803 unsigned encodedRegs = 0;
1804
1805 if (regs & RBM_EBX)
1806 encodedRegs |= 0x04;
1807 if (regs & RBM_ESI)
1808 encodedRegs |= 0x02;
1809 if (regs & RBM_EDI)
1810 encodedRegs |= 0x01;
1811
1812 return encodedRegs;
1813}
1814
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
1818 */
1819
1820inline BYTE* gceByrefPrefixI(GCInfo::regPtrDsc* rpd, BYTE* dest)
1821{
1822 // For registers, we don't need a prefix if it is going dead.
1823 assert(rpd->rpdArg || rpd->rpdCompiler.rpdDel == 0);
1824
1825 if (!rpd->rpdArg || rpd->rpdArgType == GCInfo::rpdARG_PUSH)
1826 if (rpd->rpdGCtypeGet() == GCT_BYREF)
1827 *dest++ = 0xBF;
1828
1829 return dest;
1830}
1831
1832/*****************************************************************************/
1833
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 */
1836static int zeroFN()
1837{
1838 return 0;
1839}
1840static int (*zeroFunc)() = zeroFN;
1841
1842/*****************************************************************************
1843 * Modelling of the GC ptrs pushed on the stack
1844 */
1845
1846typedef unsigned pasMaskType;
1847#define BITS_IN_pasMask (BITS_IN_BYTE * sizeof(pasMaskType))
1848#define HIGHEST_pasMask_BIT (((pasMaskType)0x1) << (BITS_IN_pasMask - 1))
1849
1850//-----------------------------------------------------------------------------
1851
1852class PendingArgsStack
1853{
1854public:
1855 PendingArgsStack(unsigned maxDepth, Compiler* pComp);
1856
1857 void pasPush(GCtype gcType);
1858 void pasPop(unsigned count);
1859 void pasKill(unsigned gcCount);
1860
1861 unsigned pasCurDepth()
1862 {
1863 return pasDepth;
1864 }
1865 pasMaskType pasArgMask()
1866 {
1867 assert(pasDepth <= BITS_IN_pasMask);
1868 return pasBottomMask;
1869 }
1870 pasMaskType pasByrefArgMask()
1871 {
1872 assert(pasDepth <= BITS_IN_pasMask);
1873 return pasByrefBottomMask;
1874 }
1875 bool pasHasGCptrs();
1876
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);
1883
1884protected:
1885 unsigned pasMaxDepth;
1886
1887 unsigned pasDepth;
1888
1889 pasMaskType pasBottomMask; // The first 32 args
1890 pasMaskType pasByrefBottomMask; // byref qualifier for pasBottomMask
1891
1892 BYTE* pasTopArray; // More than 32 args are represented here
1893 unsigned pasPtrsInTopArray; // How many GCptrs here
1894};
1895
1896//-----------------------------------------------------------------------------
1897
1898PendingArgsStack::PendingArgsStack(unsigned maxDepth, Compiler* pComp)
1899 : pasMaxDepth(maxDepth)
1900 , pasDepth(0)
1901 , pasBottomMask(0)
1902 , pasByrefBottomMask(0)
1903 , pasTopArray(NULL)
1904 , pasPtrsInTopArray(0)
1905{
1906 /* Do we need an array as well as the mask ? */
1907
1908 if (pasMaxDepth > BITS_IN_pasMask)
1909 pasTopArray = pComp->getAllocator(CMK_Unknown).allocate<BYTE>(pasMaxDepth - BITS_IN_pasMask);
1910}
1911
1912//-----------------------------------------------------------------------------
1913
1914void PendingArgsStack::pasPush(GCtype gcType)
1915{
1916 assert(pasDepth < pasMaxDepth);
1917
1918 if (pasDepth < BITS_IN_pasMask)
1919 {
1920 /* Shift the mask */
1921
1922 pasBottomMask <<= 1;
1923 pasByrefBottomMask <<= 1;
1924
1925 if (needsGC(gcType))
1926 {
1927 pasBottomMask |= 1;
1928
1929 if (gcType == GCT_BYREF)
1930 pasByrefBottomMask |= 1;
1931 }
1932 }
1933 else
1934 {
1935 /* Push on array */
1936
1937 pasTopArray[pasDepth - BITS_IN_pasMask] = (BYTE)gcType;
1938
1939 if (gcType)
1940 pasPtrsInTopArray++;
1941 }
1942
1943 pasDepth++;
1944}
1945
1946//-----------------------------------------------------------------------------
1947
1948void PendingArgsStack::pasPop(unsigned count)
1949{
1950 assert(pasDepth >= count);
1951
1952 /* First pop from array (if applicable) */
1953
1954 for (/**/; (pasDepth > BITS_IN_pasMask) && count; pasDepth--, count--)
1955 {
1956 unsigned topIndex = pasDepth - BITS_IN_pasMask - 1;
1957
1958 GCtype topArg = (GCtype)pasTopArray[topIndex];
1959
1960 if (needsGC(topArg))
1961 pasPtrsInTopArray--;
1962 }
1963 if (count == 0)
1964 return;
1965
1966 /* Now un-shift the mask */
1967
1968 assert(pasPtrsInTopArray == 0);
1969 assert(count <= BITS_IN_pasMask);
1970
1971 if (count == BITS_IN_pasMask) // (x>>32) is a nop on x86. So special-case it
1972 {
1973 pasBottomMask = pasByrefBottomMask = 0;
1974 pasDepth = 0;
1975 }
1976 else
1977 {
1978 pasBottomMask >>= count;
1979 pasByrefBottomMask >>= count;
1980 pasDepth -= count;
1981 }
1982}
1983
1984//-----------------------------------------------------------------------------
1985// Kill (but don't pop) the top 'gcCount' args
1986
1987void PendingArgsStack::pasKill(unsigned gcCount)
1988{
1989 assert(gcCount != 0);
1990
1991 /* First kill args in array (if any) */
1992
1993 for (unsigned curPos = pasDepth; (curPos > BITS_IN_pasMask) && gcCount; curPos--)
1994 {
1995 unsigned curIndex = curPos - BITS_IN_pasMask - 1;
1996
1997 GCtype curArg = (GCtype)pasTopArray[curIndex];
1998
1999 if (needsGC(curArg))
2000 {
2001 pasTopArray[curIndex] = GCT_NONE;
2002 pasPtrsInTopArray--;
2003 gcCount--;
2004 }
2005 }
2006
2007 /* Now kill bits from the mask */
2008
2009 assert(pasPtrsInTopArray == 0);
2010 assert(gcCount <= BITS_IN_pasMask);
2011
2012 for (unsigned bitPos = 1; gcCount; bitPos <<= 1)
2013 {
2014 assert(pasBottomMask != 0);
2015
2016 if (pasBottomMask & bitPos)
2017 {
2018 pasBottomMask &= ~bitPos;
2019 pasByrefBottomMask &= ~bitPos;
2020 --gcCount;
2021 }
2022 else
2023 {
2024 assert(bitPos != HIGHEST_pasMask_BIT);
2025 }
2026 }
2027}
2028
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
2032
2033bool PendingArgsStack::pasHasGCptrs()
2034{
2035 if (pasDepth <= BITS_IN_pasMask)
2036 return pasBottomMask != 0;
2037 else
2038 return pasBottomMask != 0 || pasPtrsInTopArray != 0;
2039}
2040
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
2044
2045unsigned PendingArgsStack::pasEnumGCoffsCount()
2046{
2047 /* Should only be used in the worst case, when just the mask can't be used */
2048
2049 assert(pasDepth > BITS_IN_pasMask && pasHasGCptrs());
2050
2051 /* Count number of set bits in mask */
2052
2053 unsigned count = 0;
2054
2055 for (pasMaskType mask = 0x1, i = 0; i < BITS_IN_pasMask; mask <<= 1, i++)
2056 {
2057 if (mask & pasBottomMask)
2058 count++;
2059 }
2060
2061 return count + pasPtrsInTopArray;
2062}
2063
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
2069
2070unsigned PendingArgsStack::pasEnumGCoffs(unsigned iter, unsigned* offs)
2071{
2072 if (iter == pasENUM_LAST)
2073 return pasENUM_END;
2074
2075 unsigned i = (iter == pasENUM_START) ? pasDepth : iter;
2076
2077 for (/**/; i > BITS_IN_pasMask; i--)
2078 {
2079 GCtype curArg = (GCtype)pasTopArray[i - BITS_IN_pasMask - 1];
2080 if (needsGC(curArg))
2081 {
2082 unsigned offset;
2083
2084 offset = (pasDepth - i) * TARGET_POINTER_SIZE;
2085 if (curArg == GCT_BYREF)
2086 offset |= byref_OFFSET_FLAG;
2087
2088 *offs = offset;
2089 return i - 1;
2090 }
2091 }
2092
2093 if (!pasBottomMask)
2094 return pasENUM_END;
2095
2096 // Have we already processed some of the bits in pasBottomMask ?
2097
2098 i = (iter == pasENUM_START || iter >= BITS_IN_pasMask) ? 0 // no
2099 : iter; // yes
2100
2101 for (pasMaskType mask = 0x1 << i; mask; i++, mask <<= 1)
2102 {
2103 if (mask & pasBottomMask)
2104 {
2105 unsigned lvl = (pasDepth > BITS_IN_pasMask) ? (pasDepth - BITS_IN_pasMask) : 0; // How many in pasTopArray[]
2106 lvl += i;
2107
2108 unsigned offset;
2109 offset = lvl * TARGET_POINTER_SIZE;
2110 if (mask & pasByrefBottomMask)
2111 offset |= byref_OFFSET_FLAG;
2112
2113 *offs = offset;
2114
2115 unsigned remMask = -int(mask << 1);
2116 return ((pasBottomMask & remMask) ? (i + 1) : pasENUM_LAST);
2117 }
2118 }
2119
2120 assert(!"Shouldnt reach here");
2121 return pasENUM_END;
2122}
2123
2124/*****************************************************************************
2125 *
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.
2130 */
2131
2132#ifdef _PREFAST_
2133#pragma warning(push)
2134#pragma warning(disable : 21000) // Suppress PREFast warning about overly large function
2135#endif
2136size_t GCInfo::gcMakeRegPtrTable(BYTE* dest, int mask, const InfoHdr& header, unsigned codeSize, size_t* pArgTabOffset)
2137{
2138 unsigned count;
2139
2140 unsigned varNum;
2141 LclVarDsc* varDsc;
2142
2143 unsigned pass;
2144
2145 size_t totalSize = 0;
2146 unsigned lastOffset;
2147
2148 bool thisKeptAliveIsInUntracked = false;
2149
2150 /* The mask should be all 0's or all 1's */
2151
2152 assert(mask == 0 || mask == -1);
2153
2154 /* Start computing the total size of the table */
2155
2156 BOOL emitArgTabOffset = (header.varPtrTableSize != 0 || header.untrackedCnt > SET_UNTRACKED_MAX);
2157 if (mask != 0 && emitArgTabOffset)
2158 {
2159 assert(*pArgTabOffset <= MAX_UNSIGNED_SIZE_T);
2160 unsigned sz = encodeUnsigned(dest, static_cast<unsigned>(*pArgTabOffset));
2161 dest += sz;
2162 totalSize += sz;
2163 }
2164
2165#if VERIFY_GC_TABLES
2166 if (mask)
2167 {
2168 *(short*)dest = (short)0xBEEF;
2169 dest += sizeof(short);
2170 }
2171 totalSize += sizeof(short);
2172#endif
2173
2174 /**************************************************************************
2175 *
2176 * Untracked ptr variables
2177 *
2178 **************************************************************************
2179 */
2180
2181 count = 0;
2182 for (pass = 0; pass < 2; pass++)
2183 {
2184 /* If pass==0, generate the count
2185 * If pass==1, write the table of untracked pointer variables.
2186 */
2187
2188 int lastoffset = 0;
2189 if (pass == 1)
2190 {
2191 assert(count == header.untrackedCnt);
2192 if (header.untrackedCnt == 0)
2193 break; // No entries, break exits the loop since pass==1
2194 }
2195
2196 /* Count&Write untracked locals and non-enregistered args */
2197
2198 for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
2199 {
2200 if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
2201 {
2202 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
2203 // reported through its parent local
2204 continue;
2205 }
2206
2207 if (varTypeIsGC(varDsc->TypeGet()))
2208 {
2209 /* Do we have an argument or local variable? */
2210 if (!varDsc->lvIsParam)
2211 {
2212 // If is is pinned, it must be an untracked local
2213 assert(!varDsc->lvPinned || !varDsc->lvTracked);
2214
2215 if (varDsc->lvTracked || !varDsc->lvOnFrame)
2216 continue;
2217 }
2218 else
2219 {
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
2223 */
2224
2225 /* Has this argument been enregistered? */
2226 if (!varDsc->lvOnFrame)
2227 {
2228 /* if a CEE_JMP has been used, then we need to report all the arguments
2229 even if they are enregistered, since we will be using this value
2230 in JMP call. Note that this is subtle as we require that
2231 argument offsets are always fixed up properly even if lvRegister
2232 is set */
2233 if (!compiler->compJmpOpUsed)
2234 continue;
2235 }
2236 else
2237 {
2238 if (!varDsc->lvOnFrame)
2239 {
2240 /* If this non-enregistered pointer arg is never
2241 * used, we don't need to report it
2242 */
2243 assert(varDsc->lvRefCnt() == 0); // This assert is currently a known issue for X86-RyuJit
2244 continue;
2245 }
2246 else if (varDsc->lvIsRegArg && varDsc->lvTracked)
2247 {
2248 /* If this register-passed arg is tracked, then
2249 * it has been allocated space near the other
2250 * pointer variables and we have accurate life-
2251 * time info. It will be reported with
2252 * gcVarPtrList in the "tracked-pointer" section
2253 */
2254
2255 continue;
2256 }
2257 }
2258 }
2259
2260#ifndef WIN64EXCEPTIONS
2261 // For WIN64EXCEPTIONS, "this" must always be in untracked variables
2262 // so we cannot have "this" in variable lifetimes
2263 if (compiler->lvaIsOriginalThisArg(varNum) && compiler->lvaKeepAliveAndReportThis())
2264
2265 {
2266 // Encoding of untracked variables does not support reporting
2267 // "this". So report it as a tracked variable with a liveness
2268 // extending over the entire method.
2269
2270 thisKeptAliveIsInUntracked = true;
2271 continue;
2272 }
2273#endif
2274
2275 if (pass == 0)
2276 count++;
2277 else
2278 {
2279 int offset;
2280 assert(pass == 1);
2281
2282 offset = varDsc->lvStkOffs;
2283#if DOUBLE_ALIGN
2284 // For genDoubleAlign(), locals are addressed relative to ESP and
2285 // arguments are addressed relative to EBP.
2286
2287 if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg)
2288 offset += compiler->codeGen->genTotalFrameSize();
2289#endif
2290
2291 // The lower bits of the offset encode properties of the stk ptr
2292
2293 assert(~OFFSET_MASK % sizeof(offset) == 0);
2294
2295 if (varDsc->TypeGet() == TYP_BYREF)
2296 {
2297 // Or in byref_OFFSET_FLAG for 'byref' pointer tracking
2298 offset |= byref_OFFSET_FLAG;
2299 }
2300
2301 if (varDsc->lvPinned)
2302 {
2303 // Or in pinned_OFFSET_FLAG for 'pinned' pointer tracking
2304 offset |= pinned_OFFSET_FLAG;
2305 }
2306
2307 int encodedoffset = lastoffset - offset;
2308 lastoffset = offset;
2309
2310 if (mask == 0)
2311 totalSize += encodeSigned(NULL, encodedoffset);
2312 else
2313 {
2314 unsigned sz = encodeSigned(dest, encodedoffset);
2315 dest += sz;
2316 totalSize += sz;
2317 }
2318 }
2319 }
2320
2321 // A struct will have gcSlots only if it is at least TARGET_POINTER_SIZE.
2322 if (varDsc->lvType == TYP_STRUCT && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
2323 {
2324 unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
2325 BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum);
2326
2327 // walk each member of the array
2328 for (unsigned i = 0; i < slots; i++)
2329 {
2330 if (gcPtrs[i] == TYPE_GC_NONE) // skip non-gc slots
2331 continue;
2332
2333 if (pass == 0)
2334 count++;
2335 else
2336 {
2337 assert(pass == 1);
2338
2339 unsigned offset = varDsc->lvStkOffs + i * TARGET_POINTER_SIZE;
2340#if DOUBLE_ALIGN
2341 // For genDoubleAlign(), locals are addressed relative to ESP and
2342 // arguments are addressed relative to EBP.
2343
2344 if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg)
2345 offset += compiler->codeGen->genTotalFrameSize();
2346#endif
2347 if (gcPtrs[i] == TYPE_GC_BYREF)
2348 offset |= byref_OFFSET_FLAG; // indicate it is a byref GC pointer
2349
2350 int encodedoffset = lastoffset - offset;
2351 lastoffset = offset;
2352
2353 if (mask == 0)
2354 totalSize += encodeSigned(NULL, encodedoffset);
2355 else
2356 {
2357 unsigned sz = encodeSigned(dest, encodedoffset);
2358 dest += sz;
2359 totalSize += sz;
2360 }
2361 }
2362 }
2363 }
2364 }
2365
2366 /* Count&Write spill temps that hold pointers */
2367
2368 assert(compiler->codeGen->regSet.tmpAllFree());
2369 for (TempDsc* tempItem = compiler->codeGen->regSet.tmpListBeg(); tempItem != nullptr;
2370 tempItem = compiler->codeGen->regSet.tmpListNxt(tempItem))
2371 {
2372 if (varTypeIsGC(tempItem->tdTempType()))
2373 {
2374 if (pass == 0)
2375 count++;
2376 else
2377 {
2378 int offset;
2379 assert(pass == 1);
2380
2381 offset = tempItem->tdTempOffs();
2382
2383 if (tempItem->tdTempType() == TYP_BYREF)
2384 {
2385 offset |= byref_OFFSET_FLAG;
2386 }
2387
2388 int encodedoffset = lastoffset - offset;
2389 lastoffset = offset;
2390
2391 if (mask == 0)
2392 {
2393 totalSize += encodeSigned(NULL, encodedoffset);
2394 }
2395 else
2396 {
2397 unsigned sz = encodeSigned(dest, encodedoffset);
2398 dest += sz;
2399 totalSize += sz;
2400 }
2401 }
2402 }
2403 }
2404 }
2405
2406#if VERIFY_GC_TABLES
2407 if (mask)
2408 {
2409 *(short*)dest = (short)0xCAFE;
2410 dest += sizeof(short);
2411 }
2412 totalSize += sizeof(short);
2413#endif
2414
2415 /**************************************************************************
2416 *
2417 * Generate the table of stack pointer variable lifetimes.
2418 *
2419 * In the first pass we'll count the lifetime entries and note
2420 * whether there are any that don't fit in a small encoding. In
2421 * the second pass we actually generate the table contents.
2422 *
2423 **************************************************************************
2424 */
2425
2426 // First we check for the most common case - no lifetimes at all.
2427
2428 if (header.varPtrTableSize == 0)
2429 goto DONE_VLT;
2430
2431 varPtrDsc* varTmp;
2432 count = 0;
2433
2434#ifndef WIN64EXCEPTIONS
2435 if (thisKeptAliveIsInUntracked)
2436 {
2437 count = 1;
2438
2439 // Encoding of untracked variables does not support reporting
2440 // "this". So report it as a tracked variable with a liveness
2441 // extending over the entire method.
2442
2443 assert(compiler->lvaTable[compiler->info.compThisArg].TypeGet() == TYP_REF);
2444
2445 unsigned varOffs = compiler->lvaTable[compiler->info.compThisArg].lvStkOffs;
2446
2447 /* For negative stack offsets we must reset the low bits,
2448 * take abs and then set them back */
2449
2450 varOffs = abs(static_cast<int>(varOffs));
2451 varOffs |= this_OFFSET_FLAG;
2452
2453 size_t sz = 0;
2454 sz = encodeUnsigned(mask ? (dest + sz) : NULL, varOffs);
2455 sz += encodeUDelta(mask ? (dest + sz) : NULL, 0, 0);
2456 sz += encodeUDelta(mask ? (dest + sz) : NULL, codeSize, 0);
2457
2458 dest += (sz & mask);
2459 totalSize += sz;
2460 }
2461#endif
2462
2463 for (pass = 0; pass < 2; pass++)
2464 {
2465 /* If second pass, generate the count */
2466
2467 if (pass)
2468 {
2469 assert(header.varPtrTableSize > 0);
2470 assert(header.varPtrTableSize == count);
2471 }
2472
2473 /* We'll use a delta encoding for the lifetime offsets */
2474
2475 lastOffset = 0;
2476
2477 for (varTmp = gcVarPtrList; varTmp; varTmp = varTmp->vpdNext)
2478 {
2479 unsigned varOffs;
2480 unsigned lowBits;
2481
2482 unsigned begOffs;
2483 unsigned endOffs;
2484
2485 assert(~OFFSET_MASK % TARGET_POINTER_SIZE == 0);
2486
2487 /* Get hold of the variable's stack offset */
2488
2489 lowBits = varTmp->vpdVarNum & OFFSET_MASK;
2490
2491 /* For negative stack offsets we must reset the low bits,
2492 * take abs and then set them back */
2493
2494 varOffs = abs(static_cast<int>(varTmp->vpdVarNum & ~OFFSET_MASK));
2495 varOffs |= lowBits;
2496
2497 /* Compute the actual lifetime offsets */
2498
2499 begOffs = varTmp->vpdBegOfs;
2500 endOffs = varTmp->vpdEndOfs;
2501
2502 /* Special case: skip any 0-length lifetimes */
2503
2504 if (endOffs == begOffs)
2505 continue;
2506
2507 /* Are we counting or generating? */
2508
2509 if (!pass)
2510 {
2511 count++;
2512 }
2513 else
2514 {
2515 size_t sz = 0;
2516 sz = encodeUnsigned(mask ? (dest + sz) : NULL, varOffs);
2517 sz += encodeUDelta(mask ? (dest + sz) : NULL, begOffs, lastOffset);
2518 sz += encodeUDelta(mask ? (dest + sz) : NULL, endOffs, begOffs);
2519
2520 dest += (sz & mask);
2521 totalSize += sz;
2522 }
2523
2524 /* The next entry will be relative to the one we just processed */
2525
2526 lastOffset = begOffs;
2527 }
2528 }
2529
2530DONE_VLT:
2531
2532 if (pArgTabOffset != NULL)
2533 *pArgTabOffset = totalSize;
2534
2535#if VERIFY_GC_TABLES
2536 if (mask)
2537 {
2538 *(short*)dest = (short)0xBABE;
2539 dest += sizeof(short);
2540 }
2541 totalSize += sizeof(short);
2542#endif
2543
2544 if (!mask && emitArgTabOffset)
2545 {
2546 assert(*pArgTabOffset <= MAX_UNSIGNED_SIZE_T);
2547 totalSize += encodeUnsigned(NULL, static_cast<unsigned>(*pArgTabOffset));
2548 }
2549
2550 /**************************************************************************
2551 *
2552 * Prepare to generate the pointer register/argument map
2553 *
2554 **************************************************************************
2555 */
2556
2557 lastOffset = 0;
2558
2559 if (compiler->codeGen->genInterruptible)
2560 {
2561#ifdef _TARGET_X86_
2562 assert(compiler->genFullPtrRegMap);
2563
2564 unsigned ptrRegs = 0;
2565
2566 regPtrDsc* genRegPtrTemp;
2567
2568 /* Walk the list of pointer register/argument entries */
2569
2570 for (genRegPtrTemp = gcRegPtrList; genRegPtrTemp; genRegPtrTemp = genRegPtrTemp->rpdNext)
2571 {
2572 BYTE* base = dest;
2573
2574 unsigned nextOffset;
2575 DWORD codeDelta;
2576
2577 nextOffset = genRegPtrTemp->rpdOffs;
2578
2579 /*
2580 Encoding table for methods that are fully interruptible
2581
2582 The encoding used is as follows:
2583
2584 ptr reg dead 00RRRDDD [RRR != 100]
2585 ptr reg live 01RRRDDD [RRR != 100]
2586
2587 non-ptr arg push 10110DDD [SSS == 110]
2588 ptr arg push 10SSSDDD [SSS != 110] && [SSS != 111]
2589 ptr arg pop 11CCCDDD [CCC != 000] && [CCC != 110] && [CCC != 111]
2590 little skip 11000DDD [CCC == 000]
2591 bigger skip 11110BBB [CCC == 110]
2592
2593 The values used in the above encodings are as follows:
2594
2595 DDD code offset delta from previous entry (0-7)
2596 BBB bigger delta 000=8,001=16,010=24,...,111=64
2597 RRR register number (EAX=000,ECX=001,EDX=010,EBX=011,
2598 EBP=101,ESI=110,EDI=111), ESP=100 is reserved
2599 SSS argument offset from base of stack. This is
2600 redundant for frameless methods as we can
2601 infer it from the previous pushes+pops. However,
2602 for EBP-methods, we only report GC pushes, and
2603 so we need SSS
2604 CCC argument count being popped (includes only ptrs for EBP methods)
2605
2606 The following are the 'large' versions:
2607
2608 large delta skip 10111000 [0xB8] , encodeUnsigned(delta)
2609
2610 large ptr arg push 11111000 [0xF8] , encodeUnsigned(pushCount)
2611 large non-ptr arg push 11111001 [0xF9] , encodeUnsigned(pushCount)
2612 large ptr arg pop 11111100 [0xFC] , encodeUnsigned(popCount)
2613 large arg dead 11111101 [0xFD] , encodeUnsigned(popCount) for caller-pop args.
2614 Any GC args go dead after the call,
2615 but are still sitting on the stack
2616
2617 this pointer prefix 10111100 [0xBC] the next encoding is a ptr live
2618 or a ptr arg push
2619 and contains the this pointer
2620
2621 interior or by-ref 10111111 [0xBF] the next encoding is a ptr live
2622 pointer prefix or a ptr arg push
2623 and contains an interior
2624 or by-ref pointer
2625
2626
2627 The value 11111111 [0xFF] indicates the end of the table.
2628 */
2629
2630 codeDelta = nextOffset - lastOffset;
2631 assert((int)codeDelta >= 0);
2632
2633 // If the code delta is between 8 and (64+7),
2634 // generate a 'bigger delta' encoding
2635
2636 if ((codeDelta >= 8) && (codeDelta <= (64 + 7)))
2637 {
2638 unsigned biggerDelta = ((codeDelta - 8) & 0x38) + 8;
2639 *dest++ = 0xF0 | ((biggerDelta - 8) >> 3);
2640 lastOffset += biggerDelta;
2641 codeDelta &= 0x07;
2642 }
2643
2644 // If the code delta is still bigger than 7,
2645 // generate a 'large code delta' encoding
2646
2647 if (codeDelta > 7)
2648 {
2649 *dest++ = 0xB8;
2650 dest += encodeUnsigned(dest, codeDelta);
2651 codeDelta = 0;
2652
2653 /* Remember the new 'last' offset */
2654
2655 lastOffset = nextOffset;
2656 }
2657
2658 /* Is this a pointer argument or register entry? */
2659
2660 if (genRegPtrTemp->rpdArg)
2661 {
2662 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL)
2663 {
2664 if (codeDelta)
2665 {
2666 /*
2667 Use the small encoding:
2668 little delta skip 11000DDD [0xC0]
2669 */
2670
2671 assert((codeDelta & 0x7) == codeDelta);
2672 *dest++ = 0xC0 | (BYTE)codeDelta;
2673
2674 /* Remember the new 'last' offset */
2675
2676 lastOffset = nextOffset;
2677 }
2678
2679 /* Caller-pop arguments are dead after call but are still
2680 sitting on the stack */
2681
2682 *dest++ = 0xFD;
2683 assert(genRegPtrTemp->rpdPtrArg != 0);
2684 dest += encodeUnsigned(dest, genRegPtrTemp->rpdPtrArg);
2685 }
2686 else if (genRegPtrTemp->rpdPtrArg < 6 && genRegPtrTemp->rpdGCtypeGet())
2687 {
2688 /* Is the argument offset/count smaller than 6 ? */
2689
2690 dest = gceByrefPrefixI(genRegPtrTemp, dest);
2691
2692 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH || (genRegPtrTemp->rpdPtrArg != 0))
2693 {
2694 /*
2695 Use the small encoding:
2696
2697 ptr arg push 10SSSDDD [SSS != 110] && [SSS != 111]
2698 ptr arg pop 11CCCDDD [CCC != 110] && [CCC != 111]
2699 */
2700
2701 bool isPop = genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP;
2702
2703 *dest++ = 0x80 | (BYTE)codeDelta | genRegPtrTemp->rpdPtrArg << 3 | isPop << 6;
2704
2705 /* Remember the new 'last' offset */
2706
2707 lastOffset = nextOffset;
2708 }
2709 else
2710 {
2711 assert(!"Check this");
2712 }
2713 }
2714 else if (genRegPtrTemp->rpdGCtypeGet() == GCT_NONE)
2715 {
2716 /*
2717 Use the small encoding:
2718` non-ptr arg push 10110DDD [0xB0] (push of sizeof(int))
2719 */
2720
2721 assert((codeDelta & 0x7) == codeDelta);
2722 *dest++ = 0xB0 | (BYTE)codeDelta;
2723#ifndef UNIX_X86_ABI
2724 assert(!compiler->isFramePointerUsed());
2725#endif
2726
2727 /* Remember the new 'last' offset */
2728
2729 lastOffset = nextOffset;
2730 }
2731 else
2732 {
2733 /* Will have to use large encoding;
2734 * first do the code delta
2735 */
2736
2737 if (codeDelta)
2738 {
2739 /*
2740 Use the small encoding:
2741 little delta skip 11000DDD [0xC0]
2742 */
2743
2744 assert((codeDelta & 0x7) == codeDelta);
2745 *dest++ = 0xC0 | (BYTE)codeDelta;
2746 }
2747
2748 /*
2749 Now append a large argument record:
2750
2751 large ptr arg push 11111000 [0xF8]
2752 large ptr arg pop 11111100 [0xFC]
2753 */
2754
2755 bool isPop = genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP;
2756
2757 dest = gceByrefPrefixI(genRegPtrTemp, dest);
2758
2759 *dest++ = 0xF8 | (isPop << 2);
2760 dest += encodeUnsigned(dest, genRegPtrTemp->rpdPtrArg);
2761
2762 /* Remember the new 'last' offset */
2763
2764 lastOffset = nextOffset;
2765 }
2766 }
2767 else
2768 {
2769 unsigned regMask;
2770
2771 /* Record any registers that are becoming dead */
2772
2773 regMask = genRegPtrTemp->rpdCompiler.rpdDel & ptrRegs;
2774
2775 while (regMask) // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2776 {
2777 unsigned tmpMask;
2778 regNumber regNum;
2779
2780 /* Get hold of the next register bit */
2781
2782 tmpMask = genFindLowestReg(regMask);
2783 assert(tmpMask);
2784
2785 /* Remember the new state of this register */
2786
2787 ptrRegs &= ~tmpMask;
2788
2789 /* Figure out which register the next bit corresponds to */
2790
2791 regNum = genRegNumFromMask(tmpMask);
2792 assert(regNum <= 7);
2793
2794 /* Reserve ESP, regNum==4 for future use */
2795
2796 assert(regNum != 4);
2797
2798 /*
2799 Generate a small encoding:
2800
2801 ptr reg dead 00RRRDDD
2802 */
2803
2804 assert((codeDelta & 0x7) == codeDelta);
2805 *dest++ = 0x00 | regNum << 3 | (BYTE)codeDelta;
2806
2807 /* Turn the bit we've just generated off and continue */
2808
2809 regMask -= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2810
2811 /* Remember the new 'last' offset */
2812
2813 lastOffset = nextOffset;
2814
2815 /* Any entries that follow will be at the same offset */
2816
2817 codeDelta = zeroFunc(); /* DO NOT REMOVE */
2818 }
2819
2820 /* Record any registers that are becoming live */
2821
2822 regMask = genRegPtrTemp->rpdCompiler.rpdAdd & ~ptrRegs;
2823
2824 while (regMask) // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2825 {
2826 unsigned tmpMask;
2827 regNumber regNum;
2828
2829 /* Get hold of the next register bit */
2830
2831 tmpMask = genFindLowestReg(regMask);
2832 assert(tmpMask);
2833
2834 /* Remember the new state of this register */
2835
2836 ptrRegs |= tmpMask;
2837
2838 /* Figure out which register the next bit corresponds to */
2839
2840 regNum = genRegNumFromMask(tmpMask);
2841 assert(regNum <= 7);
2842
2843 /*
2844 Generate a small encoding:
2845
2846 ptr reg live 01RRRDDD
2847 */
2848
2849 dest = gceByrefPrefixI(genRegPtrTemp, dest);
2850
2851 if (!thisKeptAliveIsInUntracked && genRegPtrTemp->rpdIsThis)
2852 {
2853 // Mark with 'this' pointer prefix
2854 *dest++ = 0xBC;
2855 // Can only have one bit set in regMask
2856 assert(regMask == tmpMask);
2857 }
2858
2859 assert((codeDelta & 0x7) == codeDelta);
2860 *dest++ = 0x40 | (regNum << 3) | (BYTE)codeDelta;
2861
2862 /* Turn the bit we've just generated off and continue */
2863
2864 regMask -= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
2865
2866 /* Remember the new 'last' offset */
2867
2868 lastOffset = nextOffset;
2869
2870 /* Any entries that follow will be at the same offset */
2871
2872 codeDelta = zeroFunc(); /* DO NOT REMOVE */
2873 }
2874 }
2875
2876 /* Keep track of the total amount of generated stuff */
2877
2878 totalSize += dest - base;
2879
2880 /* Go back to the buffer start if we're not generating a table */
2881
2882 if (!mask)
2883 dest = base;
2884 }
2885#endif // _TARGET_X86_
2886
2887 /* Terminate the table with 0xFF */
2888
2889 *dest = 0xFF;
2890 dest -= mask;
2891 totalSize++;
2892 }
2893 else if (compiler->isFramePointerUsed()) // genInterruptible is false
2894 {
2895#ifdef _TARGET_X86_
2896 /*
2897 Encoding table for methods with an EBP frame and
2898 that are not fully interruptible
2899
2900 The encoding used is as follows:
2901
2902 this pointer encodings:
2903
2904 01000000 this pointer in EBX
2905 00100000 this pointer in ESI
2906 00010000 this pointer in EDI
2907
2908 tiny encoding:
2909
2910 0bsdDDDD
2911 requires code delta > 0 & delta < 16 (4-bits)
2912 requires pushed argmask == 0
2913
2914 where DDDD is code delta
2915 b indicates that register EBX is a live pointer
2916 s indicates that register ESI is a live pointer
2917 d indicates that register EDI is a live pointer
2918
2919
2920 small encoding:
2921
2922 1DDDDDDD bsdAAAAA
2923
2924 requires code delta < 120 (7-bits)
2925 requires pushed argmask < 64 (5-bits)
2926
2927 where DDDDDDD is code delta
2928 AAAAA is the pushed args mask
2929 b indicates that register EBX is a live pointer
2930 s indicates that register ESI is a live pointer
2931 d indicates that register EDI is a live pointer
2932
2933 medium encoding
2934
2935 0xFD aaaaaaaa AAAAdddd bseDDDDD
2936
2937 requires code delta < 512 (9-bits)
2938 requires pushed argmask < 2048 (12-bits)
2939
2940 where DDDDD is the upper 5-bits of the code delta
2941 dddd is the low 4-bits of the code delta
2942 AAAA is the upper 4-bits of the pushed arg mask
2943 aaaaaaaa is the low 8-bits of the pushed arg mask
2944 b indicates that register EBX is a live pointer
2945 s indicates that register ESI is a live pointer
2946 e indicates that register EDI is a live pointer
2947
2948 medium encoding with interior pointers
2949
2950 0xF9 DDDDDDDD bsdAAAAAA iiiIIIII
2951
2952 requires code delta < 256 (8-bits)
2953 requires pushed argmask < 64 (5-bits)
2954
2955 where DDDDDDD is the code delta
2956 b indicates that register EBX is a live pointer
2957 s indicates that register ESI is a live pointer
2958 d indicates that register EDI is a live pointer
2959 AAAAA is the pushed arg mask
2960 iii indicates that EBX,EDI,ESI are interior pointers
2961 IIIII indicates that bits in the arg mask are interior
2962 pointers
2963
2964 large encoding
2965
2966 0xFE [0BSD0bsd][32-bit code delta][32-bit argMask]
2967
2968 b indicates that register EBX is a live pointer
2969 s indicates that register ESI is a live pointer
2970 d indicates that register EDI is a live pointer
2971 B indicates that register EBX is an interior pointer
2972 S indicates that register ESI is an interior pointer
2973 D indicates that register EDI is an interior pointer
2974 requires pushed argmask < 32-bits
2975
2976 large encoding with interior pointers
2977
2978 0xFA [0BSD0bsd][32-bit code delta][32-bit argMask][32-bit interior pointer mask]
2979
2980
2981 b indicates that register EBX is a live pointer
2982 s indicates that register ESI is a live pointer
2983 d indicates that register EDI is a live pointer
2984 B indicates that register EBX is an interior pointer
2985 S indicates that register ESI is an interior pointer
2986 D indicates that register EDI is an interior pointer
2987 requires pushed argmask < 32-bits
2988 requires pushed iArgmask < 32-bits
2989
2990
2991 huge encoding This is the only encoding that supports
2992 a pushed argmask which is greater than
2993 32-bits.
2994
2995 0xFB [0BSD0bsd][32-bit code delta]
2996 [32-bit table count][32-bit table size]
2997 [pushed ptr offsets table...]
2998
2999 b indicates that register EBX is a live pointer
3000 s indicates that register ESI is a live pointer
3001 d indicates that register EDI is a live pointer
3002 B indicates that register EBX is an interior pointer
3003 S indicates that register ESI is an interior pointer
3004 D indicates that register EDI is an interior pointer
3005 the list count is the number of entries in the list
3006 the list size gives the byte-length of the list
3007 the offsets in the list are variable-length
3008 */
3009
3010 /* If "this" is enregistered, note it. We do this explicitly here as
3011 genFullPtrRegMap==false, and so we don't have any regPtrDsc's. */
3012
3013 if (compiler->lvaKeepAliveAndReportThis() && compiler->lvaTable[compiler->info.compThisArg].lvRegister)
3014 {
3015 unsigned thisRegMask = genRegMask(compiler->lvaTable[compiler->info.compThisArg].lvRegNum);
3016 unsigned thisPtrRegEnc = gceEncodeCalleeSavedRegs(thisRegMask) << 4;
3017
3018 if (thisPtrRegEnc)
3019 {
3020 totalSize += 1;
3021 if (mask)
3022 *dest++ = thisPtrRegEnc;
3023 }
3024 }
3025
3026 CallDsc* call;
3027
3028 assert(compiler->genFullPtrRegMap == false);
3029
3030 /* Walk the list of pointer register/argument entries */
3031
3032 for (call = gcCallDescList; call; call = call->cdNext)
3033 {
3034 BYTE* base = dest;
3035 unsigned nextOffset;
3036
3037 /* Figure out the code offset of this entry */
3038
3039 nextOffset = call->cdOffs;
3040
3041 /* Compute the distance from the previous call */
3042
3043 DWORD codeDelta = nextOffset - lastOffset;
3044
3045 assert((int)codeDelta >= 0);
3046
3047 /* Remember the new 'last' offset */
3048
3049 lastOffset = nextOffset;
3050
3051 /* Compute the register mask */
3052
3053 unsigned gcrefRegMask = 0;
3054 unsigned byrefRegMask = 0;
3055
3056 gcrefRegMask |= gceEncodeCalleeSavedRegs(call->cdGCrefRegs);
3057 byrefRegMask |= gceEncodeCalleeSavedRegs(call->cdByrefRegs);
3058
3059 assert((gcrefRegMask & byrefRegMask) == 0);
3060
3061 unsigned regMask = gcrefRegMask | byrefRegMask;
3062
3063 bool byref = (byrefRegMask | call->u1.cdByrefArgMask) != 0;
3064
3065 /* Check for the really large argument offset case */
3066 /* The very rare Huge encodings */
3067
3068 if (call->cdArgCnt)
3069 {
3070 unsigned argNum;
3071 DWORD argCnt = call->cdArgCnt;
3072 DWORD argBytes = 0;
3073 BYTE* pArgBytes = DUMMY_INIT(NULL);
3074
3075 if (mask != 0)
3076 {
3077 *dest++ = 0xFB;
3078 *dest++ = (byrefRegMask << 4) | regMask;
3079 *(DWORD*)dest = codeDelta;
3080 dest += sizeof(DWORD);
3081 *(DWORD*)dest = argCnt;
3082 dest += sizeof(DWORD);
3083 // skip the byte-size for now. Just note where it will go
3084 pArgBytes = dest;
3085 dest += sizeof(DWORD);
3086 }
3087
3088 for (argNum = 0; argNum < argCnt; argNum++)
3089 {
3090 unsigned eltSize;
3091 eltSize = encodeUnsigned(dest, call->cdArgTable[argNum]);
3092 argBytes += eltSize;
3093 if (mask)
3094 dest += eltSize;
3095 }
3096
3097 if (mask == 0)
3098 {
3099 dest = base + 1 + 1 + 3 * sizeof(DWORD) + argBytes;
3100 }
3101 else
3102 {
3103 assert(dest == pArgBytes + sizeof(argBytes) + argBytes);
3104 *(DWORD*)pArgBytes = argBytes;
3105 }
3106 }
3107
3108 /* Check if we can use a tiny encoding */
3109 else if ((codeDelta < 16) && (codeDelta != 0) && (call->u1.cdArgMask == 0) && !byref)
3110 {
3111 *dest++ = (regMask << 4) | (BYTE)codeDelta;
3112 }
3113
3114 /* Check if we can use the small encoding */
3115 else if ((codeDelta < 0x79) && (call->u1.cdArgMask <= 0x1F) && !byref)
3116 {
3117 *dest++ = 0x80 | (BYTE)codeDelta;
3118 *dest++ = call->u1.cdArgMask | (regMask << 5);
3119 }
3120
3121 /* Check if we can use the medium encoding */
3122 else if (codeDelta <= 0x01FF && call->u1.cdArgMask <= 0x0FFF && !byref)
3123 {
3124 *dest++ = 0xFD;
3125 *dest++ = call->u1.cdArgMask;
3126 *dest++ = ((call->u1.cdArgMask >> 4) & 0xF0) | ((BYTE)codeDelta & 0x0F);
3127 *dest++ = (regMask << 5) | (BYTE)((codeDelta >> 4) & 0x1F);
3128 }
3129
3130 /* Check if we can use the medium encoding with byrefs */
3131 else if (codeDelta <= 0x0FF && call->u1.cdArgMask <= 0x01F)
3132 {
3133 *dest++ = 0xF9;
3134 *dest++ = (BYTE)codeDelta;
3135 *dest++ = (regMask << 5) | call->u1.cdArgMask;
3136 *dest++ = (byrefRegMask << 5) | call->u1.cdByrefArgMask;
3137 }
3138
3139 /* We'll use the large encoding */
3140 else if (!byref)
3141 {
3142 *dest++ = 0xFE;
3143 *dest++ = (byrefRegMask << 4) | regMask;
3144 *(DWORD*)dest = codeDelta;
3145 dest += sizeof(DWORD);
3146 *(DWORD*)dest = call->u1.cdArgMask;
3147 dest += sizeof(DWORD);
3148 }
3149
3150 /* We'll use the large encoding with byrefs */
3151 else
3152 {
3153 *dest++ = 0xFA;
3154 *dest++ = (byrefRegMask << 4) | regMask;
3155 *(DWORD*)dest = codeDelta;
3156 dest += sizeof(DWORD);
3157 *(DWORD*)dest = call->u1.cdArgMask;
3158 dest += sizeof(DWORD);
3159 *(DWORD*)dest = call->u1.cdByrefArgMask;
3160 dest += sizeof(DWORD);
3161 }
3162
3163 /* Keep track of the total amount of generated stuff */
3164
3165 totalSize += dest - base;
3166
3167 /* Go back to the buffer start if we're not generating a table */
3168
3169 if (!mask)
3170 dest = base;
3171 }
3172#endif // _TARGET_X86_
3173
3174 /* Terminate the table with 0xFF */
3175
3176 *dest = 0xFF;
3177 dest -= mask;
3178 totalSize++;
3179 }
3180 else // genInterruptible is false and we have an EBP-less frame
3181 {
3182 assert(compiler->genFullPtrRegMap);
3183
3184#ifdef _TARGET_X86_
3185
3186 regPtrDsc* genRegPtrTemp;
3187 regNumber thisRegNum = regNumber(0);
3188 PendingArgsStack pasStk(compiler->getEmitter()->emitMaxStackDepth, compiler);
3189
3190 /* Walk the list of pointer register/argument entries */
3191
3192 for (genRegPtrTemp = gcRegPtrList; genRegPtrTemp; genRegPtrTemp = genRegPtrTemp->rpdNext)
3193 {
3194
3195 /*
3196 * Encoding table for methods without an EBP frame and
3197 * that are not fully interruptible
3198 *
3199 * The encoding used is as follows:
3200 *
3201 * push 000DDDDD ESP push one item with 5-bit delta
3202 * push 00100000 [pushCount] ESP push multiple items
3203 * reserved 0010xxxx xxxx != 0000
3204 * reserved 0011xxxx
3205 * skip 01000000 [Delta] Skip Delta, arbitrary sized delta
3206 * skip 0100DDDD Skip small Delta, for call (DDDD != 0)
3207 * pop 01CCDDDD ESP pop CC items with 4-bit delta (CC != 00)
3208 * call 1PPPPPPP Call Pattern, P=[0..79]
3209 * call 1101pbsd DDCCCMMM Call RegMask=pbsd,ArgCnt=CCC,
3210 * ArgMask=MMM Delta=commonDelta[DD]
3211 * call 1110pbsd [ArgCnt] [ArgMask] Call ArgCnt,RegMask=pbsd,ArgMask
3212 * call 11111000 [PBSDpbsd][32-bit delta][32-bit ArgCnt]
3213 * [32-bit PndCnt][32-bit PndSize][PndOffs...]
3214 * iptr 11110000 [IPtrMask] Arbitrary Interior Pointer Mask
3215 * thisptr 111101RR This pointer is in Register RR
3216 * 00=EDI,01=ESI,10=EBX,11=EBP
3217 * reserved 111100xx xx != 00
3218 * reserved 111110xx xx != 00
3219 * reserved 11111xxx xxx != 000 && xxx != 111(EOT)
3220 *
3221 * The value 11111111 [0xFF] indicates the end of the table. (EOT)
3222 *
3223 * An offset (at which stack-walking is performed) without an explicit encoding
3224 * is assumed to be a trivial call-site (no GC registers, stack empty before and
3225 * after) to avoid having to encode all trivial calls.
3226 *
3227 * Note on the encoding used for interior pointers
3228 *
3229 * The iptr encoding must immediately precede a call encoding. It is used
3230 * to transform a normal GC pointer addresses into an interior pointers for
3231 * GC purposes. The mask supplied to the iptr encoding is read from the
3232 * least signicant bit to the most signicant bit. (i.e the lowest bit is
3233 * read first)
3234 *
3235 * p indicates that register EBP is a live pointer
3236 * b indicates that register EBX is a live pointer
3237 * s indicates that register ESI is a live pointer
3238 * d indicates that register EDI is a live pointer
3239 * P indicates that register EBP is an interior pointer
3240 * B indicates that register EBX is an interior pointer
3241 * S indicates that register ESI is an interior pointer
3242 * D indicates that register EDI is an interior pointer
3243 *
3244 * As an example the following sequence indicates that EDI.ESI and the
3245 * second pushed pointer in ArgMask are really interior pointers. The
3246 * pointer in ESI in a normal pointer:
3247 *
3248 * iptr 11110000 00010011 => read Interior Ptr, Interior Ptr,
3249 * Normal Ptr, Normal Ptr, Interior Ptr
3250 *
3251 * call 11010011 DDCCC011 RRRR=1011 => read EDI is a GC-pointer,
3252 * ESI is a GC-pointer.
3253 * EBP is a GC-pointer
3254 * MMM=0011 => read two GC-pointers arguments
3255 * on the stack (nested call)
3256 *
3257 * Since the call instruction mentions 5 GC-pointers we list them in
3258 * the required order: EDI, ESI, EBP, 1st-pushed pointer, 2nd-pushed pointer
3259 *
3260 * And we apply the Interior Pointer mask mmmm=10011 to the five GC-pointers
3261 * we learn that EDI and ESI are interior GC-pointers and that
3262 * the second push arg is an interior GC-pointer.
3263 */
3264
3265 BYTE* base = dest;
3266
3267 bool usePopEncoding;
3268 unsigned regMask;
3269 unsigned argMask;
3270 unsigned byrefRegMask;
3271 unsigned byrefArgMask;
3272 DWORD callArgCnt;
3273
3274 unsigned nextOffset;
3275 DWORD codeDelta;
3276
3277 nextOffset = genRegPtrTemp->rpdOffs;
3278
3279 /* Compute the distance from the previous call */
3280
3281 codeDelta = nextOffset - lastOffset;
3282 assert((int)codeDelta >= 0);
3283
3284#if REGEN_CALLPAT
3285 // Must initialize this flag to true when REGEN_CALLPAT is on
3286 usePopEncoding = true;
3287 unsigned origCodeDelta = codeDelta;
3288#endif
3289
3290 if (!thisKeptAliveIsInUntracked && genRegPtrTemp->rpdIsThis)
3291 {
3292 unsigned tmpMask = genRegPtrTemp->rpdCompiler.rpdAdd;
3293
3294 /* tmpMask must have exactly one bit set */
3295
3296 assert(tmpMask && ((tmpMask & (tmpMask - 1)) == 0));
3297
3298 thisRegNum = genRegNumFromMask(tmpMask);
3299 switch (thisRegNum)
3300 {
3301 case 0: // EAX
3302 case 1: // ECX
3303 case 2: // EDX
3304 case 4: // ESP
3305 break;
3306 case 7: // EDI
3307 *dest++ = 0xF4; /* 11110100 This pointer is in EDI */
3308 break;
3309 case 6: // ESI
3310 *dest++ = 0xF5; /* 11110100 This pointer is in ESI */
3311 break;
3312 case 3: // EBX
3313 *dest++ = 0xF6; /* 11110100 This pointer is in EBX */
3314 break;
3315 case 5: // EBP
3316 *dest++ = 0xF7; /* 11110100 This pointer is in EBP */
3317 break;
3318 default:
3319 break;
3320 }
3321 }
3322
3323 /* Is this a stack pointer change or call? */
3324
3325 if (genRegPtrTemp->rpdArg)
3326 {
3327 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL)
3328 {
3329 // kill 'rpdPtrArg' number of pointer variables in pasStk
3330 pasStk.pasKill(genRegPtrTemp->rpdPtrArg);
3331 }
3332 /* Is this a call site? */
3333 else if (genRegPtrTemp->rpdCall)
3334 {
3335 /* This is a true call site */
3336
3337 /* Remember the new 'last' offset */
3338
3339 lastOffset = nextOffset;
3340
3341 callArgCnt = genRegPtrTemp->rpdPtrArg;
3342
3343 unsigned gcrefRegMask = genRegPtrTemp->rpdCallGCrefRegs;
3344
3345 byrefRegMask = genRegPtrTemp->rpdCallByrefRegs;
3346
3347 assert((gcrefRegMask & byrefRegMask) == 0);
3348
3349 regMask = gcrefRegMask | byrefRegMask;
3350
3351 /* adjust argMask for this call-site */
3352 pasStk.pasPop(callArgCnt);
3353
3354 /* Do we have to use the fat encoding */
3355
3356 if (pasStk.pasCurDepth() > BITS_IN_pasMask && pasStk.pasHasGCptrs())
3357 {
3358 /* use fat encoding:
3359 * 11111000 [PBSDpbsd][32-bit delta][32-bit ArgCnt]
3360 * [32-bit PndCnt][32-bit PndSize][PndOffs...]
3361 */
3362
3363 DWORD pndCount = pasStk.pasEnumGCoffsCount();
3364 DWORD pndSize = 0;
3365 BYTE* pPndSize = DUMMY_INIT(NULL);
3366
3367 if (mask)
3368 {
3369 *dest++ = 0xF8;
3370 *dest++ = (byrefRegMask << 4) | regMask;
3371 *(DWORD*)dest = codeDelta;
3372 dest += sizeof(DWORD);
3373 *(DWORD*)dest = callArgCnt;
3374 dest += sizeof(DWORD);
3375 *(DWORD*)dest = pndCount;
3376 dest += sizeof(DWORD);
3377 pPndSize = dest;
3378 dest += sizeof(DWORD); // Leave space for pndSize
3379 }
3380
3381 unsigned offs, iter;
3382
3383 for (iter = pasStk.pasEnumGCoffs(pasENUM_START, &offs); pndCount;
3384 iter = pasStk.pasEnumGCoffs(iter, &offs), pndCount--)
3385 {
3386 unsigned eltSize = encodeUnsigned(dest, offs);
3387
3388 pndSize += eltSize;
3389 if (mask)
3390 dest += eltSize;
3391 }
3392 assert(iter == pasENUM_END);
3393
3394 if (mask == 0)
3395 {
3396 dest = base + 2 + 4 * sizeof(DWORD) + pndSize;
3397 }
3398 else
3399 {
3400 assert(pPndSize + sizeof(pndSize) + pndSize == dest);
3401 *(DWORD*)pPndSize = pndSize;
3402 }
3403
3404 goto NEXT_RPD;
3405 }
3406
3407 argMask = byrefArgMask = 0;
3408
3409 if (pasStk.pasHasGCptrs())
3410 {
3411 assert(pasStk.pasCurDepth() <= BITS_IN_pasMask);
3412
3413 argMask = pasStk.pasArgMask();
3414 byrefArgMask = pasStk.pasByrefArgMask();
3415 }
3416
3417 /* Shouldn't be reporting trivial call-sites */
3418
3419 assert(regMask || argMask || callArgCnt || pasStk.pasCurDepth());
3420
3421// Emit IPtrMask if needed
3422
3423#define CHK_NON_INTRPT_ESP_IPtrMask \
3424 \
3425 if (byrefRegMask || byrefArgMask) \
3426 { \
3427 *dest++ = 0xF0; \
3428 unsigned imask = (byrefArgMask << 4) | byrefRegMask; \
3429 dest += encodeUnsigned(dest, imask); \
3430 }
3431
3432 /* When usePopEncoding is true:
3433 * this is not an interesting call site
3434 * because nothing is live here.
3435 */
3436 usePopEncoding = ((callArgCnt < 4) && (regMask == 0) && (argMask == 0));
3437
3438 if (!usePopEncoding)
3439 {
3440 int pattern = lookupCallPattern(callArgCnt, regMask, argMask, codeDelta);
3441 if (pattern != -1)
3442 {
3443 if (pattern > 0xff)
3444 {
3445 codeDelta = pattern >> 8;
3446 pattern &= 0xff;
3447 if (codeDelta >= 16)
3448 {
3449 /* use encoding: */
3450 /* skip 01000000 [Delta] */
3451 *dest++ = 0x40;
3452 dest += encodeUnsigned(dest, codeDelta);
3453 codeDelta = 0;
3454 }
3455 else
3456 {
3457 /* use encoding: */
3458 /* skip 0100DDDD small delta=DDDD */
3459 *dest++ = 0x40 | (BYTE)codeDelta;
3460 }
3461 }
3462
3463 // Emit IPtrMask if needed
3464 CHK_NON_INTRPT_ESP_IPtrMask;
3465
3466 assert((pattern >= 0) && (pattern < 80));
3467 *dest++ = 0x80 | pattern;
3468 goto NEXT_RPD;
3469 }
3470
3471 /* See if we can use 2nd call encoding
3472 * 1101RRRR DDCCCMMM encoding */
3473
3474 if ((callArgCnt <= 7) && (argMask <= 7))
3475 {
3476 unsigned inx; // callCommonDelta[] index
3477 unsigned maxCommonDelta = callCommonDelta[3];
3478
3479 if (codeDelta > maxCommonDelta)
3480 {
3481 if (codeDelta > maxCommonDelta + 15)
3482 {
3483 /* use encoding: */
3484 /* skip 01000000 [Delta] */
3485 *dest++ = 0x40;
3486 dest += encodeUnsigned(dest, codeDelta - maxCommonDelta);
3487 }
3488 else
3489 {
3490 /* use encoding: */
3491 /* skip 0100DDDD small delta=DDDD */
3492 *dest++ = 0x40 | (BYTE)(codeDelta - maxCommonDelta);
3493 }
3494
3495 codeDelta = maxCommonDelta;
3496 inx = 3;
3497 goto EMIT_2ND_CALL_ENCODING;
3498 }
3499
3500 for (inx = 0; inx < 4; inx++)
3501 {
3502 if (codeDelta == callCommonDelta[inx])
3503 {
3504 EMIT_2ND_CALL_ENCODING:
3505 // Emit IPtrMask if needed
3506 CHK_NON_INTRPT_ESP_IPtrMask;
3507
3508 *dest++ = 0xD0 | regMask;
3509 *dest++ = (inx << 6) | (callArgCnt << 3) | argMask;
3510 goto NEXT_RPD;
3511 }
3512 }
3513
3514 unsigned minCommonDelta = callCommonDelta[0];
3515
3516 if ((codeDelta > minCommonDelta) && (codeDelta < maxCommonDelta))
3517 {
3518 assert((minCommonDelta + 16) > maxCommonDelta);
3519 /* use encoding: */
3520 /* skip 0100DDDD small delta=DDDD */
3521 *dest++ = 0x40 | (BYTE)(codeDelta - minCommonDelta);
3522
3523 codeDelta = minCommonDelta;
3524 inx = 0;
3525 goto EMIT_2ND_CALL_ENCODING;
3526 }
3527 }
3528 }
3529
3530 if (codeDelta >= 16)
3531 {
3532 unsigned i = (usePopEncoding ? 15 : 0);
3533 /* use encoding: */
3534 /* skip 01000000 [Delta] arbitrary sized delta */
3535 *dest++ = 0x40;
3536 dest += encodeUnsigned(dest, codeDelta - i);
3537 codeDelta = i;
3538 }
3539
3540 if ((codeDelta > 0) || usePopEncoding)
3541 {
3542 if (usePopEncoding)
3543 {
3544 /* use encoding: */
3545 /* pop 01CCDDDD ESP pop CC items, 4-bit delta */
3546 if (callArgCnt || codeDelta)
3547 *dest++ = (BYTE)(0x40 | (callArgCnt << 4) | codeDelta);
3548 goto NEXT_RPD;
3549 }
3550 else
3551 {
3552 /* use encoding: */
3553 /* skip 0100DDDD small delta=DDDD */
3554 *dest++ = 0x40 | (BYTE)codeDelta;
3555 }
3556 }
3557
3558 // Emit IPtrMask if needed
3559 CHK_NON_INTRPT_ESP_IPtrMask;
3560
3561 /* use encoding: */
3562 /* call 1110RRRR [ArgCnt] [ArgMask] */
3563
3564 *dest++ = 0xE0 | regMask;
3565 dest += encodeUnsigned(dest, callArgCnt);
3566
3567 dest += encodeUnsigned(dest, argMask);
3568 }
3569 else
3570 {
3571 /* This is a push or a pop site */
3572
3573 /* Remember the new 'last' offset */
3574
3575 lastOffset = nextOffset;
3576
3577 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP)
3578 {
3579 /* This must be a gcArgPopSingle */
3580
3581 assert(genRegPtrTemp->rpdPtrArg == 1);
3582
3583 if (codeDelta >= 16)
3584 {
3585 /* use encoding: */
3586 /* skip 01000000 [Delta] */
3587 *dest++ = 0x40;
3588 dest += encodeUnsigned(dest, codeDelta - 15);
3589 codeDelta = 15;
3590 }
3591
3592 /* use encoding: */
3593 /* pop1 0101DDDD ESP pop one item, 4-bit delta */
3594
3595 *dest++ = 0x50 | (BYTE)codeDelta;
3596
3597 /* adjust argMask for this pop */
3598 pasStk.pasPop(1);
3599 }
3600 else
3601 {
3602 /* This is a push */
3603
3604 if (codeDelta >= 32)
3605 {
3606 /* use encoding: */
3607 /* skip 01000000 [Delta] */
3608 *dest++ = 0x40;
3609 dest += encodeUnsigned(dest, codeDelta - 31);
3610 codeDelta = 31;
3611 }
3612
3613 assert(codeDelta < 32);
3614
3615 /* use encoding: */
3616 /* push 000DDDDD ESP push one item, 5-bit delta */
3617
3618 *dest++ = (BYTE)codeDelta;
3619
3620 /* adjust argMask for this push */
3621 pasStk.pasPush(genRegPtrTemp->rpdGCtypeGet());
3622 }
3623 }
3624 }
3625
3626 /* We ignore the register live/dead information, since the
3627 * rpdCallRegMask contains all the liveness information
3628 * that we need
3629 */
3630 NEXT_RPD:
3631
3632 totalSize += dest - base;
3633
3634 /* Go back to the buffer start if we're not generating a table */
3635
3636 if (!mask)
3637 dest = base;
3638
3639#if REGEN_CALLPAT
3640 if ((mask == -1) && (usePopEncoding == false) && ((dest - base) > 0))
3641 regenLog(origCodeDelta, argMask, regMask, callArgCnt, byrefArgMask, byrefRegMask, base, (dest - base));
3642#endif
3643 }
3644
3645 /* Verify that we pop every arg that was pushed and that argMask is 0 */
3646
3647 assert(pasStk.pasCurDepth() == 0);
3648
3649#endif // _TARGET_X86_
3650
3651 /* Terminate the table with 0xFF */
3652
3653 *dest = 0xFF;
3654 dest -= mask;
3655 totalSize++;
3656 }
3657
3658#if VERIFY_GC_TABLES
3659 if (mask)
3660 {
3661 *(short*)dest = (short)0xBEEB;
3662 dest += sizeof(short);
3663 }
3664 totalSize += sizeof(short);
3665#endif
3666
3667#if MEASURE_PTRTAB_SIZE
3668
3669 if (mask)
3670 s_gcTotalPtrTabSize += totalSize;
3671
3672#endif
3673
3674 return totalSize;
3675}
3676#ifdef _PREFAST_
3677#pragma warning(pop)
3678#endif
3679
3680/*****************************************************************************/
3681#if DUMP_GC_TABLES
3682/*****************************************************************************
3683 *
3684 * Dump the contents of a GC pointer table.
3685 */
3686
3687#include "gcdump.h"
3688
3689#if VERIFY_GC_TABLES
3690const bool verifyGCTables = true;
3691#else
3692const bool verifyGCTables = false;
3693#endif
3694
3695/*****************************************************************************
3696 *
3697 * Dump the info block header.
3698 */
3699
3700unsigned GCInfo::gcInfoBlockHdrDump(const BYTE* table, InfoHdr* header, unsigned* methodSize)
3701{
3702 GCDump gcDump(GCINFO_VERSION);
3703
3704 gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
3705 printf("Method info block:\n");
3706
3707 return gcDump.DumpInfoHdr(table, header, methodSize, verifyGCTables);
3708}
3709
3710/*****************************************************************************/
3711
3712unsigned GCInfo::gcDumpPtrTable(const BYTE* table, const InfoHdr& header, unsigned methodSize)
3713{
3714 printf("Pointer table:\n");
3715
3716 GCDump gcDump(GCINFO_VERSION);
3717 gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
3718
3719 return gcDump.DumpGCTable(table, header, methodSize, verifyGCTables);
3720}
3721
3722/*****************************************************************************
3723 *
3724 * Find all the live pointers in a stack frame.
3725 */
3726
3727void GCInfo::gcFindPtrsInFrame(const void* infoBlock, const void* codeBlock, unsigned offs)
3728{
3729 GCDump gcDump(GCINFO_VERSION);
3730 gcDump.gcPrintf = gcDump_logf; // use my printf (which logs to VM)
3731
3732 gcDump.DumpPtrsInFrame((PTR_CBYTE)infoBlock, (const BYTE*)codeBlock, offs, verifyGCTables);
3733}
3734
3735#endif // DUMP_GC_TABLES
3736
3737#else // !JIT32_GCENCODER
3738
3739#include "gcinfoencoder.h"
3740
3741// Do explicit instantiation.
3742template class JitHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId>;
3743template class JitHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId>;
3744
3745#ifdef DEBUG
3746
3747static const char* const GcSlotFlagsNames[] = {"",
3748 "(byref) ",
3749 "(pinned) ",
3750 "(byref, pinned) ",
3751 "(untracked) ",
3752 "(byref, untracked) ",
3753 "(pinned, untracked) ",
3754 "(byref, pinned, untracked) "};
3755
3756// I'm making a local wrapper class for GcInfoEncoder so that can add logging of my own (DLD).
3757class GcInfoEncoderWithLogging
3758{
3759 GcInfoEncoder* m_gcInfoEncoder;
3760 bool m_doLogging;
3761
3762public:
3763 GcInfoEncoderWithLogging(GcInfoEncoder* gcInfoEncoder, bool verbose)
3764 : m_gcInfoEncoder(gcInfoEncoder), m_doLogging(verbose || JitConfig.JitGCInfoLogging() != 0)
3765 {
3766 }
3767
3768 GcSlotId GetStackSlotId(INT32 spOffset, GcSlotFlags flags, GcStackSlotBase spBase = GC_CALLER_SP_REL)
3769 {
3770 GcSlotId newSlotId = m_gcInfoEncoder->GetStackSlotId(spOffset, flags, spBase);
3771 if (m_doLogging)
3772 {
3773 printf("Stack slot id for offset %d (0x%x) (%s) %s= %d.\n", spOffset, spOffset,
3774 GcStackSlotBaseNames[spBase], GcSlotFlagsNames[flags & 7], newSlotId);
3775 }
3776 return newSlotId;
3777 }
3778
3779 GcSlotId GetRegisterSlotId(UINT32 regNum, GcSlotFlags flags)
3780 {
3781 GcSlotId newSlotId = m_gcInfoEncoder->GetRegisterSlotId(regNum, flags);
3782 if (m_doLogging)
3783 {
3784 printf("Register slot id for reg %s %s= %d.\n", getRegName(regNum), GcSlotFlagsNames[flags & 7], newSlotId);
3785 }
3786 return newSlotId;
3787 }
3788
3789 void SetSlotState(UINT32 instructionOffset, GcSlotId slotId, GcSlotState slotState)
3790 {
3791 m_gcInfoEncoder->SetSlotState(instructionOffset, slotId, slotState);
3792 if (m_doLogging)
3793 {
3794 printf("Set state of slot %d at instr offset 0x%x to %s.\n", slotId, instructionOffset,
3795 (slotState == GC_SLOT_LIVE ? "Live" : "Dead"));
3796 }
3797 }
3798
3799 void DefineCallSites(UINT32* pCallSites, BYTE* pCallSiteSizes, UINT32 numCallSites)
3800 {
3801 m_gcInfoEncoder->DefineCallSites(pCallSites, pCallSiteSizes, numCallSites);
3802 if (m_doLogging)
3803 {
3804 printf("Defining %d call sites:\n", numCallSites);
3805 for (UINT32 k = 0; k < numCallSites; k++)
3806 {
3807 printf(" Offset 0x%x, size %d.\n", pCallSites[k], pCallSiteSizes[k]);
3808 }
3809 }
3810 }
3811
3812 void DefineInterruptibleRange(UINT32 startInstructionOffset, UINT32 length)
3813 {
3814 m_gcInfoEncoder->DefineInterruptibleRange(startInstructionOffset, length);
3815 if (m_doLogging)
3816 {
3817 printf("Defining interruptible range: [0x%x, 0x%x).\n", startInstructionOffset,
3818 startInstructionOffset + length);
3819 }
3820 }
3821
3822 void SetCodeLength(UINT32 length)
3823 {
3824 m_gcInfoEncoder->SetCodeLength(length);
3825 if (m_doLogging)
3826 {
3827 printf("Set code length to %d.\n", length);
3828 }
3829 }
3830
3831 void SetReturnKind(ReturnKind returnKind)
3832 {
3833 m_gcInfoEncoder->SetReturnKind(returnKind);
3834 if (m_doLogging)
3835 {
3836 printf("Set ReturnKind to %s.\n", ReturnKindToString(returnKind));
3837 }
3838 }
3839
3840 void SetStackBaseRegister(UINT32 registerNumber)
3841 {
3842 m_gcInfoEncoder->SetStackBaseRegister(registerNumber);
3843 if (m_doLogging)
3844 {
3845 printf("Set stack base register to %s.\n", getRegName(registerNumber));
3846 }
3847 }
3848
3849 void SetPrologSize(UINT32 prologSize)
3850 {
3851 m_gcInfoEncoder->SetPrologSize(prologSize);
3852 if (m_doLogging)
3853 {
3854 printf("Set prolog size 0x%x.\n", prologSize);
3855 }
3856 }
3857
3858 void SetGSCookieStackSlot(INT32 spOffsetGSCookie, UINT32 validRangeStart, UINT32 validRangeEnd)
3859 {
3860 m_gcInfoEncoder->SetGSCookieStackSlot(spOffsetGSCookie, validRangeStart, validRangeEnd);
3861 if (m_doLogging)
3862 {
3863 printf("Set GS Cookie stack slot to %d, valid from 0x%x to 0x%x.\n", spOffsetGSCookie, validRangeStart,
3864 validRangeEnd);
3865 }
3866 }
3867
3868 void SetPSPSymStackSlot(INT32 spOffsetPSPSym)
3869 {
3870 m_gcInfoEncoder->SetPSPSymStackSlot(spOffsetPSPSym);
3871 if (m_doLogging)
3872 {
3873 printf("Set PSPSym stack slot to %d.\n", spOffsetPSPSym);
3874 }
3875 }
3876
3877 void SetGenericsInstContextStackSlot(INT32 spOffsetGenericsContext, GENERIC_CONTEXTPARAM_TYPE type)
3878 {
3879 m_gcInfoEncoder->SetGenericsInstContextStackSlot(spOffsetGenericsContext, type);
3880 if (m_doLogging)
3881 {
3882 printf("Set generic instantiation context stack slot to %d, type is %s.\n", spOffsetGenericsContext,
3883 (type == GENERIC_CONTEXTPARAM_THIS
3884 ? "THIS"
3885 : (type == GENERIC_CONTEXTPARAM_MT ? "MT"
3886 : (type == GENERIC_CONTEXTPARAM_MD ? "MD" : "UNKNOWN!"))));
3887 }
3888 }
3889
3890 void SetSecurityObjectStackSlot(INT32 spOffset)
3891 {
3892 m_gcInfoEncoder->SetSecurityObjectStackSlot(spOffset);
3893 if (m_doLogging)
3894 {
3895 printf("Set security object stack slot to %d.\n", spOffset);
3896 }
3897 }
3898
3899 void SetIsVarArg()
3900 {
3901 m_gcInfoEncoder->SetIsVarArg();
3902 if (m_doLogging)
3903 {
3904 printf("SetIsVarArg.\n");
3905 }
3906 }
3907
3908#ifdef _TARGET_AMD64_
3909 void SetWantsReportOnlyLeaf()
3910 {
3911 m_gcInfoEncoder->SetWantsReportOnlyLeaf();
3912 if (m_doLogging)
3913 {
3914 printf("Set WantsReportOnlyLeaf.\n");
3915 }
3916 }
3917#elif defined(_TARGET_ARMARCH_)
3918 void SetHasTailCalls()
3919 {
3920 m_gcInfoEncoder->SetHasTailCalls();
3921 if (m_doLogging)
3922 {
3923 printf("Set HasTailCalls.\n");
3924 }
3925 }
3926#endif // _TARGET_AMD64_
3927
3928 void SetSizeOfStackOutgoingAndScratchArea(UINT32 size)
3929 {
3930 m_gcInfoEncoder->SetSizeOfStackOutgoingAndScratchArea(size);
3931 if (m_doLogging)
3932 {
3933 printf("Set Outgoing stack arg area size to %d.\n", size);
3934 }
3935 }
3936};
3937
3938#define GCENCODER_WITH_LOGGING(withLog, realEncoder) \
3939 GcInfoEncoderWithLogging withLog##Var(realEncoder, compiler->verbose || compiler->opts.dspGCtbls); \
3940 GcInfoEncoderWithLogging* withLog = &withLog##Var;
3941
3942#else // DEBUG
3943
3944#define GCENCODER_WITH_LOGGING(withLog, realEncoder) GcInfoEncoder* withLog = realEncoder;
3945
3946#endif // DEBUG
3947
3948void GCInfo::gcInfoBlockHdrSave(GcInfoEncoder* gcInfoEncoder, unsigned methodSize, unsigned prologSize)
3949{
3950#ifdef DEBUG
3951 if (compiler->verbose)
3952 {
3953 printf("*************** In gcInfoBlockHdrSave()\n");
3954 }
3955#endif
3956
3957 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
3958
3959 // Can't create tables if we've not saved code.
3960
3961 gcInfoEncoderWithLog->SetCodeLength(methodSize);
3962
3963 gcInfoEncoderWithLog->SetReturnKind(getReturnKind());
3964
3965 if (compiler->isFramePointerUsed())
3966 {
3967 gcInfoEncoderWithLog->SetStackBaseRegister(REG_FPBASE);
3968 }
3969
3970 if (compiler->info.compIsVarArgs)
3971 {
3972 gcInfoEncoderWithLog->SetIsVarArg();
3973 }
3974 // No equivalents.
3975 // header->profCallbacks = compiler->info.compProfilerCallback;
3976 // header->editNcontinue = compiler->opts.compDbgEnC;
3977 //
3978 if (compiler->lvaReportParamTypeArg())
3979 {
3980 // The predicate above is true only if there is an extra generic context parameter, not for
3981 // the case where the generic context is provided by "this."
3982 assert(compiler->info.compTypeCtxtArg != BAD_VAR_NUM);
3983 GENERIC_CONTEXTPARAM_TYPE ctxtParamType = GENERIC_CONTEXTPARAM_NONE;
3984 switch (compiler->info.compMethodInfo->options & CORINFO_GENERICS_CTXT_MASK)
3985 {
3986 case CORINFO_GENERICS_CTXT_FROM_METHODDESC:
3987 ctxtParamType = GENERIC_CONTEXTPARAM_MD;
3988 break;
3989 case CORINFO_GENERICS_CTXT_FROM_METHODTABLE:
3990 ctxtParamType = GENERIC_CONTEXTPARAM_MT;
3991 break;
3992
3993 case CORINFO_GENERICS_CTXT_FROM_THIS: // See comment above.
3994 default:
3995 // If we have a generic context parameter, then we should have
3996 // one of the two options flags handled above.
3997 assert(false);
3998 }
3999
4000 gcInfoEncoderWithLog->SetGenericsInstContextStackSlot(
4001 compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(),
4002 compiler->isFramePointerUsed()),
4003 ctxtParamType);
4004 }
4005 // As discussed above, handle the case where the generics context is obtained via
4006 // the method table of "this".
4007 else if (compiler->lvaKeepAliveAndReportThis())
4008 {
4009 assert(compiler->info.compThisArg != BAD_VAR_NUM);
4010 gcInfoEncoderWithLog->SetGenericsInstContextStackSlot(
4011 compiler->lvaToCallerSPRelativeOffset(compiler->lvaCachedGenericContextArgOffset(),
4012 compiler->isFramePointerUsed()),
4013 GENERIC_CONTEXTPARAM_THIS);
4014 }
4015
4016 if (compiler->getNeedsGSSecurityCookie())
4017 {
4018 assert(compiler->lvaGSSecurityCookie != BAD_VAR_NUM);
4019
4020 // The lv offset is FP-relative, and the using code expects caller-sp relative, so translate.
4021 // The code offset ranges assume that the GS Cookie slot is initialized in the prolog, and is valid
4022 // through the remainder of the method. We will not query for the GS Cookie while we're in an epilog,
4023 // so the question of where in the epilog it becomes invalid is moot.
4024 gcInfoEncoderWithLog->SetGSCookieStackSlot(compiler->lvaGetCallerSPRelativeOffset(
4025 compiler->lvaGSSecurityCookie),
4026 prologSize, methodSize);
4027 }
4028 else if (compiler->opts.compNeedSecurityCheck || compiler->lvaReportParamTypeArg() ||
4029 compiler->lvaKeepAliveAndReportThis())
4030 {
4031 gcInfoEncoderWithLog->SetPrologSize(prologSize);
4032 }
4033
4034 if (compiler->opts.compNeedSecurityCheck)
4035 {
4036 assert(compiler->lvaSecurityObject != BAD_VAR_NUM);
4037
4038 // A VM requirement due to how the decoder works (it ignores partially interruptible frames when
4039 // an exception has escaped, but the VM requires the security object to live on).
4040 assert(compiler->codeGen->genInterruptible);
4041
4042 // The lv offset is FP-relative, and the using code expects caller-sp relative, so translate.
4043 // The normal GC lifetime reporting mechanisms will report a proper lifetime to the GC.
4044 // The security subsystem can safely assume that anywhere it might walk the stack, it will be
4045 // valid (null or a live GC ref).
4046 gcInfoEncoderWithLog->SetSecurityObjectStackSlot(
4047 compiler->lvaGetCallerSPRelativeOffset(compiler->lvaSecurityObject));
4048 }
4049
4050#if FEATURE_EH_FUNCLETS
4051 if (compiler->lvaPSPSym != BAD_VAR_NUM)
4052 {
4053#ifdef _TARGET_AMD64_
4054 // The PSPSym is relative to InitialSP on X64 and CallerSP on other platforms.
4055 gcInfoEncoderWithLog->SetPSPSymStackSlot(compiler->lvaGetInitialSPRelativeOffset(compiler->lvaPSPSym));
4056#else // !_TARGET_AMD64_
4057 gcInfoEncoderWithLog->SetPSPSymStackSlot(compiler->lvaGetCallerSPRelativeOffset(compiler->lvaPSPSym));
4058#endif // !_TARGET_AMD64_
4059 }
4060
4061#ifdef _TARGET_AMD64_
4062 if (compiler->ehAnyFunclets())
4063 {
4064 // Set this to avoid double-reporting the parent frame (unlike JIT64)
4065 gcInfoEncoderWithLog->SetWantsReportOnlyLeaf();
4066 }
4067#endif // _TARGET_AMD64_
4068
4069#endif // FEATURE_EH_FUNCLETS
4070
4071#ifdef _TARGET_ARMARCH_
4072 if (compiler->codeGen->hasTailCalls)
4073 {
4074 gcInfoEncoderWithLog->SetHasTailCalls();
4075 }
4076#endif // _TARGET_ARMARCH_
4077
4078#if FEATURE_FIXED_OUT_ARGS
4079 // outgoing stack area size
4080 gcInfoEncoderWithLog->SetSizeOfStackOutgoingAndScratchArea(compiler->lvaOutgoingArgSpaceSize);
4081#endif // FEATURE_FIXED_OUT_ARGS
4082
4083#if DISPLAY_SIZES
4084
4085 if (compiler->codeGen->genInterruptible)
4086 {
4087 genMethodICnt++;
4088 }
4089 else
4090 {
4091 genMethodNCnt++;
4092 }
4093
4094#endif // DISPLAY_SIZES
4095}
4096
4097#ifdef DEBUG
4098#define Encoder GcInfoEncoderWithLogging
4099#else
4100#define Encoder GcInfoEncoder
4101#endif
4102
4103// Small helper class to handle the No-GC-Interrupt callbacks
4104// when reporting interruptible ranges.
4105//
4106// Encoder should be either GcInfoEncoder or GcInfoEncoderWithLogging
4107//
4108struct InterruptibleRangeReporter
4109{
4110 unsigned prevStart;
4111 Encoder* gcInfoEncoderWithLog;
4112
4113 InterruptibleRangeReporter(unsigned _prevStart, Encoder* _gcInfo)
4114 : prevStart(_prevStart), gcInfoEncoderWithLog(_gcInfo)
4115 {
4116 }
4117
4118 // This callback is called for each insGroup marked with
4119 // IGF_NOGCINTERRUPT (currently just prologs and epilogs).
4120 // Report everything between the previous region and the current
4121 // region as interruptible.
4122
4123 bool operator()(unsigned igFuncIdx, unsigned igOffs, unsigned igSize)
4124 {
4125 if (igOffs < prevStart)
4126 {
4127 // We're still in the main method prolog, which has already
4128 // had it's interruptible range reported.
4129 assert(igFuncIdx == 0);
4130 assert(igOffs + igSize <= prevStart);
4131 return true;
4132 }
4133
4134 assert(igOffs >= prevStart);
4135 if (igOffs > prevStart)
4136 {
4137 gcInfoEncoderWithLog->DefineInterruptibleRange(prevStart, igOffs - prevStart);
4138 }
4139 prevStart = igOffs + igSize;
4140 return true;
4141 }
4142};
4143
4144void GCInfo::gcMakeRegPtrTable(
4145 GcInfoEncoder* gcInfoEncoder, unsigned codeSize, unsigned prologSize, MakeRegPtrMode mode, unsigned* callCntRef)
4146{
4147 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4148
4149 const bool noTrackedGCSlots =
4150 (compiler->opts.MinOpts() && !compiler->opts.jitFlags->IsSet(JitFlags::JIT_FLAG_PREJIT) &&
4151 !JitConfig.JitMinOptsTrackGCrefs());
4152
4153 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4154 {
4155 m_regSlotMap = new (compiler->getAllocator()) RegSlotMap(compiler->getAllocator());
4156 m_stackSlotMap = new (compiler->getAllocator()) StackSlotMap(compiler->getAllocator());
4157 }
4158
4159 /**************************************************************************
4160 *
4161 * Untracked ptr variables
4162 *
4163 **************************************************************************
4164 */
4165
4166 unsigned count = 0;
4167
4168 int lastoffset = 0;
4169
4170 /* Count&Write untracked locals and non-enregistered args */
4171
4172 unsigned varNum;
4173 LclVarDsc* varDsc;
4174 for (varNum = 0, varDsc = compiler->lvaTable; varNum < compiler->lvaCount; varNum++, varDsc++)
4175 {
4176 if (compiler->lvaIsFieldOfDependentlyPromotedStruct(varDsc))
4177 {
4178 // Field local of a PROMOTION_TYPE_DEPENDENT struct must have been
4179 // reported through its parent local.
4180 continue;
4181 }
4182
4183 if (varTypeIsGC(varDsc->TypeGet()))
4184 {
4185 // Do we have an argument or local variable?
4186 if (!varDsc->lvIsParam)
4187 {
4188 // If is is pinned, it must be an untracked local.
4189 assert(!varDsc->lvPinned || !varDsc->lvTracked);
4190
4191 if (varDsc->lvTracked || !varDsc->lvOnFrame)
4192 {
4193 continue;
4194 }
4195 }
4196 else
4197 {
4198 // Stack-passed arguments which are not enregistered
4199 // are always reported in this "untracked stack
4200 // pointers" section of the GC info even if lvTracked==true
4201
4202 // Has this argument been fully enregistered?
4203 CLANG_FORMAT_COMMENT_ANCHOR;
4204
4205 if (!varDsc->lvOnFrame)
4206 {
4207 // If a CEE_JMP has been used, then we need to report all the arguments
4208 // even if they are enregistered, since we will be using this value
4209 // in a JMP call. Note that this is subtle as we require that
4210 // argument offsets are always fixed up properly even if lvRegister
4211 // is set.
4212 if (!compiler->compJmpOpUsed)
4213 {
4214 continue;
4215 }
4216 }
4217 else
4218 {
4219 if (!varDsc->lvOnFrame)
4220 {
4221 // If this non-enregistered pointer arg is never
4222 // used, we don't need to report it.
4223 assert(varDsc->lvRefCnt() == 0);
4224 continue;
4225 }
4226 else if (varDsc->lvIsRegArg && varDsc->lvTracked)
4227 {
4228 // If this register-passed arg is tracked, then
4229 // it has been allocated space near the other
4230 // pointer variables and we have accurate life-
4231 // time info. It will be reported with
4232 // gcVarPtrList in the "tracked-pointer" section.
4233 continue;
4234 }
4235 }
4236 }
4237
4238 // If we haven't continued to the next variable, we should report this as an untracked local.
4239 CLANG_FORMAT_COMMENT_ANCHOR;
4240
4241 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4242
4243 if (varDsc->TypeGet() == TYP_BYREF)
4244 {
4245 // Or in byref_OFFSET_FLAG for 'byref' pointer tracking
4246 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4247 }
4248 gcUpdateFlagForStackAllocatedObjects(flags);
4249
4250 if (varDsc->lvPinned)
4251 {
4252 // Or in pinned_OFFSET_FLAG for 'pinned' pointer tracking
4253 flags = (GcSlotFlags)(flags | GC_SLOT_PINNED);
4254 }
4255 GcStackSlotBase stackSlotBase = GC_SP_REL;
4256 if (varDsc->lvFramePointerBased)
4257 {
4258 stackSlotBase = GC_FRAMEREG_REL;
4259 }
4260 if (noTrackedGCSlots)
4261 {
4262 // No need to hash/lookup untracked GC refs; just grab a new Slot Id.
4263 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4264 {
4265 gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
4266 }
4267 }
4268 else
4269 {
4270 StackSlotIdKey sskey(varDsc->lvStkOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
4271 GcSlotId varSlotId;
4272 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4273 {
4274 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4275 {
4276 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varDsc->lvStkOffs, flags, stackSlotBase);
4277 m_stackSlotMap->Set(sskey, varSlotId);
4278 }
4279 }
4280 }
4281 }
4282
4283 // If this is a TYP_STRUCT, handle its GC pointers.
4284 // Note that the enregisterable struct types cannot have GC pointers in them.
4285 if ((varDsc->lvType == TYP_STRUCT) && varDsc->lvOnFrame && (varDsc->lvExactSize >= TARGET_POINTER_SIZE))
4286 {
4287 unsigned slots = compiler->lvaLclSize(varNum) / TARGET_POINTER_SIZE;
4288 BYTE* gcPtrs = compiler->lvaGetGcLayout(varNum);
4289
4290 // walk each member of the array
4291 for (unsigned i = 0; i < slots; i++)
4292 {
4293 if (gcPtrs[i] == TYPE_GC_NONE)
4294 { // skip non-gc slots
4295 continue;
4296 }
4297
4298 int offset = varDsc->lvStkOffs + i * TARGET_POINTER_SIZE;
4299#if DOUBLE_ALIGN
4300 // For genDoubleAlign(), locals are addressed relative to ESP and
4301 // arguments are addressed relative to EBP.
4302
4303 if (compiler->genDoubleAlign() && varDsc->lvIsParam && !varDsc->lvIsRegArg)
4304 offset += compiler->codeGen->genTotalFrameSize();
4305#endif
4306 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4307 if (gcPtrs[i] == TYPE_GC_BYREF)
4308 {
4309 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4310 }
4311
4312 GcStackSlotBase stackSlotBase = GC_SP_REL;
4313 if (varDsc->lvFramePointerBased)
4314 {
4315 stackSlotBase = GC_FRAMEREG_REL;
4316 }
4317 StackSlotIdKey sskey(offset, (stackSlotBase == GC_FRAMEREG_REL), flags);
4318 GcSlotId varSlotId;
4319 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4320 {
4321 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4322 {
4323 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(offset, flags, stackSlotBase);
4324 m_stackSlotMap->Set(sskey, varSlotId);
4325 }
4326 }
4327 }
4328 }
4329 }
4330
4331 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4332 {
4333 // Count&Write spill temps that hold pointers.
4334
4335 assert(compiler->codeGen->regSet.tmpAllFree());
4336 for (TempDsc* tempItem = compiler->codeGen->regSet.tmpListBeg(); tempItem != nullptr;
4337 tempItem = compiler->codeGen->regSet.tmpListNxt(tempItem))
4338 {
4339 if (varTypeIsGC(tempItem->tdTempType()))
4340 {
4341 int offset = tempItem->tdTempOffs();
4342
4343 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4344 if (tempItem->tdTempType() == TYP_BYREF)
4345 {
4346 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4347 }
4348 gcUpdateFlagForStackAllocatedObjects(flags);
4349
4350 GcStackSlotBase stackSlotBase = GC_SP_REL;
4351 if (compiler->isFramePointerUsed())
4352 {
4353 stackSlotBase = GC_FRAMEREG_REL;
4354 }
4355 StackSlotIdKey sskey(offset, (stackSlotBase == GC_FRAMEREG_REL), flags);
4356 GcSlotId varSlotId;
4357 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4358 {
4359 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(offset, flags, stackSlotBase);
4360 m_stackSlotMap->Set(sskey, varSlotId);
4361 }
4362 }
4363 }
4364
4365 if (compiler->lvaKeepAliveAndReportThis())
4366 {
4367 // We need to report the cached copy as an untracked pointer
4368 assert(compiler->info.compThisArg != BAD_VAR_NUM);
4369 assert(!compiler->lvaReportParamTypeArg());
4370 GcSlotFlags flags = GC_SLOT_UNTRACKED;
4371
4372 if (compiler->lvaTable[compiler->info.compThisArg].TypeGet() == TYP_BYREF)
4373 {
4374 // Or in GC_SLOT_INTERIOR for 'byref' pointer tracking
4375 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4376 }
4377
4378 GcStackSlotBase stackSlotBase = compiler->isFramePointerUsed() ? GC_FRAMEREG_REL : GC_SP_REL;
4379
4380 gcInfoEncoderWithLog->GetStackSlotId(compiler->lvaCachedGenericContextArgOffset(), flags, stackSlotBase);
4381 }
4382 }
4383
4384 // Generate the table of tracked stack pointer variable lifetimes.
4385 gcMakeVarPtrTable(gcInfoEncoder, mode);
4386
4387 /**************************************************************************
4388 *
4389 * Prepare to generate the pointer register/argument map
4390 *
4391 **************************************************************************
4392 */
4393
4394 if (compiler->codeGen->genInterruptible)
4395 {
4396 assert(compiler->genFullPtrRegMap);
4397
4398 regMaskSmall ptrRegs = 0;
4399 regPtrDsc* regStackArgFirst = nullptr;
4400
4401 // Walk the list of pointer register/argument entries.
4402
4403 for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr; genRegPtrTemp = genRegPtrTemp->rpdNext)
4404 {
4405 int nextOffset = genRegPtrTemp->rpdOffs;
4406
4407 if (genRegPtrTemp->rpdArg)
4408 {
4409 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_KILL)
4410 {
4411 // Kill all arguments for a call
4412 if ((mode == MAKE_REG_PTR_MODE_DO_WORK) && (regStackArgFirst != nullptr))
4413 {
4414 // Record any outgoing arguments as becoming dead
4415 gcInfoRecordGCStackArgsDead(gcInfoEncoder, genRegPtrTemp->rpdOffs, regStackArgFirst,
4416 genRegPtrTemp);
4417 }
4418 regStackArgFirst = nullptr;
4419 }
4420 else if (genRegPtrTemp->rpdGCtypeGet() != GCT_NONE)
4421 {
4422 if (genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH || (genRegPtrTemp->rpdPtrArg != 0))
4423 {
4424 bool isPop = genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP;
4425 assert(!isPop);
4426 gcInfoRecordGCStackArgLive(gcInfoEncoder, mode, genRegPtrTemp);
4427 if (regStackArgFirst == nullptr)
4428 {
4429 regStackArgFirst = genRegPtrTemp;
4430 }
4431 }
4432 else
4433 {
4434 // We know it's a POP. Sometimes we'll record a POP for a call, just to make sure
4435 // the call site is recorded.
4436 // This is just the negation of the condition:
4437 assert(genRegPtrTemp->rpdArgTypeGet() == rpdARG_POP && genRegPtrTemp->rpdPtrArg == 0);
4438 // This asserts that we only get here when we're recording a call site.
4439 assert(genRegPtrTemp->rpdArg && genRegPtrTemp->rpdIsCallInstr());
4440
4441 // Kill all arguments for a call
4442 if ((mode == MAKE_REG_PTR_MODE_DO_WORK) && (regStackArgFirst != nullptr))
4443 {
4444 // Record any outgoing arguments as becoming dead
4445 gcInfoRecordGCStackArgsDead(gcInfoEncoder, genRegPtrTemp->rpdOffs, regStackArgFirst,
4446 genRegPtrTemp);
4447 }
4448 regStackArgFirst = nullptr;
4449 }
4450 }
4451 }
4452 else
4453 {
4454 // Record any registers that are becoming dead.
4455
4456 regMaskSmall regMask = genRegPtrTemp->rpdCompiler.rpdDel & ptrRegs;
4457 regMaskSmall byRefMask = 0;
4458 if (genRegPtrTemp->rpdGCtypeGet() == GCT_BYREF)
4459 {
4460 byRefMask = regMask;
4461 }
4462 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, genRegPtrTemp->rpdOffs, regMask, GC_SLOT_DEAD,
4463 byRefMask, &ptrRegs);
4464
4465 // Record any registers that are becoming live.
4466 regMask = genRegPtrTemp->rpdCompiler.rpdAdd & ~ptrRegs;
4467 byRefMask = 0;
4468 // As far as I (DLD, 2010) can tell, there's one GCtype for the entire genRegPtrTemp, so if
4469 // it says byref then all the registers in "regMask" contain byrefs.
4470 if (genRegPtrTemp->rpdGCtypeGet() == GCT_BYREF)
4471 {
4472 byRefMask = regMask;
4473 }
4474 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, genRegPtrTemp->rpdOffs, regMask, GC_SLOT_LIVE,
4475 byRefMask, &ptrRegs);
4476 }
4477 }
4478
4479 // Now we can declare the entire method body fully interruptible.
4480 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4481 {
4482 assert(prologSize <= codeSize);
4483
4484 // Now exempt any other region marked as IGF_NOGCINTERRUPT
4485 // Currently just prologs and epilogs.
4486
4487 InterruptibleRangeReporter reporter(prologSize, gcInfoEncoderWithLog);
4488 compiler->getEmitter()->emitGenNoGCLst(reporter);
4489 prologSize = reporter.prevStart;
4490
4491 // Report any remainder
4492 if (prologSize < codeSize)
4493 {
4494 gcInfoEncoderWithLog->DefineInterruptibleRange(prologSize, codeSize - prologSize);
4495 }
4496 }
4497 }
4498 else if (compiler->isFramePointerUsed()) // genInterruptible is false, and we're using EBP as a frame pointer.
4499 {
4500 assert(compiler->genFullPtrRegMap == false);
4501
4502 // Walk the list of pointer register/argument entries.
4503
4504 // First count them.
4505 unsigned numCallSites = 0;
4506
4507 // Now we can allocate the information.
4508 unsigned* pCallSites = nullptr;
4509 BYTE* pCallSiteSizes = nullptr;
4510 unsigned callSiteNum = 0;
4511
4512 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4513 {
4514 if (gcCallDescList != nullptr)
4515 {
4516 if (noTrackedGCSlots)
4517 {
4518 // We have the call count from the previous run.
4519 numCallSites = *callCntRef;
4520
4521 // If there are no calls, tell the world and bail.
4522 if (numCallSites == 0)
4523 {
4524 gcInfoEncoderWithLog->DefineCallSites(nullptr, nullptr, 0);
4525 return;
4526 }
4527 }
4528 else
4529 {
4530 for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
4531 {
4532 numCallSites++;
4533 }
4534 }
4535 pCallSites = new (compiler, CMK_GC) unsigned[numCallSites];
4536 pCallSiteSizes = new (compiler, CMK_GC) BYTE[numCallSites];
4537 }
4538 }
4539
4540 // Now consider every call.
4541 for (CallDsc* call = gcCallDescList; call != nullptr; call = call->cdNext)
4542 {
4543 // Figure out the code offset of this entry.
4544 unsigned nextOffset = call->cdOffs;
4545
4546 // As far as I (DLD, 2010) can determine by asking around, the "call->u1.cdArgMask"
4547 // and "cdArgCnt" cases are to handle x86 situations in which a call expression is nested as an
4548 // argument to an outer call. The "natural" (evaluation-order-preserving) thing to do is to
4549 // evaluate the outer call's arguments, pushing those that are not enregistered, until you
4550 // encounter the nested call. These parts of the call description, then, describe the "pending"
4551 // pushed arguments. This situation does not exist outside of x86, where we're going to use a
4552 // fixed-size stack frame: in situations like this nested call, we would evaluate the pending
4553 // arguments to temporaries, and only "push" them (really, write them to the outgoing argument section
4554 // of the stack frame) when it's the outer call's "turn." So we can assert that these
4555 // situations never occur.
4556 assert(call->u1.cdArgMask == 0 && call->cdArgCnt == 0);
4557
4558 // Other than that, we just have to deal with the regmasks.
4559 regMaskSmall gcrefRegMask = call->cdGCrefRegs & RBM_CALLEE_SAVED;
4560 regMaskSmall byrefRegMask = call->cdByrefRegs & RBM_CALLEE_SAVED;
4561
4562 assert((gcrefRegMask & byrefRegMask) == 0);
4563
4564 regMaskSmall regMask = gcrefRegMask | byrefRegMask;
4565
4566 assert(call->cdOffs >= call->cdCallInstrSize);
4567 // call->cdOffs is actually the offset of the instruction *following* the call, so subtract
4568 // the call instruction size to get the offset of the actual call instruction...
4569 unsigned callOffset = nextOffset - call->cdCallInstrSize;
4570
4571 if (noTrackedGCSlots && regMask == 0)
4572 {
4573 // No live GC refs in regs at the call -> don't record the call.
4574 }
4575 else
4576 {
4577 // Append an entry for the call if doing the real thing.
4578 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4579 {
4580 pCallSites[callSiteNum] = callOffset;
4581 pCallSiteSizes[callSiteNum] = call->cdCallInstrSize;
4582 }
4583 callSiteNum++;
4584
4585 // Record that these registers are live before the call...
4586 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask,
4587 nullptr);
4588 // ...and dead after.
4589 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, nextOffset, regMask, GC_SLOT_DEAD, byrefRegMask,
4590 nullptr);
4591 }
4592 }
4593 // Make sure we've recorded the expected number of calls
4594 assert(mode != MAKE_REG_PTR_MODE_DO_WORK || numCallSites == callSiteNum);
4595 // Return the actual recorded call count to the caller
4596 *callCntRef = callSiteNum;
4597
4598 // OK, define the call sites.
4599 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4600 {
4601 gcInfoEncoderWithLog->DefineCallSites(pCallSites, pCallSiteSizes, numCallSites);
4602 }
4603 }
4604 else // genInterruptible is false and we have an EBP-less frame
4605 {
4606 assert(compiler->genFullPtrRegMap);
4607
4608 // Walk the list of pointer register/argument entries */
4609 // First count them.
4610 unsigned numCallSites = 0;
4611
4612 // Now we can allocate the information (if we're in the "DO_WORK" pass...)
4613 unsigned* pCallSites = nullptr;
4614 BYTE* pCallSiteSizes = nullptr;
4615 unsigned callSiteNum = 0;
4616
4617 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4618 {
4619 for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr;
4620 genRegPtrTemp = genRegPtrTemp->rpdNext)
4621 {
4622 if (genRegPtrTemp->rpdArg && genRegPtrTemp->rpdIsCallInstr())
4623 {
4624 numCallSites++;
4625 }
4626 }
4627
4628 if (numCallSites > 0)
4629 {
4630 pCallSites = new (compiler, CMK_GC) unsigned[numCallSites];
4631 pCallSiteSizes = new (compiler, CMK_GC) BYTE[numCallSites];
4632 }
4633 }
4634
4635 for (regPtrDsc* genRegPtrTemp = gcRegPtrList; genRegPtrTemp != nullptr; genRegPtrTemp = genRegPtrTemp->rpdNext)
4636 {
4637 if (genRegPtrTemp->rpdArg)
4638 {
4639 // Is this a call site?
4640 if (genRegPtrTemp->rpdIsCallInstr())
4641 {
4642 // This is a true call site.
4643
4644 regMaskSmall gcrefRegMask = genRegMaskFromCalleeSavedMask(genRegPtrTemp->rpdCallGCrefRegs);
4645
4646 regMaskSmall byrefRegMask = genRegMaskFromCalleeSavedMask(genRegPtrTemp->rpdCallByrefRegs);
4647
4648 assert((gcrefRegMask & byrefRegMask) == 0);
4649
4650 regMaskSmall regMask = gcrefRegMask | byrefRegMask;
4651
4652 // The "rpdOffs" is (apparently) the offset of the following instruction already.
4653 // GcInfoEncoder wants the call instruction, so subtract the width of the call instruction.
4654 assert(genRegPtrTemp->rpdOffs >= genRegPtrTemp->rpdCallInstrSize);
4655 unsigned callOffset = genRegPtrTemp->rpdOffs - genRegPtrTemp->rpdCallInstrSize;
4656
4657 // Tell the GCInfo encoder about these registers. We say that the registers become live
4658 // before the call instruction, and dead after.
4659 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, callOffset, regMask, GC_SLOT_LIVE, byrefRegMask,
4660 nullptr);
4661 gcInfoRecordGCRegStateChange(gcInfoEncoder, mode, genRegPtrTemp->rpdOffs, regMask, GC_SLOT_DEAD,
4662 byrefRegMask, nullptr);
4663
4664 // Also remember the call site.
4665 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4666 {
4667 assert(pCallSites != nullptr && pCallSiteSizes != nullptr);
4668 pCallSites[callSiteNum] = callOffset;
4669 pCallSiteSizes[callSiteNum] = genRegPtrTemp->rpdCallInstrSize;
4670 callSiteNum++;
4671 }
4672 }
4673 else
4674 {
4675 // These are reporting outgoing stack arguments, but we don't need to report anything
4676 // for partially interruptible
4677 assert(genRegPtrTemp->rpdGCtypeGet() != GCT_NONE);
4678 assert(genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH);
4679 }
4680 }
4681 }
4682 // The routine is fully interruptible.
4683 if (mode == MAKE_REG_PTR_MODE_DO_WORK)
4684 {
4685 gcInfoEncoderWithLog->DefineCallSites(pCallSites, pCallSiteSizes, numCallSites);
4686 }
4687 }
4688}
4689
4690void GCInfo::gcInfoRecordGCRegStateChange(GcInfoEncoder* gcInfoEncoder,
4691 MakeRegPtrMode mode,
4692 unsigned instrOffset,
4693 regMaskSmall regMask,
4694 GcSlotState newState,
4695 regMaskSmall byRefMask,
4696 regMaskSmall* pPtrRegs)
4697{
4698 // Precondition: byRefMask is a subset of regMask.
4699 assert((byRefMask & ~regMask) == 0);
4700
4701 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4702
4703 while (regMask)
4704 {
4705 // Get hold of the next register bit.
4706 regMaskTP tmpMask = genFindLowestReg(regMask);
4707 assert(tmpMask);
4708
4709 // Remember the new state of this register.
4710 if (pPtrRegs != nullptr)
4711 {
4712 if (newState == GC_SLOT_DEAD)
4713 {
4714 *pPtrRegs &= ~tmpMask;
4715 }
4716 else
4717 {
4718 *pPtrRegs |= tmpMask;
4719 }
4720 }
4721
4722 // Figure out which register the next bit corresponds to.
4723 regNumber regNum = genRegNumFromMask(tmpMask);
4724
4725 /* Reserve SP future use */
4726 assert(regNum != REG_SPBASE);
4727
4728 GcSlotFlags regFlags = GC_SLOT_BASE;
4729 if ((tmpMask & byRefMask) != 0)
4730 {
4731 regFlags = (GcSlotFlags)(regFlags | GC_SLOT_INTERIOR);
4732 }
4733 gcUpdateFlagForStackAllocatedObjects(regFlags);
4734
4735 RegSlotIdKey rskey(regNum, regFlags);
4736 GcSlotId regSlotId;
4737 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4738 {
4739 if (!m_regSlotMap->Lookup(rskey, &regSlotId))
4740 {
4741 regSlotId = gcInfoEncoderWithLog->GetRegisterSlotId(regNum, regFlags);
4742 m_regSlotMap->Set(rskey, regSlotId);
4743 }
4744 }
4745 else
4746 {
4747 BOOL b = m_regSlotMap->Lookup(rskey, &regSlotId);
4748 assert(b); // Should have been added in the first pass.
4749 gcInfoEncoderWithLog->SetSlotState(instrOffset, regSlotId, newState);
4750 }
4751
4752 // Turn the bit we've just generated off and continue.
4753 regMask -= tmpMask; // EAX,ECX,EDX,EBX,---,EBP,ESI,EDI
4754 }
4755}
4756
4757/**************************************************************************
4758 *
4759 * gcMakeVarPtrTable - Generate the table of tracked stack pointer
4760 * variable lifetimes.
4761 *
4762 * In the first pass we'll allocate slot Ids
4763 * In the second pass we actually generate the lifetimes.
4764 *
4765 **************************************************************************
4766 */
4767
4768void GCInfo::gcMakeVarPtrTable(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode)
4769{
4770 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4771
4772 // Make sure any flags we hide in the offset are in the bits guaranteed
4773 // unused by alignment
4774 C_ASSERT((OFFSET_MASK + 1) <= sizeof(int));
4775
4776#ifdef DEBUG
4777 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4778 {
4779 // Tracked variables can't be pinned, and the encoding takes
4780 // advantage of that by using the same bit for 'pinned' and 'this'
4781 // Since we don't track 'this', we should never see either flag here.
4782 // Check it now before we potentially add some pinned flags.
4783 for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext)
4784 {
4785 const unsigned flags = varTmp->vpdVarNum & OFFSET_MASK;
4786 assert((flags & pinned_OFFSET_FLAG) == 0);
4787 assert((flags & this_OFFSET_FLAG) == 0);
4788 }
4789 }
4790#endif // DEBUG
4791
4792 // Only need to do this once, and only if we have EH.
4793 if ((mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS) && compiler->ehAnyFunclets())
4794 {
4795 gcMarkFilterVarsPinned();
4796 }
4797
4798 for (varPtrDsc* varTmp = gcVarPtrList; varTmp != nullptr; varTmp = varTmp->vpdNext)
4799 {
4800 C_ASSERT((OFFSET_MASK + 1) <= sizeof(int));
4801
4802 // Get hold of the variable's stack offset.
4803
4804 unsigned lowBits = varTmp->vpdVarNum & OFFSET_MASK;
4805
4806 // For negative stack offsets we must reset the low bits
4807 int varOffs = static_cast<int>(varTmp->vpdVarNum & ~OFFSET_MASK);
4808
4809 // Compute the actual lifetime offsets.
4810 unsigned begOffs = varTmp->vpdBegOfs;
4811 unsigned endOffs = varTmp->vpdEndOfs;
4812
4813 // Special case: skip any 0-length lifetimes.
4814 if (endOffs == begOffs)
4815 {
4816 continue;
4817 }
4818
4819 GcSlotFlags flags = GC_SLOT_BASE;
4820 if ((lowBits & byref_OFFSET_FLAG) != 0)
4821 {
4822 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4823 }
4824 gcUpdateFlagForStackAllocatedObjects(flags);
4825
4826 if ((lowBits & pinned_OFFSET_FLAG) != 0)
4827 {
4828 flags = (GcSlotFlags)(flags | GC_SLOT_PINNED);
4829 }
4830
4831 GcStackSlotBase stackSlotBase = GC_SP_REL;
4832 if (compiler->isFramePointerUsed())
4833 {
4834 stackSlotBase = GC_FRAMEREG_REL;
4835 }
4836 StackSlotIdKey sskey(varOffs, (stackSlotBase == GC_FRAMEREG_REL), flags);
4837 GcSlotId varSlotId;
4838 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4839 {
4840 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4841 {
4842 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(varOffs, flags, stackSlotBase);
4843 m_stackSlotMap->Set(sskey, varSlotId);
4844 }
4845 }
4846 else
4847 {
4848 BOOL b = m_stackSlotMap->Lookup(sskey, &varSlotId);
4849 assert(b); // Should have been added in the first pass.
4850 // Live from the beginning to the end.
4851 gcInfoEncoderWithLog->SetSlotState(begOffs, varSlotId, GC_SLOT_LIVE);
4852 gcInfoEncoderWithLog->SetSlotState(endOffs, varSlotId, GC_SLOT_DEAD);
4853 }
4854 }
4855}
4856
4857void GCInfo::gcInfoRecordGCStackArgLive(GcInfoEncoder* gcInfoEncoder, MakeRegPtrMode mode, regPtrDsc* genStackPtr)
4858{
4859 // On non-x86 platforms, don't have pointer argument push/pop/kill declarations.
4860 // But we use the same mechanism to record writes into the outgoing argument space...
4861 assert(genStackPtr->rpdGCtypeGet() != GCT_NONE);
4862 assert(genStackPtr->rpdArg);
4863 assert(genStackPtr->rpdArgTypeGet() == rpdARG_PUSH);
4864
4865 // We only need to report these when we're doing fuly-interruptible
4866 assert(compiler->codeGen->genInterruptible);
4867
4868 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4869
4870 StackSlotIdKey sskey(genStackPtr->rpdPtrArg, FALSE,
4871 GcSlotFlags(genStackPtr->rpdGCtypeGet() == GCT_BYREF ? GC_SLOT_INTERIOR : GC_SLOT_BASE));
4872 GcSlotId varSlotId;
4873 if (mode == MAKE_REG_PTR_MODE_ASSIGN_SLOTS)
4874 {
4875 if (!m_stackSlotMap->Lookup(sskey, &varSlotId))
4876 {
4877 varSlotId = gcInfoEncoderWithLog->GetStackSlotId(sskey.m_offset, (GcSlotFlags)sskey.m_flags, GC_SP_REL);
4878 m_stackSlotMap->Set(sskey, varSlotId);
4879 }
4880 }
4881 else
4882 {
4883 BOOL b = m_stackSlotMap->Lookup(sskey, &varSlotId);
4884 assert(b); // Should have been added in the first pass.
4885 // Live until the call.
4886 gcInfoEncoderWithLog->SetSlotState(genStackPtr->rpdOffs, varSlotId, GC_SLOT_LIVE);
4887 }
4888}
4889
4890void GCInfo::gcInfoRecordGCStackArgsDead(GcInfoEncoder* gcInfoEncoder,
4891 unsigned instrOffset,
4892 regPtrDsc* genStackPtrFirst,
4893 regPtrDsc* genStackPtrLast)
4894{
4895 // After a call all of the outgoing arguments are marked as dead.
4896 // The calling loop keeps track of the first argument pushed for this call
4897 // and passes it in as genStackPtrFirst.
4898 // genStackPtrLast is the call.
4899 // Re-walk that list and mark all outgoing arguments that we're marked as live
4900 // earlier, as going dead after the call.
4901
4902 // We only need to report these when we're doing fuly-interruptible
4903 assert(compiler->codeGen->genInterruptible);
4904
4905 GCENCODER_WITH_LOGGING(gcInfoEncoderWithLog, gcInfoEncoder);
4906
4907 for (regPtrDsc* genRegPtrTemp = genStackPtrFirst; genRegPtrTemp != genStackPtrLast;
4908 genRegPtrTemp = genRegPtrTemp->rpdNext)
4909 {
4910 if (!genRegPtrTemp->rpdArg)
4911 {
4912 continue;
4913 }
4914
4915 assert(genRegPtrTemp->rpdGCtypeGet() != GCT_NONE);
4916 assert(genRegPtrTemp->rpdArgTypeGet() == rpdARG_PUSH);
4917
4918 StackSlotIdKey sskey(genRegPtrTemp->rpdPtrArg, FALSE,
4919 genRegPtrTemp->rpdGCtypeGet() == GCT_BYREF ? GC_SLOT_INTERIOR : GC_SLOT_BASE);
4920 GcSlotId varSlotId;
4921 BOOL b = m_stackSlotMap->Lookup(sskey, &varSlotId);
4922 assert(b); // Should have been added in the first pass.
4923 // Live until the call.
4924 gcInfoEncoderWithLog->SetSlotState(instrOffset, varSlotId, GC_SLOT_DEAD);
4925 }
4926}
4927
4928//------------------------------------------------------------------------
4929// gcUpdateFlagForStackAllocatedObjects: Update the flags to handle a possibly stack-allocated object.
4930// allocation.
4931// Arguments:
4932// flags - flags to update
4933//
4934//
4935// Notes:
4936// TODO-ObjectStackAllocation: This is a temporary conservative implementation.
4937// Currently variables pointing to heap and/or stack allocated objects have type TYP_REF so we
4938// conservatively report them as INTERIOR.
4939// Ideally we should have the following types for object pointers:
4940// 1. TYP_I_IMPL for variables always pointing to stack-allocated objects (not reporting to GC)
4941// 2. TYP_REF for variables always pointing to heap-allocated objects (reporting as normal objects to GC)
4942// 3. TYP_BYREF for variables that may point to the stack or to the heap (reporting as interior objects to GC)
4943
4944void GCInfo::gcUpdateFlagForStackAllocatedObjects(GcSlotFlags& flags)
4945{
4946 if ((compiler->optMethodFlags & OMF_HAS_OBJSTACKALLOC) != 0)
4947 {
4948 flags = (GcSlotFlags)(flags | GC_SLOT_INTERIOR);
4949 }
4950}
4951
4952#undef GCENCODER_WITH_LOGGING
4953
4954#endif // !JIT32_GCENCODER
4955
4956/*****************************************************************************/
4957/*****************************************************************************/
4958