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 |
6 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
7 | XX XX |
8 | XX GCEncode XX |
9 | XX XX |
10 | XX Logic to encode the JIT method header and GC pointer tables XX |
11 | XX XX |
12 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
13 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
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 | |
26 | ReturnKind 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 | |
42 | ReturnKind 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 | // |
127 | void 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 | |
339 | void 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 | |
373 | void 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 |
421 | static FILE* logFile = NULL; |
422 | CRITICAL_SECTION logFileLock; |
423 | #endif |
424 | |
425 | #if REGEN_CALLPAT |
426 | static 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 |
467 | static 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 | */ |
514 | int 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 | |
557 | static 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 | |
581 | static 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 | |
605 | static 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 | |
624 | static 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 | |
643 | BYTE 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 | |
996 | DO_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 | |
1004 | static 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 | |
1323 | const int NO_CACHED_HEADER = -1; |
1324 | |
1325 | BYTE 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 | |
1491 | size_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 | |
1781 | size_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 | |
1801 | unsigned 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 | |
1820 | inline 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 */ |
1836 | static int zeroFN() |
1837 | { |
1838 | return 0; |
1839 | } |
1840 | static int (*zeroFunc)() = zeroFN; |
1841 | |
1842 | /***************************************************************************** |
1843 | * Modelling of the GC ptrs pushed on the stack |
1844 | */ |
1845 | |
1846 | typedef unsigned pasMaskType; |
1847 | #define BITS_IN_pasMask (BITS_IN_BYTE * sizeof(pasMaskType)) |
1848 | #define HIGHEST_pasMask_BIT (((pasMaskType)0x1) << (BITS_IN_pasMask - 1)) |
1849 | |
1850 | //----------------------------------------------------------------------------- |
1851 | |
1852 | class PendingArgsStack |
1853 | { |
1854 | public: |
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 | |
1884 | protected: |
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 | |
1898 | PendingArgsStack::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 | |
1914 | void 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 | |
1948 | void 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 | |
1987 | void 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 | |
2033 | bool 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 | |
2045 | unsigned 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 | |
2070 | unsigned 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 |
2136 | size_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 | |
2530 | DONE_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 |
3690 | const bool verifyGCTables = true; |
3691 | #else |
3692 | const bool verifyGCTables = false; |
3693 | #endif |
3694 | |
3695 | /***************************************************************************** |
3696 | * |
3697 | * Dump the info block header. |
3698 | */ |
3699 | |
3700 | unsigned 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 | |
3712 | unsigned 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 | |
3727 | void 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. |
3742 | template class JitHashTable<RegSlotIdKey, RegSlotIdKey, GcSlotId>; |
3743 | template class JitHashTable<StackSlotIdKey, StackSlotIdKey, GcSlotId>; |
3744 | |
3745 | #ifdef DEBUG |
3746 | |
3747 | static 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). |
3757 | class GcInfoEncoderWithLogging |
3758 | { |
3759 | GcInfoEncoder* m_gcInfoEncoder; |
3760 | bool m_doLogging; |
3761 | |
3762 | public: |
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 | |
3948 | void 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 | // |
4108 | struct 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 | |
4144 | void 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 | |
4690 | void 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, ®SlotId)) |
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, ®SlotId); |
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 | |
4768 | void 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 | |
4857 | void 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 | |
4890 | void 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 | |
4944 | void 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 | |