1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4
5/*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
7XX XX
8XX ScopeInfo XX
9XX XX
10XX Classes to gather the Scope information from the local variable info. XX
11XX Translates the given LocalVarTab from IL instruction offsets into XX
12XX native code offsets. XX
13XX XX
14XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
16*/
17
18/******************************************************************************
19 * Debuggable code
20 *
21 * We break up blocks at the start and end IL ranges of the local variables.
22 * This is because IL offsets do not correspond exactly to native offsets
23 * except at block boundaries. No basic-blocks are deleted (not even
24 * unreachable), so there will not be any missing address-ranges, though the
25 * blocks themselves may not be ordered. (Also, internal blocks may be added).
26 * o At the start of each basic block, siBeginBlock() checks if any variables
27 * are coming in scope, and adds an open scope to siOpenScopeList if needed.
28 * o At the end of each basic block, siEndBlock() checks if any variables
29 * are going out of scope and moves the open scope from siOpenScopeLast
30 * to siScopeList.
31 *
32 * Optimized code
33 *
34 * We cannot break up the blocks as this will produce different code under
35 * the debugger. Instead we try to do a best effort.
36 * o At the start of each basic block, siBeginBlock() adds open scopes
37 * corresponding to block->bbLiveIn to siOpenScopeList. Also siUpdate()
38 * is called to close scopes for variables which are not live anymore.
39 * o siEndBlock() closes scopes for any variables which go out of range
40 * before bbCodeOffsEnd.
41 * o siCloseAllOpenScopes() closes any open scopes after all the blocks.
42 * This should only be needed if some basic block are deleted/out of order,
43 * etc.
44 * Also,
45 * o At every assignment to a variable, siCheckVarScope() adds an open scope
46 * for the variable being assigned to.
47 * o genChangeLife() calls siUpdate() which closes scopes for variables which
48 * are not live anymore.
49 *
50 ******************************************************************************
51 */
52
53#include "jitpch.h"
54#ifdef _MSC_VER
55#pragma hdrstop
56#endif
57
58#include "emit.h"
59#include "codegen.h"
60
61bool Compiler::siVarLoc::vlIsInReg(regNumber reg)
62{
63 switch (vlType)
64 {
65 case VLT_REG:
66 return (vlReg.vlrReg == reg);
67 case VLT_REG_REG:
68 return ((vlRegReg.vlrrReg1 == reg) || (vlRegReg.vlrrReg2 == reg));
69 case VLT_REG_STK:
70 return (vlRegStk.vlrsReg == reg);
71 case VLT_STK_REG:
72 return (vlStkReg.vlsrReg == reg);
73
74 case VLT_STK:
75 case VLT_STK2:
76 case VLT_FPSTK:
77 return false;
78
79 default:
80 assert(!"Bad locType");
81 return false;
82 }
83}
84
85bool Compiler::siVarLoc::vlIsOnStk(regNumber reg, signed offset)
86{
87 regNumber actualReg;
88
89 switch (vlType)
90 {
91
92 case VLT_REG_STK:
93 actualReg = vlRegStk.vlrsStk.vlrssBaseReg;
94 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
95 {
96 actualReg = REG_SPBASE;
97 }
98 return ((actualReg == reg) && (vlRegStk.vlrsStk.vlrssOffset == offset));
99 case VLT_STK_REG:
100 actualReg = vlStkReg.vlsrStk.vlsrsBaseReg;
101 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
102 {
103 actualReg = REG_SPBASE;
104 }
105 return ((actualReg == reg) && (vlStkReg.vlsrStk.vlsrsOffset == offset));
106 case VLT_STK:
107 actualReg = vlStk.vlsBaseReg;
108 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
109 {
110 actualReg = REG_SPBASE;
111 }
112 return ((actualReg == reg) && (vlStk.vlsOffset == offset));
113 case VLT_STK2:
114 actualReg = vlStk2.vls2BaseReg;
115 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
116 {
117 actualReg = REG_SPBASE;
118 }
119 return ((actualReg == reg) && ((vlStk2.vls2Offset == offset) || (vlStk2.vls2Offset == (offset - 4))));
120
121 case VLT_REG:
122 case VLT_REG_FP:
123 case VLT_REG_REG:
124 case VLT_FPSTK:
125 return false;
126
127 default:
128 assert(!"Bad locType");
129 return false;
130 }
131}
132
133/*============================================================================
134 *
135 * Implementation for ScopeInfo
136 *
137 *
138 * Whenever a variable comes into scope, add it to the list.
139 * When a varDsc goes dead, end its previous scope entry, and make a new one
140 * which is unavailable.
141 * When a varDsc goes live, end its previous un-available entry (if any) and
142 * set its new entry as available.
143 *
144 *============================================================================
145 */
146
147/*****************************************************************************
148 * siNewScope
149 *
150 * Creates a new scope and adds it to the Open scope list.
151 */
152
153CodeGen::siScope* CodeGen::siNewScope(unsigned LVnum, unsigned varNum)
154{
155 bool tracked = compiler->lvaTable[varNum].lvTracked;
156 unsigned varIndex = compiler->lvaTable[varNum].lvVarIndex;
157
158 if (tracked)
159 {
160 siEndTrackedScope(varIndex);
161 }
162
163 siScope* newScope = compiler->getAllocator(CMK_SiScope).allocate<siScope>(1);
164
165 newScope->scStartLoc.CaptureLocation(getEmitter());
166 assert(newScope->scStartLoc.Valid());
167
168 newScope->scEndLoc.Init();
169
170 newScope->scLVnum = LVnum;
171 newScope->scVarNum = varNum;
172 newScope->scNext = nullptr;
173 newScope->scStackLevel = genStackLevel; // used only by stack vars
174
175 siOpenScopeLast->scNext = newScope;
176 newScope->scPrev = siOpenScopeLast;
177 siOpenScopeLast = newScope;
178
179 if (tracked)
180 {
181 siLatestTrackedScopes[varIndex] = newScope;
182 }
183
184 return newScope;
185}
186
187/*****************************************************************************
188 * siRemoveFromOpenScopeList
189 *
190 * Removes a scope from the open-scope list and puts it into the done-scope list
191 */
192
193void CodeGen::siRemoveFromOpenScopeList(CodeGen::siScope* scope)
194{
195 assert(scope);
196 assert(scope->scEndLoc.Valid());
197
198 // Remove from open-scope list
199
200 scope->scPrev->scNext = scope->scNext;
201 if (scope->scNext)
202 {
203 scope->scNext->scPrev = scope->scPrev;
204 }
205 else
206 {
207 siOpenScopeLast = scope->scPrev;
208 }
209
210 // Add to the finished scope list. (Try to) filter out scopes of length 0.
211
212 if (scope->scStartLoc != scope->scEndLoc)
213 {
214 siScopeLast->scNext = scope;
215 siScopeLast = scope;
216 siScopeCnt++;
217 }
218}
219
220/*----------------------------------------------------------------------------
221 * These functions end scopes given different types of parameters
222 *----------------------------------------------------------------------------
223 */
224
225/*****************************************************************************
226 * For tracked vars, we don't need to search for the scope in the list as we
227 * have a pointer to the open scopes of all tracked variables.
228 */
229
230void CodeGen::siEndTrackedScope(unsigned varIndex)
231{
232 siScope* scope = siLatestTrackedScopes[varIndex];
233 if (!scope)
234 {
235 return;
236 }
237
238 scope->scEndLoc.CaptureLocation(getEmitter());
239 assert(scope->scEndLoc.Valid());
240
241 siRemoveFromOpenScopeList(scope);
242
243 siLatestTrackedScopes[varIndex] = nullptr;
244}
245
246/*****************************************************************************
247 * If we don't know that the variable is tracked, this function handles both
248 * cases.
249 */
250
251void CodeGen::siEndScope(unsigned varNum)
252{
253 for (siScope* scope = siOpenScopeList.scNext; scope; scope = scope->scNext)
254 {
255 if (scope->scVarNum == varNum)
256 {
257 siEndScope(scope);
258 return;
259 }
260 }
261
262 JITDUMP("siEndScope: Failed to end scope for V%02u\n", varNum);
263
264 // At this point, we probably have a bad LocalVarTab
265 if (compiler->opts.compDbgCode)
266 {
267 JITDUMP("...checking var tab validity\n");
268
269 // Note the following assert is saying that we expect
270 // the VM supplied info to be invalid...
271 assert(!siVerifyLocalVarTab());
272
273 compiler->opts.compScopeInfo = false;
274 }
275}
276
277/*****************************************************************************
278 * If we have a handle to the siScope structure, we handle ending this scope
279 * differently than if we just had a variable number. This saves us searching
280 * the open-scope list again.
281 */
282
283void CodeGen::siEndScope(siScope* scope)
284{
285 scope->scEndLoc.CaptureLocation(getEmitter());
286 assert(scope->scEndLoc.Valid());
287
288 siRemoveFromOpenScopeList(scope);
289
290 LclVarDsc& lclVarDsc1 = compiler->lvaTable[scope->scVarNum];
291 if (lclVarDsc1.lvTracked)
292 {
293 siLatestTrackedScopes[lclVarDsc1.lvVarIndex] = nullptr;
294 }
295}
296
297/*****************************************************************************
298 * siVerifyLocalVarTab
299 *
300 * Checks the LocalVarTab for consistency. The VM may not have properly
301 * verified the LocalVariableTable.
302 */
303
304#ifdef DEBUG
305
306bool CodeGen::siVerifyLocalVarTab()
307{
308 // No entries with overlapping lives should have the same slot.
309
310 for (unsigned i = 0; i < compiler->info.compVarScopesCount; i++)
311 {
312 for (unsigned j = i + 1; j < compiler->info.compVarScopesCount; j++)
313 {
314 unsigned slot1 = compiler->info.compVarScopes[i].vsdVarNum;
315 unsigned beg1 = compiler->info.compVarScopes[i].vsdLifeBeg;
316 unsigned end1 = compiler->info.compVarScopes[i].vsdLifeEnd;
317
318 unsigned slot2 = compiler->info.compVarScopes[j].vsdVarNum;
319 unsigned beg2 = compiler->info.compVarScopes[j].vsdLifeBeg;
320 unsigned end2 = compiler->info.compVarScopes[j].vsdLifeEnd;
321
322 if (slot1 == slot2 && (end1 > beg2 && beg1 < end2))
323 {
324 return false;
325 }
326 }
327 }
328
329 return true;
330}
331
332#endif
333
334/*============================================================================
335 * INTERFACE (public) Functions for ScopeInfo
336 *============================================================================
337 */
338
339void CodeGen::siInit()
340{
341#ifdef _TARGET_X86_
342 assert((unsigned)ICorDebugInfo::REGNUM_EAX == REG_EAX);
343 assert((unsigned)ICorDebugInfo::REGNUM_ECX == REG_ECX);
344 assert((unsigned)ICorDebugInfo::REGNUM_EDX == REG_EDX);
345 assert((unsigned)ICorDebugInfo::REGNUM_EBX == REG_EBX);
346 assert((unsigned)ICorDebugInfo::REGNUM_ESP == REG_ESP);
347 assert((unsigned)ICorDebugInfo::REGNUM_EBP == REG_EBP);
348 assert((unsigned)ICorDebugInfo::REGNUM_ESI == REG_ESI);
349 assert((unsigned)ICorDebugInfo::REGNUM_EDI == REG_EDI);
350#endif
351
352 assert((unsigned)ICorDebugInfo::VLT_REG == Compiler::VLT_REG);
353 assert((unsigned)ICorDebugInfo::VLT_STK == Compiler::VLT_STK);
354 assert((unsigned)ICorDebugInfo::VLT_REG_REG == Compiler::VLT_REG_REG);
355 assert((unsigned)ICorDebugInfo::VLT_REG_STK == Compiler::VLT_REG_STK);
356 assert((unsigned)ICorDebugInfo::VLT_STK_REG == Compiler::VLT_STK_REG);
357 assert((unsigned)ICorDebugInfo::VLT_STK2 == Compiler::VLT_STK2);
358 assert((unsigned)ICorDebugInfo::VLT_FPSTK == Compiler::VLT_FPSTK);
359 assert((unsigned)ICorDebugInfo::VLT_FIXED_VA == Compiler::VLT_FIXED_VA);
360 assert((unsigned)ICorDebugInfo::VLT_COUNT == Compiler::VLT_COUNT);
361 assert((unsigned)ICorDebugInfo::VLT_INVALID == Compiler::VLT_INVALID);
362
363 /* ICorDebugInfo::VarLoc and siVarLoc should overlap exactly as we cast
364 * one to the other in eeSetLVinfo()
365 * Below is a "required but not sufficient" condition
366 */
367
368 assert(sizeof(ICorDebugInfo::VarLoc) == sizeof(Compiler::siVarLoc));
369
370 assert(compiler->opts.compScopeInfo);
371
372 siOpenScopeList.scNext = nullptr;
373 siOpenScopeLast = &siOpenScopeList;
374 siScopeLast = &siScopeList;
375
376 siScopeCnt = 0;
377
378 VarSetOps::AssignNoCopy(compiler, siLastLife, VarSetOps::MakeEmpty(compiler));
379 siLastEndOffs = 0;
380
381 if (compiler->info.compVarScopesCount == 0)
382 {
383 siLatestTrackedScopes = nullptr;
384 }
385 else
386 {
387#if FEATURE_EH_FUNCLETS
388 siInFuncletRegion = false;
389#endif // FEATURE_EH_FUNCLETS
390
391 unsigned scopeCount = compiler->lvaTrackedCount;
392
393 if (scopeCount == 0)
394 {
395 siLatestTrackedScopes = nullptr;
396 }
397 else
398 {
399 siLatestTrackedScopes = new (compiler->getAllocator(CMK_SiScope)) siScope* [scopeCount] {};
400 }
401
402 compiler->compResetScopeLists();
403 }
404}
405
406/*****************************************************************************
407 * siBeginBlock
408 *
409 * Called at the beginning of code-gen for a block. Checks if any scopes
410 * need to be opened.
411 */
412
413void CodeGen::siBeginBlock(BasicBlock* block)
414{
415 assert(block != nullptr);
416
417 if (!compiler->opts.compScopeInfo)
418 {
419 return;
420 }
421
422 if (compiler->info.compVarScopesCount == 0)
423 {
424 return;
425 }
426
427#if FEATURE_EH_FUNCLETS
428 if (siInFuncletRegion)
429 {
430 return;
431 }
432
433 if (block->bbFlags & BBF_FUNCLET_BEG)
434 {
435 // For now, don't report any scopes in funclets. JIT64 doesn't.
436 siInFuncletRegion = true;
437
438 JITDUMP("Scope info: found beginning of funclet region at block " FMT_BB "; ignoring following blocks\n",
439 block->bbNum);
440
441 return;
442 }
443#endif // FEATURE_EH_FUNCLETS
444
445#ifdef DEBUG
446 if (verbose)
447 {
448 printf("\nScope info: begin block " FMT_BB ", IL range ", block->bbNum);
449 block->dspBlockILRange();
450 printf("\n");
451 }
452#endif // DEBUG
453
454 unsigned beginOffs = block->bbCodeOffs;
455
456 if (beginOffs == BAD_IL_OFFSET)
457 {
458 JITDUMP("Scope info: ignoring block beginning\n");
459 return;
460 }
461
462 // If we have tracked locals, use liveness to update the debug state.
463 //
464 // Note: we can improve on this some day -- if there are any tracked
465 // locals, untracked locals will fail to be reported.
466 if (compiler->lvaTrackedCount > 0)
467 {
468 // End scope of variables which are not live for this block
469 siUpdate();
470
471 // Check that vars which are live on entry have an open scope
472 VarSetOps::Iter iter(compiler, block->bbLiveIn);
473 unsigned varIndex = 0;
474 while (iter.NextElem(&varIndex))
475 {
476 unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
477 // lvRefCnt may go down to 0 after liveness-analysis.
478 // So we need to check if this tracked variable is actually used.
479 if (!compiler->lvaTable[varNum].lvIsInReg() && !compiler->lvaTable[varNum].lvOnFrame)
480 {
481 assert(compiler->lvaTable[varNum].lvRefCnt() == 0);
482 continue;
483 }
484
485 siCheckVarScope(varNum, beginOffs);
486 }
487 }
488 else
489 {
490 // There aren't any tracked locals.
491 //
492 // For debuggable or minopts code, scopes can begin only on block boundaries.
493 // For other codegen modes (eg minopts/tier0) we currently won't report any
494 // untracked locals.
495 if (compiler->opts.OptimizationDisabled())
496 {
497 // Check if there are any scopes on the current block's start boundary.
498 VarScopeDsc* varScope = nullptr;
499
500#if FEATURE_EH_FUNCLETS
501
502 // If we find a spot where the code offset isn't what we expect, because
503 // there is a gap, it might be because we've moved the funclets out of
504 // line. Catch up with the enter and exit scopes of the current block.
505 // Ignore the enter/exit scope changes of the missing scopes, which for
506 // funclets must be matched.
507 if (siLastEndOffs != beginOffs)
508 {
509 assert(beginOffs > 0);
510 assert(siLastEndOffs < beginOffs);
511
512 JITDUMP("Scope info: found offset hole. lastOffs=%u, currOffs=%u\n", siLastEndOffs, beginOffs);
513
514 // Skip enter scopes
515 while ((varScope = compiler->compGetNextEnterScope(beginOffs - 1, true)) != nullptr)
516 {
517 /* do nothing */
518 JITDUMP("Scope info: skipping enter scope, LVnum=%u\n", varScope->vsdLVnum);
519 }
520
521 // Skip exit scopes
522 while ((varScope = compiler->compGetNextExitScope(beginOffs - 1, true)) != nullptr)
523 {
524 /* do nothing */
525 JITDUMP("Scope info: skipping exit scope, LVnum=%u\n", varScope->vsdLVnum);
526 }
527 }
528
529#else // FEATURE_EH_FUNCLETS
530
531 if (siLastEndOffs != beginOffs)
532 {
533 assert(siLastEndOffs < beginOffs);
534 return;
535 }
536
537#endif // FEATURE_EH_FUNCLETS
538
539 while ((varScope = compiler->compGetNextEnterScope(beginOffs)) != nullptr)
540 {
541 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
542
543 // Only report locals that were referenced, if we're not doing debug codegen
544 if (compiler->opts.compDbgCode || (lclVarDsc1->lvRefCnt() > 0))
545 {
546 // brace-matching editor workaround for following line: (
547 JITDUMP("Scope info: opening scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum,
548 varScope->vsdLifeBeg, varScope->vsdLifeEnd);
549
550 siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
551
552#ifdef DEBUG
553 if (VERBOSE)
554 {
555 printf("Scope info: >> new scope, VarNum=%u, tracked? %s, VarIndex=%u, bbLiveIn=%s ",
556 varScope->vsdVarNum, lclVarDsc1->lvTracked ? "yes" : "no", lclVarDsc1->lvVarIndex,
557 VarSetOps::ToString(compiler, block->bbLiveIn));
558 dumpConvertedVarSet(compiler, block->bbLiveIn);
559 printf("\n");
560 }
561 assert(!lclVarDsc1->lvTracked ||
562 VarSetOps::IsMember(compiler, block->bbLiveIn, lclVarDsc1->lvVarIndex));
563#endif // DEBUG
564 }
565 else
566 {
567 JITDUMP("Skipping open scope for V%02u, unreferenced\n", varScope->vsdVarNum);
568 }
569 }
570 }
571 }
572
573#ifdef DEBUG
574 if (verbose)
575 {
576 siDispOpenScopes();
577 }
578#endif
579}
580
581/*****************************************************************************
582 * siEndBlock
583 *
584 * Called at the end of code-gen for a block. Any closing scopes are marked
585 * as such. Note that if we are collecting LocalVar info, scopes can
586 * only begin or end at block boundaries for debuggable code.
587 */
588
589void CodeGen::siEndBlock(BasicBlock* block)
590{
591 assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));
592
593#if FEATURE_EH_FUNCLETS
594 if (siInFuncletRegion)
595 {
596 return;
597 }
598#endif // FEATURE_EH_FUNCLETS
599
600#ifdef DEBUG
601 if (verbose)
602 {
603 printf("\nScope info: end block " FMT_BB ", IL range ", block->bbNum);
604 block->dspBlockILRange();
605 printf("\n");
606 }
607#endif // DEBUG
608
609 unsigned endOffs = block->bbCodeOffsEnd;
610
611 if (endOffs == BAD_IL_OFFSET)
612 {
613 JITDUMP("Scope info: ignoring block end\n");
614 return;
615 }
616
617 // If non-debuggable code, find all scopes which end over this block
618 // and close them. For debuggable code, scopes will only end on block
619 // boundaries.
620
621 VarScopeDsc* varScope;
622 while ((varScope = compiler->compGetNextExitScope(endOffs, !compiler->opts.compDbgCode)) != nullptr)
623 {
624 // brace-matching editor workaround for following line: (
625 JITDUMP("Scope info: ending scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum, varScope->vsdLifeBeg,
626 varScope->vsdLifeEnd);
627
628 unsigned varNum = varScope->vsdVarNum;
629 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varNum];
630
631 assert(lclVarDsc1);
632
633 if (lclVarDsc1->lvTracked)
634 {
635 siEndTrackedScope(lclVarDsc1->lvVarIndex);
636 }
637 else
638 {
639 siEndScope(varNum);
640 }
641 }
642
643 siLastEndOffs = endOffs;
644
645#ifdef DEBUG
646 if (verbose)
647 {
648 siDispOpenScopes();
649 }
650#endif
651}
652
653/*****************************************************************************
654 * siUpdate
655 *
656 * Called at the start of basic blocks, and during code-gen of a block,
657 * for non-debuggable code, whenever the life of any tracked variable changes
658 * and the appropriate code has been generated. For debuggable code, variables are
659 * live over their entire scope, and so they go live or dead only on
660 * block boundaries.
661 */
662void CodeGen::siUpdate()
663{
664 if (!compiler->opts.compScopeInfo)
665 {
666 return;
667 }
668
669 if (compiler->opts.compDbgCode)
670 {
671 return;
672 }
673
674 if (compiler->info.compVarScopesCount == 0)
675 {
676 return;
677 }
678
679#if FEATURE_EH_FUNCLETS
680 if (siInFuncletRegion)
681 {
682 return;
683 }
684#endif // FEATURE_EH_FUNCLETS
685
686 VARSET_TP killed(VarSetOps::Diff(compiler, siLastLife, compiler->compCurLife));
687 assert(VarSetOps::IsSubset(compiler, killed, compiler->lvaTrackedVars));
688
689 VarSetOps::Iter iter(compiler, killed);
690 unsigned varIndex = 0;
691 while (iter.NextElem(&varIndex))
692 {
693#ifdef DEBUG
694 unsigned lclNum = compiler->lvaTrackedToVarNum[varIndex];
695 LclVarDsc* lclVar = &compiler->lvaTable[lclNum];
696 assert(lclVar->lvTracked);
697#endif
698 siEndTrackedScope(varIndex);
699 }
700
701 VarSetOps::Assign(compiler, siLastLife, compiler->compCurLife);
702}
703
704/*****************************************************************************
705 * In optimized code, we may not have access to gtLclVar.gtLclILoffs.
706 * So there may be ambiguity as to which entry in compiler->info.compVarScopes
707 * to use. We search the entire table and find the entry whose life
708 * begins closest to the given offset.
709 */
710
711/*****************************************************************************
712 * siCheckVarScope
713 *
714 * For non-debuggable code, whenever we come across a GenTree which is an
715 * assignment to a local variable, this function is called to check if the
716 * variable has an open scope. Also, check if it has the correct LVnum.
717 */
718
719void CodeGen::siCheckVarScope(unsigned varNum, IL_OFFSET offs)
720{
721 assert(compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0));
722
723#if FEATURE_EH_FUNCLETS
724 if (siInFuncletRegion)
725 {
726 return;
727 }
728#endif // FEATURE_EH_FUNCLETS
729
730 if (offs == BAD_IL_OFFSET)
731 {
732 return;
733 }
734
735 siScope* scope;
736 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varNum];
737
738 // If there is an open scope corresponding to varNum, find it
739
740 if (lclVarDsc1->lvTracked)
741 {
742 scope = siLatestTrackedScopes[lclVarDsc1->lvVarIndex];
743 }
744 else
745 {
746 for (scope = siOpenScopeList.scNext; scope; scope = scope->scNext)
747 {
748 if (scope->scVarNum == varNum)
749 {
750 break;
751 }
752 }
753 }
754
755 // Look up the compiler->info.compVarScopes[] to find the local var info for (varNum->lvSlotNum, offs)
756 VarScopeDsc* varScope = compiler->compFindLocalVar(varNum, offs);
757 if (varScope == nullptr)
758 {
759 return;
760 }
761
762 // If the currently open scope does not have the correct LVnum, close it
763 // and create a new scope with this new LVnum
764
765 if (scope)
766 {
767 if (scope->scLVnum != varScope->vsdLVnum)
768 {
769 siEndScope(scope);
770 siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
771 }
772 }
773 else
774 {
775 siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
776 }
777}
778
779/*****************************************************************************
780 * siCloseAllOpenScopes
781 *
782 * For unreachable code, or optimized code with blocks reordered, there may be
783 * scopes left open at the end. Simply close them.
784 */
785
786void CodeGen::siCloseAllOpenScopes()
787{
788 assert(siOpenScopeList.scNext);
789
790 while (siOpenScopeList.scNext)
791 {
792 siEndScope(siOpenScopeList.scNext);
793 }
794}
795
796/*****************************************************************************
797 * siDispOpenScopes
798 *
799 * Displays all the vars on the open-scope list
800 */
801
802#ifdef DEBUG
803
804void CodeGen::siDispOpenScopes()
805{
806 assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));
807
808 printf("Scope info: open scopes =\n");
809
810 if (siOpenScopeList.scNext == nullptr)
811 {
812 printf(" <none>\n");
813 }
814 else
815 {
816 for (siScope* scope = siOpenScopeList.scNext; scope != nullptr; scope = scope->scNext)
817 {
818 VarScopeDsc* localVars = compiler->info.compVarScopes;
819
820 for (unsigned i = 0; i < compiler->info.compVarScopesCount; i++, localVars++)
821 {
822 if (localVars->vsdLVnum == scope->scLVnum)
823 {
824 const char* name = compiler->VarNameToStr(localVars->vsdName);
825 // brace-matching editor workaround for following line: (
826 printf(" %u (%s) [%03X..%03X)\n", localVars->vsdLVnum, name == nullptr ? "UNKNOWN" : name,
827 localVars->vsdLifeBeg, localVars->vsdLifeEnd);
828 break;
829 }
830 }
831 }
832 }
833}
834
835#endif // DEBUG
836
837/*============================================================================
838 *
839 * Implementation for PrologScopeInfo
840 *
841 *============================================================================
842 */
843
844/*****************************************************************************
845 * psiNewPrologScope
846 *
847 * Creates a new scope and adds it to the Open scope list.
848 */
849
850CodeGen::psiScope* CodeGen::psiNewPrologScope(unsigned LVnum, unsigned slotNum)
851{
852 psiScope* newScope = compiler->getAllocator(CMK_SiScope).allocate<psiScope>(1);
853
854 newScope->scStartLoc.CaptureLocation(getEmitter());
855 assert(newScope->scStartLoc.Valid());
856
857 newScope->scEndLoc.Init();
858
859 newScope->scLVnum = LVnum;
860 newScope->scSlotNum = slotNum;
861
862 newScope->scNext = nullptr;
863 psiOpenScopeLast->scNext = newScope;
864 newScope->scPrev = psiOpenScopeLast;
865 psiOpenScopeLast = newScope;
866
867 return newScope;
868}
869
870/*****************************************************************************
871 * psiEndPrologScope
872 *
873 * Remove the scope from the Open-scope list and add it to the finished-scopes
874 * list if its length is non-zero
875 */
876
877void CodeGen::psiEndPrologScope(psiScope* scope)
878{
879 scope->scEndLoc.CaptureLocation(getEmitter());
880 assert(scope->scEndLoc.Valid());
881
882 // Remove from open-scope list
883 scope->scPrev->scNext = scope->scNext;
884 if (scope->scNext)
885 {
886 scope->scNext->scPrev = scope->scPrev;
887 }
888 else
889 {
890 psiOpenScopeLast = scope->scPrev;
891 }
892
893 // Add to the finished scope list.
894 // If the length is zero, it means that the prolog is empty. In that case,
895 // CodeGen::genSetScopeInfo will report the liveness of all arguments
896 // as spanning the first instruction in the method, so that they can
897 // at least be inspected on entry to the method.
898 if (scope->scStartLoc != scope->scEndLoc || scope->scStartLoc.IsOffsetZero())
899 {
900 psiScopeLast->scNext = scope;
901 psiScopeLast = scope;
902 psiScopeCnt++;
903 }
904}
905
906/*============================================================================
907 * INTERFACE (protected) Functions for PrologScopeInfo
908 *============================================================================
909 */
910
911//------------------------------------------------------------------------
912// psSetScopeOffset: Set the offset of the newScope to the offset of the LslVar
913//
914// Arguments:
915// 'newScope' the new scope object whose offset is to be set to the lclVarDsc offset.
916// 'lclVarDsc' is an op that will now be contained by its parent.
917//
918//
919void CodeGen::psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc)
920{
921 newScope->scRegister = false;
922 newScope->u2.scBaseReg = REG_SPBASE;
923
924#ifdef _TARGET_AMD64_
925 // scOffset = offset from caller SP - REGSIZE_BYTES
926 // TODO-Cleanup - scOffset needs to be understood. For now just matching with the existing definition.
927 newScope->u2.scOffset =
928 compiler->lvaToCallerSPRelativeOffset(lclVarDsc->lvStkOffs, lclVarDsc->lvFramePointerBased) + REGSIZE_BYTES;
929#else // !_TARGET_AMD64_
930 if (doubleAlignOrFramePointerUsed())
931 {
932 // REGSIZE_BYTES - for the pushed value of EBP
933 newScope->u2.scOffset = lclVarDsc->lvStkOffs - REGSIZE_BYTES;
934 }
935 else
936 {
937 newScope->u2.scOffset = lclVarDsc->lvStkOffs - genTotalFrameSize();
938 }
939#endif // !_TARGET_AMD64_
940}
941
942/*============================================================================
943* INTERFACE (public) Functions for PrologScopeInfo
944*============================================================================
945*/
946
947/*****************************************************************************
948 * psiBegProlog
949 *
950 * Initializes the PrologScopeInfo, and creates open scopes for all the
951 * parameters of the method.
952 */
953
954void CodeGen::psiBegProlog()
955{
956 assert(compiler->compGeneratingProlog);
957
958 VarScopeDsc* varScope;
959
960 psiOpenScopeList.scNext = nullptr;
961 psiOpenScopeLast = &psiOpenScopeList;
962 psiScopeLast = &psiScopeList;
963 psiScopeCnt = 0;
964
965 compiler->compResetScopeLists();
966
967 while ((varScope = compiler->compGetNextEnterScope(0)) != nullptr)
968 {
969 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
970
971 if (!lclVarDsc1->lvIsParam)
972 {
973 continue;
974 }
975
976 psiScope* newScope = psiNewPrologScope(varScope->vsdLVnum, varScope->vsdVarNum);
977
978 if (lclVarDsc1->lvIsRegArg)
979 {
980 bool isStructHandled = false;
981#if defined(UNIX_AMD64_ABI)
982 SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
983 if (varTypeIsStruct(lclVarDsc1))
984 {
985 CORINFO_CLASS_HANDLE typeHnd = lclVarDsc1->lvVerTypeInfo.GetClassHandle();
986 assert(typeHnd != nullptr);
987 compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
988 if (structDesc.passedInRegisters)
989 {
990 regNumber regNum = REG_NA;
991 regNumber otherRegNum = REG_NA;
992 for (unsigned nCnt = 0; nCnt < structDesc.eightByteCount; nCnt++)
993 {
994 unsigned len = structDesc.eightByteSizes[nCnt];
995 var_types regType = TYP_UNDEF;
996
997 if (nCnt == 0)
998 {
999 regNum = lclVarDsc1->lvArgReg;
1000 }
1001 else if (nCnt == 1)
1002 {
1003 otherRegNum = lclVarDsc1->lvOtherArgReg;
1004 }
1005 else
1006 {
1007 assert(false && "Invalid eightbyte number.");
1008 }
1009
1010 regType = compiler->GetEightByteType(structDesc, nCnt);
1011#ifdef DEBUG
1012 regType = compiler->mangleVarArgsType(regType);
1013 assert(genMapRegNumToRegArgNum((nCnt == 0 ? regNum : otherRegNum), regType) != (unsigned)-1);
1014#endif // DEBUG
1015 }
1016
1017 newScope->scRegister = true;
1018 newScope->u1.scRegNum = (regNumberSmall)regNum;
1019 newScope->u1.scOtherReg = (regNumberSmall)otherRegNum;
1020 }
1021 else
1022 {
1023 // Stack passed argument. Get the offset from the caller's frame.
1024 psSetScopeOffset(newScope, lclVarDsc1);
1025 }
1026
1027 isStructHandled = true;
1028 }
1029#endif // !defined(UNIX_AMD64_ABI)
1030 if (!isStructHandled)
1031 {
1032#ifdef DEBUG
1033 var_types regType = compiler->mangleVarArgsType(lclVarDsc1->TypeGet());
1034 if (lclVarDsc1->lvIsHfaRegArg())
1035 {
1036 regType = lclVarDsc1->GetHfaType();
1037 }
1038 assert(genMapRegNumToRegArgNum(lclVarDsc1->lvArgReg, regType) != (unsigned)-1);
1039#endif // DEBUG
1040
1041 newScope->scRegister = true;
1042 newScope->u1.scRegNum = (regNumberSmall)lclVarDsc1->lvArgReg;
1043 }
1044 }
1045 else
1046 {
1047 psSetScopeOffset(newScope, lclVarDsc1);
1048 }
1049 }
1050}
1051
1052/*****************************************************************************
1053 Enable this macro to get accurate prolog information for every instruction
1054 in the prolog. However, this is overkill as nobody steps through the
1055 disassembly of the prolog. Even if they do they will not expect rich debug info.
1056
1057 We still report all the arguments at the very start of the method so that
1058 the user can see the arguments at the very start of the method (offset=0).
1059
1060 Disabling this decreased the debug maps in mscorlib by 10% (01/2003)
1061 */
1062
1063#if 0
1064#define ACCURATE_PROLOG_DEBUG_INFO
1065#endif
1066
1067/*****************************************************************************
1068 * psiAdjustStackLevel
1069 *
1070 * When ESP changes, all scopes relative to ESP have to be updated.
1071 */
1072
1073void CodeGen::psiAdjustStackLevel(unsigned size)
1074{
1075 if (!compiler->opts.compScopeInfo || (compiler->info.compVarScopesCount == 0))
1076 {
1077 return;
1078 }
1079
1080 assert(compiler->compGeneratingProlog);
1081
1082#ifdef ACCURATE_PROLOG_DEBUG_INFO
1083
1084 psiScope* scope;
1085
1086 // walk the list backwards
1087 // Works as psiEndPrologScope does not change scPrev
1088 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1089 {
1090 if (scope->scRegister)
1091 {
1092 assert(compiler->lvaTable[scope->scSlotNum].lvIsRegArg);
1093 continue;
1094 }
1095 assert(scope->u2.scBaseReg == REG_SPBASE);
1096
1097 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1098 newScope->scRegister = false;
1099 newScope->u2.scBaseReg = REG_SPBASE;
1100 newScope->u2.scOffset = scope->u2.scOffset + size;
1101
1102 psiEndPrologScope(scope);
1103 }
1104
1105#endif // ACCURATE_PROLOG_DEBUG_INFO
1106}
1107
1108/*****************************************************************************
1109 * psiMoveESPtoEBP
1110 *
1111 * For EBP-frames, the parameters are accessed via ESP on entry to the function,
1112 * but via EBP right after a "mov ebp,esp" instruction
1113 */
1114
1115void CodeGen::psiMoveESPtoEBP()
1116{
1117 if (!compiler->opts.compScopeInfo || (compiler->info.compVarScopesCount == 0))
1118 {
1119 return;
1120 }
1121
1122 assert(compiler->compGeneratingProlog);
1123 assert(doubleAlignOrFramePointerUsed());
1124
1125#ifdef ACCURATE_PROLOG_DEBUG_INFO
1126
1127 psiScope* scope;
1128
1129 // walk the list backwards
1130 // Works as psiEndPrologScope does not change scPrev
1131 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1132 {
1133 if (scope->scRegister)
1134 {
1135 assert(compiler->lvaTable[scope->scSlotNum].lvIsRegArg);
1136 continue;
1137 }
1138 assert(scope->u2.scBaseReg == REG_SPBASE);
1139
1140 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1141 newScope->scRegister = false;
1142 newScope->u2.scBaseReg = REG_FPBASE;
1143 newScope->u2.scOffset = scope->u2.scOffset;
1144
1145 psiEndPrologScope(scope);
1146 }
1147
1148#endif // ACCURATE_PROLOG_DEBUG_INFO
1149}
1150
1151/*****************************************************************************
1152 * psiMoveToReg
1153 *
1154 * Called when a parameter is loaded into its assigned register from the stack,
1155 * or when parameters are moved around due to circular dependancy.
1156 * If reg != REG_NA, then the parameter is being moved into its assigned
1157 * register, else it may be being moved to a temp register.
1158 */
1159
1160void CodeGen::psiMoveToReg(unsigned varNum, regNumber reg, regNumber otherReg)
1161{
1162 assert(compiler->compGeneratingProlog);
1163
1164 if (!compiler->opts.compScopeInfo)
1165 {
1166 return;
1167 }
1168
1169 if (compiler->info.compVarScopesCount == 0)
1170 {
1171 return;
1172 }
1173
1174 assert((int)varNum >= 0); // It's not a spill temp number.
1175 assert(compiler->lvaTable[varNum].lvIsInReg());
1176
1177#ifdef ACCURATE_PROLOG_DEBUG_INFO
1178
1179 /* If reg!=REG_NA, the parameter is part of a cirular dependancy, and is
1180 * being moved through temp register "reg".
1181 * If reg==REG_NA, it is being moved to its assigned register.
1182 */
1183 if (reg == REG_NA)
1184 {
1185 // Grab the assigned registers.
1186
1187 reg = compiler->lvaTable[varNum].lvRegNum;
1188 otherReg = compiler->lvaTable[varNum].lvOtherReg;
1189 }
1190
1191 psiScope* scope;
1192
1193 // walk the list backwards
1194 // Works as psiEndPrologScope does not change scPrev
1195 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1196 {
1197 if (scope->scSlotNum != compiler->lvaTable[varNum].lvSlotNum)
1198 continue;
1199
1200 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1201 newScope->scRegister = true;
1202 newScope->u1.scRegNum = reg;
1203 newScope->u1.scOtherReg = otherReg;
1204
1205 psiEndPrologScope(scope);
1206 return;
1207 }
1208
1209 // May happen if a parameter does not have an entry in the LocalVarTab
1210 // But assert() just in case it is because of something else.
1211 assert(varNum == compiler->info.compRetBuffArg ||
1212 !"Parameter scope not found (Assert doesnt always indicate error)");
1213
1214#endif // ACCURATE_PROLOG_DEBUG_INFO
1215}
1216
1217/*****************************************************************************
1218 * CodeGen::psiMoveToStack
1219 *
1220 * A incoming register-argument is being moved to its final home on the stack
1221 * (ie. all adjustments to {F/S}PBASE have been made
1222 */
1223
1224void CodeGen::psiMoveToStack(unsigned varNum)
1225{
1226 if (!compiler->opts.compScopeInfo || (compiler->info.compVarScopesCount == 0))
1227 {
1228 return;
1229 }
1230
1231 assert(compiler->compGeneratingProlog);
1232 assert(compiler->lvaTable[varNum].lvIsRegArg);
1233 assert(!compiler->lvaTable[varNum].lvRegister);
1234
1235#ifdef ACCURATE_PROLOG_DEBUG_INFO
1236
1237 psiScope* scope;
1238
1239 // walk the list backwards
1240 // Works as psiEndPrologScope does not change scPrev
1241 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1242 {
1243 if (scope->scSlotNum != compiler->lvaTable[varNum].lvSlotNum)
1244 continue;
1245
1246 /* The param must be currently sitting in the register in which it
1247 was passed in */
1248 assert(scope->scRegister);
1249 assert(scope->u1.scRegNum == compiler->lvaTable[varNum].lvArgReg);
1250
1251 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1252 newScope->scRegister = false;
1253 newScope->u2.scBaseReg = (compiler->lvaTable[varNum].lvFramePointerBased) ? REG_FPBASE : REG_SPBASE;
1254 newScope->u2.scOffset = compiler->lvaTable[varNum].lvStkOffs;
1255
1256 psiEndPrologScope(scope);
1257 return;
1258 }
1259
1260 // May happen if a parameter does not have an entry in the LocalVarTab
1261 // But assert() just in case it is because of something else.
1262 assert(varNum == compiler->info.compRetBuffArg ||
1263 !"Parameter scope not found (Assert doesnt always indicate error)");
1264
1265#endif // ACCURATE_PROLOG_DEBUG_INFO
1266}
1267
1268/*****************************************************************************
1269 * psiEndProlog
1270 */
1271
1272void CodeGen::psiEndProlog()
1273{
1274 assert(compiler->compGeneratingProlog);
1275 psiScope* scope;
1276
1277 for (scope = psiOpenScopeList.scNext; scope; scope = psiOpenScopeList.scNext)
1278 {
1279 psiEndPrologScope(scope);
1280 }
1281}
1282