public override void ClosingScope(ILBuilder builder) { switch (_type) { case ScopeType.Finally: case ScopeType.Fault: // Emit endfinally|endfault - they are the same opcode. builder.EmitEndFinally(); break; default: // Emit branch to label after exception handler. // ("br" will be rewritten as "leave" later by ILBuilder.) var endLabel = _containingScope.EndLabel; Debug.Assert(endLabel != null); builder.EmitBranch(ILOpCode.Br, endLabel); break; } }
public override void CloseScope(ILBuilder builder) { Debug.Assert(_handlers.Count > 1); // Fix up the NextExceptionHandler reference of each leader block. ExceptionHandlerScope tryScope = _handlers[0]; ExceptionHandlerLeaderBlock previousBlock = tryScope.LeaderBlock; for (int i = 1; i < _handlers.Count; i++) { ExceptionHandlerScope handlerScope = _handlers[i]; ExceptionHandlerLeaderBlock nextBlock = handlerScope.LeaderBlock; previousBlock.NextExceptionHandler = nextBlock; previousBlock = nextBlock; } // Generate label for try/catch "leave" target. builder.MarkLabel(_endLabel); // hide the following code, since it could be reached through the label above. builder.DefineHiddenSequencePoint(); Debug.Assert(builder._currentBlock == builder._labelInfos[_endLabel].bb); if (_handlers[1].Type == ScopeType.Finally) { // Generate "nop" branch to itself. If this block is unreachable // (because the finally block does not complete), the "nop" will be // replaced by Br_s. On the other hand, if this block is reachable, // the "nop" will be skipped so any "leave" instructions jumping // to this block will jump to the next instead. builder.EmitBranch(ILOpCode.Nop, _endLabel); _handlers[1].SetBlockedByFinallyDestination(_endLabel); } }
internal void EmitJumpTable() { // For emitting the switch statement (integral governing type) jump table with a non-constant // switch expression, we can use a naive approach and generate a single big MSIL switch instruction // with all the case labels and fall through label. However, this approach can be optimized // to improve both the code size and speed using the following optimization steps: // a) Sort the switch case labels based on their constant values. // b) Divide the sorted switch labels into buckets with good enough density (>50%). For example: // switch(..) // { // case 1: // case 100: // break; // case 2: // case 4: // break; // case 200: // case 201: // case 202: // break; // } // can be divided into 3 buckets: (1, 2, 4) (100) (200, 201, 202). // We do this bucketing so that we have reasonable size jump tables for generated switch instructions. // c) After bucketing, generate code to perform a binary search on these buckets array, // emitting conditional jumps if current bucket sub-array has more than one bucket and // emitting the switch instruction when we are down to a single bucket in the sub-array. // (a) Sort switch labels: This was done in the constructor Debug.Assert(!_sortedCaseLabels.IsEmpty); var sortedCaseLabels = _sortedCaseLabels; int endLabelIndex = sortedCaseLabels.Length - 1; int startLabelIndex; // Check for a label with ConstantValue.Null. // Sorting ensures that if we do have one, it will be // the first label in the sorted list. if (sortedCaseLabels[0].Key != ConstantValue.Null) { startLabelIndex = 0; } else { // Skip null label for emitting switch table header. // We should have inserted a conditional branch to 'null' label during rewriting. // See LocalRewriter.MakeSwitchStatementWithNullableExpression startLabelIndex = 1; } if (startLabelIndex <= endLabelIndex) { // We have at least one non-null case label, emit jump table // (b) Generate switch buckets ImmutableArray <SwitchBucket> switchBuckets = this.GenerateSwitchBuckets(startLabelIndex, endLabelIndex); // (c) Emit switch buckets this.EmitSwitchBuckets(switchBuckets, 0, switchBuckets.Length - 1); } else { _builder.EmitBranch(ILOpCode.Br, _fallThroughLabel); } }