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 Exception Handling XX |
9 | XX XX |
10 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
11 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
12 | */ |
13 | #include "jitpch.h" |
14 | #ifdef _MSC_VER |
15 | #pragma hdrstop |
16 | #endif |
17 | |
18 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
19 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
20 | XX XX |
21 | XX "EHblkDsc" functions XX |
22 | XX XX |
23 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
24 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
25 | */ |
26 | |
27 | /*****************************************************************************/ |
28 | |
29 | BasicBlock* EHblkDsc::BBFilterLast() |
30 | { |
31 | noway_assert(HasFilter()); |
32 | noway_assert(ebdFilter != nullptr); |
33 | noway_assert(ebdHndBeg != nullptr); |
34 | |
35 | // The last block of the filter is the block immediately preceding the first block of the handler. |
36 | return ebdHndBeg->bbPrev; |
37 | } |
38 | |
39 | BasicBlock* EHblkDsc::ExFlowBlock() |
40 | { |
41 | if (HasFilter()) |
42 | { |
43 | return ebdFilter; |
44 | } |
45 | else |
46 | { |
47 | return ebdHndBeg; |
48 | } |
49 | } |
50 | |
51 | bool EHblkDsc::InTryRegionILRange(BasicBlock* pBlk) |
52 | { |
53 | // BBF_INTERNAL blocks may not have a valid bbCodeOffs. This function |
54 | // should only be used before any BBF_INTERNAL blocks have been added. |
55 | assert(!(pBlk->bbFlags & BBF_INTERNAL)); |
56 | |
57 | return Compiler::jitIsBetween(pBlk->bbCodeOffs, ebdTryBegOffs(), ebdTryEndOffs()); |
58 | } |
59 | |
60 | bool EHblkDsc::InFilterRegionILRange(BasicBlock* pBlk) |
61 | { |
62 | // BBF_INTERNAL blocks may not have a valid bbCodeOffs. This function |
63 | // should only be used before any BBF_INTERNAL blocks have been added. |
64 | assert(!(pBlk->bbFlags & BBF_INTERNAL)); |
65 | |
66 | return HasFilter() && Compiler::jitIsBetween(pBlk->bbCodeOffs, ebdFilterBegOffs(), ebdFilterEndOffs()); |
67 | } |
68 | |
69 | bool EHblkDsc::InHndRegionILRange(BasicBlock* pBlk) |
70 | { |
71 | // BBF_INTERNAL blocks may not have a valid bbCodeOffs. This function |
72 | // should only be used before any BBF_INTERNAL blocks have been added. |
73 | assert(!(pBlk->bbFlags & BBF_INTERNAL)); |
74 | |
75 | return Compiler::jitIsBetween(pBlk->bbCodeOffs, ebdHndBegOffs(), ebdHndEndOffs()); |
76 | } |
77 | |
78 | // HasCatchHandler: returns 'true' for either try/catch, or try/filter/filter-handler. |
79 | bool EHblkDsc::HasCatchHandler() |
80 | { |
81 | return (ebdHandlerType == EH_HANDLER_CATCH) || (ebdHandlerType == EH_HANDLER_FILTER); |
82 | } |
83 | |
84 | bool EHblkDsc::HasFilter() |
85 | { |
86 | return ebdHandlerType == EH_HANDLER_FILTER; |
87 | } |
88 | |
89 | bool EHblkDsc::HasFinallyHandler() |
90 | { |
91 | return ebdHandlerType == EH_HANDLER_FINALLY; |
92 | } |
93 | |
94 | bool EHblkDsc::HasFaultHandler() |
95 | { |
96 | return (ebdHandlerType == EH_HANDLER_FAULT) || (ebdHandlerType == EH_HANDLER_FAULT_WAS_FINALLY); |
97 | } |
98 | |
99 | bool EHblkDsc::HasFinallyOrFaultHandler() |
100 | { |
101 | return HasFinallyHandler() || HasFaultHandler(); |
102 | } |
103 | |
104 | /***************************************************************************** |
105 | * Returns true if pBlk is a block in the range [pStart..pEnd). |
106 | * The check is inclusive of pStart, exclusive of pEnd. |
107 | */ |
108 | |
109 | bool EHblkDsc::InBBRange(BasicBlock* pBlk, BasicBlock* pStart, BasicBlock* pEnd) |
110 | { |
111 | for (BasicBlock* pWalk = pStart; pWalk != pEnd; pWalk = pWalk->bbNext) |
112 | { |
113 | if (pWalk == pBlk) |
114 | { |
115 | return true; |
116 | } |
117 | } |
118 | return false; |
119 | } |
120 | |
121 | bool EHblkDsc::InTryRegionBBRange(BasicBlock* pBlk) |
122 | { |
123 | return InBBRange(pBlk, ebdTryBeg, ebdTryLast->bbNext); |
124 | } |
125 | |
126 | bool EHblkDsc::InFilterRegionBBRange(BasicBlock* pBlk) |
127 | { |
128 | return HasFilter() && InBBRange(pBlk, ebdFilter, ebdHndBeg); |
129 | } |
130 | |
131 | bool EHblkDsc::InHndRegionBBRange(BasicBlock* pBlk) |
132 | { |
133 | return InBBRange(pBlk, ebdHndBeg, ebdHndLast->bbNext); |
134 | } |
135 | |
136 | unsigned EHblkDsc::ebdGetEnclosingRegionIndex(bool* inTryRegion) |
137 | { |
138 | if ((ebdEnclosingTryIndex == NO_ENCLOSING_INDEX) && (ebdEnclosingHndIndex == NO_ENCLOSING_INDEX)) |
139 | { |
140 | return NO_ENCLOSING_INDEX; |
141 | } |
142 | else if (ebdEnclosingTryIndex == NO_ENCLOSING_INDEX) |
143 | { |
144 | assert(ebdEnclosingHndIndex != NO_ENCLOSING_INDEX); |
145 | *inTryRegion = false; |
146 | return ebdEnclosingHndIndex; |
147 | } |
148 | else if (ebdEnclosingHndIndex == NO_ENCLOSING_INDEX) |
149 | { |
150 | assert(ebdEnclosingTryIndex != NO_ENCLOSING_INDEX); |
151 | *inTryRegion = true; |
152 | return ebdEnclosingTryIndex; |
153 | } |
154 | else |
155 | { |
156 | assert(ebdEnclosingTryIndex != NO_ENCLOSING_INDEX); |
157 | assert(ebdEnclosingHndIndex != NO_ENCLOSING_INDEX); |
158 | assert(ebdEnclosingTryIndex != ebdEnclosingHndIndex); |
159 | if (ebdEnclosingTryIndex < ebdEnclosingHndIndex) |
160 | { |
161 | *inTryRegion = true; |
162 | return ebdEnclosingTryIndex; |
163 | } |
164 | else |
165 | { |
166 | *inTryRegion = false; |
167 | return ebdEnclosingHndIndex; |
168 | } |
169 | } |
170 | } |
171 | |
172 | /*****************************************************************************/ |
173 | |
174 | // We used to assert that the IL offsets in the EH table matched the IL offset stored |
175 | // on the blocks pointed to by the try/filter/handler block pointers. This is true at |
176 | // import time, but can fail to be true later in compilation when we start doing |
177 | // flow optimizations. |
178 | // |
179 | // That being said, the IL offsets in the EH table should only be examined early, |
180 | // during importing. After importing, use block info instead. |
181 | |
182 | IL_OFFSET EHblkDsc::ebdTryBegOffs() |
183 | { |
184 | return ebdTryBegOffset; |
185 | } |
186 | |
187 | IL_OFFSET EHblkDsc::ebdTryEndOffs() |
188 | { |
189 | return ebdTryEndOffset; |
190 | } |
191 | |
192 | IL_OFFSET EHblkDsc::ebdHndBegOffs() |
193 | { |
194 | return ebdHndBegOffset; |
195 | } |
196 | |
197 | IL_OFFSET EHblkDsc::ebdHndEndOffs() |
198 | { |
199 | return ebdHndEndOffset; |
200 | } |
201 | |
202 | IL_OFFSET EHblkDsc::ebdFilterBegOffs() |
203 | { |
204 | assert(HasFilter()); |
205 | return ebdFilterBegOffset; |
206 | } |
207 | |
208 | IL_OFFSET EHblkDsc::ebdFilterEndOffs() |
209 | { |
210 | assert(HasFilter()); |
211 | return ebdHndBegOffs(); // end of filter is beginning of handler |
212 | } |
213 | |
214 | /* static */ |
215 | bool EHblkDsc::ebdIsSameILTry(EHblkDsc* h1, EHblkDsc* h2) |
216 | { |
217 | return ((h1->ebdTryBegOffset == h2->ebdTryBegOffset) && (h1->ebdTryEndOffset == h2->ebdTryEndOffset)); |
218 | } |
219 | |
220 | /*****************************************************************************/ |
221 | |
222 | /* static */ |
223 | bool EHblkDsc::ebdIsSameTry(EHblkDsc* h1, EHblkDsc* h2) |
224 | { |
225 | return ((h1->ebdTryBeg == h2->ebdTryBeg) && (h1->ebdTryLast == h2->ebdTryLast)); |
226 | } |
227 | |
228 | bool EHblkDsc::ebdIsSameTry(Compiler* comp, unsigned t2) |
229 | { |
230 | EHblkDsc* h2 = comp->ehGetDsc(t2); |
231 | return ebdIsSameTry(this, h2); |
232 | } |
233 | |
234 | bool EHblkDsc::ebdIsSameTry(BasicBlock* ebdTryBeg, BasicBlock* ebdTryLast) |
235 | { |
236 | return ((this->ebdTryBeg == ebdTryBeg) && (this->ebdTryLast == ebdTryLast)); |
237 | } |
238 | |
239 | /*****************************************************************************/ |
240 | #ifdef DEBUG |
241 | /*****************************************************************************/ |
242 | |
243 | void EHblkDsc::DispEntry(unsigned XTnum) |
244 | { |
245 | printf(" %2u ::" , XTnum); |
246 | |
247 | #if !FEATURE_EH_FUNCLETS |
248 | printf(" %2u " , XTnum, ebdHandlerNestingLevel); |
249 | #endif // !FEATURE_EH_FUNCLETS |
250 | |
251 | if (ebdEnclosingTryIndex == NO_ENCLOSING_INDEX) |
252 | { |
253 | printf(" " ); |
254 | } |
255 | else |
256 | { |
257 | printf(" %2u " , ebdEnclosingTryIndex); |
258 | } |
259 | |
260 | if (ebdEnclosingHndIndex == NO_ENCLOSING_INDEX) |
261 | { |
262 | printf(" " ); |
263 | } |
264 | else |
265 | { |
266 | printf(" %2u " , ebdEnclosingHndIndex); |
267 | } |
268 | |
269 | ////////////// |
270 | ////////////// Protected (try) region |
271 | ////////////// |
272 | |
273 | printf("- Try at " FMT_BB ".." FMT_BB, ebdTryBeg->bbNum, ebdTryLast->bbNum); |
274 | |
275 | /* ( brace matching editor workaround to compensate for the following line */ |
276 | printf(" [%03X..%03X), " , ebdTryBegOffset, ebdTryEndOffset); |
277 | |
278 | ////////////// |
279 | ////////////// Filter region |
280 | ////////////// |
281 | |
282 | if (HasFilter()) |
283 | { |
284 | /* ( brace matching editor workaround to compensate for the following line */ |
285 | printf("Filter at " FMT_BB ".." FMT_BB " [%03X..%03X), " , ebdFilter->bbNum, BBFilterLast()->bbNum, |
286 | ebdFilterBegOffset, ebdHndBegOffset); |
287 | } |
288 | |
289 | ////////////// |
290 | ////////////// Handler region |
291 | ////////////// |
292 | |
293 | if (ebdHndBeg->bbCatchTyp == BBCT_FINALLY) |
294 | { |
295 | printf("Finally" ); |
296 | } |
297 | else if (ebdHndBeg->bbCatchTyp == BBCT_FAULT) |
298 | { |
299 | printf("Fault " ); |
300 | } |
301 | else |
302 | { |
303 | printf("Handler" ); |
304 | } |
305 | |
306 | printf(" at " FMT_BB ".." FMT_BB, ebdHndBeg->bbNum, ebdHndLast->bbNum); |
307 | |
308 | /* ( brace matching editor workaround to compensate for the following line */ |
309 | printf(" [%03X..%03X)" , ebdHndBegOffset, ebdHndEndOffset); |
310 | |
311 | printf("\n" ); |
312 | } |
313 | |
314 | /*****************************************************************************/ |
315 | #endif // DEBUG |
316 | /*****************************************************************************/ |
317 | |
318 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
319 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
320 | XX XX |
321 | XX "Compiler" functions XX |
322 | XX XX |
323 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
324 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
325 | */ |
326 | |
327 | bool Compiler::bbInCatchHandlerILRange(BasicBlock* blk) |
328 | { |
329 | EHblkDsc* HBtab = ehGetBlockHndDsc(blk); |
330 | |
331 | if (HBtab == nullptr) |
332 | { |
333 | return false; |
334 | } |
335 | |
336 | return HBtab->HasCatchHandler() && HBtab->InHndRegionILRange(blk); |
337 | } |
338 | |
339 | bool Compiler::bbInFilterILRange(BasicBlock* blk) |
340 | { |
341 | EHblkDsc* HBtab = ehGetBlockHndDsc(blk); |
342 | |
343 | if (HBtab == nullptr) |
344 | { |
345 | return false; |
346 | } |
347 | |
348 | return HBtab->InFilterRegionILRange(blk); |
349 | } |
350 | |
351 | // Given a handler region, find the innermost try region that contains it. |
352 | // NOTE: handlerIndex is 1-based (0 means no handler). |
353 | unsigned short Compiler::bbFindInnermostTryRegionContainingHandlerRegion(unsigned handlerIndex) |
354 | { |
355 | if (handlerIndex > 0) |
356 | { |
357 | unsigned XTnum; |
358 | EHblkDsc* ehDsc; |
359 | BasicBlock* blk = ehGetDsc(handlerIndex - 1)->ebdHndBeg; |
360 | |
361 | // handlerIndex is 1 based, therefore our interesting clauses start from clause compHndBBtab[handlerIndex] |
362 | EHblkDsc* ehDscEnd = compHndBBtab + compHndBBtabCount; |
363 | for (ehDsc = compHndBBtab + handlerIndex, XTnum = handlerIndex; ehDsc < ehDscEnd; ehDsc++, XTnum++) |
364 | { |
365 | if (bbInTryRegions(XTnum, blk)) |
366 | { |
367 | noway_assert(XTnum < MAX_XCPTN_INDEX); |
368 | return (unsigned short)(XTnum + 1); // Return the tryIndex |
369 | } |
370 | } |
371 | } |
372 | |
373 | return 0; |
374 | } |
375 | |
376 | // Given a try region, find the innermost handler region that contains it. |
377 | // NOTE: tryIndex is 1-based (0 means no handler). |
378 | unsigned short Compiler::bbFindInnermostHandlerRegionContainingTryRegion(unsigned tryIndex) |
379 | { |
380 | if (tryIndex > 0) |
381 | { |
382 | unsigned XTnum; |
383 | EHblkDsc* ehDsc; |
384 | BasicBlock* blk = ehGetDsc(tryIndex - 1)->ebdTryBeg; |
385 | |
386 | // tryIndex is 1 based, our interesting clauses start from clause compHndBBtab[tryIndex] |
387 | EHblkDsc* ehDscEnd = compHndBBtab + compHndBBtabCount; |
388 | for (ehDsc = compHndBBtab + tryIndex, XTnum = tryIndex; ehDsc < ehDscEnd; ehDsc++, XTnum++) |
389 | { |
390 | if (bbInHandlerRegions(XTnum, blk)) |
391 | { |
392 | noway_assert(XTnum < MAX_XCPTN_INDEX); |
393 | return (unsigned short)(XTnum + 1); // Return the handlerIndex |
394 | } |
395 | } |
396 | } |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | /* |
402 | Given a block and a try region index, check to see if the block is within |
403 | the try body. For this check, a funclet is considered to be in the region |
404 | it was extracted from. |
405 | */ |
406 | bool Compiler::bbInTryRegions(unsigned regionIndex, BasicBlock* blk) |
407 | { |
408 | assert(regionIndex < EHblkDsc::NO_ENCLOSING_INDEX); |
409 | unsigned tryIndex = blk->hasTryIndex() ? blk->getTryIndex() : EHblkDsc::NO_ENCLOSING_INDEX; |
410 | |
411 | // Loop outward until we find an enclosing try that is the same as the one |
412 | // we are looking for or an outer/later one |
413 | while (tryIndex < regionIndex) |
414 | { |
415 | tryIndex = ehGetEnclosingTryIndex(tryIndex); |
416 | } |
417 | |
418 | // Now we have the index of 2 try bodies, either they match or not! |
419 | return (tryIndex == regionIndex); |
420 | } |
421 | |
422 | //------------------------------------------------------------------------ |
423 | // bbInExnFlowRegions: |
424 | // Check to see if an exception raised in the given block could be |
425 | // handled by the given region (possibly after inner regions). |
426 | // |
427 | // Arguments: |
428 | // regionIndex - Check if this region can handle exceptions from 'blk' |
429 | // blk - Consider exceptions raised from this block |
430 | // |
431 | // Return Value: |
432 | // true - The region with index 'regionIndex' can handle exceptions from 'blk' |
433 | // false - The region with index 'regionIndex' can't handle exceptions from 'blk' |
434 | // |
435 | // Notes: |
436 | // For this check, a funclet is considered to be in the region it was |
437 | // extracted from. |
438 | |
439 | bool Compiler::bbInExnFlowRegions(unsigned regionIndex, BasicBlock* blk) |
440 | { |
441 | assert(regionIndex < EHblkDsc::NO_ENCLOSING_INDEX); |
442 | EHblkDsc* ExnFlowRegion = ehGetBlockExnFlowDsc(blk); |
443 | unsigned tryIndex = (ExnFlowRegion == nullptr ? EHblkDsc::NO_ENCLOSING_INDEX : ehGetIndex(ExnFlowRegion)); |
444 | |
445 | // Loop outward until we find an enclosing try that is the same as the one |
446 | // we are looking for or an outer/later one |
447 | while (tryIndex < regionIndex) |
448 | { |
449 | tryIndex = ehGetEnclosingTryIndex(tryIndex); |
450 | } |
451 | |
452 | // Now we have the index of 2 try bodies, either they match or not! |
453 | return (tryIndex == regionIndex); |
454 | } |
455 | |
456 | /* |
457 | Given a block, check to see if it is in the handler block of the EH descriptor. |
458 | For this check, a funclet is considered to be in the region it was extracted from. |
459 | */ |
460 | bool Compiler::bbInHandlerRegions(unsigned regionIndex, BasicBlock* blk) |
461 | { |
462 | assert(regionIndex < EHblkDsc::NO_ENCLOSING_INDEX); |
463 | unsigned hndIndex = blk->hasHndIndex() ? blk->getHndIndex() : EHblkDsc::NO_ENCLOSING_INDEX; |
464 | |
465 | // We can't use the same simple trick here because there is no required ordering |
466 | // of handlers (which also have no required ordering with respect to their try |
467 | // bodies). |
468 | while (hndIndex < EHblkDsc::NO_ENCLOSING_INDEX && hndIndex != regionIndex) |
469 | { |
470 | hndIndex = ehGetEnclosingHndIndex(hndIndex); |
471 | } |
472 | |
473 | // Now we have the index of 2 try bodies, either they match or not! |
474 | return (hndIndex == regionIndex); |
475 | } |
476 | |
477 | /* |
478 | Given a hndBlk, see if it is in one of tryBlk's catch handler regions. |
479 | |
480 | Since we create one EHblkDsc for each "catch" of a "try", we might end up |
481 | with multiple EHblkDsc's that have the same ebdTryBeg and ebdTryLast, but different |
482 | ebdHndBeg and ebdHndLast. Unfortunately getTryIndex() only returns the index of the first EHblkDsc. |
483 | |
484 | E.g. The following example shows that BB02 has a catch in BB03 and another catch in BB04. |
485 | |
486 | index nest, enclosing |
487 | 0 :: 0, 1 - Try at BB01..BB02 [000..008], Handler at BB03 [009..016] |
488 | 1 :: 0, - Try at BB01..BB02 [000..008], Handler at BB04 [017..022] |
489 | |
490 | This function will return true for |
491 | bbInCatchHandlerRegions(BB02, BB03) and bbInCatchHandlerRegions(BB02, BB04) |
492 | |
493 | */ |
494 | bool Compiler::bbInCatchHandlerRegions(BasicBlock* tryBlk, BasicBlock* hndBlk) |
495 | { |
496 | assert(tryBlk->hasTryIndex()); |
497 | if (!hndBlk->hasHndIndex()) |
498 | { |
499 | return false; |
500 | } |
501 | |
502 | unsigned XTnum = tryBlk->getTryIndex(); |
503 | EHblkDsc* firstEHblkDsc = ehGetDsc(XTnum); |
504 | EHblkDsc* ehDsc = firstEHblkDsc; |
505 | |
506 | // Rather than searching the whole list, take advantage of our sorting. |
507 | // We will only match against blocks with the same try body (mutually |
508 | // protect regions). Because of our sort ordering, such regions will |
509 | // always be immediately adjacent, any nested regions will be before the |
510 | // first of the set, and any outer regions will be after the last. |
511 | // Also siblings will be before or after according to their location, |
512 | // but never in between; |
513 | |
514 | while (XTnum > 0) |
515 | { |
516 | assert(EHblkDsc::ebdIsSameTry(firstEHblkDsc, ehDsc)); |
517 | |
518 | // Stop when the previous region is not mutually protect |
519 | if (!EHblkDsc::ebdIsSameTry(firstEHblkDsc, ehDsc - 1)) |
520 | { |
521 | break; |
522 | } |
523 | |
524 | ehDsc--; |
525 | XTnum--; |
526 | } |
527 | |
528 | // XTnum and ehDsc are now referring to the first region in the set of |
529 | // mutually protect regions. |
530 | assert(EHblkDsc::ebdIsSameTry(firstEHblkDsc, ehDsc)); |
531 | assert((ehDsc == compHndBBtab) || !EHblkDsc::ebdIsSameTry(firstEHblkDsc, ehDsc - 1)); |
532 | |
533 | do |
534 | { |
535 | if (ehDsc->HasCatchHandler() && bbInHandlerRegions(XTnum, hndBlk)) |
536 | { |
537 | return true; |
538 | } |
539 | XTnum++; |
540 | ehDsc++; |
541 | } while (XTnum < compHndBBtabCount && EHblkDsc::ebdIsSameTry(firstEHblkDsc, ehDsc)); |
542 | |
543 | return false; |
544 | } |
545 | |
546 | /****************************************************************************************** |
547 | * Give two blocks, return the inner-most enclosing try region that contains both of them. |
548 | * Return 0 if it does not find any try region (which means the inner-most region |
549 | * is the method itself). |
550 | */ |
551 | |
552 | unsigned short Compiler::bbFindInnermostCommonTryRegion(BasicBlock* bbOne, BasicBlock* bbTwo) |
553 | { |
554 | unsigned XTnum; |
555 | |
556 | for (XTnum = 0; XTnum < compHndBBtabCount; XTnum++) |
557 | { |
558 | if (bbInTryRegions(XTnum, bbOne) && bbInTryRegions(XTnum, bbTwo)) |
559 | { |
560 | noway_assert(XTnum < MAX_XCPTN_INDEX); |
561 | return (unsigned short)(XTnum + 1); // Return the tryIndex |
562 | } |
563 | } |
564 | |
565 | return 0; |
566 | } |
567 | |
568 | // bbIsTryBeg() returns true if this block is the start of any try region. |
569 | // This is computed by examining the current values in the |
570 | // EH table rather than just looking at the block->bbFlags. |
571 | // |
572 | // Note that a block is the beginning of any try region if it is the beginning of the |
573 | // most nested try region it is a member of. Thus, we only need to check the EH |
574 | // table entry related to the try index stored on the block. |
575 | // |
576 | bool Compiler::bbIsTryBeg(BasicBlock* block) |
577 | { |
578 | EHblkDsc* ehDsc = ehGetBlockTryDsc(block); |
579 | return (ehDsc != nullptr) && (block == ehDsc->ebdTryBeg); |
580 | } |
581 | |
582 | // bbIsHanderBeg() returns true if "block" is the start of any handler or filter. |
583 | // Note that if a block is the beginning of a handler or filter, it must be the beginning |
584 | // of the most nested handler or filter region it is in. Thus, we only need to look at the EH |
585 | // descriptor corresponding to the handler index on the block. |
586 | // |
587 | bool Compiler::bbIsHandlerBeg(BasicBlock* block) |
588 | { |
589 | EHblkDsc* ehDsc = ehGetBlockHndDsc(block); |
590 | return (ehDsc != nullptr) && ((block == ehDsc->ebdHndBeg) || (ehDsc->HasFilter() && (block == ehDsc->ebdFilter))); |
591 | } |
592 | |
593 | bool Compiler::bbIsExFlowBlock(BasicBlock* block, unsigned* regionIndex) |
594 | { |
595 | if (block->hasHndIndex()) |
596 | { |
597 | *regionIndex = block->getHndIndex(); |
598 | return block == ehGetDsc(*regionIndex)->ExFlowBlock(); |
599 | } |
600 | else |
601 | { |
602 | return false; |
603 | } |
604 | } |
605 | |
606 | bool Compiler::ehHasCallableHandlers() |
607 | { |
608 | #if FEATURE_EH_FUNCLETS |
609 | |
610 | // Any EH in the function? |
611 | |
612 | return compHndBBtabCount > 0; |
613 | |
614 | #else // FEATURE_EH_FUNCLETS |
615 | |
616 | return ehNeedsShadowSPslots(); |
617 | |
618 | #endif // FEATURE_EH_FUNCLETS |
619 | } |
620 | |
621 | /****************************************************************************************** |
622 | * Determine if 'block' is the last block of an EH 'try' or handler (ignoring filters). If so, |
623 | * return the EH descriptor pointer for that EH region. Otherwise, return nullptr. |
624 | */ |
625 | EHblkDsc* Compiler::ehIsBlockTryLast(BasicBlock* block) |
626 | { |
627 | EHblkDsc* HBtab = ehGetBlockTryDsc(block); |
628 | if ((HBtab != nullptr) && (HBtab->ebdTryLast == block)) |
629 | { |
630 | return HBtab; |
631 | } |
632 | return nullptr; |
633 | } |
634 | |
635 | EHblkDsc* Compiler::ehIsBlockHndLast(BasicBlock* block) |
636 | { |
637 | EHblkDsc* HBtab = ehGetBlockHndDsc(block); |
638 | if ((HBtab != nullptr) && (HBtab->ebdHndLast == block)) |
639 | { |
640 | return HBtab; |
641 | } |
642 | return nullptr; |
643 | } |
644 | |
645 | bool Compiler::ehIsBlockEHLast(BasicBlock* block) |
646 | { |
647 | return (ehIsBlockTryLast(block) != nullptr) || (ehIsBlockHndLast(block) != nullptr); |
648 | } |
649 | |
650 | //------------------------------------------------------------------------ |
651 | // ehGetBlockExnFlowDsc: |
652 | // Get the EH descriptor for the most nested region (if any) that may |
653 | // handle exceptions raised in the given block |
654 | // |
655 | // Arguments: |
656 | // block - Consider exceptions raised from this block |
657 | // |
658 | // Return Value: |
659 | // nullptr - The given block's exceptions propagate to caller |
660 | // non-null - This region is the innermost handler for exceptions raised in |
661 | // the given block |
662 | |
663 | EHblkDsc* Compiler::ehGetBlockExnFlowDsc(BasicBlock* block) |
664 | { |
665 | EHblkDsc* hndDesc = ehGetBlockHndDsc(block); |
666 | |
667 | if ((hndDesc != nullptr) && hndDesc->InFilterRegionBBRange(block)) |
668 | { |
669 | // If an exception is thrown in a filter (or escapes a callee in a filter), |
670 | // or if exception_continue_search (0/false) is returned at |
671 | // the end of a filter, the (original) exception is propagated to |
672 | // the next outer handler. The "next outer handler" is the handler |
673 | // of the try region enclosing the try that the filter protects. |
674 | // This may not be the same as the try region enclosing the filter, |
675 | // e.g. in cases like this: |
676 | // try { |
677 | // ... |
678 | // } filter (filter-part) { |
679 | // handler-part |
680 | // } catch { (or finally/fault/filter) |
681 | // which is represented as two EHblkDscs with the same try range, |
682 | // the inner protected by a filter and the outer protected by the |
683 | // other handler; exceptions in the filter-part propagate to the |
684 | // other handler, even though the other handler's try region does not |
685 | // enclose the filter. |
686 | |
687 | unsigned outerIndex = hndDesc->ebdEnclosingTryIndex; |
688 | |
689 | if (outerIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
690 | { |
691 | assert(!block->hasTryIndex()); |
692 | return nullptr; |
693 | } |
694 | return ehGetDsc(outerIndex); |
695 | } |
696 | |
697 | return ehGetBlockTryDsc(block); |
698 | } |
699 | |
700 | bool Compiler::ehBlockHasExnFlowDsc(BasicBlock* block) |
701 | { |
702 | if (block->hasTryIndex()) |
703 | { |
704 | return true; |
705 | } |
706 | |
707 | EHblkDsc* hndDesc = ehGetBlockHndDsc(block); |
708 | |
709 | return ((hndDesc != nullptr) && hndDesc->InFilterRegionBBRange(block) && |
710 | (hndDesc->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX)); |
711 | } |
712 | |
713 | //------------------------------------------------------------------------ |
714 | // ehGetMostNestedRegionIndex: Return the region index of the most nested EH region this block is in. |
715 | // The return value is in the range [0..compHndBBtabCount]. It is same scale as bbTryIndex/bbHndIndex: |
716 | // 0 means main method, N is used as an index to compHndBBtab[N - 1]. If we don't return 0, then |
717 | // *inTryRegion indicates whether the most nested region for the block is a 'try' clause or |
718 | // filter/handler clause. For 0 return, *inTryRegion is set to true. |
719 | // |
720 | // Arguments: |
721 | // block - the BasicBlock we want the region index for. |
722 | // inTryRegion - an out parameter. As described above. |
723 | // |
724 | // Return Value: |
725 | // As described above. |
726 | // |
727 | unsigned Compiler::ehGetMostNestedRegionIndex(BasicBlock* block, bool* inTryRegion) |
728 | { |
729 | assert(block != nullptr); |
730 | assert(inTryRegion != nullptr); |
731 | |
732 | unsigned mostNestedRegion; |
733 | if (block->bbHndIndex == 0) |
734 | { |
735 | mostNestedRegion = block->bbTryIndex; |
736 | *inTryRegion = true; |
737 | } |
738 | else if (block->bbTryIndex == 0) |
739 | { |
740 | mostNestedRegion = block->bbHndIndex; |
741 | *inTryRegion = false; |
742 | } |
743 | else |
744 | { |
745 | if (block->bbTryIndex < block->bbHndIndex) |
746 | { |
747 | mostNestedRegion = block->bbTryIndex; |
748 | *inTryRegion = true; |
749 | } |
750 | else |
751 | { |
752 | assert(block->bbTryIndex != block->bbHndIndex); // A block can't be both in the 'try' and 'handler' region |
753 | // of the same EH region |
754 | mostNestedRegion = block->bbHndIndex; |
755 | *inTryRegion = false; |
756 | } |
757 | } |
758 | |
759 | assert(mostNestedRegion <= compHndBBtabCount); |
760 | return mostNestedRegion; |
761 | } |
762 | |
763 | /***************************************************************************** |
764 | * Returns the try index of the enclosing try, skipping all EH regions with the |
765 | * same try region (that is, all 'mutual protect' regions). If there is no such |
766 | * enclosing try, returns EHblkDsc::NO_ENCLOSING_INDEX. |
767 | */ |
768 | unsigned Compiler::ehTrueEnclosingTryIndexIL(unsigned regionIndex) |
769 | { |
770 | assert(regionIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
771 | |
772 | EHblkDsc* ehDscRoot = ehGetDsc(regionIndex); |
773 | EHblkDsc* HBtab = ehDscRoot; |
774 | |
775 | for (;;) |
776 | { |
777 | regionIndex = HBtab->ebdEnclosingTryIndex; |
778 | if (regionIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
779 | { |
780 | // No enclosing 'try'; we're done |
781 | break; |
782 | } |
783 | |
784 | HBtab = ehGetDsc(regionIndex); |
785 | if (!EHblkDsc::ebdIsSameILTry(ehDscRoot, HBtab)) |
786 | { |
787 | // Found an enclosing 'try' that has a different 'try' region (is not mutually-protect with the |
788 | // original region). Return it. |
789 | break; |
790 | } |
791 | } |
792 | |
793 | return regionIndex; |
794 | } |
795 | |
796 | unsigned Compiler::ehGetEnclosingRegionIndex(unsigned regionIndex, bool* inTryRegion) |
797 | { |
798 | assert(regionIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
799 | |
800 | EHblkDsc* ehDsc = ehGetDsc(regionIndex); |
801 | return ehDsc->ebdGetEnclosingRegionIndex(inTryRegion); |
802 | } |
803 | |
804 | /***************************************************************************** |
805 | * The argument 'block' has been deleted. Update the EH table so 'block' is no longer listed |
806 | * as a 'last' block. You can't delete a 'begin' block this way. |
807 | */ |
808 | void Compiler::ehUpdateForDeletedBlock(BasicBlock* block) |
809 | { |
810 | assert(block->bbFlags & BBF_REMOVED); |
811 | |
812 | if (!block->hasTryIndex() && !block->hasHndIndex()) |
813 | { |
814 | // The block is not part of any EH region, so there is nothing to do. |
815 | return; |
816 | } |
817 | |
818 | BasicBlock* bPrev = block->bbPrev; |
819 | assert(bPrev != nullptr); |
820 | |
821 | ehUpdateLastBlocks(block, bPrev); |
822 | } |
823 | |
824 | /***************************************************************************** |
825 | * Determine if an empty block can be deleted, and still preserve the EH normalization |
826 | * rules on blocks. |
827 | * |
828 | * We only consider the case where the block to be deleted is the last block of a region, |
829 | * and the region is being contracted such that the previous block will become the new |
830 | * 'last' block. If this previous block is already a 'last' block, then we can't do the |
831 | * delete, as that would cause a single block to be the 'last' block of multiple regions. |
832 | */ |
833 | bool Compiler::ehCanDeleteEmptyBlock(BasicBlock* block) |
834 | { |
835 | assert(block->isEmpty()); |
836 | |
837 | return true; |
838 | |
839 | #if 0 // This is disabled while the "multiple last block" normalization is disabled |
840 | if (!fgNormalizeEHDone) |
841 | { |
842 | return true; |
843 | } |
844 | |
845 | if (ehIsBlockEHLast(block)) |
846 | { |
847 | BasicBlock* bPrev = block->bbPrev; |
848 | if ((bPrev != nullptr) && ehIsBlockEHLast(bPrev)) |
849 | { |
850 | return false; |
851 | } |
852 | } |
853 | |
854 | return true; |
855 | #endif // 0 |
856 | } |
857 | |
858 | /***************************************************************************** |
859 | * The 'last' block of one or more EH regions might have changed. Update the EH table. |
860 | * This can happen if the EH region shrinks, where one or more blocks have been removed |
861 | * from the region. It can happen if the EH region grows, where one or more blocks |
862 | * have been added at the end of the region. |
863 | * |
864 | * We might like to verify the handler table integrity after doing this update, but we |
865 | * can't because this might just be one step by the caller in a transformation back to |
866 | * a legal state. |
867 | * |
868 | * Arguments: |
869 | * oldLast -- Search for this block as the 'last' block of one or more EH regions. |
870 | * newLast -- If 'oldLast' is found to be the 'last' block of an EH region, replace it by 'newLast'. |
871 | */ |
872 | void Compiler::ehUpdateLastBlocks(BasicBlock* oldLast, BasicBlock* newLast) |
873 | { |
874 | EHblkDsc* HBtab; |
875 | EHblkDsc* HBtabEnd; |
876 | |
877 | for (HBtab = compHndBBtab, HBtabEnd = compHndBBtab + compHndBBtabCount; HBtab < HBtabEnd; HBtab++) |
878 | { |
879 | if (HBtab->ebdTryLast == oldLast) |
880 | { |
881 | fgSetTryEnd(HBtab, newLast); |
882 | } |
883 | if (HBtab->ebdHndLast == oldLast) |
884 | { |
885 | fgSetHndEnd(HBtab, newLast); |
886 | } |
887 | } |
888 | } |
889 | |
890 | unsigned Compiler::ehGetCallFinallyRegionIndex(unsigned finallyIndex, bool* inTryRegion) |
891 | { |
892 | assert(finallyIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
893 | assert(ehGetDsc(finallyIndex)->HasFinallyHandler()); |
894 | |
895 | #if defined(_TARGET_AMD64_) || defined(_TARGET_ARM64_) |
896 | return ehGetDsc(finallyIndex)->ebdGetEnclosingRegionIndex(inTryRegion); |
897 | #else |
898 | *inTryRegion = true; |
899 | return finallyIndex; |
900 | #endif |
901 | } |
902 | |
903 | void Compiler::ehGetCallFinallyBlockRange(unsigned finallyIndex, BasicBlock** begBlk, BasicBlock** endBlk) |
904 | { |
905 | assert(finallyIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
906 | assert(ehGetDsc(finallyIndex)->HasFinallyHandler()); |
907 | assert(begBlk != nullptr); |
908 | assert(endBlk != nullptr); |
909 | |
910 | EHblkDsc* ehDsc = ehGetDsc(finallyIndex); |
911 | |
912 | #if FEATURE_EH_CALLFINALLY_THUNKS |
913 | bool inTryRegion; |
914 | unsigned callFinallyRegionIndex = ehGetCallFinallyRegionIndex(finallyIndex, &inTryRegion); |
915 | |
916 | if (callFinallyRegionIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
917 | { |
918 | *begBlk = fgFirstBB; |
919 | *endBlk = fgEndBBAfterMainFunction(); |
920 | } |
921 | else |
922 | { |
923 | EHblkDsc* ehDsc = ehGetDsc(callFinallyRegionIndex); |
924 | |
925 | if (inTryRegion) |
926 | { |
927 | *begBlk = ehDsc->ebdTryBeg; |
928 | *endBlk = ehDsc->ebdTryLast->bbNext; |
929 | } |
930 | else |
931 | { |
932 | *begBlk = ehDsc->ebdHndBeg; |
933 | *endBlk = ehDsc->ebdHndLast->bbNext; |
934 | } |
935 | } |
936 | #else // !FEATURE_EH_CALLFINALLY_THUNKS |
937 | *begBlk = ehDsc->ebdTryBeg; |
938 | *endBlk = ehDsc->ebdTryLast->bbNext; |
939 | #endif // !FEATURE_EH_CALLFINALLY_THUNKS |
940 | } |
941 | |
942 | #ifdef DEBUG |
943 | |
944 | bool Compiler::ehCallFinallyInCorrectRegion(BasicBlock* blockCallFinally, unsigned finallyIndex) |
945 | { |
946 | assert(blockCallFinally->bbJumpKind == BBJ_CALLFINALLY); |
947 | assert(finallyIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
948 | assert(finallyIndex < compHndBBtabCount); |
949 | assert(ehGetDsc(finallyIndex)->HasFinallyHandler()); |
950 | |
951 | bool inTryRegion; |
952 | unsigned callFinallyIndex = ehGetCallFinallyRegionIndex(finallyIndex, &inTryRegion); |
953 | if (callFinallyIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
954 | { |
955 | if (blockCallFinally->hasTryIndex() || blockCallFinally->hasHndIndex()) |
956 | { |
957 | // The BBJ_CALLFINALLY is supposed to be in the main function body, not in any EH region. |
958 | return false; |
959 | } |
960 | else |
961 | { |
962 | return true; |
963 | } |
964 | } |
965 | else |
966 | { |
967 | if (inTryRegion) |
968 | { |
969 | if (bbInTryRegions(callFinallyIndex, blockCallFinally)) |
970 | { |
971 | return true; |
972 | } |
973 | } |
974 | else |
975 | { |
976 | if (bbInHandlerRegions(callFinallyIndex, blockCallFinally)) |
977 | { |
978 | return true; |
979 | } |
980 | } |
981 | } |
982 | |
983 | return false; |
984 | } |
985 | |
986 | #endif // DEBUG |
987 | |
988 | #if FEATURE_EH_FUNCLETS |
989 | |
990 | /***************************************************************************** |
991 | * |
992 | * Are there (or will there be) any funclets in the function? |
993 | */ |
994 | |
995 | bool Compiler::ehAnyFunclets() |
996 | { |
997 | return compHndBBtabCount > 0; // if there is any EH, there will be funclets |
998 | } |
999 | |
1000 | /***************************************************************************** |
1001 | * |
1002 | * Count the number of EH funclets in the function. This will return the number |
1003 | * there will be after funclets have been created, but because it runs over the |
1004 | * EH table, it is accurate at any time. |
1005 | */ |
1006 | |
1007 | unsigned Compiler::ehFuncletCount() |
1008 | { |
1009 | unsigned funcletCnt = 0; |
1010 | EHblkDsc* HBtab; |
1011 | EHblkDsc* HBtabEnd; |
1012 | |
1013 | for (HBtab = compHndBBtab, HBtabEnd = compHndBBtab + compHndBBtabCount; HBtab < HBtabEnd; HBtab++) |
1014 | { |
1015 | if (HBtab->HasFilter()) |
1016 | { |
1017 | ++funcletCnt; |
1018 | } |
1019 | ++funcletCnt; |
1020 | } |
1021 | return funcletCnt; |
1022 | } |
1023 | |
1024 | /***************************************************************************** |
1025 | * |
1026 | * Get the index to use as the cache key for sharing throw blocks. |
1027 | * For non-funclet platforms, this is just the block's bbTryIndex, to ensure |
1028 | * that throw is protected by the correct set of trys. However, when we have |
1029 | * funclets we also have to ensure that the throw blocks are *not* shared |
1030 | * across funclets, so we use EHblkDsc index of either the funclet or |
1031 | * the containing try region, whichever is inner-most. We differentiate |
1032 | * between the 3 cases by setting the high bits (0 = try, 1 = handler, |
1033 | * 2 = filter) |
1034 | * |
1035 | */ |
1036 | unsigned Compiler::bbThrowIndex(BasicBlock* blk) |
1037 | { |
1038 | if (!blk->hasTryIndex() && !blk->hasHndIndex()) |
1039 | { |
1040 | return -1; |
1041 | } |
1042 | |
1043 | const unsigned tryIndex = blk->hasTryIndex() ? blk->getTryIndex() : USHRT_MAX; |
1044 | const unsigned hndIndex = blk->hasHndIndex() ? blk->getHndIndex() : USHRT_MAX; |
1045 | assert(tryIndex != hndIndex); |
1046 | assert(tryIndex != USHRT_MAX || hndIndex != USHRT_MAX); |
1047 | |
1048 | if (tryIndex < hndIndex) |
1049 | { |
1050 | // The most enclosing region is a try body, use it |
1051 | assert(tryIndex <= 0x3FFFFFFF); |
1052 | return tryIndex; |
1053 | } |
1054 | |
1055 | // The most enclosing region is a handler which will be a funclet |
1056 | // Now we have to figure out if blk is in the filter or handler |
1057 | assert(hndIndex <= 0x3FFFFFFF); |
1058 | if (ehGetDsc(hndIndex)->InFilterRegionBBRange(blk)) |
1059 | { |
1060 | return hndIndex | 0x40000000; |
1061 | } |
1062 | |
1063 | return hndIndex | 0x80000000; |
1064 | } |
1065 | |
1066 | #endif // FEATURE_EH_FUNCLETS |
1067 | |
1068 | /***************************************************************************** |
1069 | * Determine the emitter code cookie for a block, for unwind purposes. |
1070 | */ |
1071 | |
1072 | void* Compiler::ehEmitCookie(BasicBlock* block) |
1073 | { |
1074 | noway_assert(block); |
1075 | |
1076 | void* cookie; |
1077 | |
1078 | #if FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_) |
1079 | if (block->bbFlags & BBF_FINALLY_TARGET) |
1080 | { |
1081 | // Use the offset of the beginning of the NOP padding, not the main block. |
1082 | // This might include loop head padding, too, if this is a loop head. |
1083 | assert(block->bbUnwindNopEmitCookie); // probably not null-initialized, though, so this might not tell us |
1084 | // anything |
1085 | cookie = block->bbUnwindNopEmitCookie; |
1086 | } |
1087 | else |
1088 | #endif // FEATURE_EH_FUNCLETS && defined(_TARGET_ARM_) |
1089 | { |
1090 | cookie = block->bbEmitCookie; |
1091 | } |
1092 | |
1093 | noway_assert(cookie != nullptr); |
1094 | return cookie; |
1095 | } |
1096 | |
1097 | /***************************************************************************** |
1098 | * Determine the emitter code offset for a block. If the block is a finally |
1099 | * target, choose the offset of the NOP padding that precedes the block. |
1100 | */ |
1101 | |
1102 | UNATIVE_OFFSET Compiler::ehCodeOffset(BasicBlock* block) |
1103 | { |
1104 | return genEmitter->emitCodeOffset(ehEmitCookie(block), 0); |
1105 | } |
1106 | |
1107 | /****************************************************************************/ |
1108 | |
1109 | EHblkDsc* Compiler::ehInitHndRange(BasicBlock* blk, IL_OFFSET* hndBeg, IL_OFFSET* hndEnd, bool* inFilter) |
1110 | { |
1111 | EHblkDsc* hndTab = ehGetBlockHndDsc(blk); |
1112 | if (hndTab != nullptr) |
1113 | { |
1114 | if (hndTab->InFilterRegionILRange(blk)) |
1115 | { |
1116 | *hndBeg = hndTab->ebdFilterBegOffs(); |
1117 | *hndEnd = hndTab->ebdFilterEndOffs(); |
1118 | *inFilter = true; |
1119 | } |
1120 | else |
1121 | { |
1122 | *hndBeg = hndTab->ebdHndBegOffs(); |
1123 | *hndEnd = hndTab->ebdHndEndOffs(); |
1124 | *inFilter = false; |
1125 | } |
1126 | } |
1127 | else |
1128 | { |
1129 | *hndBeg = 0; |
1130 | *hndEnd = info.compILCodeSize; |
1131 | *inFilter = false; |
1132 | } |
1133 | return hndTab; |
1134 | } |
1135 | |
1136 | /****************************************************************************/ |
1137 | |
1138 | EHblkDsc* Compiler::ehInitTryRange(BasicBlock* blk, IL_OFFSET* tryBeg, IL_OFFSET* tryEnd) |
1139 | { |
1140 | EHblkDsc* tryTab = ehGetBlockTryDsc(blk); |
1141 | if (tryTab != nullptr) |
1142 | { |
1143 | *tryBeg = tryTab->ebdTryBegOffs(); |
1144 | *tryEnd = tryTab->ebdTryEndOffs(); |
1145 | } |
1146 | else |
1147 | { |
1148 | *tryBeg = 0; |
1149 | *tryEnd = info.compILCodeSize; |
1150 | } |
1151 | return tryTab; |
1152 | } |
1153 | |
1154 | /****************************************************************************/ |
1155 | |
1156 | EHblkDsc* Compiler::ehInitHndBlockRange(BasicBlock* blk, BasicBlock** hndBeg, BasicBlock** hndLast, bool* inFilter) |
1157 | { |
1158 | EHblkDsc* hndTab = ehGetBlockHndDsc(blk); |
1159 | if (hndTab != nullptr) |
1160 | { |
1161 | if (hndTab->InFilterRegionBBRange(blk)) |
1162 | { |
1163 | *hndBeg = hndTab->ebdFilter; |
1164 | if (hndLast != nullptr) |
1165 | { |
1166 | *hndLast = hndTab->BBFilterLast(); |
1167 | } |
1168 | *inFilter = true; |
1169 | } |
1170 | else |
1171 | { |
1172 | *hndBeg = hndTab->ebdHndBeg; |
1173 | if (hndLast != nullptr) |
1174 | { |
1175 | *hndLast = hndTab->ebdHndLast; |
1176 | } |
1177 | *inFilter = false; |
1178 | } |
1179 | } |
1180 | else |
1181 | { |
1182 | *hndBeg = nullptr; |
1183 | if (hndLast != nullptr) |
1184 | { |
1185 | *hndLast = nullptr; |
1186 | } |
1187 | *inFilter = false; |
1188 | } |
1189 | return hndTab; |
1190 | } |
1191 | |
1192 | /****************************************************************************/ |
1193 | |
1194 | EHblkDsc* Compiler::ehInitTryBlockRange(BasicBlock* blk, BasicBlock** tryBeg, BasicBlock** tryLast) |
1195 | { |
1196 | EHblkDsc* tryTab = ehGetBlockTryDsc(blk); |
1197 | if (tryTab != nullptr) |
1198 | { |
1199 | *tryBeg = tryTab->ebdTryBeg; |
1200 | if (tryLast != nullptr) |
1201 | { |
1202 | *tryLast = tryTab->ebdTryLast; |
1203 | } |
1204 | } |
1205 | else |
1206 | { |
1207 | *tryBeg = nullptr; |
1208 | if (tryLast != nullptr) |
1209 | { |
1210 | *tryLast = nullptr; |
1211 | } |
1212 | } |
1213 | return tryTab; |
1214 | } |
1215 | |
1216 | /***************************************************************************** |
1217 | * This method updates the value of ebdTryLast. |
1218 | */ |
1219 | |
1220 | void Compiler::fgSetTryEnd(EHblkDsc* handlerTab, BasicBlock* newTryLast) |
1221 | { |
1222 | assert(newTryLast != nullptr); |
1223 | |
1224 | // |
1225 | // Check if we are going to change the existing value of endTryLast |
1226 | // |
1227 | if (handlerTab->ebdTryLast != newTryLast) |
1228 | { |
1229 | // Update the EH table with the newTryLast block |
1230 | handlerTab->ebdTryLast = newTryLast; |
1231 | |
1232 | #ifdef DEBUG |
1233 | if (verbose) |
1234 | { |
1235 | printf("EH#%u: New last block of try: " FMT_BB "\n" , ehGetIndex(handlerTab), newTryLast->bbNum); |
1236 | } |
1237 | #endif // DEBUG |
1238 | } |
1239 | } |
1240 | |
1241 | /***************************************************************************** |
1242 | * |
1243 | * This method updates the value of ebdHndLast. |
1244 | */ |
1245 | |
1246 | void Compiler::fgSetHndEnd(EHblkDsc* handlerTab, BasicBlock* newHndLast) |
1247 | { |
1248 | assert(newHndLast != nullptr); |
1249 | |
1250 | // |
1251 | // Check if we are going to change the existing value of endHndLast |
1252 | // |
1253 | if (handlerTab->ebdHndLast != newHndLast) |
1254 | { |
1255 | // Update the EH table with the newHndLast block |
1256 | handlerTab->ebdHndLast = newHndLast; |
1257 | |
1258 | #ifdef DEBUG |
1259 | if (verbose) |
1260 | { |
1261 | printf("EH#%u: New last block of handler: " FMT_BB "\n" , ehGetIndex(handlerTab), newHndLast->bbNum); |
1262 | } |
1263 | #endif // DEBUG |
1264 | } |
1265 | } |
1266 | |
1267 | /***************************************************************************** |
1268 | * |
1269 | * Given a EH handler table entry update the ebdTryLast and ebdHndLast pointers |
1270 | * to skip basic blocks that have been removed. They are set to the first |
1271 | * non-removed block after ebdTryBeg and ebdHndBeg, respectively. |
1272 | * |
1273 | * Note that removed blocks are not in the global list of blocks (no block in the |
1274 | * global list points to them). However, their pointers are still valid. We use |
1275 | * this fact when we walk lists of removed blocks until we find a non-removed |
1276 | * block, to be used for ending our iteration. |
1277 | */ |
1278 | |
1279 | void Compiler::fgSkipRmvdBlocks(EHblkDsc* handlerTab) |
1280 | { |
1281 | BasicBlock* block; |
1282 | BasicBlock* bEnd; |
1283 | BasicBlock* bLast; |
1284 | |
1285 | // Update ebdTryLast |
1286 | bLast = nullptr; |
1287 | |
1288 | // Find the first non-removed block after the 'try' region to end our iteration. |
1289 | bEnd = handlerTab->ebdTryLast->bbNext; |
1290 | while ((bEnd != nullptr) && (bEnd->bbFlags & BBF_REMOVED)) |
1291 | { |
1292 | bEnd = bEnd->bbNext; |
1293 | } |
1294 | |
1295 | // Update bLast to account for any removed blocks |
1296 | block = handlerTab->ebdTryBeg; |
1297 | while (block != nullptr) |
1298 | { |
1299 | if ((block->bbFlags & BBF_REMOVED) == 0) |
1300 | { |
1301 | bLast = block; |
1302 | } |
1303 | |
1304 | block = block->bbNext; |
1305 | |
1306 | if (block == bEnd) |
1307 | { |
1308 | break; |
1309 | } |
1310 | } |
1311 | |
1312 | fgSetTryEnd(handlerTab, bLast); |
1313 | |
1314 | // Update ebdHndLast |
1315 | bLast = nullptr; |
1316 | |
1317 | // Find the first non-removed block after the handler region to end our iteration. |
1318 | bEnd = handlerTab->ebdHndLast->bbNext; |
1319 | while ((bEnd != nullptr) && (bEnd->bbFlags & BBF_REMOVED)) |
1320 | { |
1321 | bEnd = bEnd->bbNext; |
1322 | } |
1323 | |
1324 | // Update bLast to account for any removed blocks |
1325 | block = handlerTab->ebdHndBeg; |
1326 | while (block != nullptr) |
1327 | { |
1328 | if ((block->bbFlags & BBF_REMOVED) == 0) |
1329 | { |
1330 | bLast = block; |
1331 | } |
1332 | |
1333 | block = block->bbNext; |
1334 | if (block == bEnd) |
1335 | { |
1336 | break; |
1337 | } |
1338 | } |
1339 | |
1340 | fgSetHndEnd(handlerTab, bLast); |
1341 | } |
1342 | |
1343 | /***************************************************************************** |
1344 | * |
1345 | * Allocate the EH table |
1346 | */ |
1347 | void Compiler::fgAllocEHTable() |
1348 | { |
1349 | #if FEATURE_EH_FUNCLETS |
1350 | |
1351 | // We need to allocate space for EH clauses that will be used by funclets |
1352 | // as well as one for each EH clause from the IL. Nested EH clauses pulled |
1353 | // out as funclets create one EH clause for each enclosing region. Thus, |
1354 | // the maximum number of clauses we will need might be very large. We allocate |
1355 | // twice the number of EH clauses in the IL, which should be good in practice. |
1356 | // In extreme cases, we might need to abandon this and reallocate. See |
1357 | // fgAddEHTableEntry() for more details. |
1358 | CLANG_FORMAT_COMMENT_ANCHOR; |
1359 | |
1360 | #ifdef DEBUG |
1361 | compHndBBtabAllocCount = info.compXcptnsCount; // force the resizing code to hit more frequently in DEBUG |
1362 | #else // DEBUG |
1363 | compHndBBtabAllocCount = info.compXcptnsCount * 2; |
1364 | #endif // DEBUG |
1365 | |
1366 | #else // FEATURE_EH_FUNCLETS |
1367 | |
1368 | compHndBBtabAllocCount = info.compXcptnsCount; |
1369 | |
1370 | #endif // FEATURE_EH_FUNCLETS |
1371 | |
1372 | compHndBBtab = new (this, CMK_BasicBlock) EHblkDsc[compHndBBtabAllocCount]; |
1373 | |
1374 | compHndBBtabCount = info.compXcptnsCount; |
1375 | } |
1376 | |
1377 | /***************************************************************************** |
1378 | * |
1379 | * Remove a single exception table entry. Note that this changes the size of |
1380 | * the exception table. If calling this within a loop over the exception table |
1381 | * be careful to iterate again on the current entry (if XTnum) to not skip any. |
1382 | */ |
1383 | void Compiler::fgRemoveEHTableEntry(unsigned XTnum) |
1384 | { |
1385 | assert(compHndBBtabCount > 0); |
1386 | assert(XTnum < compHndBBtabCount); |
1387 | |
1388 | EHblkDsc* HBtab; |
1389 | |
1390 | /* Reduce the number of entries in the EH table by one */ |
1391 | compHndBBtabCount--; |
1392 | |
1393 | if (compHndBBtabCount == 0) |
1394 | { |
1395 | // No more entries remaining. |
1396 | INDEBUG(compHndBBtab = (EHblkDsc*)INVALID_POINTER_VALUE;) |
1397 | } |
1398 | else |
1399 | { |
1400 | /* If we recorded an enclosing index for xtab then see |
1401 | * if it needs to be updated due to the removal of this entry |
1402 | */ |
1403 | |
1404 | HBtab = compHndBBtab + XTnum; |
1405 | |
1406 | EHblkDsc* xtabEnd; |
1407 | EHblkDsc* xtab; |
1408 | for (xtab = compHndBBtab, xtabEnd = compHndBBtab + compHndBBtabCount; xtab < xtabEnd; xtab++) |
1409 | { |
1410 | if ((xtab != HBtab) && (xtab->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX) && |
1411 | (xtab->ebdEnclosingTryIndex >= XTnum)) |
1412 | { |
1413 | // Update the enclosing scope link |
1414 | if (xtab->ebdEnclosingTryIndex == XTnum) |
1415 | { |
1416 | xtab->ebdEnclosingTryIndex = HBtab->ebdEnclosingTryIndex; |
1417 | } |
1418 | if ((xtab->ebdEnclosingTryIndex > XTnum) && |
1419 | (xtab->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX)) |
1420 | { |
1421 | xtab->ebdEnclosingTryIndex--; |
1422 | } |
1423 | } |
1424 | |
1425 | if ((xtab != HBtab) && (xtab->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX) && |
1426 | (xtab->ebdEnclosingHndIndex >= XTnum)) |
1427 | { |
1428 | // Update the enclosing scope link |
1429 | if (xtab->ebdEnclosingHndIndex == XTnum) |
1430 | { |
1431 | xtab->ebdEnclosingHndIndex = HBtab->ebdEnclosingHndIndex; |
1432 | } |
1433 | if ((xtab->ebdEnclosingHndIndex > XTnum) && |
1434 | (xtab->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX)) |
1435 | { |
1436 | xtab->ebdEnclosingHndIndex--; |
1437 | } |
1438 | } |
1439 | } |
1440 | |
1441 | /* We need to update all of the blocks' bbTryIndex */ |
1442 | |
1443 | for (BasicBlock* blk = fgFirstBB; blk; blk = blk->bbNext) |
1444 | { |
1445 | if (blk->hasTryIndex()) |
1446 | { |
1447 | if (blk->getTryIndex() == XTnum) |
1448 | { |
1449 | noway_assert(blk->bbFlags & BBF_REMOVED); |
1450 | INDEBUG(blk->setTryIndex(MAX_XCPTN_INDEX);) // Note: this is still a legal index, just unlikely |
1451 | } |
1452 | else if (blk->getTryIndex() > XTnum) |
1453 | { |
1454 | blk->setTryIndex(blk->getTryIndex() - 1); |
1455 | } |
1456 | } |
1457 | |
1458 | if (blk->hasHndIndex()) |
1459 | { |
1460 | if (blk->getHndIndex() == XTnum) |
1461 | { |
1462 | noway_assert(blk->bbFlags & BBF_REMOVED); |
1463 | INDEBUG(blk->setHndIndex(MAX_XCPTN_INDEX);) // Note: this is still a legal index, just unlikely |
1464 | } |
1465 | else if (blk->getHndIndex() > XTnum) |
1466 | { |
1467 | blk->setHndIndex(blk->getHndIndex() - 1); |
1468 | } |
1469 | } |
1470 | } |
1471 | |
1472 | /* Now remove the unused entry from the table */ |
1473 | |
1474 | if (XTnum < compHndBBtabCount) |
1475 | { |
1476 | /* We copy over the old entry */ |
1477 | memmove(HBtab, HBtab + 1, (compHndBBtabCount - XTnum) * sizeof(*HBtab)); |
1478 | } |
1479 | else |
1480 | { |
1481 | /* Last entry. Don't need to do anything */ |
1482 | noway_assert(XTnum == compHndBBtabCount); |
1483 | } |
1484 | } |
1485 | } |
1486 | |
1487 | #if FEATURE_EH_FUNCLETS |
1488 | |
1489 | /***************************************************************************** |
1490 | * |
1491 | * Add a single exception table entry at index 'XTnum', [0 <= XTnum <= compHndBBtabCount]. |
1492 | * If 'XTnum' is compHndBBtabCount, then add the entry at the end. |
1493 | * Note that this changes the size of the exception table. |
1494 | * All the blocks referring to the various index values are updated. |
1495 | * The table entry itself is not filled in. |
1496 | * Returns a pointer to the new entry. |
1497 | */ |
1498 | EHblkDsc* Compiler::fgAddEHTableEntry(unsigned XTnum) |
1499 | { |
1500 | if (XTnum != compHndBBtabCount) |
1501 | { |
1502 | // Update all enclosing links that will get invalidated by inserting an entry at 'XTnum' |
1503 | |
1504 | EHblkDsc* xtabEnd; |
1505 | EHblkDsc* xtab; |
1506 | for (xtab = compHndBBtab, xtabEnd = compHndBBtab + compHndBBtabCount; xtab < xtabEnd; xtab++) |
1507 | { |
1508 | if ((xtab->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX) && (xtab->ebdEnclosingTryIndex >= XTnum)) |
1509 | { |
1510 | // Update the enclosing scope link |
1511 | xtab->ebdEnclosingTryIndex++; |
1512 | } |
1513 | if ((xtab->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX) && (xtab->ebdEnclosingHndIndex >= XTnum)) |
1514 | { |
1515 | // Update the enclosing scope link |
1516 | xtab->ebdEnclosingHndIndex++; |
1517 | } |
1518 | } |
1519 | |
1520 | // We need to update the BasicBlock bbTryIndex and bbHndIndex field for all blocks |
1521 | |
1522 | for (BasicBlock* blk = fgFirstBB; blk; blk = blk->bbNext) |
1523 | { |
1524 | if (blk->hasTryIndex() && (blk->getTryIndex() >= XTnum)) |
1525 | { |
1526 | blk->setTryIndex(blk->getTryIndex() + 1); |
1527 | } |
1528 | |
1529 | if (blk->hasHndIndex() && (blk->getHndIndex() >= XTnum)) |
1530 | { |
1531 | blk->setHndIndex(blk->getHndIndex() + 1); |
1532 | } |
1533 | } |
1534 | } |
1535 | |
1536 | // Increase the number of entries in the EH table by one |
1537 | |
1538 | if (compHndBBtabCount == compHndBBtabAllocCount) |
1539 | { |
1540 | // We need to reallocate the table |
1541 | |
1542 | if (compHndBBtabAllocCount == MAX_XCPTN_INDEX) |
1543 | { // We're already at the max size for indices to be unsigned short |
1544 | IMPL_LIMITATION("too many exception clauses" ); |
1545 | } |
1546 | |
1547 | // Double the table size. For stress, we could use +1. Note that if the table isn't allocated |
1548 | // yet, such as when we add an EH region for synchronized methods that don't already have one, |
1549 | // we start at zero, so we need to make sure the new table has at least one entry. |
1550 | unsigned newHndBBtabAllocCount = max(1, compHndBBtabAllocCount * 2); |
1551 | noway_assert(compHndBBtabAllocCount < newHndBBtabAllocCount); // check for overflow |
1552 | |
1553 | if (newHndBBtabAllocCount > MAX_XCPTN_INDEX) |
1554 | { |
1555 | newHndBBtabAllocCount = MAX_XCPTN_INDEX; // increase to the maximum size we allow |
1556 | } |
1557 | |
1558 | JITDUMP("*********** fgAddEHTableEntry: increasing EH table size from %d to %d\n" , compHndBBtabAllocCount, |
1559 | newHndBBtabAllocCount); |
1560 | |
1561 | compHndBBtabAllocCount = newHndBBtabAllocCount; |
1562 | |
1563 | EHblkDsc* newTable = new (this, CMK_BasicBlock) EHblkDsc[compHndBBtabAllocCount]; |
1564 | |
1565 | // Move over the stuff before the new entry |
1566 | |
1567 | memcpy_s(newTable, compHndBBtabAllocCount * sizeof(*compHndBBtab), compHndBBtab, XTnum * sizeof(*compHndBBtab)); |
1568 | |
1569 | if (XTnum != compHndBBtabCount) |
1570 | { |
1571 | // Move over the stuff after the new entry |
1572 | memcpy_s(newTable + XTnum + 1, (compHndBBtabAllocCount - XTnum - 1) * sizeof(*compHndBBtab), |
1573 | compHndBBtab + XTnum, (compHndBBtabCount - XTnum) * sizeof(*compHndBBtab)); |
1574 | } |
1575 | |
1576 | // Now set the new table as the table to use. The old one gets lost, but we can't |
1577 | // free it because we don't have a freeing allocator. |
1578 | |
1579 | compHndBBtab = newTable; |
1580 | } |
1581 | else if (XTnum != compHndBBtabCount) |
1582 | { |
1583 | // Leave the elements before the new element alone. Move the ones after it, to make space. |
1584 | |
1585 | EHblkDsc* HBtab = compHndBBtab + XTnum; |
1586 | |
1587 | memmove_s(HBtab + 1, (compHndBBtabAllocCount - XTnum - 1) * sizeof(*compHndBBtab), HBtab, |
1588 | (compHndBBtabCount - XTnum) * sizeof(*compHndBBtab)); |
1589 | } |
1590 | |
1591 | // Now the entry is there, but not filled in |
1592 | |
1593 | compHndBBtabCount++; |
1594 | return compHndBBtab + XTnum; |
1595 | } |
1596 | |
1597 | #endif // FEATURE_EH_FUNCLETS |
1598 | |
1599 | #if !FEATURE_EH |
1600 | |
1601 | /***************************************************************************** |
1602 | * fgRemoveEH: To facilitiate the bring-up of new platforms without having to |
1603 | * worry about fully implementing EH, we want to simply remove EH constructs |
1604 | * from the IR. This works because a large percentage of our tests contain |
1605 | * EH constructs but don't actually throw exceptions. This function removes |
1606 | * 'catch', 'filter', 'filter-handler', and 'fault' clauses completely. |
1607 | * It requires that the importer has created the EH table, and that normal |
1608 | * EH well-formedness tests have been done, and 'leave' opcodes have been |
1609 | * imported. |
1610 | * |
1611 | * It currently does not handle 'finally' clauses, so tests that include |
1612 | * 'finally' will NYI(). To handle 'finally', we would need to inline the |
1613 | * 'finally' clause IL at each exit from a finally-protected 'try', or |
1614 | * else call the 'finally' clause, like normal. |
1615 | * |
1616 | * Walk the EH table from beginning to end. If a table entry is nested within |
1617 | * a handler, we skip it, as we'll delete its code when we get to the enclosing |
1618 | * handler. If a clause is enclosed within a 'try', or has no nesting, then we delete |
1619 | * it (and its range of code blocks). We don't need to worry about cleaning up |
1620 | * the EH table entries as we remove the individual handlers (such as calling |
1621 | * fgRemoveEHTableEntry()), as we'll null out the entire table at the end. |
1622 | * |
1623 | * This function assumes FEATURE_EH_FUNCLETS is defined. |
1624 | */ |
1625 | void Compiler::fgRemoveEH() |
1626 | { |
1627 | #ifdef DEBUG |
1628 | if (verbose) |
1629 | printf("\n*************** In fgRemoveEH()\n" ); |
1630 | #endif // DEBUG |
1631 | |
1632 | if (compHndBBtabCount == 0) |
1633 | { |
1634 | JITDUMP("No EH to remove\n\n" ); |
1635 | return; |
1636 | } |
1637 | |
1638 | #ifdef DEBUG |
1639 | if (verbose) |
1640 | { |
1641 | printf("\n*************** Before fgRemoveEH()\n" ); |
1642 | fgDispBasicBlocks(); |
1643 | fgDispHandlerTab(); |
1644 | printf("\n" ); |
1645 | } |
1646 | #endif // DEBUG |
1647 | |
1648 | // Make sure we're early in compilation, so we don't need to update lots of data structures. |
1649 | assert(!fgComputePredsDone); |
1650 | assert(!fgDomsComputed); |
1651 | assert(!fgFuncletsCreated); |
1652 | assert(fgFirstFuncletBB == nullptr); // this should follow from "!fgFuncletsCreated" |
1653 | assert(!optLoopsMarked); |
1654 | |
1655 | unsigned XTnum; |
1656 | EHblkDsc* HBtab; |
1657 | |
1658 | for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) |
1659 | { |
1660 | if (HBtab->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
1661 | { |
1662 | // This entry is nested within some other handler. So, don't delete the |
1663 | // EH entry here; let the enclosing handler delete it. Note that for this |
1664 | // EH entry, both the 'try' and handler portions are fully nested within |
1665 | // the enclosing handler region, due to proper nesting rules. |
1666 | continue; |
1667 | } |
1668 | |
1669 | if (HBtab->HasCatchHandler() || HBtab->HasFilter() || HBtab->HasFaultHandler()) |
1670 | { |
1671 | // Remove all the blocks associated with the handler. Note that there is no |
1672 | // fall-through into the handler, or fall-through out of the handler, so |
1673 | // just deleting the blocks is sufficient. Note, however, that for every |
1674 | // BBJ_EHCATCHRET we delete, we need to fix up the reference count of the |
1675 | // block it points to (by subtracting one from its reference count). |
1676 | // Note that the blocks for a filter immediately preceed the blocks for its associated filter-handler. |
1677 | |
1678 | BasicBlock* blkBeg = HBtab->HasFilter() ? HBtab->ebdFilter : HBtab->ebdHndBeg; |
1679 | BasicBlock* blkLast = HBtab->ebdHndLast; |
1680 | |
1681 | // Splice out the range of blocks from blkBeg to blkLast (inclusive). |
1682 | fgUnlinkRange(blkBeg, blkLast); |
1683 | |
1684 | BasicBlock* blk; |
1685 | |
1686 | // Walk the unlinked blocks and marked them as having been removed. |
1687 | for (blk = blkBeg; blk != blkLast->bbNext; blk = blk->bbNext) |
1688 | { |
1689 | blk->bbFlags |= BBF_REMOVED; |
1690 | |
1691 | if (blk->bbJumpKind == BBJ_EHCATCHRET) |
1692 | { |
1693 | assert(blk->bbJumpDest->bbRefs > 0); |
1694 | blk->bbJumpDest->bbRefs -= 1; |
1695 | } |
1696 | } |
1697 | |
1698 | // Walk the blocks of the 'try' and clear data that makes them appear to be within a 'try'. |
1699 | for (blk = HBtab->ebdTryBeg; blk != HBtab->ebdTryLast->bbNext; blk = blk->bbNext) |
1700 | { |
1701 | blk->clearTryIndex(); |
1702 | blk->bbFlags &= ~BBF_TRY_BEG; |
1703 | } |
1704 | |
1705 | // If we are deleting a range of blocks whose last block is |
1706 | // the 'last' block of an enclosing try/hnd region, we need to |
1707 | // fix up the EH table. We only care about less nested |
1708 | // EH table entries, since we've already deleted everything up to XTnum. |
1709 | |
1710 | unsigned XTnum2; |
1711 | EHblkDsc* HBtab2; |
1712 | for (XTnum2 = XTnum + 1, HBtab2 = compHndBBtab + XTnum2; XTnum2 < compHndBBtabCount; XTnum2++, HBtab2++) |
1713 | { |
1714 | // Handle case where deleted range is at the end of a 'try'. |
1715 | if (HBtab2->ebdTryLast == blkLast) |
1716 | { |
1717 | fgSetTryEnd(HBtab2, blkBeg->bbPrev); |
1718 | } |
1719 | // Handle case where deleted range is at the end of a handler. |
1720 | // (This shouldn't happen, though, because we don't delete handlers |
1721 | // nested within other handlers; we wait until we get to the |
1722 | // enclosing handler.) |
1723 | if (HBtab2->ebdHndLast == blkLast) |
1724 | { |
1725 | unreached(); |
1726 | } |
1727 | } |
1728 | } |
1729 | else |
1730 | { |
1731 | // It must be a 'finally'. We still need to call the finally. Note that the |
1732 | // 'finally' can be "called" from multiple locations (e.g., the 'try' block |
1733 | // can have multiple 'leave' instructions, each leaving to different targets, |
1734 | // and each going through the 'finally'). We could inline the 'finally' at each |
1735 | // LEAVE site within a 'try'. If the 'try' exits at all (that is, no infinite loop), |
1736 | // there will be at least one since there is no "fall through" at the end of |
1737 | // the 'try'. |
1738 | |
1739 | assert(HBtab->HasFinallyHandler()); |
1740 | |
1741 | NYI("remove finally blocks" ); |
1742 | } |
1743 | } /* end of the for loop over XTnum */ |
1744 | |
1745 | #ifdef DEBUG |
1746 | // Make sure none of the remaining blocks have any EH. |
1747 | |
1748 | BasicBlock* blk; |
1749 | foreach_block(this, blk) |
1750 | { |
1751 | assert(!blk->hasTryIndex()); |
1752 | assert(!blk->hasHndIndex()); |
1753 | assert((blk->bbFlags & BBF_TRY_BEG) == 0); |
1754 | assert((blk->bbFlags & BBF_FUNCLET_BEG) == 0); |
1755 | assert((blk->bbFlags & BBF_REMOVED) == 0); |
1756 | assert(blk->bbCatchTyp == BBCT_NONE); |
1757 | } |
1758 | #endif // DEBUG |
1759 | |
1760 | // Delete the EH table |
1761 | |
1762 | compHndBBtab = nullptr; |
1763 | compHndBBtabCount = 0; |
1764 | // Leave compHndBBtabAllocCount alone. |
1765 | |
1766 | // Renumber the basic blocks |
1767 | JITDUMP("\nRenumbering the basic blocks for fgRemoveEH\n" ); |
1768 | fgRenumberBlocks(); |
1769 | |
1770 | #ifdef DEBUG |
1771 | if (verbose) |
1772 | { |
1773 | printf("\n*************** After fgRemoveEH()\n" ); |
1774 | fgDispBasicBlocks(); |
1775 | fgDispHandlerTab(); |
1776 | printf("\n" ); |
1777 | } |
1778 | #endif |
1779 | } |
1780 | |
1781 | #endif // !FEATURE_EH |
1782 | |
1783 | /***************************************************************************** |
1784 | * |
1785 | * Sort the EH table if necessary. |
1786 | */ |
1787 | |
1788 | void Compiler::fgSortEHTable() |
1789 | { |
1790 | if (!fgNeedToSortEHTable) |
1791 | { |
1792 | return; |
1793 | } |
1794 | |
1795 | // Now, all fields of the EH table are set except for those that are related |
1796 | // to nesting. We need to first sort the table to ensure that an EH clause |
1797 | // appears before any try or handler that it is nested within. The CLI spec |
1798 | // requires this for nesting in 'try' clauses, but does not require this |
1799 | // for handler clauses. However, parts of the JIT do assume this ordering. |
1800 | // |
1801 | // For example: |
1802 | // |
1803 | // try { // A |
1804 | // } catch { |
1805 | // try { // B |
1806 | // } catch { |
1807 | // } |
1808 | // } |
1809 | // |
1810 | // In this case, the EH clauses for A and B have no required ordering: the |
1811 | // clause for either A or B can come first, despite B being nested within |
1812 | // the catch clause for A. |
1813 | // |
1814 | // The CLI spec, section 12.4.2.5 "Overview of exception handling", states: |
1815 | // "The ordering of the exception clauses in the Exception Handler Table is |
1816 | // important. If handlers are nested, the most deeply nested try blocks shall |
1817 | // come before the try blocks that enclose them." |
1818 | // |
1819 | // Note, in particular, that it doesn't say "shall come before the *handler* |
1820 | // blocks that enclose them". |
1821 | // |
1822 | // Also, the same section states, "When an exception occurs, the CLI searches |
1823 | // the array for the first protected block that (1) Protects a region including the |
1824 | // current instruction pointer and (2) Is a catch handler block and (3) Whose |
1825 | // filter wishes to handle the exception." |
1826 | // |
1827 | // Once again, nothing about the ordering of the catch blocks. |
1828 | // |
1829 | // A more complicated example: |
1830 | // |
1831 | // try { // A |
1832 | // } catch { |
1833 | // try { // B |
1834 | // try { // C |
1835 | // } catch { |
1836 | // } |
1837 | // } catch { |
1838 | // } |
1839 | // } |
1840 | // |
1841 | // The clause for C must come before the clause for B, but the clause for A can |
1842 | // be anywhere. Thus, we could have these orderings: ACB, CAB, CBA. |
1843 | // |
1844 | // One more example: |
1845 | // |
1846 | // try { // A |
1847 | // } catch { |
1848 | // try { // B |
1849 | // } catch { |
1850 | // try { // C |
1851 | // } catch { |
1852 | // } |
1853 | // } |
1854 | // } |
1855 | // |
1856 | // There is no ordering requirement: the EH clauses can come in any order. |
1857 | // |
1858 | // In Dev11 (Visual Studio 2012), x86 did not sort the EH table (it never had before) |
1859 | // but ARM did. It turns out not sorting the table can cause the EH table to incorrectly |
1860 | // set the bbHndIndex value in some nested cases, and that can lead to a security exploit |
1861 | // that allows the execution of arbitrary code. |
1862 | CLANG_FORMAT_COMMENT_ANCHOR; |
1863 | |
1864 | #ifdef DEBUG |
1865 | if (verbose) |
1866 | { |
1867 | printf("fgSortEHTable: Sorting EH table\n" ); |
1868 | } |
1869 | #endif // DEBUG |
1870 | |
1871 | EHblkDsc* xtab1; |
1872 | EHblkDsc* xtab2; |
1873 | unsigned xtabnum1, xtabnum2; |
1874 | |
1875 | for (xtabnum1 = 0, xtab1 = compHndBBtab; xtabnum1 < compHndBBtabCount; xtabnum1++, xtab1++) |
1876 | { |
1877 | for (xtabnum2 = xtabnum1 + 1, xtab2 = xtab1 + 1; xtabnum2 < compHndBBtabCount; xtabnum2++, xtab2++) |
1878 | { |
1879 | // If the nesting is wrong, swap them. The nesting is wrong if |
1880 | // EH region 2 is nested in the try, handler, or filter of EH region 1. |
1881 | // Note that due to proper nesting rules, if any of 2 is nested in |
1882 | // the try or handler or filter of 1, then all of 2 is nested. |
1883 | // We must be careful when comparing the offsets of the 'try' clause, because |
1884 | // for "mutually-protect" try/catch, the 'try' bodies will be identical. |
1885 | // For this reason, we use the handler region to check nesting. Note |
1886 | // that we must check both beginning and end: a nested region can have a 'try' |
1887 | // body that starts at the beginning of a handler. Thus, if we just compared the |
1888 | // handler begin offset, we might get confused and think it is nested. |
1889 | |
1890 | IL_OFFSET hndBegOff = xtab2->ebdHndBegOffset; |
1891 | IL_OFFSET hndEndOff = xtab2->ebdHndEndOffset; |
1892 | assert(hndEndOff > hndBegOff); |
1893 | |
1894 | if ((hndBegOff >= xtab1->ebdTryBegOffset && hndEndOff <= xtab1->ebdTryEndOffset) || |
1895 | (hndBegOff >= xtab1->ebdHndBegOffset && hndEndOff <= xtab1->ebdHndEndOffset) || |
1896 | (xtab1->HasFilter() && (hndBegOff >= xtab1->ebdFilterBegOffset && hndEndOff <= xtab1->ebdHndBegOffset)) |
1897 | // Note that end of filter is beginning of handler |
1898 | ) |
1899 | { |
1900 | #ifdef DEBUG |
1901 | if (verbose) |
1902 | { |
1903 | printf("fgSortEHTable: Swapping out-of-order EH#%u and EH#%u\n" , xtabnum1, xtabnum2); |
1904 | } |
1905 | |
1906 | // Assert that the 'try' region is also nested in the same place as the handler |
1907 | |
1908 | IL_OFFSET tryBegOff = xtab2->ebdTryBegOffset; |
1909 | IL_OFFSET tryEndOff = xtab2->ebdTryEndOffset; |
1910 | assert(tryEndOff > tryBegOff); |
1911 | |
1912 | if (hndBegOff >= xtab1->ebdTryBegOffset && hndEndOff <= xtab1->ebdTryEndOffset) |
1913 | { |
1914 | assert(tryBegOff >= xtab1->ebdTryBegOffset && tryEndOff <= xtab1->ebdTryEndOffset); |
1915 | } |
1916 | if (hndBegOff >= xtab1->ebdHndBegOffset && hndEndOff <= xtab1->ebdHndEndOffset) |
1917 | { |
1918 | assert(tryBegOff >= xtab1->ebdHndBegOffset && tryEndOff <= xtab1->ebdHndEndOffset); |
1919 | } |
1920 | if (xtab1->HasFilter() && |
1921 | (hndBegOff >= xtab1->ebdFilterBegOffset && hndEndOff <= xtab1->ebdHndBegOffset)) |
1922 | { |
1923 | assert(tryBegOff >= xtab1->ebdFilterBegOffset && tryEndOff <= xtab1->ebdHndBegOffset); |
1924 | } |
1925 | #endif // DEBUG |
1926 | |
1927 | // Swap them! |
1928 | EHblkDsc tmp = *xtab1; |
1929 | *xtab1 = *xtab2; |
1930 | *xtab2 = tmp; |
1931 | } |
1932 | } |
1933 | } |
1934 | } |
1935 | |
1936 | // fgNormalizeEH: Enforce the following invariants: |
1937 | // |
1938 | // 1. No block is both the first block of a handler and the first block of a try. In IL (and on entry |
1939 | // to this function), this can happen if the "try" is more nested than the handler. |
1940 | // |
1941 | // For example, consider: |
1942 | // |
1943 | // try1 ----------------- BB01 |
1944 | // | BB02 |
1945 | // |--------------------- BB03 |
1946 | // handler1 |
1947 | // |----- try2 ---------- BB04 |
1948 | // | | BB05 |
1949 | // | handler2 ------ BB06 |
1950 | // | | BB07 |
1951 | // | --------------- BB08 |
1952 | // |--------------------- BB09 |
1953 | // |
1954 | // Thus, the start of handler1 and the start of try2 are the same block. We will transform this to: |
1955 | // |
1956 | // try1 ----------------- BB01 |
1957 | // | BB02 |
1958 | // |--------------------- BB03 |
1959 | // handler1 ------------- BB10 // empty block |
1960 | // | try2 ---------- BB04 |
1961 | // | | BB05 |
1962 | // | handler2 ------ BB06 |
1963 | // | | BB07 |
1964 | // | --------------- BB08 |
1965 | // |--------------------- BB09 |
1966 | // |
1967 | // 2. No block is the first block of more than one try or handler region. |
1968 | // (Note that filters cannot have EH constructs nested within them, so there can be no nested try or |
1969 | // handler that shares the filter begin or last block. For try/filter/filter-handler constructs nested |
1970 | // within a try or handler region, note that the filter block cannot be the first block of the try, |
1971 | // nor can it be the first block of the handler, since you can't "fall into" a filter, which that situation |
1972 | // would require.) |
1973 | // |
1974 | // For example, we will transform this: |
1975 | // |
1976 | // try3 try2 try1 |
1977 | // |--- |--- |--- BB01 |
1978 | // | | | BB02 |
1979 | // | | |--- BB03 |
1980 | // | | BB04 |
1981 | // | |------------ BB05 |
1982 | // | BB06 |
1983 | // |------------------- BB07 |
1984 | // |
1985 | // to this: |
1986 | // |
1987 | // try3 ------------- BB08 // empty BBJ_NONE block |
1988 | // | try2 ------ BB09 // empty BBJ_NONE block |
1989 | // | | try1 |
1990 | // | | |--- BB01 |
1991 | // | | | BB02 |
1992 | // | | |--- BB03 |
1993 | // | | BB04 |
1994 | // | |------------ BB05 |
1995 | // | BB06 |
1996 | // |------------------- BB07 |
1997 | // |
1998 | // The benefit of this is that adding a block to an EH region will not require examining every EH region, |
1999 | // looking for possible shared "first" blocks to adjust. It also makes it easier to put code at the top |
2000 | // of a particular EH region, especially for loop optimizations. |
2001 | // |
2002 | // These empty blocks (BB08, BB09) will generate no code (unless some code is subsequently placed into them), |
2003 | // and will have the same native code offset as BB01 after code is generated. There may be labels generated |
2004 | // for them, if they are branch targets, so it is possible to have multiple labels targeting the same native |
2005 | // code offset. The blocks will not be merged with the blocks they are split from, because they will have a |
2006 | // different EH region, and we don't merge blocks from two different EH regions. |
2007 | // |
2008 | // In the example, if there are branches to BB01, we need to distribute them to BB01, BB08, or BB09, appropriately. |
2009 | // 1. A branch from BB01/BB02/BB03 to BB01 will still go to BB01. Branching to BB09 or BB08 would not be legal, |
2010 | // since it would branch out of a try region. |
2011 | // 2. A branch from BB04/BB05 to BB01 will instead branch to BB09. Branching to BB08 would not be legal. Note |
2012 | // that branching to BB01 would still be legal, so we have a choice. It makes the most sense to branch to BB09, |
2013 | // so the source and target of a branch are in the same EH region. |
2014 | // 3. Similarly, a branch from BB06/BB07 to BB01 will go to BB08, even though branching to BB09 would be legal. |
2015 | // 4. A branch from outside this loop (at the top-level) to BB01 will go to BB08. This is one case where the |
2016 | // source and target of the branch are not in the same EH region. |
2017 | // |
2018 | // The EH nesting rules for IL branches are described in the ECMA spec section 12.4.2.8.2.7 "Branches" and |
2019 | // section 12.4.2.8.2.9 "Examples". |
2020 | // |
2021 | // There is one exception to this normalization rule: we do not change "mutually protect" regions. These are cases |
2022 | // where two EH table entries have exactly the same 'try' region, used to implement C# "try / catch / catch". |
2023 | // The first handler appears by our nesting to be an "inner" handler, with ebdEnclosingTryIndex pointing to the |
2024 | // second one. It is not true nesting, though, since they both protect the same "try". Both the these EH table |
2025 | // entries must keep the same "try" region begin/last block pointers. A block in this "try" region has a try index |
2026 | // of the first ("most nested") EH table entry. |
2027 | // |
2028 | // 3. No block is the last block of more than one try or handler region. Again, as described above, |
2029 | // filters need not be considered. |
2030 | // |
2031 | // For example, we will transform this: |
2032 | // |
2033 | // try3 ----------------- BB01 |
2034 | // | try2 ---------- BB02 |
2035 | // | | handler1 BB03 |
2036 | // | | | BB04 |
2037 | // |----- |----- |------- BB05 |
2038 | // |
2039 | // (where all three try regions end at BB05) to this: |
2040 | // |
2041 | // try3 ----------------- BB01 |
2042 | // | try2 ---------- BB02 |
2043 | // | | handler1 BB03 |
2044 | // | | | BB04 |
2045 | // | | |------- BB05 |
2046 | // | |-------------- BB06 // empty BBJ_NONE block |
2047 | // |--------------------- BB07 // empty BBJ_NONE block |
2048 | // |
2049 | // No branches need to change: if something branched to BB05, it will still branch to BB05. If BB05 is a |
2050 | // BBJ_NONE block, then control flow will fall through the newly added blocks as well. If it is anything |
2051 | // else, it will retain that block branch type and BB06 and BB07 will be unreachable. |
2052 | // |
2053 | // The benefit of this is, once again, to remove the need to consider every EH region when adding new blocks. |
2054 | // |
2055 | // Overall, a block can appear in the EH table exactly once: as the begin or last block of a single try, filter, or |
2056 | // handler. There is one exception: for a single-block EH region, the block can appear as both the "begin" and "last" |
2057 | // block of the try, or the "begin" and "last" block of the handler (note that filters don't have a "last" block stored, |
2058 | // so this case doesn't apply.) |
2059 | // (Note: we could remove this special case if we wanted, and if it helps anything, but it doesn't appear that it will |
2060 | // help.) |
2061 | // |
2062 | // These invariants simplify a number of things. When inserting a new block into a region, it is not necessary to |
2063 | // traverse the entire EH table looking to see if any EH region needs to be updated. You only ever need to update a |
2064 | // single region (except for mutually-protect "try" regions). |
2065 | // |
2066 | // Also, for example, when we're trying to determine the successors of a block B1 that leads into a try T1, if a block |
2067 | // B2 violates invariant #3 by being the first block of both the handler of T1, and an enclosed try T2, inserting a |
2068 | // block to enforce this invariant prevents us from having to consider the first block of T2's handler as a possible |
2069 | // successor of B1. This is somewhat akin to breaking of "critical edges" in a flowgraph. |
2070 | |
2071 | void Compiler::fgNormalizeEH() |
2072 | { |
2073 | if (compHndBBtabCount == 0) |
2074 | { |
2075 | // No EH? Nothing to do. |
2076 | INDEBUG(fgNormalizeEHDone = true;) |
2077 | return; |
2078 | } |
2079 | |
2080 | #ifdef DEBUG |
2081 | if (verbose) |
2082 | { |
2083 | printf("*************** In fgNormalizeEH()\n" ); |
2084 | fgDispBasicBlocks(); |
2085 | fgDispHandlerTab(); |
2086 | } |
2087 | #endif |
2088 | |
2089 | bool modified = false; |
2090 | |
2091 | // Case #1: Prevent the first block of a handler from also being the first block of a 'try'. |
2092 | if (fgNormalizeEHCase1()) |
2093 | { |
2094 | modified = true; |
2095 | } |
2096 | |
2097 | // Case #2: Prevent any two EH regions from starting with the same block (after case #3, we only need to worry about |
2098 | // 'try' blocks). |
2099 | if (fgNormalizeEHCase2()) |
2100 | { |
2101 | modified = true; |
2102 | } |
2103 | |
2104 | #if 0 |
2105 | // Case 3 normalization is disabled. The JIT really doesn't like having extra empty blocks around, especially |
2106 | // blocks that are unreachable. There are lots of asserts when such things occur. We will re-evaluate whether we |
2107 | // can do this normalization. |
2108 | // Note: there are cases in fgVerifyHandlerTab() that are also disabled to match this. |
2109 | |
2110 | // Case #3: Prevent any two EH regions from ending with the same block. |
2111 | if (fgNormalizeEHCase3()) |
2112 | { |
2113 | modified = true; |
2114 | } |
2115 | |
2116 | #endif // 0 |
2117 | |
2118 | INDEBUG(fgNormalizeEHDone = true;) |
2119 | |
2120 | if (modified) |
2121 | { |
2122 | // If we computed the cheap preds, don't let them leak out, in case other code doesn't maintain them properly. |
2123 | if (fgCheapPredsValid) |
2124 | { |
2125 | fgRemovePreds(); |
2126 | } |
2127 | |
2128 | JITDUMP("Added at least one basic block in fgNormalizeEH.\n" ); |
2129 | fgRenumberBlocks(); |
2130 | #ifdef DEBUG |
2131 | // fgRenumberBlocks() will dump all the blocks and the handler table, so we don't need to do it here. |
2132 | fgVerifyHandlerTab(); |
2133 | #endif |
2134 | } |
2135 | else |
2136 | { |
2137 | JITDUMP("No EH normalization performed.\n" ); |
2138 | } |
2139 | } |
2140 | |
2141 | bool Compiler::fgNormalizeEHCase1() |
2142 | { |
2143 | bool modified = false; |
2144 | |
2145 | // |
2146 | // Case #1: Is the first block of a handler also the first block of any try? |
2147 | // |
2148 | // Do this as a separate loop from case #2 to simplify the logic for cases where we have both multiple identical |
2149 | // 'try' begin blocks as well as this case, e.g.: |
2150 | // try { |
2151 | // } finally { try { try { |
2152 | // } catch {} |
2153 | // } catch {} |
2154 | // } |
2155 | // where the finally/try/try are all the same block. |
2156 | // We also do this before case #2, so when we get to case #2, we only need to worry about updating 'try' begin |
2157 | // blocks (and only those within the 'try' region's parents), not handler begin blocks, when we are inserting new |
2158 | // header blocks. |
2159 | // |
2160 | |
2161 | for (unsigned XTnum = 0; XTnum < compHndBBtabCount; XTnum++) |
2162 | { |
2163 | EHblkDsc* eh = ehGetDsc(XTnum); |
2164 | |
2165 | BasicBlock* handlerStart = eh->ebdHndBeg; |
2166 | EHblkDsc* handlerStartContainingTry = ehGetBlockTryDsc(handlerStart); |
2167 | // If the handler start block is in a try, and is in fact the first block of that try... |
2168 | if (handlerStartContainingTry != nullptr && handlerStartContainingTry->ebdTryBeg == handlerStart) |
2169 | { |
2170 | // ...then we want to insert an empty, non-removable block outside the try to be the new first block of the |
2171 | // handler. |
2172 | BasicBlock* newHndStart = bbNewBasicBlock(BBJ_NONE); |
2173 | fgInsertBBbefore(eh->ebdHndBeg, newHndStart); |
2174 | |
2175 | #ifdef DEBUG |
2176 | if (verbose) |
2177 | { |
2178 | printf("Handler begin for EH#%02u and 'try' begin for EH%02u are the same block; inserted new " FMT_BB |
2179 | " " |
2180 | "before " FMT_BB " as new handler begin for EH#%u.\n" , |
2181 | XTnum, ehGetIndex(handlerStartContainingTry), newHndStart->bbNum, eh->ebdHndBeg->bbNum, XTnum); |
2182 | } |
2183 | #endif // DEBUG |
2184 | |
2185 | // The new block is the new handler begin. |
2186 | eh->ebdHndBeg = newHndStart; |
2187 | |
2188 | // Try index is the same as the enclosing try, if any, of eh: |
2189 | if (eh->ebdEnclosingTryIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
2190 | { |
2191 | newHndStart->clearTryIndex(); |
2192 | } |
2193 | else |
2194 | { |
2195 | newHndStart->setTryIndex(eh->ebdEnclosingTryIndex); |
2196 | } |
2197 | newHndStart->setHndIndex(XTnum); |
2198 | newHndStart->bbCatchTyp = handlerStart->bbCatchTyp; |
2199 | handlerStart->bbCatchTyp = BBCT_NONE; // Now handlerStart is no longer the start of a handler... |
2200 | newHndStart->bbCodeOffs = handlerStart->bbCodeOffs; |
2201 | newHndStart->bbCodeOffsEnd = newHndStart->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead? |
2202 | newHndStart->inheritWeight(handlerStart); |
2203 | newHndStart->bbFlags |= (BBF_DONT_REMOVE | BBF_INTERNAL | BBF_HAS_LABEL); |
2204 | modified = true; |
2205 | |
2206 | #ifdef DEBUG |
2207 | if (0 && verbose) // Normally this is way too verbose, but it is useful for debugging |
2208 | { |
2209 | printf("*************** fgNormalizeEH() made a change\n" ); |
2210 | fgDispBasicBlocks(); |
2211 | fgDispHandlerTab(); |
2212 | } |
2213 | #endif // DEBUG |
2214 | } |
2215 | } |
2216 | |
2217 | return modified; |
2218 | } |
2219 | |
2220 | bool Compiler::fgNormalizeEHCase2() |
2221 | { |
2222 | bool modified = false; |
2223 | |
2224 | // |
2225 | // Case #2: Make sure no two 'try' have the same begin block (except for mutually-protect regions). |
2226 | // Note that this can only happen for nested 'try' regions, so we only need to look through the |
2227 | // 'try' nesting hierarchy. |
2228 | // |
2229 | |
2230 | for (unsigned XTnum = 0; XTnum < compHndBBtabCount; XTnum++) |
2231 | { |
2232 | EHblkDsc* eh = ehGetDsc(XTnum); |
2233 | |
2234 | if (eh->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
2235 | { |
2236 | BasicBlock* tryStart = eh->ebdTryBeg; |
2237 | BasicBlock* insertBeforeBlk = tryStart; // If we need to insert new blocks, we insert before this block. |
2238 | |
2239 | // We need to keep track of the last "mutually protect" region so we can properly not add additional header |
2240 | // blocks to the second and subsequent mutually protect try blocks. We can't just keep track of the EH |
2241 | // region pointer, because we're updating the 'try' begin blocks as we go. So, we need to keep track of the |
2242 | // pre-update 'try' begin/last blocks themselves. |
2243 | BasicBlock* mutualTryBeg = eh->ebdTryBeg; |
2244 | BasicBlock* mutualTryLast = eh->ebdTryLast; |
2245 | unsigned mutualProtectIndex = XTnum; |
2246 | |
2247 | EHblkDsc* ehOuter = eh; |
2248 | do |
2249 | { |
2250 | unsigned ehOuterTryIndex = ehOuter->ebdEnclosingTryIndex; |
2251 | ehOuter = ehGetDsc(ehOuterTryIndex); |
2252 | BasicBlock* outerTryStart = ehOuter->ebdTryBeg; |
2253 | if (outerTryStart == tryStart) |
2254 | { |
2255 | // We found two EH regions with the same 'try' begin! Should we do something about it? |
2256 | |
2257 | if (ehOuter->ebdIsSameTry(mutualTryBeg, mutualTryLast)) |
2258 | { |
2259 | // clang-format off |
2260 | // Don't touch mutually-protect regions: their 'try' regions must remain identical! |
2261 | // We want to continue the looping outwards, in case we have something like this: |
2262 | // |
2263 | // try3 try2 try1 |
2264 | // |--- |---- |---- BB01 |
2265 | // | | | BB02 |
2266 | // | |---- |---- BB03 |
2267 | // | BB04 |
2268 | // |------------------- BB05 |
2269 | // |
2270 | // (Thus, try1 & try2 are mutually-protect 'try' regions from BB01 to BB03. They are nested inside try3, |
2271 | // which also starts at BB01. The 'catch' clauses have been elided.) |
2272 | // In this case, we'll decline to add a new header block for try2, but we will add a new one for try3, ending with: |
2273 | // |
2274 | // try3 try2 try1 |
2275 | // |------------------- BB06 |
2276 | // | |---- |---- BB01 |
2277 | // | | | BB02 |
2278 | // | |---- |---- BB03 |
2279 | // | BB04 |
2280 | // |------------------- BB05 |
2281 | // |
2282 | // More complicated (yes, this is real): |
2283 | // |
2284 | // try { |
2285 | // try { |
2286 | // try { |
2287 | // try { |
2288 | // try { |
2289 | // try { |
2290 | // try { |
2291 | // try { |
2292 | // } |
2293 | // catch {} // mutually-protect set #1 |
2294 | // catch {} |
2295 | // } finally {} |
2296 | // } |
2297 | // catch {} // mutually-protect set #2 |
2298 | // catch {} |
2299 | // catch {} |
2300 | // } finally {} |
2301 | // } catch {} |
2302 | // } finally {} |
2303 | // } catch {} |
2304 | // } finally {} |
2305 | // |
2306 | // In this case, all the 'try' start at the same block! Note that there are two sets of mutually-protect regions, |
2307 | // separated by some nesting. |
2308 | // clang-format on |
2309 | |
2310 | #ifdef DEBUG |
2311 | if (verbose) |
2312 | { |
2313 | printf("Mutually protect regions EH#%u and EH#%u; leaving identical 'try' begin blocks.\n" , |
2314 | mutualProtectIndex, ehGetIndex(ehOuter)); |
2315 | } |
2316 | #endif // DEBUG |
2317 | |
2318 | // We still need to update the tryBeg, if something more nested already did that. |
2319 | ehOuter->ebdTryBeg = insertBeforeBlk; |
2320 | } |
2321 | else |
2322 | { |
2323 | // We're in a new set of mutual protect regions, so don't compare against the original. |
2324 | mutualTryBeg = ehOuter->ebdTryBeg; |
2325 | mutualTryLast = ehOuter->ebdTryLast; |
2326 | mutualProtectIndex = ehOuterTryIndex; |
2327 | |
2328 | // We're going to need the preds. We compute them here, before inserting the new block, |
2329 | // so our logic to add/remove preds below is the same for both the first time preds are |
2330 | // created and subsequent times. |
2331 | if (!fgCheapPredsValid) |
2332 | { |
2333 | fgComputeCheapPreds(); |
2334 | } |
2335 | |
2336 | // We've got multiple 'try' blocks starting at the same place! |
2337 | // Add a new first 'try' block for 'ehOuter' that will be outside 'eh'. |
2338 | |
2339 | BasicBlock* newTryStart = bbNewBasicBlock(BBJ_NONE); |
2340 | fgInsertBBbefore(insertBeforeBlk, newTryStart); |
2341 | |
2342 | #ifdef DEBUG |
2343 | if (verbose) |
2344 | { |
2345 | printf("'try' begin for EH#%u and EH#%u are same block; inserted new " FMT_BB |
2346 | " before " FMT_BB " " |
2347 | "as new 'try' begin for EH#%u.\n" , |
2348 | ehOuterTryIndex, XTnum, newTryStart->bbNum, insertBeforeBlk->bbNum, ehOuterTryIndex); |
2349 | } |
2350 | #endif // DEBUG |
2351 | |
2352 | // The new block is the new 'try' begin. |
2353 | ehOuter->ebdTryBeg = newTryStart; |
2354 | |
2355 | newTryStart->copyEHRegion(tryStart); // Copy the EH region info |
2356 | newTryStart->setTryIndex(ehOuterTryIndex); // ... but overwrite the 'try' index |
2357 | newTryStart->bbCatchTyp = BBCT_NONE; |
2358 | newTryStart->bbCodeOffs = tryStart->bbCodeOffs; |
2359 | newTryStart->bbCodeOffsEnd = |
2360 | newTryStart->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead? |
2361 | newTryStart->inheritWeight(tryStart); |
2362 | |
2363 | // Note that we don't need to clear any flags on the old try start, since it is still a 'try' |
2364 | // start. |
2365 | newTryStart->bbFlags |= (BBF_TRY_BEG | BBF_DONT_REMOVE | BBF_INTERNAL | BBF_HAS_LABEL); |
2366 | |
2367 | // Now we need to split any flow edges targetting the old try begin block between the old |
2368 | // and new block. Note that if we are handling a multiply-nested 'try', we may have already |
2369 | // split the inner set. So we need to split again, from the most enclosing block that we've |
2370 | // already created, namely, insertBeforeBlk. |
2371 | // |
2372 | // For example: |
2373 | // |
2374 | // try3 try2 try1 |
2375 | // |---- |---- |---- BB01 |
2376 | // | | | BB02 |
2377 | // | | |---- BB03 |
2378 | // | |----------- BB04 |
2379 | // |------------------ BB05 |
2380 | // |
2381 | // We'll loop twice, to create two header blocks, one for try2, and the second time for try3 |
2382 | // (in that order). |
2383 | // After the first loop, we have: |
2384 | // |
2385 | // try3 try2 try1 |
2386 | // |---- BB06 |
2387 | // |---- | |---- BB01 |
2388 | // | | | BB02 |
2389 | // | | |---- BB03 |
2390 | // | |----------- BB04 |
2391 | // |------------------ BB05 |
2392 | // |
2393 | // And all the external edges have been changed to point at try2. On the next loop, we'll create |
2394 | // a unique header block for try3, and split the edges between try2 and try3, leaving us with: |
2395 | // |
2396 | // try3 try2 try1 |
2397 | // |---- BB07 |
2398 | // | |---- BB06 |
2399 | // | | |---- BB01 |
2400 | // | | | BB02 |
2401 | // | | |---- BB03 |
2402 | // | |----------- BB04 |
2403 | // |------------------ BB05 |
2404 | |
2405 | BasicBlockList* nextPred; // we're going to update the pred list as we go, so we need to keep |
2406 | // track of the next pred in case it gets deleted. |
2407 | for (BasicBlockList* pred = insertBeforeBlk->bbCheapPreds; pred != nullptr; pred = nextPred) |
2408 | { |
2409 | nextPred = pred->next; |
2410 | |
2411 | // Who gets this predecessor? |
2412 | BasicBlock* predBlock = pred->block; |
2413 | |
2414 | if (!BasicBlock::sameTryRegion(insertBeforeBlk, predBlock)) |
2415 | { |
2416 | // Move the edge to target newTryStart instead of insertBeforeBlk. |
2417 | fgAddCheapPred(newTryStart, predBlock); |
2418 | fgRemoveCheapPred(insertBeforeBlk, predBlock); |
2419 | |
2420 | // Now change the branch. If it was a BBJ_NONE fall-through to the top block, this will |
2421 | // do nothing. Since cheap preds contains dups (for switch duplicates), we will call |
2422 | // this once per dup. |
2423 | fgReplaceJumpTarget(predBlock, newTryStart, insertBeforeBlk); |
2424 | |
2425 | // Need to adjust ref counts here since we're retargeting edges. |
2426 | newTryStart->bbRefs++; |
2427 | assert(insertBeforeBlk->countOfInEdges() > 0); |
2428 | insertBeforeBlk->bbRefs--; |
2429 | |
2430 | #ifdef DEBUG |
2431 | if (verbose) |
2432 | { |
2433 | printf("Redirect " FMT_BB " target from " FMT_BB " to " FMT_BB ".\n" , |
2434 | predBlock->bbNum, insertBeforeBlk->bbNum, newTryStart->bbNum); |
2435 | } |
2436 | #endif // DEBUG |
2437 | } |
2438 | } |
2439 | |
2440 | // The new block (a fall-through block) is a new predecessor. |
2441 | fgAddCheapPred(insertBeforeBlk, newTryStart); |
2442 | |
2443 | // We don't need to update the tryBeg block of other EH regions here because we are looping |
2444 | // outwards in enclosing try index order, and we'll get to them later. |
2445 | |
2446 | // Move the insert block backwards, to the one we just inserted. |
2447 | insertBeforeBlk = insertBeforeBlk->bbPrev; |
2448 | assert(insertBeforeBlk == newTryStart); |
2449 | |
2450 | modified = true; |
2451 | |
2452 | #ifdef DEBUG |
2453 | if (0 && verbose) // Normally this is way too verbose, but it is useful for debugging |
2454 | { |
2455 | printf("*************** fgNormalizeEH() made a change\n" ); |
2456 | fgDispBasicBlocks(); |
2457 | fgDispHandlerTab(); |
2458 | } |
2459 | #endif // DEBUG |
2460 | } |
2461 | } |
2462 | else |
2463 | { |
2464 | // If the 'try' start block in the outer block isn't the same, then none of the more-enclosing |
2465 | // try regions (if any) can have the same 'try' start block, so we're done. |
2466 | // Note that we could have a situation like this: |
2467 | // |
2468 | // try4 try3 try2 try1 |
2469 | // |--- |--- | | BB01 |
2470 | // | | | | BB02 |
2471 | // | | |---- |---- BB03 |
2472 | // | | | BB04 |
2473 | // | | |------------ BB05 |
2474 | // | | BB06 |
2475 | // | |------------------- BB07 |
2476 | // |-------------------------- BB08 |
2477 | // |
2478 | // (Thus, try1 & try2 start at BB03, and are nested inside try3 & try4, which both start at BB01.) |
2479 | // In this case, we'll process try1 and try2, then break out. Later, we'll get to try3 and process |
2480 | // it and try4. |
2481 | |
2482 | break; |
2483 | } |
2484 | } while (ehOuter->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
2485 | } |
2486 | } |
2487 | |
2488 | return modified; |
2489 | } |
2490 | |
2491 | bool Compiler::fgNormalizeEHCase3() |
2492 | { |
2493 | bool modified = false; |
2494 | |
2495 | // |
2496 | // Case #3: Make sure no two 'try' or handler regions have the same 'last' block (except for mutually protect 'try' |
2497 | // regions). As above, there has to be EH region nesting for this to occur. However, since we need to consider |
2498 | // handlers, there are more cases. |
2499 | // |
2500 | // There are four cases to consider: |
2501 | // (1) try nested in try |
2502 | // (2) handler nested in try |
2503 | // (3) try nested in handler |
2504 | // (4) handler nested in handler |
2505 | // |
2506 | // Note that, before funclet generation, it would be unusual, though legal IL, for a 'try' to come at the end |
2507 | // of an EH region (either 'try' or handler region), since that implies that its corresponding handler precedes it. |
2508 | // That will never happen in C#, but is legal in IL. |
2509 | // |
2510 | // Only one of these cases can happen. For example, if we have case (2), where a try/catch is nested in a 'try' and |
2511 | // the nested handler has the same 'last' block as the outer handler, then, due to nesting rules, the nested 'try' |
2512 | // must also be within the outer handler, and obviously cannot share the same 'last' block. |
2513 | // |
2514 | |
2515 | for (unsigned XTnum = 0; XTnum < compHndBBtabCount; XTnum++) |
2516 | { |
2517 | EHblkDsc* eh = ehGetDsc(XTnum); |
2518 | |
2519 | // Find the EH region 'eh' is most nested within, either 'try' or handler or none. |
2520 | bool outerIsTryRegion; |
2521 | unsigned ehOuterIndex = eh->ebdGetEnclosingRegionIndex(&outerIsTryRegion); |
2522 | |
2523 | if (ehOuterIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
2524 | { |
2525 | EHblkDsc* ehInner = eh; // This gets updated as we loop outwards in the EH nesting |
2526 | unsigned ehInnerIndex = XTnum; // This gets updated as we loop outwards in the EH nesting |
2527 | bool innerIsTryRegion; |
2528 | |
2529 | EHblkDsc* ehOuter = ehGetDsc(ehOuterIndex); |
2530 | |
2531 | // Debugging: say what type of block we're updating. |
2532 | INDEBUG(const char* outerType = "" ; const char* innerType = "" ;) |
2533 | |
2534 | // 'insertAfterBlk' is the place we will insert new "normalization" blocks. We don't know yet if we will |
2535 | // insert them after the innermost 'try' or handler's "last" block, so we set it to nullptr. Once we |
2536 | // determine the innermost region that is equivalent, we set this, and then update it incrementally as we |
2537 | // loop outwards. |
2538 | BasicBlock* insertAfterBlk = nullptr; |
2539 | |
2540 | bool foundMatchingLastBlock = false; |
2541 | |
2542 | // This is set to 'false' for mutual protect regions for which we will not insert a normalization block. |
2543 | bool insertNormalizationBlock = true; |
2544 | |
2545 | // Keep track of what the 'try' index and handler index should be for any new normalization block that we |
2546 | // insert. If we have a sequence of alternating nested 'try' and handlers with the same 'last' block, we'll |
2547 | // need to update these as we go. For example: |
2548 | // try { // EH#5 |
2549 | // ... |
2550 | // catch { // EH#4 |
2551 | // ... |
2552 | // try { // EH#3 |
2553 | // ... |
2554 | // catch { // EH#2 |
2555 | // ... |
2556 | // try { // EH#1 |
2557 | // BB01 // try=1, hnd=2 |
2558 | // } } } } } // all the 'last' blocks are the same |
2559 | // |
2560 | // after normalization: |
2561 | // |
2562 | // try { // EH#5 |
2563 | // ... |
2564 | // catch { // EH#4 |
2565 | // ... |
2566 | // try { // EH#3 |
2567 | // ... |
2568 | // catch { // EH#2 |
2569 | // ... |
2570 | // try { // EH#1 |
2571 | // BB01 // try=1, hnd=2 |
2572 | // } |
2573 | // BB02 // try=3, hnd=2 |
2574 | // } |
2575 | // BB03 // try=3, hnd=4 |
2576 | // } |
2577 | // BB04 // try=5, hnd=4 |
2578 | // } |
2579 | // BB05 // try=5, hnd=0 (no enclosing hnd) |
2580 | // } |
2581 | // |
2582 | unsigned nextTryIndex = EHblkDsc::NO_ENCLOSING_INDEX; // Initialization only needed to quell compiler |
2583 | // warnings. |
2584 | unsigned nextHndIndex = EHblkDsc::NO_ENCLOSING_INDEX; |
2585 | |
2586 | // We compare the outer region against the inner region's 'try' or handler, determined by the |
2587 | // 'outerIsTryRegion' variable. Once we decide that, we know exactly the 'last' pointer that we will use to |
2588 | // compare against all enclosing EH regions. |
2589 | // |
2590 | // For example, if we have these nested EH regions (omitting some corresponding try/catch clauses for each |
2591 | // nesting level): |
2592 | // |
2593 | // try { |
2594 | // ... |
2595 | // catch { |
2596 | // ... |
2597 | // try { |
2598 | // } } } // all the 'last' blocks are the same |
2599 | // |
2600 | // then we determine that the innermost region we are going to compare against is the 'try' region. There's |
2601 | // no reason to compare against its handler region for any enclosing region (since it couldn't possibly |
2602 | // share a 'last' block with the enclosing region). However, there's no harm, either (and it simplifies |
2603 | // the code for the first set of comparisons to be the same as subsequent, more enclosing cases). |
2604 | BasicBlock* lastBlockPtrToCompare = nullptr; |
2605 | |
2606 | // We need to keep track of the last "mutual protect" region so we can properly not add additional blocks |
2607 | // to the second and subsequent mutual protect try blocks. We can't just keep track of the EH region |
2608 | // pointer, because we're updating the last blocks as we go. So, we need to keep track of the |
2609 | // pre-update 'try' begin/last blocks themselves. These only matter if the "last" blocks that match are |
2610 | // from two (or more) nested 'try' regions. |
2611 | BasicBlock* mutualTryBeg = nullptr; |
2612 | BasicBlock* mutualTryLast = nullptr; |
2613 | |
2614 | if (outerIsTryRegion) |
2615 | { |
2616 | nextTryIndex = EHblkDsc::NO_ENCLOSING_INDEX; // unused, since the outer block is a 'try' region. |
2617 | |
2618 | // The outer (enclosing) region is a 'try' |
2619 | if (ehOuter->ebdTryLast == ehInner->ebdTryLast) |
2620 | { |
2621 | // Case (1) try nested in try. |
2622 | foundMatchingLastBlock = true; |
2623 | INDEBUG(innerType = "try" ; outerType = "try" ;) |
2624 | insertAfterBlk = ehOuter->ebdTryLast; |
2625 | lastBlockPtrToCompare = insertAfterBlk; |
2626 | |
2627 | if (EHblkDsc::ebdIsSameTry(ehOuter, ehInner)) |
2628 | { |
2629 | // We can't touch this 'try', since it's mutual protect. |
2630 | CLANG_FORMAT_COMMENT_ANCHOR; |
2631 | #ifdef DEBUG |
2632 | if (verbose) |
2633 | { |
2634 | printf("Mutual protect regions EH#%u and EH#%u; leaving identical 'try' last blocks.\n" , |
2635 | ehOuterIndex, ehInnerIndex); |
2636 | } |
2637 | #endif // DEBUG |
2638 | |
2639 | insertNormalizationBlock = false; |
2640 | } |
2641 | else |
2642 | { |
2643 | nextHndIndex = ehInner->ebdTryLast->hasHndIndex() ? ehInner->ebdTryLast->getHndIndex() |
2644 | : EHblkDsc::NO_ENCLOSING_INDEX; |
2645 | } |
2646 | } |
2647 | else if (ehOuter->ebdTryLast == ehInner->ebdHndLast) |
2648 | { |
2649 | // Case (2) handler nested in try. |
2650 | foundMatchingLastBlock = true; |
2651 | INDEBUG(innerType = "handler" ; outerType = "try" ;) |
2652 | insertAfterBlk = ehOuter->ebdTryLast; |
2653 | lastBlockPtrToCompare = insertAfterBlk; |
2654 | |
2655 | assert(ehInner->ebdHndLast->getHndIndex() == ehInnerIndex); |
2656 | nextHndIndex = ehInner->ebdEnclosingHndIndex; |
2657 | } |
2658 | else |
2659 | { |
2660 | // No "last" pointers match! |
2661 | } |
2662 | |
2663 | if (foundMatchingLastBlock) |
2664 | { |
2665 | // The outer might be part of a new set of mutual protect regions (if it isn't part of one already). |
2666 | mutualTryBeg = ehOuter->ebdTryBeg; |
2667 | mutualTryLast = ehOuter->ebdTryLast; |
2668 | } |
2669 | } |
2670 | else |
2671 | { |
2672 | nextHndIndex = EHblkDsc::NO_ENCLOSING_INDEX; // unused, since the outer block is a handler region. |
2673 | |
2674 | // The outer (enclosing) region is a handler (note that it can't be a filter; there is no nesting |
2675 | // within a filter). |
2676 | if (ehOuter->ebdHndLast == ehInner->ebdTryLast) |
2677 | { |
2678 | // Case (3) try nested in handler. |
2679 | foundMatchingLastBlock = true; |
2680 | INDEBUG(innerType = "try" ; outerType = "handler" ;) |
2681 | insertAfterBlk = ehOuter->ebdHndLast; |
2682 | lastBlockPtrToCompare = insertAfterBlk; |
2683 | |
2684 | assert(ehInner->ebdTryLast->getTryIndex() == ehInnerIndex); |
2685 | nextTryIndex = ehInner->ebdEnclosingTryIndex; |
2686 | } |
2687 | else if (ehOuter->ebdHndLast == ehInner->ebdHndLast) |
2688 | { |
2689 | // Case (4) handler nested in handler. |
2690 | foundMatchingLastBlock = true; |
2691 | INDEBUG(innerType = "handler" ; outerType = "handler" ;) |
2692 | insertAfterBlk = ehOuter->ebdHndLast; |
2693 | lastBlockPtrToCompare = insertAfterBlk; |
2694 | |
2695 | nextTryIndex = ehInner->ebdTryLast->hasTryIndex() ? ehInner->ebdTryLast->getTryIndex() |
2696 | : EHblkDsc::NO_ENCLOSING_INDEX; |
2697 | } |
2698 | else |
2699 | { |
2700 | // No "last" pointers match! |
2701 | } |
2702 | } |
2703 | |
2704 | while (foundMatchingLastBlock) |
2705 | { |
2706 | assert(lastBlockPtrToCompare != nullptr); |
2707 | assert(insertAfterBlk != nullptr); |
2708 | assert(ehOuterIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
2709 | assert(ehOuter != nullptr); |
2710 | |
2711 | // Add a normalization block |
2712 | |
2713 | if (insertNormalizationBlock) |
2714 | { |
2715 | // Add a new last block for 'ehOuter' that will be outside the EH region with which it encloses and |
2716 | // shares a 'last' pointer |
2717 | |
2718 | BasicBlock* newLast = bbNewBasicBlock(BBJ_NONE); |
2719 | assert(insertAfterBlk != nullptr); |
2720 | fgInsertBBafter(insertAfterBlk, newLast); |
2721 | |
2722 | #ifdef DEBUG |
2723 | if (verbose) |
2724 | { |
2725 | printf( |
2726 | "last %s block for EH#%u and last %s block for EH#%u are same block; inserted new " FMT_BB |
2727 | " after " FMT_BB " as new last %s block for EH#%u.\n" , |
2728 | outerType, ehOuterIndex, innerType, ehInnerIndex, newLast->bbNum, insertAfterBlk->bbNum, |
2729 | outerType, ehOuterIndex); |
2730 | } |
2731 | #endif // DEBUG |
2732 | |
2733 | if (outerIsTryRegion) |
2734 | { |
2735 | ehOuter->ebdTryLast = newLast; |
2736 | newLast->setTryIndex(ehOuterIndex); |
2737 | if (nextHndIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
2738 | { |
2739 | newLast->clearHndIndex(); |
2740 | } |
2741 | else |
2742 | { |
2743 | newLast->setHndIndex(nextHndIndex); |
2744 | } |
2745 | } |
2746 | else |
2747 | { |
2748 | ehOuter->ebdHndLast = newLast; |
2749 | if (nextTryIndex == EHblkDsc::NO_ENCLOSING_INDEX) |
2750 | { |
2751 | newLast->clearTryIndex(); |
2752 | } |
2753 | else |
2754 | { |
2755 | newLast->setTryIndex(nextTryIndex); |
2756 | } |
2757 | newLast->setHndIndex(ehOuterIndex); |
2758 | } |
2759 | |
2760 | newLast->bbCatchTyp = |
2761 | BBCT_NONE; // bbCatchTyp is only set on the first block of a handler, which is this not |
2762 | newLast->bbCodeOffs = insertAfterBlk->bbCodeOffsEnd; |
2763 | newLast->bbCodeOffsEnd = newLast->bbCodeOffs; // code size = 0. TODO: use BAD_IL_OFFSET instead? |
2764 | newLast->inheritWeight(insertAfterBlk); |
2765 | newLast->bbFlags |= BBF_INTERNAL; |
2766 | |
2767 | // The new block (a fall-through block) is a new predecessor. |
2768 | if (fgCheapPredsValid) |
2769 | { |
2770 | fgAddCheapPred(newLast, insertAfterBlk); |
2771 | } |
2772 | |
2773 | // Move the insert pointer. More enclosing equivalent 'last' blocks will be inserted after this. |
2774 | insertAfterBlk = newLast; |
2775 | |
2776 | modified = true; |
2777 | |
2778 | #ifdef DEBUG |
2779 | if (verbose) // Normally this is way too verbose, but it is useful for debugging |
2780 | { |
2781 | printf("*************** fgNormalizeEH() made a change\n" ); |
2782 | fgDispBasicBlocks(); |
2783 | fgDispHandlerTab(); |
2784 | } |
2785 | #endif // DEBUG |
2786 | } |
2787 | |
2788 | // Now find the next outer enclosing EH region and see if it also shares the last block. |
2789 | foundMatchingLastBlock = false; // assume nothing will match |
2790 | ehInner = ehOuter; |
2791 | ehInnerIndex = ehOuterIndex; |
2792 | innerIsTryRegion = outerIsTryRegion; |
2793 | |
2794 | ehOuterIndex = |
2795 | ehOuter->ebdGetEnclosingRegionIndex(&outerIsTryRegion); // Loop outwards in the EH nesting. |
2796 | if (ehOuterIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
2797 | { |
2798 | // There are more enclosing regions; check for equivalent 'last' pointers. |
2799 | |
2800 | INDEBUG(innerType = outerType; outerType = "" ;) |
2801 | |
2802 | ehOuter = ehGetDsc(ehOuterIndex); |
2803 | |
2804 | insertNormalizationBlock = true; // assume it's not mutual protect |
2805 | |
2806 | if (outerIsTryRegion) |
2807 | { |
2808 | nextTryIndex = EHblkDsc::NO_ENCLOSING_INDEX; // unused, since the outer block is a 'try' region. |
2809 | |
2810 | // The outer (enclosing) region is a 'try' |
2811 | if (ehOuter->ebdTryLast == lastBlockPtrToCompare) |
2812 | { |
2813 | // Case (1) and (2): try or handler nested in try. |
2814 | foundMatchingLastBlock = true; |
2815 | INDEBUG(outerType = "try" ;) |
2816 | |
2817 | if (innerIsTryRegion && ehOuter->ebdIsSameTry(mutualTryBeg, mutualTryLast)) |
2818 | { |
2819 | // We can't touch this 'try', since it's mutual protect. |
2820 | CLANG_FORMAT_COMMENT_ANCHOR; |
2821 | |
2822 | #ifdef DEBUG |
2823 | if (verbose) |
2824 | { |
2825 | printf("Mutual protect regions EH#%u and EH#%u; leaving identical 'try' last " |
2826 | "blocks.\n" , |
2827 | ehOuterIndex, ehInnerIndex); |
2828 | } |
2829 | #endif // DEBUG |
2830 | |
2831 | insertNormalizationBlock = false; |
2832 | |
2833 | // We still need to update the 'last' pointer, in case someone inserted a normalization |
2834 | // block before the start of the mutual protect 'try' region. |
2835 | ehOuter->ebdTryLast = insertAfterBlk; |
2836 | } |
2837 | else |
2838 | { |
2839 | if (innerIsTryRegion) |
2840 | { |
2841 | // Case (1) try nested in try. |
2842 | nextHndIndex = ehInner->ebdTryLast->hasHndIndex() |
2843 | ? ehInner->ebdTryLast->getHndIndex() |
2844 | : EHblkDsc::NO_ENCLOSING_INDEX; |
2845 | } |
2846 | else |
2847 | { |
2848 | // Case (2) handler nested in try. |
2849 | assert(ehInner->ebdHndLast->getHndIndex() == ehInnerIndex); |
2850 | nextHndIndex = ehInner->ebdEnclosingHndIndex; |
2851 | } |
2852 | } |
2853 | |
2854 | // The outer might be part of a new set of mutual protect regions (if it isn't part of one |
2855 | // already). |
2856 | mutualTryBeg = ehOuter->ebdTryBeg; |
2857 | mutualTryLast = ehOuter->ebdTryLast; |
2858 | } |
2859 | } |
2860 | else |
2861 | { |
2862 | nextHndIndex = |
2863 | EHblkDsc::NO_ENCLOSING_INDEX; // unused, since the outer block is a handler region. |
2864 | |
2865 | // The outer (enclosing) region is a handler (note that it can't be a filter; there is no |
2866 | // nesting within a filter). |
2867 | if (ehOuter->ebdHndLast == lastBlockPtrToCompare) |
2868 | { |
2869 | // Case (3) and (4): try nested in try or handler. |
2870 | foundMatchingLastBlock = true; |
2871 | INDEBUG(outerType = "handler" ;) |
2872 | |
2873 | if (innerIsTryRegion) |
2874 | { |
2875 | // Case (3) try nested in handler. |
2876 | assert(ehInner->ebdTryLast->getTryIndex() == ehInnerIndex); |
2877 | nextTryIndex = ehInner->ebdEnclosingTryIndex; |
2878 | } |
2879 | else |
2880 | { |
2881 | // Case (4) handler nested in handler. |
2882 | nextTryIndex = ehInner->ebdTryLast->hasTryIndex() ? ehInner->ebdTryLast->getTryIndex() |
2883 | : EHblkDsc::NO_ENCLOSING_INDEX; |
2884 | } |
2885 | } |
2886 | } |
2887 | } |
2888 | |
2889 | // If we get to here and foundMatchingLastBlock is false, then the inner and outer region don't share |
2890 | // any 'last' blocks, so we're done. Note that we could have a situation like this: |
2891 | // |
2892 | // try4 try3 try2 try1 |
2893 | // |---- | | | BB01 |
2894 | // | |---- | | BB02 |
2895 | // | | |---- | BB03 |
2896 | // | | | |----- BB04 |
2897 | // | | |----- |----- BB05 |
2898 | // |---- |------------------- BB06 |
2899 | // |
2900 | // (Thus, try1 & try2 end at BB05, and are nested inside try3 & try4, which both end at BB06.) |
2901 | // In this case, we'll process try1 and try2, then break out. Later, as we iterate through the EH table, |
2902 | // we'll get to try3 and process it and try4. |
2903 | |
2904 | } // end while (foundMatchingLastBlock) |
2905 | } // if (ehOuterIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
2906 | } // EH table iteration |
2907 | |
2908 | return modified; |
2909 | } |
2910 | |
2911 | /*****************************************************************************/ |
2912 | #ifdef DEBUG |
2913 | |
2914 | void Compiler::dispIncomingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause) |
2915 | { |
2916 | printf("EH clause #%u:\n" , num); |
2917 | printf(" Flags: 0x%x" , clause.Flags); |
2918 | |
2919 | // Note: the flags field is kind of weird. It should be compared for equality |
2920 | // to determine the type of clause, even though it looks like a bitfield. In |
2921 | // Particular, CORINFO_EH_CLAUSE_NONE is zero, so you can't use "&" to check it. |
2922 | const DWORD CORINFO_EH_CLAUSE_TYPE_MASK = 0x7; |
2923 | switch (clause.Flags & CORINFO_EH_CLAUSE_TYPE_MASK) |
2924 | { |
2925 | case CORINFO_EH_CLAUSE_NONE: |
2926 | printf(" (catch)" ); |
2927 | break; |
2928 | case CORINFO_EH_CLAUSE_FILTER: |
2929 | printf(" (filter)" ); |
2930 | break; |
2931 | case CORINFO_EH_CLAUSE_FINALLY: |
2932 | printf(" (finally)" ); |
2933 | break; |
2934 | case CORINFO_EH_CLAUSE_FAULT: |
2935 | printf(" (fault)" ); |
2936 | break; |
2937 | default: |
2938 | printf(" (UNKNOWN type %u!)" , clause.Flags & CORINFO_EH_CLAUSE_TYPE_MASK); |
2939 | break; |
2940 | } |
2941 | if (clause.Flags & ~CORINFO_EH_CLAUSE_TYPE_MASK) |
2942 | { |
2943 | printf(" (extra unknown bits: 0x%x)" , clause.Flags & ~CORINFO_EH_CLAUSE_TYPE_MASK); |
2944 | } |
2945 | printf("\n" ); |
2946 | |
2947 | printf(" TryOffset: 0x%x\n" , clause.TryOffset); |
2948 | printf(" TryLength: 0x%x\n" , clause.TryLength); |
2949 | printf(" HandlerOffset: 0x%x\n" , clause.HandlerOffset); |
2950 | printf(" HandlerLength: 0x%x\n" , clause.HandlerLength); |
2951 | if (clause.Flags & CORINFO_EH_CLAUSE_FILTER) |
2952 | { |
2953 | printf(" FilterOffset: 0x%x\n" , clause.FilterOffset); |
2954 | } |
2955 | else |
2956 | { |
2957 | printf(" ClassToken: 0x%x\n" , clause.ClassToken); |
2958 | } |
2959 | } |
2960 | |
2961 | void Compiler::dispOutgoingEHClause(unsigned num, const CORINFO_EH_CLAUSE& clause) |
2962 | { |
2963 | if (opts.dspDiffable) |
2964 | { |
2965 | /* (( brace matching editor workaround to compensate for the following line */ |
2966 | printf("EH#%u: try [%s..%s) handled by [%s..%s) " , num, genEmitter->emitOffsetToLabel(clause.TryOffset), |
2967 | genEmitter->emitOffsetToLabel(clause.TryLength), genEmitter->emitOffsetToLabel(clause.HandlerOffset), |
2968 | genEmitter->emitOffsetToLabel(clause.HandlerLength)); |
2969 | } |
2970 | else |
2971 | { |
2972 | /* (( brace matching editor workaround to compensate for the following line */ |
2973 | printf("EH#%u: try [%04X..%04X) handled by [%04X..%04X) " , num, dspOffset(clause.TryOffset), |
2974 | dspOffset(clause.TryLength), dspOffset(clause.HandlerOffset), dspOffset(clause.HandlerLength)); |
2975 | } |
2976 | |
2977 | // Note: the flags field is kind of weird. It should be compared for equality |
2978 | // to determine the type of clause, even though it looks like a bitfield. In |
2979 | // Particular, CORINFO_EH_CLAUSE_NONE is zero, so you can "&" to check it. |
2980 | // You do need to mask off the bits, though, because CORINFO_EH_CLAUSE_DUPLICATE |
2981 | // is and'ed in. |
2982 | const DWORD CORINFO_EH_CLAUSE_TYPE_MASK = 0x7; |
2983 | switch (clause.Flags & CORINFO_EH_CLAUSE_TYPE_MASK) |
2984 | { |
2985 | case CORINFO_EH_CLAUSE_NONE: |
2986 | printf("(class: %04X)" , clause.ClassToken); |
2987 | break; |
2988 | case CORINFO_EH_CLAUSE_FILTER: |
2989 | if (opts.dspDiffable) |
2990 | { |
2991 | /* ( brace matching editor workaround to compensate for the following line */ |
2992 | printf("filter at [%s..%s)" , genEmitter->emitOffsetToLabel(clause.ClassToken), |
2993 | genEmitter->emitOffsetToLabel(clause.HandlerOffset)); |
2994 | } |
2995 | else |
2996 | { |
2997 | /* ( brace matching editor workaround to compensate for the following line */ |
2998 | printf("filter at [%04X..%04X)" , dspOffset(clause.ClassToken), dspOffset(clause.HandlerOffset)); |
2999 | } |
3000 | break; |
3001 | case CORINFO_EH_CLAUSE_FINALLY: |
3002 | printf("(finally)" ); |
3003 | break; |
3004 | case CORINFO_EH_CLAUSE_FAULT: |
3005 | printf("(fault)" ); |
3006 | break; |
3007 | default: |
3008 | printf("(UNKNOWN type %u!)" , clause.Flags & CORINFO_EH_CLAUSE_TYPE_MASK); |
3009 | assert(!"unknown type" ); |
3010 | break; |
3011 | } |
3012 | |
3013 | if ((clause.TryOffset == clause.TryLength) && (clause.TryOffset == clause.HandlerOffset) && |
3014 | ((clause.Flags & (CORINFO_EH_CLAUSE_DUPLICATE | CORINFO_EH_CLAUSE_FINALLY)) == |
3015 | (CORINFO_EH_CLAUSE_DUPLICATE | CORINFO_EH_CLAUSE_FINALLY))) |
3016 | { |
3017 | printf(" cloned finally" ); |
3018 | } |
3019 | else if (clause.Flags & CORINFO_EH_CLAUSE_DUPLICATE) |
3020 | { |
3021 | printf(" duplicated" ); |
3022 | } |
3023 | else if (clause.Flags & CORINFO_EH_CLAUSE_SAMETRY) |
3024 | { |
3025 | printf(" same try" ); |
3026 | } |
3027 | printf("\n" ); |
3028 | } |
3029 | |
3030 | /*****************************************************************************/ |
3031 | |
3032 | void Compiler::fgVerifyHandlerTab() |
3033 | { |
3034 | if (compIsForInlining()) |
3035 | { |
3036 | // We don't inline functions with EH. Don't bother verifying the EH table in the inlinee Compiler. |
3037 | return; |
3038 | } |
3039 | |
3040 | if (compHndBBtabCount == 0) |
3041 | { |
3042 | return; |
3043 | } |
3044 | |
3045 | // Did we do the normalization that prevents the first block of a handler from being a 'try' block (case 1)? |
3046 | bool handlerBegIsTryBegNormalizationDone = fgNormalizeEHDone; |
3047 | |
3048 | // Did we do the normalization that prevents multiple EH regions (namely, 'try' blocks) from starting on the same |
3049 | // block (case 2)? |
3050 | bool multipleBegBlockNormalizationDone = fgNormalizeEHDone; |
3051 | |
3052 | // Did we do the normalization that prevents multiple EH regions ('try' or handler blocks) from ending on the same |
3053 | // block (case 3)? |
3054 | bool multipleLastBlockNormalizationDone = false; // Currently disabled |
3055 | |
3056 | assert(compHndBBtabCount <= compHndBBtabAllocCount); |
3057 | |
3058 | unsigned XTnum; |
3059 | EHblkDsc* HBtab; |
3060 | |
3061 | for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) |
3062 | { |
3063 | assert(HBtab->ebdTryBeg != nullptr); |
3064 | assert(HBtab->ebdTryLast != nullptr); |
3065 | assert(HBtab->ebdHndBeg != nullptr); |
3066 | assert(HBtab->ebdHndLast != nullptr); |
3067 | |
3068 | assert(HBtab->ebdTryBeg->bbFlags & BBF_TRY_BEG); |
3069 | assert(HBtab->ebdTryBeg->bbFlags & BBF_DONT_REMOVE); |
3070 | assert(HBtab->ebdTryBeg->bbFlags & BBF_HAS_LABEL); |
3071 | |
3072 | assert(HBtab->ebdHndBeg->bbFlags & BBF_DONT_REMOVE); |
3073 | assert(HBtab->ebdHndBeg->bbFlags & BBF_HAS_LABEL); |
3074 | |
3075 | assert((HBtab->ebdTryBeg->bbFlags & BBF_REMOVED) == 0); |
3076 | assert((HBtab->ebdTryLast->bbFlags & BBF_REMOVED) == 0); |
3077 | assert((HBtab->ebdHndBeg->bbFlags & BBF_REMOVED) == 0); |
3078 | assert((HBtab->ebdHndLast->bbFlags & BBF_REMOVED) == 0); |
3079 | |
3080 | if (HBtab->HasFilter()) |
3081 | { |
3082 | assert(HBtab->ebdFilter != nullptr); |
3083 | assert(HBtab->ebdFilter->bbFlags & BBF_DONT_REMOVE); |
3084 | assert((HBtab->ebdFilter->bbFlags & BBF_REMOVED) == 0); |
3085 | } |
3086 | |
3087 | #if FEATURE_EH_FUNCLETS |
3088 | if (fgFuncletsCreated) |
3089 | { |
3090 | assert(HBtab->ebdHndBeg->bbFlags & BBF_FUNCLET_BEG); |
3091 | |
3092 | if (HBtab->HasFilter()) |
3093 | { |
3094 | assert(HBtab->ebdFilter->bbFlags & BBF_FUNCLET_BEG); |
3095 | } |
3096 | } |
3097 | #endif // FEATURE_EH_FUNCLETS |
3098 | } |
3099 | |
3100 | // I want to assert things about the relative ordering of blocks in the block list using |
3101 | // block number, but I don't want to renumber the basic blocks, which might cause a difference |
3102 | // between debug and non-debug code paths. So, create a renumbered block mapping: map the |
3103 | // existing block number to a renumbered block number that is ordered by block list order. |
3104 | |
3105 | unsigned bbNumMax = compIsForInlining() ? impInlineInfo->InlinerCompiler->fgBBNumMax : fgBBNumMax; |
3106 | |
3107 | // blockNumMap[old block number] => new block number |
3108 | size_t blockNumBytes = (bbNumMax + 1) * sizeof(unsigned); |
3109 | unsigned* blockNumMap = (unsigned*)_alloca(blockNumBytes); |
3110 | memset(blockNumMap, 0, blockNumBytes); |
3111 | |
3112 | BasicBlock* block; |
3113 | unsigned newBBnum = 1; |
3114 | for (block = fgFirstBB; block != nullptr; block = block->bbNext) |
3115 | { |
3116 | assert((block->bbFlags & BBF_REMOVED) == 0); |
3117 | assert(1 <= block->bbNum && block->bbNum <= bbNumMax); |
3118 | assert(blockNumMap[block->bbNum] == 0); // If this fails, we have two blocks with the same block number. |
3119 | blockNumMap[block->bbNum] = newBBnum++; |
3120 | } |
3121 | // Note that there may be some blockNumMap[x] == 0, for a block number 'x' that has been deleted, if the blocks |
3122 | // haven't been renumbered since the deletion. |
3123 | |
3124 | #if 0 // Useful for debugging, but don't want to put this in the dump all the time |
3125 | if (verbose) |
3126 | { |
3127 | printf("fgVerifyHandlerTab block number map: BB current => BB new\n" ); |
3128 | for (unsigned i = 0; i <= bbNumMax; i++) |
3129 | { |
3130 | if (blockNumMap[i] != 0) |
3131 | { |
3132 | printf(FMT_BB " => " FMT_BB "\n" , i, blockNumMap[i]); |
3133 | } |
3134 | } |
3135 | } |
3136 | #endif |
3137 | |
3138 | // To verify that bbCatchTyp is set properly on all blocks, and that some BBF_* flags are only set on the first |
3139 | // block |
3140 | // of 'try' or handlers, create two bool arrays indexed by block number: one for the set of blocks that are the |
3141 | // beginning |
3142 | // blocks of 'try' regions, and one for blocks that are the beginning of handlers (including filters). Note that |
3143 | // since |
3144 | // this checking function runs before EH normalization, we have to handle the case where blocks can be both the |
3145 | // beginning |
3146 | // of a 'try' as well as the beginning of a handler. After we've iterated over the EH table, loop |
3147 | // over all blocks and verify that only handler begin blocks have bbCatchTyp == BBCT_NONE, and some other things. |
3148 | |
3149 | size_t blockBoolSetBytes = (bbNumMax + 1) * sizeof(bool); |
3150 | bool* blockTryBegSet = (bool*)_alloca(blockBoolSetBytes); |
3151 | bool* blockHndBegSet = (bool*)_alloca(blockBoolSetBytes); |
3152 | for (unsigned i = 0; i <= bbNumMax; i++) |
3153 | { |
3154 | blockTryBegSet[i] = false; |
3155 | blockHndBegSet[i] = false; |
3156 | } |
3157 | |
3158 | #if FEATURE_EH_FUNCLETS |
3159 | bool isLegalFirstFunclet = false; |
3160 | unsigned bbNumFirstFunclet = 0; |
3161 | |
3162 | if (fgFuncletsCreated) |
3163 | { |
3164 | // Assert some things about the "first funclet block" pointer. |
3165 | assert(fgFirstFuncletBB != nullptr); |
3166 | assert((fgFirstFuncletBB->bbFlags & BBF_REMOVED) == 0); |
3167 | bbNumFirstFunclet = blockNumMap[fgFirstFuncletBB->bbNum]; |
3168 | assert(bbNumFirstFunclet != 0); |
3169 | } |
3170 | else |
3171 | { |
3172 | assert(fgFirstFuncletBB == nullptr); |
3173 | } |
3174 | #endif // FEATURE_EH_FUNCLETS |
3175 | |
3176 | for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) |
3177 | { |
3178 | unsigned bbNumTryBeg = blockNumMap[HBtab->ebdTryBeg->bbNum]; |
3179 | unsigned bbNumTryLast = blockNumMap[HBtab->ebdTryLast->bbNum]; |
3180 | unsigned bbNumHndBeg = blockNumMap[HBtab->ebdHndBeg->bbNum]; |
3181 | unsigned bbNumHndLast = blockNumMap[HBtab->ebdHndLast->bbNum]; |
3182 | unsigned bbNumFilter = 0; // This should never get used except under "if (HBtab->HasFilter())" |
3183 | if (HBtab->HasFilter()) |
3184 | { |
3185 | bbNumFilter = blockNumMap[HBtab->ebdFilter->bbNum]; |
3186 | } |
3187 | |
3188 | // Assert that the EH blocks are in the main block list |
3189 | assert(bbNumTryBeg != 0); |
3190 | assert(bbNumTryLast != 0); |
3191 | assert(bbNumHndBeg != 0); |
3192 | assert(bbNumHndLast != 0); |
3193 | if (HBtab->HasFilter()) |
3194 | { |
3195 | assert(bbNumFilter != 0); |
3196 | } |
3197 | |
3198 | // Check relative ordering of the 'beg' and 'last' blocks. Note that in IL (and in our initial block list) |
3199 | // there is no required ordering between the 'try' and handler regions: the handler might come first! |
3200 | // After funclets have been created, all the handler blocks come in sequence at the end of the |
3201 | // function (this is checked below, with checks for the first funclet block). Note that a handler |
3202 | // might contain a nested 'try', which will also then be in the "funclet region". |
3203 | // Also, the 'try' and handler regions do not need to be adjacent. |
3204 | assert(bbNumTryBeg <= bbNumTryLast); |
3205 | assert(bbNumHndBeg <= bbNumHndLast); |
3206 | if (HBtab->HasFilter()) |
3207 | { |
3208 | // Since the filter block must be different from the handler, this condition is "<", not "<=". |
3209 | assert(bbNumFilter < bbNumHndBeg); |
3210 | } |
3211 | |
3212 | // The EH regions are disjoint: the handler (including the filter, if applicable) is strictly before or after |
3213 | // the 'try'. |
3214 | if (HBtab->HasFilter()) |
3215 | { |
3216 | assert((bbNumHndLast < bbNumTryBeg) || (bbNumTryLast < bbNumFilter)); |
3217 | } |
3218 | else |
3219 | { |
3220 | assert((bbNumHndLast < bbNumTryBeg) || (bbNumTryLast < bbNumHndBeg)); |
3221 | } |
3222 | |
3223 | #if FEATURE_EH_FUNCLETS |
3224 | // If funclets have been created, check the first funclet block. The first funclet block must be the |
3225 | // first block of a filter or handler. All filter/handler blocks must come after it. |
3226 | // Note that 'try' blocks might come either before or after it. If after, they will be nested within |
3227 | // a handler. If before, they might be nested within a try, but not within a handler. |
3228 | |
3229 | if (fgFuncletsCreated) |
3230 | { |
3231 | if (bbNumTryLast < bbNumFirstFunclet) |
3232 | { |
3233 | // This EH region can't be nested in a handler, or else it would be in the funclet region. |
3234 | assert(HBtab->ebdEnclosingHndIndex == EHblkDsc::NO_ENCLOSING_INDEX); |
3235 | } |
3236 | else |
3237 | { |
3238 | // The last block of the 'try' is in the funclet region; make sure the whole thing is. |
3239 | if (multipleBegBlockNormalizationDone) |
3240 | { |
3241 | assert(bbNumTryBeg > bbNumFirstFunclet); // ">" because a 'try' can't be the first block of a |
3242 | // handler (by EH normalization). |
3243 | } |
3244 | else |
3245 | { |
3246 | assert(bbNumTryBeg >= bbNumFirstFunclet); |
3247 | } |
3248 | |
3249 | // This EH region must be nested in a handler. |
3250 | assert(HBtab->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX); |
3251 | } |
3252 | |
3253 | if (HBtab->HasFilter()) |
3254 | { |
3255 | assert(bbNumFirstFunclet <= bbNumFilter); |
3256 | if (fgFirstFuncletBB == HBtab->ebdFilter) |
3257 | { |
3258 | assert(!isLegalFirstFunclet); // We can't have already found a matching block for the first funclet. |
3259 | isLegalFirstFunclet = true; |
3260 | } |
3261 | } |
3262 | else |
3263 | { |
3264 | assert(bbNumFirstFunclet <= bbNumHndBeg); |
3265 | if (fgFirstFuncletBB == HBtab->ebdHndBeg) |
3266 | { |
3267 | assert(!isLegalFirstFunclet); // We can't have already found a matching block for the first funclet. |
3268 | isLegalFirstFunclet = true; |
3269 | } |
3270 | } |
3271 | } |
3272 | #endif // FEATURE_EH_FUNCLETS |
3273 | |
3274 | // Check the 'try' region nesting, using ebdEnclosingTryIndex. |
3275 | // Only check one level of nesting, since we'll check the outer EH region (and its nesting) when we get to it |
3276 | // later. |
3277 | |
3278 | if (HBtab->ebdEnclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
3279 | { |
3280 | assert(HBtab->ebdEnclosingTryIndex > XTnum); // The enclosing region must come after this one in the table |
3281 | EHblkDsc* HBtabOuter = ehGetDsc(HBtab->ebdEnclosingTryIndex); |
3282 | unsigned bbNumOuterTryBeg = blockNumMap[HBtabOuter->ebdTryBeg->bbNum]; |
3283 | unsigned bbNumOuterTryLast = blockNumMap[HBtabOuter->ebdTryLast->bbNum]; |
3284 | |
3285 | // A few basic asserts (that will also get covered later, when this outer region gets handled). |
3286 | assert(bbNumOuterTryBeg != 0); |
3287 | assert(bbNumOuterTryLast != 0); |
3288 | assert(bbNumOuterTryBeg <= bbNumOuterTryLast); |
3289 | |
3290 | if (!EHblkDsc::ebdIsSameTry(HBtab, HBtabOuter)) |
3291 | { |
3292 | // If it's not a mutually protect region, then the outer 'try' must completely lexically contain all the |
3293 | // blocks in the nested EH region. However, if funclets have been created, this is no longer true, since |
3294 | // this 'try' might be in a handler that is pulled out to the funclet region, while the outer 'try' |
3295 | // remains in the main function region. |
3296 | CLANG_FORMAT_COMMENT_ANCHOR; |
3297 | |
3298 | #if FEATURE_EH_FUNCLETS |
3299 | if (fgFuncletsCreated) |
3300 | { |
3301 | // If both the 'try' region and the outer 'try' region are in the main function area, then we can |
3302 | // do the normal nesting check. Otherwise, it's harder to find a useful assert to make about their |
3303 | // relationship. |
3304 | if ((bbNumTryLast < bbNumFirstFunclet) && (bbNumOuterTryLast < bbNumFirstFunclet)) |
3305 | { |
3306 | if (multipleBegBlockNormalizationDone) |
3307 | { |
3308 | assert(bbNumOuterTryBeg < bbNumTryBeg); // Two 'try' regions can't start at the same |
3309 | // block (by EH normalization). |
3310 | } |
3311 | else |
3312 | { |
3313 | assert(bbNumOuterTryBeg <= bbNumTryBeg); |
3314 | } |
3315 | if (multipleLastBlockNormalizationDone) |
3316 | { |
3317 | assert(bbNumTryLast < bbNumOuterTryLast); // Two 'try' regions can't end at the same block |
3318 | //(by EH normalization). |
3319 | } |
3320 | else |
3321 | { |
3322 | assert(bbNumTryLast <= bbNumOuterTryLast); |
3323 | } |
3324 | } |
3325 | |
3326 | // With funclets, all we can say about the handler blocks is that they are disjoint from the |
3327 | // enclosing try. |
3328 | assert((bbNumHndLast < bbNumOuterTryBeg) || (bbNumOuterTryLast < bbNumHndBeg)); |
3329 | } |
3330 | else |
3331 | #endif // FEATURE_EH_FUNCLETS |
3332 | { |
3333 | if (multipleBegBlockNormalizationDone) |
3334 | { |
3335 | assert(bbNumOuterTryBeg < bbNumTryBeg); // Two 'try' regions can't start at the same block |
3336 | // (by EH normalization). |
3337 | } |
3338 | else |
3339 | { |
3340 | assert(bbNumOuterTryBeg <= bbNumTryBeg); |
3341 | } |
3342 | assert(bbNumOuterTryBeg < bbNumHndBeg); // An inner handler can never start at the same |
3343 | // block as an outer 'try' (by IL rules). |
3344 | if (multipleLastBlockNormalizationDone) |
3345 | { |
3346 | // An inner EH region can't share a 'last' block with the outer 'try' (by EH normalization). |
3347 | assert(bbNumTryLast < bbNumOuterTryLast); |
3348 | assert(bbNumHndLast < bbNumOuterTryLast); |
3349 | } |
3350 | else |
3351 | { |
3352 | assert(bbNumTryLast <= bbNumOuterTryLast); |
3353 | assert(bbNumHndLast <= bbNumOuterTryLast); |
3354 | } |
3355 | } |
3356 | } |
3357 | } |
3358 | |
3359 | // Check the handler region nesting, using ebdEnclosingHndIndex. |
3360 | // Only check one level of nesting, since we'll check the outer EH region (and its nesting) when we get to it |
3361 | // later. |
3362 | |
3363 | if (HBtab->ebdEnclosingHndIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
3364 | { |
3365 | assert(HBtab->ebdEnclosingHndIndex > XTnum); // The enclosing region must come after this one in the table |
3366 | EHblkDsc* HBtabOuter = ehGetDsc(HBtab->ebdEnclosingHndIndex); |
3367 | unsigned bbNumOuterHndBeg = blockNumMap[HBtabOuter->ebdHndBeg->bbNum]; |
3368 | unsigned bbNumOuterHndLast = blockNumMap[HBtabOuter->ebdHndLast->bbNum]; |
3369 | |
3370 | // A few basic asserts (that will also get covered later, when this outer regions gets handled). |
3371 | assert(bbNumOuterHndBeg != 0); |
3372 | assert(bbNumOuterHndLast != 0); |
3373 | assert(bbNumOuterHndBeg <= bbNumOuterHndLast); |
3374 | |
3375 | // The outer handler must completely contain all the blocks in the EH region nested within it. However, if |
3376 | // funclets have been created, it's harder to make any relationship asserts about the order of nested |
3377 | // handlers, which also have been made into funclets. |
3378 | |
3379 | #if FEATURE_EH_FUNCLETS |
3380 | if (fgFuncletsCreated) |
3381 | { |
3382 | if (handlerBegIsTryBegNormalizationDone) |
3383 | { |
3384 | assert(bbNumOuterHndBeg < bbNumTryBeg); // An inner 'try' can't start at the same block as an |
3385 | // outer handler (by EH normalization). |
3386 | } |
3387 | else |
3388 | { |
3389 | assert(bbNumOuterHndBeg <= bbNumTryBeg); |
3390 | } |
3391 | if (multipleLastBlockNormalizationDone) |
3392 | { |
3393 | assert(bbNumTryLast < bbNumOuterHndLast); // An inner 'try' can't end at the same block as an |
3394 | // outer handler (by EH normalization). |
3395 | } |
3396 | else |
3397 | { |
3398 | assert(bbNumTryLast <= bbNumOuterHndLast); |
3399 | } |
3400 | |
3401 | // With funclets, all we can say about the handler blocks is that they are disjoint from the enclosing |
3402 | // handler. |
3403 | assert((bbNumHndLast < bbNumOuterHndBeg) || (bbNumOuterHndLast < bbNumHndBeg)); |
3404 | } |
3405 | else |
3406 | #endif // FEATURE_EH_FUNCLETS |
3407 | { |
3408 | if (handlerBegIsTryBegNormalizationDone) |
3409 | { |
3410 | assert(bbNumOuterHndBeg < bbNumTryBeg); // An inner 'try' can't start at the same block as an |
3411 | // outer handler (by EH normalization). |
3412 | } |
3413 | else |
3414 | { |
3415 | assert(bbNumOuterHndBeg <= bbNumTryBeg); |
3416 | } |
3417 | assert(bbNumOuterHndBeg < bbNumHndBeg); // An inner handler can never start at the same block |
3418 | // as an outer handler (by IL rules). |
3419 | if (multipleLastBlockNormalizationDone) |
3420 | { |
3421 | // An inner EH region can't share a 'last' block with the outer handler (by EH normalization). |
3422 | assert(bbNumTryLast < bbNumOuterHndLast); |
3423 | assert(bbNumHndLast < bbNumOuterHndLast); |
3424 | } |
3425 | else |
3426 | { |
3427 | assert(bbNumTryLast <= bbNumOuterHndLast); |
3428 | assert(bbNumHndLast <= bbNumOuterHndLast); |
3429 | } |
3430 | } |
3431 | } |
3432 | |
3433 | // Set up blockTryBegSet and blockHndBegSet. |
3434 | // We might want to have this assert: |
3435 | // if (fgNormalizeEHDone) assert(!blockTryBegSet[HBtab->ebdTryBeg->bbNum]); |
3436 | // But we can't, because if we have mutually-protect 'try' regions, we'll see exactly the same tryBeg twice |
3437 | // (or more). |
3438 | blockTryBegSet[HBtab->ebdTryBeg->bbNum] = true; |
3439 | assert(!blockHndBegSet[HBtab->ebdHndBeg->bbNum]); |
3440 | blockHndBegSet[HBtab->ebdHndBeg->bbNum] = true; |
3441 | |
3442 | if (HBtab->HasFilter()) |
3443 | { |
3444 | assert(HBtab->ebdFilter->bbCatchTyp == BBCT_FILTER); |
3445 | assert(!blockHndBegSet[HBtab->ebdFilter->bbNum]); |
3446 | blockHndBegSet[HBtab->ebdFilter->bbNum] = true; |
3447 | } |
3448 | |
3449 | // Check the block bbCatchTyp for this EH region's filter and handler. |
3450 | |
3451 | if (HBtab->HasFilter()) |
3452 | { |
3453 | assert(HBtab->ebdHndBeg->bbCatchTyp == BBCT_FILTER_HANDLER); |
3454 | } |
3455 | else if (HBtab->HasCatchHandler()) |
3456 | { |
3457 | assert((HBtab->ebdHndBeg->bbCatchTyp != BBCT_NONE) && (HBtab->ebdHndBeg->bbCatchTyp != BBCT_FAULT) && |
3458 | (HBtab->ebdHndBeg->bbCatchTyp != BBCT_FINALLY) && (HBtab->ebdHndBeg->bbCatchTyp != BBCT_FILTER) && |
3459 | (HBtab->ebdHndBeg->bbCatchTyp != BBCT_FILTER_HANDLER)); |
3460 | } |
3461 | else if (HBtab->HasFaultHandler()) |
3462 | { |
3463 | assert(HBtab->ebdHndBeg->bbCatchTyp == BBCT_FAULT); |
3464 | } |
3465 | else if (HBtab->HasFinallyHandler()) |
3466 | { |
3467 | assert(HBtab->ebdHndBeg->bbCatchTyp == BBCT_FINALLY); |
3468 | } |
3469 | } |
3470 | |
3471 | #if FEATURE_EH_FUNCLETS |
3472 | assert(!fgFuncletsCreated || isLegalFirstFunclet); |
3473 | #endif // FEATURE_EH_FUNCLETS |
3474 | |
3475 | // Figure out what 'try' and handler index each basic block should have, |
3476 | // and check the blocks against that. This depends on the more nested EH |
3477 | // clauses appearing first. For duplicate clauses, we use the duplicate |
3478 | // clause 'try' region to set the try index, since a handler that has |
3479 | // been pulled out of an enclosing 'try' wouldn't have had its try index |
3480 | // otherwise set. The duplicate clause handler is truly a duplicate of |
3481 | // a previously processed handler, so we ignore it. |
3482 | |
3483 | size_t blockIndexBytes = (bbNumMax + 1) * sizeof(unsigned short); |
3484 | unsigned short* blockTryIndex = (unsigned short*)_alloca(blockIndexBytes); |
3485 | unsigned short* blockHndIndex = (unsigned short*)_alloca(blockIndexBytes); |
3486 | memset(blockTryIndex, 0, blockIndexBytes); |
3487 | memset(blockHndIndex, 0, blockIndexBytes); |
3488 | |
3489 | for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) |
3490 | { |
3491 | BasicBlock* blockEnd; |
3492 | |
3493 | for (block = HBtab->ebdTryBeg, blockEnd = HBtab->ebdTryLast->bbNext; block != blockEnd; block = block->bbNext) |
3494 | { |
3495 | if (blockTryIndex[block->bbNum] == 0) |
3496 | { |
3497 | blockTryIndex[block->bbNum] = (unsigned short)(XTnum + 1); |
3498 | } |
3499 | } |
3500 | |
3501 | for (block = (HBtab->HasFilter() ? HBtab->ebdFilter : HBtab->ebdHndBeg), blockEnd = HBtab->ebdHndLast->bbNext; |
3502 | block != blockEnd; block = block->bbNext) |
3503 | { |
3504 | if (blockHndIndex[block->bbNum] == 0) |
3505 | { |
3506 | blockHndIndex[block->bbNum] = (unsigned short)(XTnum + 1); |
3507 | } |
3508 | } |
3509 | } |
3510 | |
3511 | #if FEATURE_EH_FUNCLETS |
3512 | if (fgFuncletsCreated) |
3513 | { |
3514 | // Mark all the funclet 'try' indices correctly, since they do not exist in the linear 'try' region that |
3515 | // we looped over above. This is similar to duplicate clause logic, but we only need to look at the most |
3516 | // nested enclosing try index, not the entire set of enclosing try indices, since that is what we store |
3517 | // on the block. |
3518 | for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) |
3519 | { |
3520 | unsigned enclosingTryIndex = ehTrueEnclosingTryIndexIL(XTnum); // find the true enclosing try index, |
3521 | // ignoring 'mutual protect' trys |
3522 | if (enclosingTryIndex != EHblkDsc::NO_ENCLOSING_INDEX) |
3523 | { |
3524 | // The handler funclet for 'XTnum' has a try index of 'enclosingTryIndex' (at least, the parts of the |
3525 | // funclet that don't already have a more nested 'try' index because a 'try' is nested within the |
3526 | // handler). |
3527 | |
3528 | BasicBlock* blockEnd; |
3529 | for (block = (HBtab->HasFilter() ? HBtab->ebdFilter : HBtab->ebdHndBeg), |
3530 | blockEnd = HBtab->ebdHndLast->bbNext; |
3531 | block != blockEnd; block = block->bbNext) |
3532 | { |
3533 | if (blockTryIndex[block->bbNum] == 0) |
3534 | { |
3535 | blockTryIndex[block->bbNum] = (unsigned short)(enclosingTryIndex + 1); |
3536 | } |
3537 | } |
3538 | } |
3539 | } |
3540 | } |
3541 | #endif // FEATURE_EH_FUNCLETS |
3542 | |
3543 | // Make sure that all blocks have the right index, including those blocks that should have zero (no EH region). |
3544 | for (block = fgFirstBB; block != nullptr; block = block->bbNext) |
3545 | { |
3546 | assert(block->bbTryIndex == blockTryIndex[block->bbNum]); |
3547 | assert(block->bbHndIndex == blockHndIndex[block->bbNum]); |
3548 | |
3549 | // Also, since we're walking the blocks, check that all blocks we didn't mark as EH handler 'begin' blocks |
3550 | // already have bbCatchTyp set properly. |
3551 | if (!blockHndBegSet[block->bbNum]) |
3552 | { |
3553 | assert(block->bbCatchTyp == BBCT_NONE); |
3554 | |
3555 | #if FEATURE_EH_FUNCLETS |
3556 | if (fgFuncletsCreated) |
3557 | { |
3558 | // Make sure blocks that aren't the first block of a funclet do not have the BBF_FUNCLET_BEG flag set. |
3559 | assert((block->bbFlags & BBF_FUNCLET_BEG) == 0); |
3560 | } |
3561 | #endif // FEATURE_EH_FUNCLETS |
3562 | } |
3563 | |
3564 | // Only the first block of 'try' regions should have BBF_TRY_BEG set. |
3565 | if (!blockTryBegSet[block->bbNum]) |
3566 | { |
3567 | assert((block->bbFlags & BBF_TRY_BEG) == 0); |
3568 | } |
3569 | } |
3570 | } |
3571 | |
3572 | void Compiler::fgDispHandlerTab() |
3573 | { |
3574 | printf("\n*************** Exception Handling table" ); |
3575 | |
3576 | if (compHndBBtabCount == 0) |
3577 | { |
3578 | printf(" is empty\n" ); |
3579 | return; |
3580 | } |
3581 | |
3582 | printf("\nindex " ); |
3583 | #if !FEATURE_EH_FUNCLETS |
3584 | printf("nest, " ); |
3585 | #endif // !FEATURE_EH_FUNCLETS |
3586 | printf("eTry, eHnd\n" ); |
3587 | |
3588 | unsigned XTnum; |
3589 | EHblkDsc* HBtab; |
3590 | |
3591 | for (XTnum = 0, HBtab = compHndBBtab; XTnum < compHndBBtabCount; XTnum++, HBtab++) |
3592 | { |
3593 | HBtab->DispEntry(XTnum); |
3594 | } |
3595 | } |
3596 | |
3597 | #endif // DEBUG |
3598 | /*****************************************************************************/ |
3599 | |
3600 | /*XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3601 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3602 | XX XX |
3603 | XX "Compiler" functions: EH tree verification XX |
3604 | XX XX |
3605 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3606 | XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX |
3607 | */ |
3608 | |
3609 | /***************************************************************************** |
3610 | * The following code checks the following rules for the EH table: |
3611 | * 1. Overlapping of try blocks not allowed. |
3612 | * 2. Handler blocks cannot be shared between different try blocks. |
3613 | * 3. Try blocks with Finally or Fault blocks cannot have other handlers. |
3614 | * 4. If block A contains block B, A should also contain B's try/filter/handler. |
3615 | * 5. A block cannot contain it's related try/filter/handler. |
3616 | * 6. Nested block must appear before containing block |
3617 | * |
3618 | */ |
3619 | |
3620 | void Compiler::verInitEHTree(unsigned numEHClauses) |
3621 | { |
3622 | ehnNext = new (this, CMK_BasicBlock) EHNodeDsc[numEHClauses * 3]; |
3623 | ehnTree = nullptr; |
3624 | } |
3625 | |
3626 | /* Inserts the try, handler and filter (optional) clause information in a tree structure |
3627 | * in order to catch incorrect eh formatting (e.g. illegal overlaps, incorrect order) |
3628 | */ |
3629 | |
3630 | void Compiler::verInsertEhNode(CORINFO_EH_CLAUSE* clause, EHblkDsc* handlerTab) |
3631 | { |
3632 | EHNodeDsc* tryNode = ehnNext++; |
3633 | EHNodeDsc* handlerNode = ehnNext++; |
3634 | EHNodeDsc* filterNode = nullptr; // optional |
3635 | |
3636 | tryNode->ehnSetTryNodeType(); |
3637 | tryNode->ehnStartOffset = clause->TryOffset; |
3638 | tryNode->ehnEndOffset = clause->TryOffset + clause->TryLength - 1; |
3639 | tryNode->ehnHandlerNode = handlerNode; |
3640 | |
3641 | if (clause->Flags & CORINFO_EH_CLAUSE_FINALLY) |
3642 | { |
3643 | handlerNode->ehnSetFinallyNodeType(); |
3644 | } |
3645 | else if (clause->Flags & CORINFO_EH_CLAUSE_FAULT) |
3646 | { |
3647 | handlerNode->ehnSetFaultNodeType(); |
3648 | } |
3649 | else |
3650 | { |
3651 | handlerNode->ehnSetHandlerNodeType(); |
3652 | } |
3653 | |
3654 | handlerNode->ehnStartOffset = clause->HandlerOffset; |
3655 | handlerNode->ehnEndOffset = clause->HandlerOffset + clause->HandlerLength - 1; |
3656 | handlerNode->ehnTryNode = tryNode; |
3657 | |
3658 | if (clause->Flags & CORINFO_EH_CLAUSE_FILTER) |
3659 | { |
3660 | filterNode = ehnNext++; |
3661 | filterNode->ehnStartOffset = clause->FilterOffset; |
3662 | BasicBlock* blk = handlerTab->BBFilterLast(); |
3663 | filterNode->ehnEndOffset = blk->bbCodeOffsEnd - 1; |
3664 | |
3665 | noway_assert(filterNode->ehnEndOffset != 0); |
3666 | filterNode->ehnSetFilterNodeType(); |
3667 | filterNode->ehnTryNode = tryNode; |
3668 | tryNode->ehnFilterNode = filterNode; |
3669 | } |
3670 | |
3671 | verInsertEhNodeInTree(&ehnTree, tryNode); |
3672 | verInsertEhNodeInTree(&ehnTree, handlerNode); |
3673 | if (filterNode) |
3674 | { |
3675 | verInsertEhNodeInTree(&ehnTree, filterNode); |
3676 | } |
3677 | } |
3678 | |
3679 | /* |
3680 | The root node could be changed by this method. |
3681 | |
3682 | node is inserted to |
3683 | |
3684 | (a) right of root (root.right <-- node) |
3685 | (b) left of root (node.right <-- root; node becomes root) |
3686 | (c) child of root (root.child <-- node) |
3687 | (d) parent of root (node.child <-- root; node becomes root) |
3688 | (e) equivalent of root (root.equivalent <-- node) |
3689 | |
3690 | such that siblings are ordered from left to right |
3691 | child parent relationship and equivalence relationship are not violated |
3692 | |
3693 | |
3694 | Here is a list of all possible cases |
3695 | |
3696 | Case 1 2 3 4 5 6 7 8 9 10 11 12 13 |
3697 | |
3698 | | | | | | |
3699 | | | | | | |
3700 | .......|.|.|.|..................... [ root start ] ..... |
3701 | | | | | | | | |
3702 | | | | | | | | |
3703 | r| | | | | | | | |
3704 | o| | | | | | |
3705 | o| | | | | | |
3706 | t| | | | | | |
3707 | | | | | | | | | |
3708 | | | | | | | | |
3709 | |..........|.|.|.|.....|........|.. [ root end ] ........ |
3710 | | | | | |
3711 | | | | | | |
3712 | | | | | | |
3713 | |
3714 | |<-- - - - n o d e - - - -->| |
3715 | |
3716 | |
3717 | Case Operation |
3718 | -------------- |
3719 | 1 (b) |
3720 | 2 Error |
3721 | 3 Error |
3722 | 4 (d) |
3723 | 5 (d) |
3724 | 6 (d) |
3725 | 7 Error |
3726 | 8 Error |
3727 | 9 (a) |
3728 | 10 (c) |
3729 | 11 (c) |
3730 | 12 (c) |
3731 | 13 (e) |
3732 | |
3733 | |
3734 | */ |
3735 | |
3736 | void Compiler::verInsertEhNodeInTree(EHNodeDsc** ppRoot, EHNodeDsc* node) |
3737 | { |
3738 | unsigned nStart = node->ehnStartOffset; |
3739 | unsigned nEnd = node->ehnEndOffset; |
3740 | |
3741 | if (nStart > nEnd) |
3742 | { |
3743 | BADCODE("start offset greater or equal to end offset" ); |
3744 | } |
3745 | node->ehnNext = nullptr; |
3746 | node->ehnChild = nullptr; |
3747 | node->ehnEquivalent = nullptr; |
3748 | |
3749 | while (TRUE) |
3750 | { |
3751 | if (*ppRoot == nullptr) |
3752 | { |
3753 | *ppRoot = node; |
3754 | break; |
3755 | } |
3756 | unsigned rStart = (*ppRoot)->ehnStartOffset; |
3757 | unsigned rEnd = (*ppRoot)->ehnEndOffset; |
3758 | |
3759 | if (nStart < rStart) |
3760 | { |
3761 | // Case 1 |
3762 | if (nEnd < rStart) |
3763 | { |
3764 | // Left sibling |
3765 | node->ehnNext = *ppRoot; |
3766 | *ppRoot = node; |
3767 | return; |
3768 | } |
3769 | // Case 2, 3 |
3770 | if (nEnd < rEnd) |
3771 | { |
3772 | //[Error] |
3773 | BADCODE("Overlapping try regions" ); |
3774 | } |
3775 | |
3776 | // Case 4, 5 |
3777 | //[Parent] |
3778 | verInsertEhNodeParent(ppRoot, node); |
3779 | return; |
3780 | } |
3781 | |
3782 | // Cases 6 - 13 (nStart >= rStart) |
3783 | |
3784 | if (nEnd > rEnd) |
3785 | { // Case 6, 7, 8, 9 |
3786 | |
3787 | // Case 9 |
3788 | if (nStart > rEnd) |
3789 | { |
3790 | //[RightSibling] |
3791 | |
3792 | // Recurse with Root.Sibling as the new root |
3793 | ppRoot = &((*ppRoot)->ehnNext); |
3794 | continue; |
3795 | } |
3796 | |
3797 | // Case 6 |
3798 | if (nStart == rStart) |
3799 | { |
3800 | //[Parent] |
3801 | if (node->ehnIsTryBlock() || (*ppRoot)->ehnIsTryBlock()) |
3802 | { |
3803 | verInsertEhNodeParent(ppRoot, node); |
3804 | return; |
3805 | } |
3806 | |
3807 | // non try blocks are not allowed to start at the same offset |
3808 | BADCODE("Handlers start at the same offset" ); |
3809 | } |
3810 | |
3811 | // Case 7, 8 |
3812 | BADCODE("Overlapping try regions" ); |
3813 | } |
3814 | |
3815 | // Case 10-13 (nStart >= rStart && nEnd <= rEnd) |
3816 | if ((nStart != rStart) || (nEnd != rEnd)) |
3817 | { // Cases 10,11,12 |
3818 | //[Child] |
3819 | |
3820 | if ((*ppRoot)->ehnIsTryBlock()) |
3821 | { |
3822 | BADCODE("Inner try appears after outer try in exception handling table" ); |
3823 | } |
3824 | else |
3825 | { |
3826 | // We have an EH clause nested within a handler, but the parent |
3827 | // handler clause came first in the table. The rest of the compiler |
3828 | // doesn't expect this, so sort the EH table. |
3829 | |
3830 | fgNeedToSortEHTable = true; |
3831 | |
3832 | // Case 12 (nStart == rStart) |
3833 | // non try blocks are not allowed to start at the same offset |
3834 | if ((nStart == rStart) && !node->ehnIsTryBlock()) |
3835 | { |
3836 | BADCODE("Handlers start at the same offset" ); |
3837 | } |
3838 | |
3839 | // check this! |
3840 | ppRoot = &((*ppRoot)->ehnChild); |
3841 | continue; |
3842 | } |
3843 | } |
3844 | |
3845 | // Case 13 |
3846 | //[Equivalent] |
3847 | if (!node->ehnIsTryBlock() && !(*ppRoot)->ehnIsTryBlock()) |
3848 | { |
3849 | BADCODE("Handlers cannot be shared" ); |
3850 | } |
3851 | |
3852 | if (!node->ehnIsTryBlock() || !(*ppRoot)->ehnIsTryBlock()) |
3853 | { |
3854 | // Equivalent is only allowed for try bodies |
3855 | // If one is a handler, this means the nesting is wrong |
3856 | BADCODE("Handler and try with the same offset" ); |
3857 | } |
3858 | |
3859 | node->ehnEquivalent = node->ehnNext = *ppRoot; |
3860 | |
3861 | // check that the corresponding handler is either a catch handler |
3862 | // or a filter |
3863 | if (node->ehnHandlerNode->ehnIsFaultBlock() || node->ehnHandlerNode->ehnIsFinallyBlock() || |
3864 | (*ppRoot)->ehnHandlerNode->ehnIsFaultBlock() || (*ppRoot)->ehnHandlerNode->ehnIsFinallyBlock()) |
3865 | { |
3866 | BADCODE("Try block with multiple non-filter/non-handler blocks" ); |
3867 | } |
3868 | |
3869 | break; |
3870 | } |
3871 | } |
3872 | |
3873 | /********************************************************************** |
3874 | * Make node the parent of *ppRoot. All siblings of *ppRoot that are |
3875 | * fully or partially nested in node remain siblings of *ppRoot |
3876 | */ |
3877 | |
3878 | void Compiler::verInsertEhNodeParent(EHNodeDsc** ppRoot, EHNodeDsc* node) |
3879 | { |
3880 | noway_assert(node->ehnNext == nullptr); |
3881 | noway_assert(node->ehnChild == nullptr); |
3882 | |
3883 | // Root is nested in Node |
3884 | noway_assert(node->ehnStartOffset <= (*ppRoot)->ehnStartOffset); |
3885 | noway_assert(node->ehnEndOffset >= (*ppRoot)->ehnEndOffset); |
3886 | |
3887 | // Root is not the same as Node |
3888 | noway_assert(node->ehnStartOffset != (*ppRoot)->ehnStartOffset || node->ehnEndOffset != (*ppRoot)->ehnEndOffset); |
3889 | |
3890 | if (node->ehnIsFilterBlock()) |
3891 | { |
3892 | BADCODE("Protected block appearing within filter block" ); |
3893 | } |
3894 | |
3895 | EHNodeDsc* lastChild = nullptr; |
3896 | EHNodeDsc* sibling = (*ppRoot)->ehnNext; |
3897 | |
3898 | while (sibling) |
3899 | { |
3900 | // siblings are ordered left to right, largest right. |
3901 | // nodes have a width of at least one. |
3902 | // Hence sibling start will always be after Node start. |
3903 | |
3904 | noway_assert(sibling->ehnStartOffset > node->ehnStartOffset); // (1) |
3905 | |
3906 | // disjoint |
3907 | if (sibling->ehnStartOffset > node->ehnEndOffset) |
3908 | { |
3909 | break; |
3910 | } |
3911 | |
3912 | // partial containment. |
3913 | if (sibling->ehnEndOffset > node->ehnEndOffset) // (2) |
3914 | { |
3915 | BADCODE("Overlapping try regions" ); |
3916 | } |
3917 | // else full containment (follows from (1) and (2)) |
3918 | |
3919 | lastChild = sibling; |
3920 | sibling = sibling->ehnNext; |
3921 | } |
3922 | |
3923 | // All siblings of Root up to and including lastChild will continue to be |
3924 | // siblings of Root (and children of Node). The node to the right of |
3925 | // lastChild will become the first sibling of Node. |
3926 | // |
3927 | |
3928 | if (lastChild) |
3929 | { |
3930 | // Node has more than one child including Root |
3931 | |
3932 | node->ehnNext = lastChild->ehnNext; |
3933 | lastChild->ehnNext = nullptr; |
3934 | } |
3935 | else |
3936 | { |
3937 | // Root is the only child of Node |
3938 | node->ehnNext = (*ppRoot)->ehnNext; |
3939 | (*ppRoot)->ehnNext = nullptr; |
3940 | } |
3941 | |
3942 | node->ehnChild = *ppRoot; |
3943 | *ppRoot = node; |
3944 | } |
3945 | |
3946 | /***************************************************************************** |
3947 | * Checks the following two conditions: |
3948 | * 1) If block A contains block B, A should also contain B's try/filter/handler. |
3949 | * 2) A block cannot contain its related try/filter/handler. |
3950 | * Both these conditions are checked by making sure that all the blocks for an |
3951 | * exception clause are at the same level. |
3952 | * The algorithm is: for each exception clause, determine the first block and |
3953 | * search through the next links for its corresponding try/handler/filter as the |
3954 | * case may be. If not found, then fail. |
3955 | */ |
3956 | void Compiler::verCheckNestingLevel(EHNodeDsc* root) |
3957 | { |
3958 | EHNodeDsc* ehnNode = root; |
3959 | |
3960 | #define exchange(a, b) \ |
3961 | { \ |
3962 | temp = a; \ |
3963 | a = b; \ |
3964 | b = temp; \ |
3965 | } |
3966 | |
3967 | for (unsigned XTnum = 0; XTnum < compHndBBtabCount; XTnum++) |
3968 | { |
3969 | EHNodeDsc *p1, *p2, *p3, *temp, *search; |
3970 | |
3971 | p1 = ehnNode++; |
3972 | p2 = ehnNode++; |
3973 | |
3974 | // we are relying on the fact that ehn nodes are allocated sequentially. |
3975 | noway_assert(p1->ehnHandlerNode == p2); |
3976 | noway_assert(p2->ehnTryNode == p1); |
3977 | |
3978 | // arrange p1 and p2 in sequential order |
3979 | if (p1->ehnStartOffset == p2->ehnStartOffset) |
3980 | { |
3981 | BADCODE("shared exception handler" ); |
3982 | } |
3983 | |
3984 | if (p1->ehnStartOffset > p2->ehnStartOffset) |
3985 | exchange(p1, p2); |
3986 | |
3987 | temp = p1->ehnNext; |
3988 | unsigned numSiblings = 0; |
3989 | |
3990 | search = p2; |
3991 | if (search->ehnEquivalent) |
3992 | { |
3993 | search = search->ehnEquivalent; |
3994 | } |
3995 | |
3996 | do |
3997 | { |
3998 | if (temp == search) |
3999 | { |
4000 | numSiblings++; |
4001 | break; |
4002 | } |
4003 | if (temp) |
4004 | { |
4005 | temp = temp->ehnNext; |
4006 | } |
4007 | } while (temp); |
4008 | |
4009 | CORINFO_EH_CLAUSE clause; |
4010 | info.compCompHnd->getEHinfo(info.compMethodHnd, XTnum, &clause); |
4011 | |
4012 | if (clause.Flags & CORINFO_EH_CLAUSE_FILTER) |
4013 | { |
4014 | p3 = ehnNode++; |
4015 | |
4016 | noway_assert(p3->ehnTryNode == p1 || p3->ehnTryNode == p2); |
4017 | noway_assert(p1->ehnFilterNode == p3 || p2->ehnFilterNode == p3); |
4018 | |
4019 | if (p3->ehnStartOffset < p1->ehnStartOffset) |
4020 | { |
4021 | temp = p3; |
4022 | search = p1; |
4023 | } |
4024 | else if (p3->ehnStartOffset < p2->ehnStartOffset) |
4025 | { |
4026 | temp = p1; |
4027 | search = p3; |
4028 | } |
4029 | else |
4030 | { |
4031 | temp = p2; |
4032 | search = p3; |
4033 | } |
4034 | if (search->ehnEquivalent) |
4035 | { |
4036 | search = search->ehnEquivalent; |
4037 | } |
4038 | do |
4039 | { |
4040 | if (temp == search) |
4041 | { |
4042 | numSiblings++; |
4043 | break; |
4044 | } |
4045 | temp = temp->ehnNext; |
4046 | } while (temp); |
4047 | } |
4048 | else |
4049 | { |
4050 | numSiblings++; |
4051 | } |
4052 | |
4053 | if (numSiblings != 2) |
4054 | { |
4055 | BADCODE("Outer block does not contain all code in inner handler" ); |
4056 | } |
4057 | } |
4058 | } |
4059 | |