1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
6 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
10 XX Classes to gather the Scope information from the local variable info. XX
11 XX Translates the given LocalVarTab from IL instruction offsets into XX
12 XX native code offsets. XX
14 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
15 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
18 /******************************************************************************
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
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,
45 * o At every assignment to a variable, siCheckVarScope() adds an open scope
46 * for the variable being assigned to.
47 * o UpdateLifeVar() calls siUpdate() which closes scopes for variables which
48 * are not live anymore.
50 ******************************************************************************
61 bool CodeGenInterface::siVarLoc::vlIsInReg(regNumber reg) const
65 case CodeGenInterface::VLT_REG:
66 return (vlReg.vlrReg == reg);
67 case CodeGenInterface::VLT_REG_REG:
68 return ((vlRegReg.vlrrReg1 == reg) || (vlRegReg.vlrrReg2 == reg));
69 case CodeGenInterface::VLT_REG_STK:
70 return (vlRegStk.vlrsReg == reg);
71 case CodeGenInterface::VLT_STK_REG:
72 return (vlStkReg.vlsrReg == reg);
74 case CodeGenInterface::VLT_STK:
75 case CodeGenInterface::VLT_STK2:
76 case CodeGenInterface::VLT_FPSTK:
80 assert(!"Bad locType");
85 bool CodeGenInterface::siVarLoc::vlIsOnStk(regNumber reg, signed offset) const
92 case CodeGenInterface::VLT_REG_STK:
93 actualReg = vlRegStk.vlrsStk.vlrssBaseReg;
94 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
96 actualReg = REG_SPBASE;
98 return ((actualReg == reg) && (vlRegStk.vlrsStk.vlrssOffset == offset));
99 case CodeGenInterface::VLT_STK_REG:
100 actualReg = vlStkReg.vlsrStk.vlsrsBaseReg;
101 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
103 actualReg = REG_SPBASE;
105 return ((actualReg == reg) && (vlStkReg.vlsrStk.vlsrsOffset == offset));
106 case CodeGenInterface::VLT_STK:
107 actualReg = vlStk.vlsBaseReg;
108 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
110 actualReg = REG_SPBASE;
112 return ((actualReg == reg) && (vlStk.vlsOffset == offset));
113 case CodeGenInterface::VLT_STK2:
114 actualReg = vlStk2.vls2BaseReg;
115 if ((int)actualReg == (int)ICorDebugInfo::REGNUM_AMBIENT_SP)
117 actualReg = REG_SPBASE;
119 return ((actualReg == reg) && ((vlStk2.vls2Offset == offset) || (vlStk2.vls2Offset == (offset - 4))));
121 case CodeGenInterface::VLT_REG:
122 case CodeGenInterface::VLT_REG_FP:
123 case CodeGenInterface::VLT_REG_REG:
124 case CodeGenInterface::VLT_FPSTK:
128 assert(!"Bad locType");
133 //------------------------------------------------------------------------
134 // Equals: Compares first reference and then values of the structures.
137 // lhs - a "siVarLoc *" to compare.
138 // rhs - a "siVarLoc *" to compare.
141 // Return true if both are nullptr.
144 bool CodeGenInterface::siVarLoc::Equals(const siVarLoc* lhs, const siVarLoc* rhs)
148 // Are both nullptr or the same reference
151 if ((lhs == nullptr) || (rhs == nullptr))
153 // Just one of them is a nullptr
156 if (lhs->vlType != rhs->vlType)
160 assert(lhs->vlType == rhs->vlType);
161 // If neither is nullptr, and are not the same reference, compare values
166 return (lhs->vlStk.vlsBaseReg == rhs->vlStk.vlsBaseReg) && (lhs->vlStk.vlsOffset == rhs->vlStk.vlsOffset);
169 return (lhs->vlStk2.vls2BaseReg == rhs->vlStk2.vls2BaseReg) &&
170 (lhs->vlStk2.vls2Offset == rhs->vlStk2.vls2Offset);
175 return (lhs->vlReg.vlrReg == rhs->vlReg.vlrReg);
178 return (lhs->vlRegReg.vlrrReg1 == rhs->vlRegReg.vlrrReg1) &&
179 (lhs->vlRegReg.vlrrReg2 == rhs->vlRegReg.vlrrReg2);
182 return (lhs->vlRegStk.vlrsReg == rhs->vlRegStk.vlrsReg) &&
183 (lhs->vlRegStk.vlrsStk.vlrssBaseReg == rhs->vlRegStk.vlrsStk.vlrssBaseReg) &&
184 (lhs->vlRegStk.vlrsStk.vlrssOffset == rhs->vlRegStk.vlrsStk.vlrssOffset);
187 return (lhs->vlStkReg.vlsrReg == rhs->vlStkReg.vlsrReg) &&
188 (lhs->vlStkReg.vlsrStk.vlsrsBaseReg == rhs->vlStkReg.vlsrStk.vlsrsBaseReg) &&
189 (lhs->vlStkReg.vlsrStk.vlsrsOffset == rhs->vlStkReg.vlsrStk.vlsrsOffset);
192 return (lhs->vlFPstk.vlfReg == rhs->vlFPstk.vlfReg);
195 return (lhs->vlFixedVarArg.vlfvOffset == rhs->vlFixedVarArg.vlfvOffset);
206 //------------------------------------------------------------------------
207 // siFillStackVarLoc: Fill "siVarLoc" struct indicating the stack position of the variable
208 // using "LclVarDsc" and "baseReg"/"offset".
211 // varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
212 // varLoc - a "siVarLoc &" to fill with the data of the "varDsc".
213 // type - a "var_types" which indicate the type of the variable.
214 // baseReg - a "regNumber" use as a base for the offset.
215 // offset - a signed amount of bytes distance from "baseReg" for the position of the variable.
216 // isFramePointerUsed - a boolean variable
219 // The "varLoc" argument is filled depending of the "type" argument but as a VLT_STK... variation.
220 // "baseReg" and "offset" are used to indicate the position of the variable in the stack.
221 void CodeGenInterface::siVarLoc::siFillStackVarLoc(
222 const LclVarDsc* varDsc, var_types type, regNumber baseReg, int offset, bool isFramePointerUsed)
224 assert(offset != BAD_STK_OFFS);
233 case TYP_BLK: // Needed because of the TYP_BLK stress mode
240 #ifdef _TARGET_64BIT_
243 #endif // _TARGET_64BIT_
244 #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
245 // In the AMD64 ABI we are supposed to pass a struct by reference when its
246 // size is not 1, 2, 4 or 8 bytes in size. During fgMorph, the compiler modifies
247 // the IR to comply with the ABI and therefore changes the type of the lclVar
248 // that holds the struct from TYP_STRUCT to TYP_BYREF but it gives us a hint that
249 // this is still a struct by setting the lvIsTemp flag.
250 // The same is true for ARM64 and structs > 16 bytes.
251 // (See Compiler::fgMarkImplicitByRefArgs in Morph.cpp for further detail)
252 // Now, the VM expects a special enum for these type of local vars: VLT_STK_BYREF
253 // to accomodate for this situation.
254 if (varDsc->lvType == TYP_BYREF && varDsc->lvIsTemp)
256 assert(varDsc->lvIsParam);
257 this->vlType = VLT_STK_BYREF;
260 #endif // defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_)
262 this->vlType = VLT_STK;
264 this->vlStk.vlsBaseReg = baseReg;
265 this->vlStk.vlsOffset = offset;
266 if (!isFramePointerUsed && this->vlStk.vlsBaseReg == REG_SPBASE)
268 this->vlStk.vlsBaseReg = (regNumber)ICorDebugInfo::REGNUM_AMBIENT_SP;
272 #ifndef _TARGET_64BIT_
275 this->vlType = VLT_STK2;
276 this->vlStk2.vls2BaseReg = baseReg;
277 this->vlStk2.vls2Offset = offset;
278 if (!isFramePointerUsed && this->vlStk2.vls2BaseReg == REG_SPBASE)
280 this->vlStk2.vls2BaseReg = (regNumber)ICorDebugInfo::REGNUM_AMBIENT_SP;
283 #endif // !_TARGET_64BIT_
286 noway_assert(!"Invalid type");
290 //------------------------------------------------------------------------
291 // siFillRegisterVarLoc: Fill "siVarLoc" struct indicating the register position of the variable
292 // using "LclVarDsc" and "baseReg"/"offset" if it has a part in the stack (x64 bit float or long).
295 // varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
296 // varLoc - a "siVarLoc &" to fill with the data of the "varDsc".
297 // type - a "var_types" which indicate the type of the variable.
298 // baseReg - a "regNumber" use as a base for the offset.
299 // offset - a signed amount of bytes distance from "baseReg" for the position of the variable.
300 // isFramePointerUsed - a boolean indicating whether the current method sets up an
301 // explicit stack frame or not.
304 // The "varLoc" argument is filled depending of the "type" argument but as a VLT_REG... variation.
305 // "baseReg" and "offset" are used .for not 64 bit and values that are splitted in two parts.
306 void CodeGenInterface::siVarLoc::siFillRegisterVarLoc(
307 const LclVarDsc* varDsc, var_types type, regNumber baseReg, int offset, bool isFramePointerUsed)
314 #ifdef _TARGET_64BIT_
316 #endif // _TARGET_64BIT_
317 this->vlType = VLT_REG;
318 this->vlReg.vlrReg = varDsc->lvRegNum;
321 #ifndef _TARGET_64BIT_
323 #if !CPU_HAS_FP_SUPPORT
326 if (varDsc->lvOtherReg != REG_STK)
328 this->vlType = VLT_REG_REG;
329 this->vlRegReg.vlrrReg1 = varDsc->lvRegNum;
330 this->vlRegReg.vlrrReg2 = varDsc->lvOtherReg;
334 this->vlType = VLT_REG_STK;
335 this->vlRegStk.vlrsReg = varDsc->lvRegNum;
336 this->vlRegStk.vlrsStk.vlrssBaseReg = baseReg;
337 if (isFramePointerUsed && this->vlRegStk.vlrsStk.vlrssBaseReg == REG_SPBASE)
339 this->vlRegStk.vlrsStk.vlrssBaseReg = (regNumber)ICorDebugInfo::REGNUM_AMBIENT_SP;
341 this->vlRegStk.vlrsStk.vlrssOffset = offset + sizeof(int);
344 #endif // !_TARGET_64BIT_
346 #ifdef _TARGET_64BIT_
349 // TODO-AMD64-Bug: ndp\clr\src\inc\corinfo.h has a definition of RegNum that only goes up to R15,
350 // so no XMM registers can get debug information.
351 this->vlType = VLT_REG_FP;
352 this->vlReg.vlrReg = varDsc->lvRegNum;
355 #else // !_TARGET_64BIT_
357 #if CPU_HAS_FP_SUPPORT
360 if (isFloatRegType(type))
362 this->vlType = VLT_FPSTK;
363 this->vlFPstk.vlfReg = varDsc->lvRegNum;
366 #endif // CPU_HAS_FP_SUPPORT
368 #endif // !_TARGET_64BIT_
375 this->vlType = VLT_REG_FP;
377 // TODO-AMD64-Bug: ndp\clr\src\inc\corinfo.h has a definition of RegNum that only goes up to R15,
378 // so no XMM registers can get debug information.
380 // Note: Need to initialize vlrReg field, otherwise during jit dump hitting an assert
381 // in eeDispVar() --> getRegName() that regNumber is valid.
382 this->vlReg.vlrReg = varDsc->lvRegNum;
384 #endif // FEATURE_SIMD
387 noway_assert(!"Invalid type");
391 //------------------------------------------------------------------------
392 // siVarLoc: Non-empty constructor of siVarLoc struct
394 // varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
395 // baseReg - a "regNumber" use as a base for the offset.
396 // offset - a signed amount of bytes distance from "baseReg" for the position of the variable.
397 // isFramePointerUsed - a boolean variable
400 // Called for every psiScope in "psiScopeList" codegen.h
401 CodeGenInterface::siVarLoc::siVarLoc(const LclVarDsc* varDsc, regNumber baseReg, int offset, bool isFramePointerUsed)
403 var_types type = genActualType(varDsc->TypeGet());
405 if (varDsc->lvIsInReg())
407 siFillRegisterVarLoc(varDsc, type, baseReg, offset, isFramePointerUsed);
411 siFillStackVarLoc(varDsc, type, baseReg, offset, isFramePointerUsed);
415 #ifdef USING_SCOPE_INFO
416 //------------------------------------------------------------------------
417 // getSiVarLoc: Returns a "siVarLoc" instance representing the place where the variable
418 // is given its description, "baseReg", and "offset" (if needed).
421 // varDsc - a "LclVarDsc *" to the variable it is desired to build the "siVarLoc".
422 // scope - a "siScope" Scope info of the variable.
425 // A "siVarLoc" filled with the correct case struct fields for the variable, which could live
426 // in a register, an stack position, or a combination of both.
429 // Called for each siScope in siScopeList when "genSetScopeInfo".
430 CodeGenInterface::siVarLoc CodeGen::getSiVarLoc(const LclVarDsc* varDsc, const siScope* scope) const
432 // For stack vars, find the base register, and offset
435 signed offset = varDsc->lvStkOffs;
437 if (!varDsc->lvFramePointerBased)
439 baseReg = REG_SPBASE;
440 offset += scope->scStackLevel;
444 baseReg = REG_FPBASE;
447 return CodeGenInterface::siVarLoc(varDsc, baseReg, offset, isFramePointerUsed());
450 //------------------------------------------------------------------------
451 // getSiVarLoc: Creates a "CodegenInterface::siVarLoc" instance from using the properties
452 // of the "psiScope" instance.
455 // Called for every psiScope in "psiScopeList" codegen.h
456 CodeGenInterface::siVarLoc CodeGen::psiScope::getSiVarLoc() const
458 CodeGenInterface::siVarLoc varLoc;
462 varLoc.vlType = VLT_REG;
463 varLoc.vlReg.vlrReg = (regNumber)u1.scRegNum;
467 varLoc.vlType = VLT_STK;
468 varLoc.vlStk.vlsBaseReg = (regNumber)u2.scBaseReg;
469 varLoc.vlStk.vlsOffset = u2.scOffset;
475 /*============================================================================
477 * Implementation for ScopeInfo
480 * Whenever a variable comes into scope, add it to the list.
481 * When a varDsc goes dead, end its previous scope entry, and make a new one
482 * which is unavailable.
483 * When a varDsc goes live, end its previous un-available entry (if any) and
484 * set its new entry as available.
486 *============================================================================
489 /*****************************************************************************
492 * Creates a new scope and adds it to the Open scope list.
495 CodeGen::siScope* CodeGen::siNewScope(unsigned LVnum, unsigned varNum)
497 bool tracked = compiler->lvaTable[varNum].lvTracked;
498 unsigned varIndex = compiler->lvaTable[varNum].lvVarIndex;
502 siEndTrackedScope(varIndex);
505 siScope* newScope = compiler->getAllocator(CMK_SiScope).allocate<siScope>(1);
507 newScope->scStartLoc.CaptureLocation(getEmitter());
508 assert(newScope->scStartLoc.Valid());
510 newScope->scEndLoc.Init();
512 newScope->scLVnum = LVnum;
513 newScope->scVarNum = varNum;
514 newScope->scNext = nullptr;
515 newScope->scStackLevel = genStackLevel; // used only by stack vars
517 siOpenScopeLast->scNext = newScope;
518 newScope->scPrev = siOpenScopeLast;
519 siOpenScopeLast = newScope;
523 siLatestTrackedScopes[varIndex] = newScope;
529 /*****************************************************************************
530 * siRemoveFromOpenScopeList
532 * Removes a scope from the open-scope list and puts it into the done-scope list
535 void CodeGen::siRemoveFromOpenScopeList(CodeGen::siScope* scope)
538 assert(scope->scEndLoc.Valid());
540 // Remove from open-scope list
542 scope->scPrev->scNext = scope->scNext;
545 scope->scNext->scPrev = scope->scPrev;
549 siOpenScopeLast = scope->scPrev;
552 // Add to the finished scope list. (Try to) filter out scopes of length 0.
554 if (scope->scStartLoc != scope->scEndLoc)
556 siScopeLast->scNext = scope;
562 /*----------------------------------------------------------------------------
563 * These functions end scopes given different types of parameters
564 *----------------------------------------------------------------------------
567 /*****************************************************************************
568 * For tracked vars, we don't need to search for the scope in the list as we
569 * have a pointer to the open scopes of all tracked variables.
572 void CodeGen::siEndTrackedScope(unsigned varIndex)
574 siScope* scope = siLatestTrackedScopes[varIndex];
580 scope->scEndLoc.CaptureLocation(getEmitter());
581 assert(scope->scEndLoc.Valid());
583 siRemoveFromOpenScopeList(scope);
585 siLatestTrackedScopes[varIndex] = nullptr;
588 /*****************************************************************************
589 * If we don't know that the variable is tracked, this function handles both
593 void CodeGen::siEndScope(unsigned varNum)
595 for (siScope* scope = siOpenScopeList.scNext; scope; scope = scope->scNext)
597 if (scope->scVarNum == varNum)
604 JITDUMP("siEndScope: Failed to end scope for V%02u\n", varNum);
606 // At this point, we probably have a bad LocalVarTab
607 if (compiler->opts.compDbgCode)
609 JITDUMP("...checking var tab validity\n");
611 // Note the following assert is saying that we expect
612 // the VM supplied info to be invalid...
613 assert(!siVerifyLocalVarTab());
615 compiler->opts.compScopeInfo = false;
619 /*****************************************************************************
620 * If we have a handle to the siScope structure, we handle ending this scope
621 * differently than if we just had a variable number. This saves us searching
622 * the open-scope list again.
625 void CodeGen::siEndScope(siScope* scope)
627 scope->scEndLoc.CaptureLocation(getEmitter());
628 assert(scope->scEndLoc.Valid());
630 siRemoveFromOpenScopeList(scope);
632 LclVarDsc& lclVarDsc1 = compiler->lvaTable[scope->scVarNum];
633 if (lclVarDsc1.lvTracked)
635 siLatestTrackedScopes[lclVarDsc1.lvVarIndex] = nullptr;
639 /*****************************************************************************
640 * siVerifyLocalVarTab
642 * Checks the LocalVarTab for consistency. The VM may not have properly
643 * verified the LocalVariableTable.
648 bool CodeGen::siVerifyLocalVarTab()
650 // No entries with overlapping lives should have the same slot.
652 for (unsigned i = 0; i < compiler->info.compVarScopesCount; i++)
654 for (unsigned j = i + 1; j < compiler->info.compVarScopesCount; j++)
656 unsigned slot1 = compiler->info.compVarScopes[i].vsdVarNum;
657 unsigned beg1 = compiler->info.compVarScopes[i].vsdLifeBeg;
658 unsigned end1 = compiler->info.compVarScopes[i].vsdLifeEnd;
660 unsigned slot2 = compiler->info.compVarScopes[j].vsdVarNum;
661 unsigned beg2 = compiler->info.compVarScopes[j].vsdLifeBeg;
662 unsigned end2 = compiler->info.compVarScopes[j].vsdLifeEnd;
664 if (slot1 == slot2 && (end1 > beg2 && beg1 < end2))
676 /*============================================================================
677 * INTERFACE (public) Functions for ScopeInfo
678 *============================================================================
681 void CodeGen::siInit()
684 assert((unsigned)ICorDebugInfo::REGNUM_EAX == REG_EAX);
685 assert((unsigned)ICorDebugInfo::REGNUM_ECX == REG_ECX);
686 assert((unsigned)ICorDebugInfo::REGNUM_EDX == REG_EDX);
687 assert((unsigned)ICorDebugInfo::REGNUM_EBX == REG_EBX);
688 assert((unsigned)ICorDebugInfo::REGNUM_ESP == REG_ESP);
689 assert((unsigned)ICorDebugInfo::REGNUM_EBP == REG_EBP);
690 assert((unsigned)ICorDebugInfo::REGNUM_ESI == REG_ESI);
691 assert((unsigned)ICorDebugInfo::REGNUM_EDI == REG_EDI);
694 assert((unsigned)ICorDebugInfo::VLT_REG == CodeGenInterface::VLT_REG);
695 assert((unsigned)ICorDebugInfo::VLT_STK == CodeGenInterface::VLT_STK);
696 assert((unsigned)ICorDebugInfo::VLT_REG_REG == CodeGenInterface::VLT_REG_REG);
697 assert((unsigned)ICorDebugInfo::VLT_REG_STK == CodeGenInterface::VLT_REG_STK);
698 assert((unsigned)ICorDebugInfo::VLT_STK_REG == CodeGenInterface::VLT_STK_REG);
699 assert((unsigned)ICorDebugInfo::VLT_STK2 == CodeGenInterface::VLT_STK2);
700 assert((unsigned)ICorDebugInfo::VLT_FPSTK == CodeGenInterface::VLT_FPSTK);
701 assert((unsigned)ICorDebugInfo::VLT_FIXED_VA == CodeGenInterface::VLT_FIXED_VA);
702 assert((unsigned)ICorDebugInfo::VLT_COUNT == CodeGenInterface::VLT_COUNT);
703 assert((unsigned)ICorDebugInfo::VLT_INVALID == CodeGenInterface::VLT_INVALID);
705 /* ICorDebugInfo::VarLoc and siVarLoc should overlap exactly as we cast
706 * one to the other in eeSetLVinfo()
707 * Below is a "required but not sufficient" condition
710 assert(sizeof(ICorDebugInfo::VarLoc) == sizeof(CodeGenInterface::siVarLoc));
712 assert(compiler->opts.compScopeInfo);
714 siOpenScopeList.scNext = nullptr;
715 siOpenScopeLast = &siOpenScopeList;
716 siScopeLast = &siScopeList;
720 VarSetOps::AssignNoCopy(compiler, siLastLife, VarSetOps::MakeEmpty(compiler));
723 if (compiler->info.compVarScopesCount == 0)
725 siLatestTrackedScopes = nullptr;
729 #if FEATURE_EH_FUNCLETS
730 siInFuncletRegion = false;
731 #endif // FEATURE_EH_FUNCLETS
733 unsigned scopeCount = compiler->lvaTrackedCount;
737 siLatestTrackedScopes = nullptr;
741 siLatestTrackedScopes = new (compiler->getAllocator(CMK_SiScope)) siScope* [scopeCount] {};
744 compiler->compResetScopeLists();
748 /*****************************************************************************
751 * Called at the beginning of code-gen for a block. Checks if any scopes
755 void CodeGen::siBeginBlock(BasicBlock* block)
757 assert(block != nullptr);
759 if (!compiler->opts.compScopeInfo)
764 if (compiler->info.compVarScopesCount == 0)
769 #if FEATURE_EH_FUNCLETS
770 if (siInFuncletRegion)
775 if (block->bbFlags & BBF_FUNCLET_BEG)
777 // For now, don't report any scopes in funclets. JIT64 doesn't.
778 siInFuncletRegion = true;
780 JITDUMP("Scope info: found beginning of funclet region at block " FMT_BB "; ignoring following blocks\n",
785 #endif // FEATURE_EH_FUNCLETS
790 printf("\nScope info: begin block " FMT_BB ", IL range ", block->bbNum);
791 block->dspBlockILRange();
796 unsigned beginOffs = block->bbCodeOffs;
798 if (beginOffs == BAD_IL_OFFSET)
800 JITDUMP("Scope info: ignoring block beginning\n");
804 // If we have tracked locals, use liveness to update the debug state.
806 // Note: we can improve on this some day -- if there are any tracked
807 // locals, untracked locals will fail to be reported.
808 if (compiler->lvaTrackedCount > 0)
810 // End scope of variables which are not live for this block
813 // Check that vars which are live on entry have an open scope
814 VarSetOps::Iter iter(compiler, block->bbLiveIn);
815 unsigned varIndex = 0;
816 while (iter.NextElem(&varIndex))
818 unsigned varNum = compiler->lvaTrackedToVarNum[varIndex];
819 // lvRefCnt may go down to 0 after liveness-analysis.
820 // So we need to check if this tracked variable is actually used.
821 if (!compiler->lvaTable[varNum].lvIsInReg() && !compiler->lvaTable[varNum].lvOnFrame)
823 assert(compiler->lvaTable[varNum].lvRefCnt() == 0);
827 siCheckVarScope(varNum, beginOffs);
832 // There aren't any tracked locals.
834 // For debuggable or minopts code, scopes can begin only on block boundaries.
835 // For other codegen modes (eg minopts/tier0) we currently won't report any
837 if (compiler->opts.OptimizationDisabled())
839 // Check if there are any scopes on the current block's start boundary.
840 VarScopeDsc* varScope = nullptr;
842 #if FEATURE_EH_FUNCLETS
844 // If we find a spot where the code offset isn't what we expect, because
845 // there is a gap, it might be because we've moved the funclets out of
846 // line. Catch up with the enter and exit scopes of the current block.
847 // Ignore the enter/exit scope changes of the missing scopes, which for
848 // funclets must be matched.
849 if (siLastEndOffs != beginOffs)
851 assert(beginOffs > 0);
852 assert(siLastEndOffs < beginOffs);
854 JITDUMP("Scope info: found offset hole. lastOffs=%u, currOffs=%u\n", siLastEndOffs, beginOffs);
857 while ((varScope = compiler->compGetNextEnterScope(beginOffs - 1, true)) != nullptr)
860 JITDUMP("Scope info: skipping enter scope, LVnum=%u\n", varScope->vsdLVnum);
864 while ((varScope = compiler->compGetNextExitScope(beginOffs - 1, true)) != nullptr)
867 JITDUMP("Scope info: skipping exit scope, LVnum=%u\n", varScope->vsdLVnum);
871 #else // FEATURE_EH_FUNCLETS
873 if (siLastEndOffs != beginOffs)
875 assert(siLastEndOffs < beginOffs);
879 #endif // FEATURE_EH_FUNCLETS
881 while ((varScope = compiler->compGetNextEnterScope(beginOffs)) != nullptr)
883 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
885 // Only report locals that were referenced, if we're not doing debug codegen
886 if (compiler->opts.compDbgCode || (lclVarDsc1->lvRefCnt() > 0))
888 // brace-matching editor workaround for following line: (
889 JITDUMP("Scope info: opening scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum,
890 varScope->vsdLifeBeg, varScope->vsdLifeEnd);
892 siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
897 printf("Scope info: >> new scope, VarNum=%u, tracked? %s, VarIndex=%u, bbLiveIn=%s ",
898 varScope->vsdVarNum, lclVarDsc1->lvTracked ? "yes" : "no", lclVarDsc1->lvVarIndex,
899 VarSetOps::ToString(compiler, block->bbLiveIn));
900 dumpConvertedVarSet(compiler, block->bbLiveIn);
903 assert(!lclVarDsc1->lvTracked ||
904 VarSetOps::IsMember(compiler, block->bbLiveIn, lclVarDsc1->lvVarIndex));
909 JITDUMP("Skipping open scope for V%02u, unreferenced\n", varScope->vsdVarNum);
923 /*****************************************************************************
926 * Called at the end of code-gen for a block. Any closing scopes are marked
927 * as such. Note that if we are collecting LocalVar info, scopes can
928 * only begin or end at block boundaries for debuggable code.
931 void CodeGen::siEndBlock(BasicBlock* block)
933 assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));
935 #if FEATURE_EH_FUNCLETS
936 if (siInFuncletRegion)
940 #endif // FEATURE_EH_FUNCLETS
945 printf("\nScope info: end block " FMT_BB ", IL range ", block->bbNum);
946 block->dspBlockILRange();
951 unsigned endOffs = block->bbCodeOffsEnd;
953 if (endOffs == BAD_IL_OFFSET)
955 JITDUMP("Scope info: ignoring block end\n");
959 // If non-debuggable code, find all scopes which end over this block
960 // and close them. For debuggable code, scopes will only end on block
963 VarScopeDsc* varScope;
964 while ((varScope = compiler->compGetNextExitScope(endOffs, !compiler->opts.compDbgCode)) != nullptr)
966 // brace-matching editor workaround for following line: (
967 JITDUMP("Scope info: ending scope, LVnum=%u [%03X..%03X)\n", varScope->vsdLVnum, varScope->vsdLifeBeg,
968 varScope->vsdLifeEnd);
970 unsigned varNum = varScope->vsdVarNum;
971 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varNum];
975 if (lclVarDsc1->lvTracked)
977 siEndTrackedScope(lclVarDsc1->lvVarIndex);
985 siLastEndOffs = endOffs;
995 //------------------------------------------------------------------------
996 // siUpdate: Closes the "ScopeInfo" of the tracked variables that has become dead.
999 // Called at the start of basic blocks, and during code-gen of a block,
1000 // for non-debuggable code, whenever the life of any tracked variable changes
1001 // and the appropriate code has been generated. For debuggable code, variables are
1002 // live over their entire scope, and so they go live or dead only on
1003 // block boundaries.
1004 void CodeGen::siUpdate()
1006 if (!compiler->opts.compScopeInfo)
1011 if (compiler->opts.compDbgCode)
1016 if (compiler->info.compVarScopesCount == 0)
1021 #if FEATURE_EH_FUNCLETS
1022 if (siInFuncletRegion)
1026 #endif // FEATURE_EH_FUNCLETS
1028 VARSET_TP killed(VarSetOps::Diff(compiler, siLastLife, compiler->compCurLife));
1029 assert(VarSetOps::IsSubset(compiler, killed, compiler->lvaTrackedVars));
1031 VarSetOps::Iter iter(compiler, killed);
1032 unsigned varIndex = 0;
1033 while (iter.NextElem(&varIndex))
1036 unsigned lclNum = compiler->lvaTrackedToVarNum[varIndex];
1037 LclVarDsc* lclVar = &compiler->lvaTable[lclNum];
1038 assert(lclVar->lvTracked);
1040 siEndTrackedScope(varIndex);
1043 VarSetOps::Assign(compiler, siLastLife, compiler->compCurLife);
1046 /*****************************************************************************
1049 * For non-debuggable code, whenever we come across a GenTree which is an
1050 * assignment to a local variable, this function is called to check if the
1051 * variable has an open scope. Also, check if it has the correct LVnum.
1054 void CodeGen::siCheckVarScope(unsigned varNum, IL_OFFSET offs)
1056 assert(compiler->opts.compScopeInfo && !compiler->opts.compDbgCode && (compiler->info.compVarScopesCount > 0));
1058 #if FEATURE_EH_FUNCLETS
1059 if (siInFuncletRegion)
1063 #endif // FEATURE_EH_FUNCLETS
1065 if (offs == BAD_IL_OFFSET)
1071 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varNum];
1073 // If there is an open scope corresponding to varNum, find it
1075 if (lclVarDsc1->lvTracked)
1077 scope = siLatestTrackedScopes[lclVarDsc1->lvVarIndex];
1081 for (scope = siOpenScopeList.scNext; scope; scope = scope->scNext)
1083 if (scope->scVarNum == varNum)
1090 // Look up the compiler->info.compVarScopes[] to find the local var info for (varNum->lvSlotNum, offs)
1091 VarScopeDsc* varScope = compiler->compFindLocalVar(varNum, offs);
1092 if (varScope == nullptr)
1097 // If the currently open scope does not have the correct LVnum, close it
1098 // and create a new scope with this new LVnum
1102 if (scope->scLVnum != varScope->vsdLVnum)
1105 siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
1110 siNewScope(varScope->vsdLVnum, varScope->vsdVarNum);
1114 /*****************************************************************************
1115 * siCloseAllOpenScopes
1117 * For unreachable code, or optimized code with blocks reordered, there may be
1118 * scopes left open at the end. Simply close them.
1121 void CodeGen::siCloseAllOpenScopes()
1123 assert(siOpenScopeList.scNext);
1125 while (siOpenScopeList.scNext)
1127 siEndScope(siOpenScopeList.scNext);
1131 /*****************************************************************************
1134 * Displays all the vars on the open-scope list
1139 void CodeGen::siDispOpenScopes()
1141 assert(compiler->opts.compScopeInfo && (compiler->info.compVarScopesCount > 0));
1143 printf("Scope info: open scopes =\n");
1145 if (siOpenScopeList.scNext == nullptr)
1147 printf(" <none>\n");
1151 for (siScope* scope = siOpenScopeList.scNext; scope != nullptr; scope = scope->scNext)
1153 VarScopeDsc* localVars = compiler->info.compVarScopes;
1155 for (unsigned i = 0; i < compiler->info.compVarScopesCount; i++, localVars++)
1157 if (localVars->vsdLVnum == scope->scLVnum)
1159 const char* name = compiler->VarNameToStr(localVars->vsdName);
1160 // brace-matching editor workaround for following line: (
1161 printf(" %u (%s) [%03X..%03X)\n", localVars->vsdLVnum, name == nullptr ? "UNKNOWN" : name,
1162 localVars->vsdLifeBeg, localVars->vsdLifeEnd);
1172 /*============================================================================
1174 * Implementation for PrologScopeInfo
1176 *============================================================================
1179 /*****************************************************************************
1182 * Creates a new scope and adds it to the Open scope list.
1185 CodeGen::psiScope* CodeGen::psiNewPrologScope(unsigned LVnum, unsigned slotNum)
1187 psiScope* newScope = compiler->getAllocator(CMK_SiScope).allocate<psiScope>(1);
1189 newScope->scStartLoc.CaptureLocation(getEmitter());
1190 assert(newScope->scStartLoc.Valid());
1192 newScope->scEndLoc.Init();
1194 newScope->scLVnum = LVnum;
1195 newScope->scSlotNum = slotNum;
1197 newScope->scNext = nullptr;
1198 psiOpenScopeLast->scNext = newScope;
1199 newScope->scPrev = psiOpenScopeLast;
1200 psiOpenScopeLast = newScope;
1205 /*****************************************************************************
1208 * Remove the scope from the Open-scope list and add it to the finished-scopes
1209 * list if its length is non-zero
1212 void CodeGen::psiEndPrologScope(psiScope* scope)
1214 scope->scEndLoc.CaptureLocation(getEmitter());
1215 assert(scope->scEndLoc.Valid());
1217 // Remove from open-scope list
1218 scope->scPrev->scNext = scope->scNext;
1221 scope->scNext->scPrev = scope->scPrev;
1225 psiOpenScopeLast = scope->scPrev;
1228 // Add to the finished scope list.
1229 // If the length is zero, it means that the prolog is empty. In that case,
1230 // CodeGen::genSetScopeInfo will report the liveness of all arguments
1231 // as spanning the first instruction in the method, so that they can
1232 // at least be inspected on entry to the method.
1233 if (scope->scStartLoc != scope->scEndLoc || scope->scStartLoc.IsOffsetZero())
1235 psiScopeLast->scNext = scope;
1236 psiScopeLast = scope;
1241 /*============================================================================
1242 * INTERFACE (protected) Functions for PrologScopeInfo
1243 *============================================================================
1246 //------------------------------------------------------------------------
1247 // psSetScopeOffset: Set the offset of the newScope to the offset of the LslVar
1250 // 'newScope' the new scope object whose offset is to be set to the lclVarDsc offset.
1251 // 'lclVarDsc' is an op that will now be contained by its parent.
1254 void CodeGen::psSetScopeOffset(psiScope* newScope, LclVarDsc* lclVarDsc)
1256 newScope->scRegister = false;
1257 newScope->u2.scBaseReg = REG_SPBASE;
1259 #ifdef _TARGET_AMD64_
1260 // scOffset = offset from caller SP - REGSIZE_BYTES
1261 // TODO-Cleanup - scOffset needs to be understood. For now just matching with the existing definition.
1262 newScope->u2.scOffset =
1263 compiler->lvaToCallerSPRelativeOffset(lclVarDsc->lvStkOffs, lclVarDsc->lvFramePointerBased) + REGSIZE_BYTES;
1264 #else // !_TARGET_AMD64_
1265 if (doubleAlignOrFramePointerUsed())
1267 // REGSIZE_BYTES - for the pushed value of EBP
1268 newScope->u2.scOffset = lclVarDsc->lvStkOffs - REGSIZE_BYTES;
1272 newScope->u2.scOffset = lclVarDsc->lvStkOffs - genTotalFrameSize();
1274 #endif // !_TARGET_AMD64_
1277 /*============================================================================
1278 * INTERFACE (public) Functions for PrologScopeInfo
1279 *============================================================================
1282 /*****************************************************************************
1285 * Initializes the PrologScopeInfo, and creates open scopes for all the
1286 * parameters of the method.
1289 void CodeGen::psiBegProlog()
1291 assert(compiler->compGeneratingProlog);
1293 VarScopeDsc* varScope;
1295 psiOpenScopeList.scNext = nullptr;
1296 psiOpenScopeLast = &psiOpenScopeList;
1297 psiScopeLast = &psiScopeList;
1300 compiler->compResetScopeLists();
1302 while ((varScope = compiler->compGetNextEnterScope(0)) != nullptr)
1304 LclVarDsc* lclVarDsc1 = &compiler->lvaTable[varScope->vsdVarNum];
1306 if (!lclVarDsc1->lvIsParam)
1311 psiScope* newScope = psiNewPrologScope(varScope->vsdLVnum, varScope->vsdVarNum);
1313 if (lclVarDsc1->lvIsRegArg)
1315 bool isStructHandled = false;
1316 #if defined(UNIX_AMD64_ABI)
1317 SYSTEMV_AMD64_CORINFO_STRUCT_REG_PASSING_DESCRIPTOR structDesc;
1318 if (varTypeIsStruct(lclVarDsc1))
1320 CORINFO_CLASS_HANDLE typeHnd = lclVarDsc1->lvVerTypeInfo.GetClassHandle();
1321 assert(typeHnd != nullptr);
1322 compiler->eeGetSystemVAmd64PassStructInRegisterDescriptor(typeHnd, &structDesc);
1323 if (structDesc.passedInRegisters)
1325 regNumber regNum = REG_NA;
1326 regNumber otherRegNum = REG_NA;
1327 for (unsigned nCnt = 0; nCnt < structDesc.eightByteCount; nCnt++)
1329 unsigned len = structDesc.eightByteSizes[nCnt];
1330 var_types regType = TYP_UNDEF;
1334 regNum = lclVarDsc1->lvArgReg;
1338 otherRegNum = lclVarDsc1->lvOtherArgReg;
1342 assert(false && "Invalid eightbyte number.");
1345 regType = compiler->GetEightByteType(structDesc, nCnt);
1347 regType = compiler->mangleVarArgsType(regType);
1348 assert(genMapRegNumToRegArgNum((nCnt == 0 ? regNum : otherRegNum), regType) != (unsigned)-1);
1352 newScope->scRegister = true;
1353 newScope->u1.scRegNum = (regNumberSmall)regNum;
1354 newScope->u1.scOtherReg = (regNumberSmall)otherRegNum;
1358 // Stack passed argument. Get the offset from the caller's frame.
1359 psSetScopeOffset(newScope, lclVarDsc1);
1362 isStructHandled = true;
1364 #endif // !defined(UNIX_AMD64_ABI)
1365 if (!isStructHandled)
1368 var_types regType = compiler->mangleVarArgsType(lclVarDsc1->TypeGet());
1369 if (lclVarDsc1->lvIsHfaRegArg())
1371 regType = lclVarDsc1->GetHfaType();
1373 assert(genMapRegNumToRegArgNum(lclVarDsc1->lvArgReg, regType) != (unsigned)-1);
1376 newScope->scRegister = true;
1377 newScope->u1.scRegNum = (regNumberSmall)lclVarDsc1->lvArgReg;
1382 psSetScopeOffset(newScope, lclVarDsc1);
1387 /*****************************************************************************
1388 Enable this macro to get accurate prolog information for every instruction
1389 in the prolog. However, this is overkill as nobody steps through the
1390 disassembly of the prolog. Even if they do they will not expect rich debug info.
1392 We still report all the arguments at the very start of the method so that
1393 the user can see the arguments at the very start of the method (offset=0).
1395 Disabling this decreased the debug maps in mscorlib by 10% (01/2003)
1399 #define ACCURATE_PROLOG_DEBUG_INFO
1402 /*****************************************************************************
1403 * psiAdjustStackLevel
1405 * When ESP changes, all scopes relative to ESP have to be updated.
1408 void CodeGen::psiAdjustStackLevel(unsigned size)
1410 if (!compiler->opts.compScopeInfo || (compiler->info.compVarScopesCount == 0))
1415 assert(compiler->compGeneratingProlog);
1417 #ifdef ACCURATE_PROLOG_DEBUG_INFO
1421 // walk the list backwards
1422 // Works as psiEndPrologScope does not change scPrev
1423 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1425 if (scope->scRegister)
1427 assert(compiler->lvaTable[scope->scSlotNum].lvIsRegArg);
1430 assert(scope->u2.scBaseReg == REG_SPBASE);
1432 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1433 newScope->scRegister = false;
1434 newScope->u2.scBaseReg = REG_SPBASE;
1435 newScope->u2.scOffset = scope->u2.scOffset + size;
1437 psiEndPrologScope(scope);
1440 #endif // ACCURATE_PROLOG_DEBUG_INFO
1443 /*****************************************************************************
1446 * For EBP-frames, the parameters are accessed via ESP on entry to the function,
1447 * but via EBP right after a "mov ebp,esp" instruction
1450 void CodeGen::psiMoveESPtoEBP()
1452 if (!compiler->opts.compScopeInfo || (compiler->info.compVarScopesCount == 0))
1457 assert(compiler->compGeneratingProlog);
1458 assert(doubleAlignOrFramePointerUsed());
1460 #ifdef ACCURATE_PROLOG_DEBUG_INFO
1464 // walk the list backwards
1465 // Works as psiEndPrologScope does not change scPrev
1466 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1468 if (scope->scRegister)
1470 assert(compiler->lvaTable[scope->scSlotNum].lvIsRegArg);
1473 assert(scope->u2.scBaseReg == REG_SPBASE);
1475 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1476 newScope->scRegister = false;
1477 newScope->u2.scBaseReg = REG_FPBASE;
1478 newScope->u2.scOffset = scope->u2.scOffset;
1480 psiEndPrologScope(scope);
1483 #endif // ACCURATE_PROLOG_DEBUG_INFO
1486 /*****************************************************************************
1489 * Called when a parameter is loaded into its assigned register from the stack,
1490 * or when parameters are moved around due to circular dependancy.
1491 * If reg != REG_NA, then the parameter is being moved into its assigned
1492 * register, else it may be being moved to a temp register.
1495 void CodeGen::psiMoveToReg(unsigned varNum, regNumber reg, regNumber otherReg)
1497 assert(compiler->compGeneratingProlog);
1499 if (!compiler->opts.compScopeInfo)
1504 if (compiler->info.compVarScopesCount == 0)
1509 assert((int)varNum >= 0); // It's not a spill temp number.
1510 assert(compiler->lvaTable[varNum].lvIsInReg());
1512 #ifdef ACCURATE_PROLOG_DEBUG_INFO
1514 /* If reg!=REG_NA, the parameter is part of a cirular dependancy, and is
1515 * being moved through temp register "reg".
1516 * If reg==REG_NA, it is being moved to its assigned register.
1520 // Grab the assigned registers.
1522 reg = compiler->lvaTable[varNum].lvRegNum;
1523 otherReg = compiler->lvaTable[varNum].lvOtherReg;
1528 // walk the list backwards
1529 // Works as psiEndPrologScope does not change scPrev
1530 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1532 if (scope->scSlotNum != compiler->lvaTable[varNum].lvSlotNum)
1535 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1536 newScope->scRegister = true;
1537 newScope->u1.scRegNum = reg;
1538 newScope->u1.scOtherReg = otherReg;
1540 psiEndPrologScope(scope);
1544 // May happen if a parameter does not have an entry in the LocalVarTab
1545 // But assert() just in case it is because of something else.
1546 assert(varNum == compiler->info.compRetBuffArg ||
1547 !"Parameter scope not found (Assert doesnt always indicate error)");
1549 #endif // ACCURATE_PROLOG_DEBUG_INFO
1552 /*****************************************************************************
1553 * CodeGen::psiMoveToStack
1555 * A incoming register-argument is being moved to its final home on the stack
1556 * (ie. all adjustments to {F/S}PBASE have been made
1559 void CodeGen::psiMoveToStack(unsigned varNum)
1561 if (!compiler->opts.compScopeInfo || (compiler->info.compVarScopesCount == 0))
1566 assert(compiler->compGeneratingProlog);
1567 assert(compiler->lvaTable[varNum].lvIsRegArg);
1568 assert(!compiler->lvaTable[varNum].lvRegister);
1570 #ifdef ACCURATE_PROLOG_DEBUG_INFO
1574 // walk the list backwards
1575 // Works as psiEndPrologScope does not change scPrev
1576 for (scope = psiOpenScopeLast; scope != &psiOpenScopeList; scope = scope->scPrev)
1578 if (scope->scSlotNum != compiler->lvaTable[varNum].lvSlotNum)
1581 /* The param must be currently sitting in the register in which it
1583 assert(scope->scRegister);
1584 assert(scope->u1.scRegNum == compiler->lvaTable[varNum].lvArgReg);
1586 psiScope* newScope = psiNewPrologScope(scope->scLVnum, scope->scSlotNum);
1587 newScope->scRegister = false;
1588 newScope->u2.scBaseReg = (compiler->lvaTable[varNum].lvFramePointerBased) ? REG_FPBASE : REG_SPBASE;
1589 newScope->u2.scOffset = compiler->lvaTable[varNum].lvStkOffs;
1591 psiEndPrologScope(scope);
1595 // May happen if a parameter does not have an entry in the LocalVarTab
1596 // But assert() just in case it is because of something else.
1597 assert(varNum == compiler->info.compRetBuffArg ||
1598 !"Parameter scope not found (Assert doesnt always indicate error)");
1600 #endif // ACCURATE_PROLOG_DEBUG_INFO
1603 /*****************************************************************************
1607 void CodeGen::psiEndProlog()
1609 assert(compiler->compGeneratingProlog);
1612 for (scope = psiOpenScopeList.scNext; scope; scope = psiOpenScopeList.scNext)
1614 psiEndPrologScope(scope);
1617 #endif // USING_SCOPE_INFO