提交 a98e5998 编写于 作者: V Vladimir Sadov 提交者: GitHub

Merge pull request #16054 from dotnet/degenerateSwitch

Better codegen quality when emitting degenerate switches.
......@@ -1075,11 +1075,15 @@ private void EmitSwitchStatement(BoundSwitchStatement switchStatement)
EmitSwitchBody(switchStatement.InnerLocals, switchSections, breakLabel, switchStatement.Syntax);
}
private static KeyValuePair<ConstantValue, object>[] GetSwitchCaseLabels(ImmutableArray<BoundSwitchSection> sections, ref LabelSymbol fallThroughLabel)
private KeyValuePair<ConstantValue, object>[] GetSwitchCaseLabels(ImmutableArray<BoundSwitchSection> sections, ref LabelSymbol fallThroughLabel)
{
var labelsBuilder = ArrayBuilder<KeyValuePair<ConstantValue, object>>.GetInstance();
foreach (var section in sections)
{
// all labels in a section are labeling the same region of code,
// so we could use just the first one for a better codegen
object firstLabelInSection = null;
foreach (BoundSwitchLabel boundLabel in section.SwitchLabels)
{
var label = boundLabel.Label;
......@@ -1092,7 +1096,13 @@ private void EmitSwitchStatement(BoundSwitchStatement switchStatement)
var value = boundLabel.ConstantValueOpt;
Debug.Assert(value != null
&& SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(value));
labelsBuilder.Add(new KeyValuePair<ConstantValue, object>(value, label));
if (firstLabelInSection == null)
{
firstLabelInSection = label;
}
labelsBuilder.Add(new KeyValuePair<ConstantValue, object>(value, firstLabelInSection));
}
}
}
......
......@@ -441,7 +441,7 @@ .maxstack 2
");
compilation.VerifyIL("Program.<Int0>d__0.System.IDisposable.Dispose()", @"
{
// Code size 121 (0x79)
// Code size 76 (0x4c)
.maxstack 2
.locals init (int V_0)
IL_0000: ldarg.0
......@@ -450,70 +450,63 @@ .locals init (int V_0)
IL_0007: ldloc.0
IL_0008: ldc.i4.s -5
IL_000a: sub
IL_000b: switch (
IL_0041,
IL_0041,
IL_0041,
IL_0078,
IL_0078,
IL_0078,
IL_0078,
IL_0041,
IL_0041,
IL_0041,
IL_0041,
IL_0041)
IL_0040: ret
IL_0041: nop
IL_000b: ldc.i4.2
IL_000c: ble.un.s IL_0014
IL_000e: ldloc.0
IL_000f: ldc.i4.2
IL_0010: sub
IL_0011: ldc.i4.4
IL_0012: bgt.un.s IL_004b
IL_0014: nop
.try
{
IL_0042: ldloc.0
IL_0043: ldc.i4.s -4
IL_0045: bgt.s IL_0053
IL_0047: ldloc.0
IL_0048: ldc.i4.s -5
IL_004a: beq.s IL_0067
IL_004c: ldloc.0
IL_004d: ldc.i4.s -4
IL_004f: beq.s IL_005d
IL_0051: leave.s IL_0078
IL_0053: ldloc.0
IL_0054: ldc.i4.3
IL_0055: beq.s IL_005d
IL_0057: ldloc.0
IL_0058: ldc.i4.5
IL_0059: beq.s IL_0067
IL_005b: leave.s IL_0078
IL_005d: nop
IL_0015: ldloc.0
IL_0016: ldc.i4.s -4
IL_0018: bgt.s IL_0026
IL_001a: ldloc.0
IL_001b: ldc.i4.s -5
IL_001d: beq.s IL_003a
IL_001f: ldloc.0
IL_0020: ldc.i4.s -4
IL_0022: beq.s IL_0030
IL_0024: leave.s IL_004b
IL_0026: ldloc.0
IL_0027: ldc.i4.3
IL_0028: beq.s IL_0030
IL_002a: ldloc.0
IL_002b: ldc.i4.5
IL_002c: beq.s IL_003a
IL_002e: leave.s IL_004b
IL_0030: nop
.try
{
IL_005e: leave.s IL_0078
IL_0031: leave.s IL_004b
}
finally
{
IL_0060: ldarg.0
IL_0061: call ""void Program.<Int0>d__0.<>m__Finally2()""
IL_0066: endfinally
IL_0033: ldarg.0
IL_0034: call ""void Program.<Int0>d__0.<>m__Finally2()""
IL_0039: endfinally
}
IL_0067: nop
IL_003a: nop
.try
{
IL_0068: leave.s IL_0078
IL_003b: leave.s IL_004b
}
finally
{
IL_006a: ldarg.0
IL_006b: call ""void Program.<Int0>d__0.<>m__Finally3()""
IL_0070: endfinally
IL_003d: ldarg.0
IL_003e: call ""void Program.<Int0>d__0.<>m__Finally3()""
IL_0043: endfinally
}
}
finally
{
IL_0071: ldarg.0
IL_0072: call ""void Program.<Int0>d__0.<>m__Finally1()""
IL_0077: endfinally
IL_0044: ldarg.0
IL_0045: call ""void Program.<Int0>d__0.<>m__Finally1()""
IL_004a: endfinally
}
IL_0078: ret
IL_004b: ret
}
");
}
......
......@@ -12,18 +12,49 @@ internal partial struct SwitchIntegralJumpTableEmitter
{
private struct SwitchBucket
{
// sorted case labels
private readonly ImmutableArray<KeyValuePair<ConstantValue, object>> _allLabels;
// range of sorted case labels within this bucket
private readonly int _startLabelIndex;
private readonly int _endLabelIndex;
// sorted case labels
private readonly ImmutableArray<KeyValuePair<ConstantValue, object>> _allLabels;
private readonly bool _isKnownDegenerate;
/// <summary>
/// Degenerate buckets here are buckets with contiguous range of constants
/// leading to the same label. Like:
///
/// case 0:
/// case 1:
/// case 2:
/// case 3:
/// DoOneThing();
/// break;
///
/// case 4:
/// case 5:
/// case 6:
/// case 7:
/// DoAnotherThing();
/// break;
///
/// NOTE: A trivial bucket with only one case constant is by definition degenerate.
/// </summary>
internal bool IsDegenerate
{
get
{
return _isKnownDegenerate;
}
}
internal SwitchBucket(ImmutableArray<KeyValuePair<ConstantValue, object>> allLabels, int index)
{
_startLabelIndex = index;
_endLabelIndex = index;
_allLabels = allLabels;
_isKnownDegenerate = true;
}
private SwitchBucket(ImmutableArray<KeyValuePair<ConstantValue, object>> allLabels, int startIndex, int endIndex)
......@@ -33,6 +64,18 @@ private SwitchBucket(ImmutableArray<KeyValuePair<ConstantValue, object>> allLabe
_startLabelIndex = startIndex;
_endLabelIndex = endIndex;
_allLabels = allLabels;
_isKnownDegenerate = false;
}
internal SwitchBucket(ImmutableArray<KeyValuePair<ConstantValue, object>> allLabels, int startIndex, int endIndex, bool isDegenerate)
{
Debug.Assert((uint)startIndex <= (uint)endIndex);
Debug.Assert((uint)startIndex != (uint)endIndex || isDegenerate);
_startLabelIndex = startIndex;
_endLabelIndex = endIndex;
_allLabels = allLabels;
_isKnownDegenerate = isDegenerate;
}
internal uint LabelsCount
......@@ -60,26 +103,77 @@ internal ulong BucketSize
}
}
// Relative cost of the bucket
// roughly proportional to the number of compares it needs in the success case.
internal int BucketCost
// if a bucket could be split into two degenerate ones
// specifies a label index where the second bucket would start
// -1 indicates that the bucket cannot be split into degenerate ones
// 0 indicates that the bucket is already degenerate
//
// Code Review question: why are we supporting splitting only in two buckets. Why not in more?
// Explanation:
// The input here is a "dense" bucket - the one that previous heuristics
// determined as not worth splitting.
//
// A dense bucket has rough execution cost of 1 conditional branch (range check)
// and 1 computed branch (which cost roughly the same as conditional one or perhaps more).
// The only way to surely beat that cost via splitting is if the bucket can be
// split into 2 degenerate buckets. Then we have just 2 conditional branches.
//
// 3 degenerate buckets would require up to 3 conditional branches.
// On some hardware computed jumps may cost significantly more than
// conditional ones (because they are harder to predict or whatever),
// so it could still be profitable, but I did not want to guess that.
//
// Basically if we have 3 degenerate buckets that can be merged into a dense bucket,
// we prefer a dense bucket, which we emit as "switch" opcode.
//
internal int DegenerateBucketSplit
{
get
{
if (_startLabelIndex == _endLabelIndex)
if (IsDegenerate)
{
return 0;
}
Debug.Assert(_startLabelIndex != _endLabelIndex, "1-sized buckets should be already known as degenerate.");
var allLabels = this._allLabels;
var split = 0;
var lastConst = this.StartConstant;
var lastLabel = allLabels[_startLabelIndex].Value;
for(int idx = _startLabelIndex + 1; idx <= _endLabelIndex; idx++)
{
// single element bucket needs exactly one compare
return 1;
var switchLabel = allLabels[idx];
if (lastLabel != switchLabel.Value ||
!IsContiguous(lastConst, switchLabel.Key))
{
if (split != 0)
{
// found another discontinuity, so cannot be split
return -1;
}
split = idx;
lastLabel = switchLabel.Value;
}
lastConst = switchLabel.Key;
}
// dense switch will perform two branches (range check and the actual computed jump)
// computed jump is more expensive than a regular conditional branch
// based on benchmarks the combined cost seems to be closer to 3
//
// this also allows in the "mostly sparse" scenario to avoid numerous
// little switches with only 2 labels in them.
return 3;
return split;
}
}
private bool IsContiguous(ConstantValue lastConst, ConstantValue nextConst)
{
if (!lastConst.IsNumeric || !nextConst.IsNumeric)
{
return false;
}
return GetBucketSize(lastConst, nextConst) == 2;
}
private static ulong GetBucketSize(ConstantValue startConstant, ConstantValue endConstant)
......
......@@ -217,22 +217,28 @@ private ImmutableArray<SwitchBucket> GenerateSwitchBuckets(int startLabelIndex,
Debug.Assert(!switchBucketsStack.IsEmpty());
// crumble leaf buckets that are too small
// crumble leaf buckets into degenerate buckets where possible
var crumbled = ArrayBuilder<SwitchBucket>.GetInstance();
foreach (var uncrumbled in switchBucketsStack)
{
if (uncrumbled.BucketCost > uncrumbled.LabelsCount)
var degenerateSplit = uncrumbled.DegenerateBucketSplit;
switch (degenerateSplit)
{
// this bucket is no better than testing each label individually.
// we do not want to keep it.
for (int i = uncrumbled.StartLabelIndex, l = uncrumbled.EndLabelIndex; i <= l; i++)
{
crumbled.Add(new SwitchBucket(_sortedCaseLabels, i));
}
}
else
{
crumbled.Add(uncrumbled);
case -1:
// cannot be split
crumbled.Add(uncrumbled);
break;
case 0:
// already degenerate
crumbled.Add(new SwitchBucket(_sortedCaseLabels, uncrumbled.StartLabelIndex, uncrumbled.EndLabelIndex, isDegenerate: true));
break;
default:
// can split
crumbled.Add(new SwitchBucket(_sortedCaseLabels, uncrumbled.StartLabelIndex, degenerateSplit - 1, isDegenerate: true));
crumbled.Add(new SwitchBucket(_sortedCaseLabels, degenerateSplit, uncrumbled.EndLabelIndex, isDegenerate: true));
break;
}
}
......@@ -322,19 +328,25 @@ private void EmitSwitchBucket(SwitchBucket switchBucket, object bucketFallThroug
}
else
{
// Emit key normalized to startConstant (i.e. key - startConstant)
// switch (N, label1, label2... labelN)
// goto fallThroughLabel;
this.EmitNormalizedSwitchKey(switchBucket.StartConstant, switchBucket.EndConstant, bucketFallThroughLabel);
if (switchBucket.IsDegenerate)
{
EmitRangeCheckedBranch(switchBucket.StartConstant, switchBucket.EndConstant, switchBucket[0].Value);
}
else
{
// Emit key normalized to startConstant (i.e. key - startConstant)
this.EmitNormalizedSwitchKey(switchBucket.StartConstant, switchBucket.EndConstant, bucketFallThroughLabel);
// Create the labels array for emitting a switch instruction for the bucket
object[] labels = this.CreateBucketLabels(switchBucket);
// Create the labels array for emitting a switch instruction for the bucket
object[] labels = this.CreateBucketLabels(switchBucket);
// Emit the switch instruction
_builder.EmitSwitch(labels);
// switch (N, label1, label2... labelN)
// Emit the switch instruction
_builder.EmitSwitch(labels);
}
}
// goto fallThroughLabel;
_builder.EmitBranch(ILOpCode.Br, bucketFallThroughLabel);
}
......@@ -434,6 +446,32 @@ private void EmitEqBranchForSwitch(ConstantValue constant, object targetLabel)
}
}
private void EmitRangeCheckedBranch(ConstantValue startConstant, ConstantValue endConstant, object targetLabel)
{
_builder.EmitLoad(_key);
// Normalize the key to 0 if needed
// Emit: ldc constant
// sub
if (!startConstant.IsDefaultValue)
{
_builder.EmitConstantValue(startConstant);
_builder.EmitOpCode(ILOpCode.Sub);
}
if (_keyTypeCode.Is64BitIntegral())
{
_builder.EmitLongConstant(endConstant.Int64Value - startConstant.Int64Value);
}
else
{
_builder.EmitIntConstant(endConstant.Int32Value - startConstant.Int32Value);
}
_builder.EmitBranch(ILOpCode.Ble_un, targetLabel, ILOpCode.Bgt_un);
}
private static ILOpCode GetReverseBranchCode(ILOpCode branchCode)
{
switch (branchCode)
......
......@@ -1625,7 +1625,7 @@ End Module
c.VerifyIL("Form1.VB$StateMachine_1_f.MoveNext", <![CDATA[
{
// Code size 236 (0xec)
// Code size 233 (0xe9)
.maxstack 3
.locals init (Integer V_0,
Integer V_1,
......@@ -1638,105 +1638,103 @@ End Module
.try
{
IL_0007: ldloc.1
IL_0008: brfalse.s IL_000e
IL_000a: ldloc.1
IL_000b: ldc.i4.1
IL_000c: pop
IL_000d: pop
IL_000e: nop
IL_0008: ldc.i4.1
IL_0009: pop
IL_000a: pop
IL_000b: nop
.try
{
IL_000c: ldloc.1
IL_000d: brfalse.s IL_0065
IL_000f: ldloc.1
IL_0010: brfalse.s IL_0068
IL_0012: ldloc.1
IL_0013: ldc.i4.1
IL_0014: bne.un.s IL_0024
IL_0016: ldarg.0
IL_0017: ldc.i4.m1
IL_0018: dup
IL_0019: stloc.1
IL_001a: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_001f: leave IL_00eb
IL_0024: ldstr "2 "
IL_0029: call "Sub System.Console.Write(String)"
IL_002e: call "Function System.Threading.Tasks.Task.Yield() As System.Runtime.CompilerServices.YieldAwaitable"
IL_0033: stloc.3
IL_0034: ldloca.s V_3
IL_0036: call "Function System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter() As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_003b: stloc.2
IL_003c: ldloca.s V_2
IL_003e: call "Function System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.get_IsCompleted() As Boolean"
IL_0043: brtrue.s IL_0084
IL_0045: ldarg.0
IL_0046: ldc.i4.0
IL_0047: dup
IL_0048: stloc.1
IL_0049: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_004e: ldarg.0
IL_004f: ldloc.2
IL_0050: stfld "Form1.VB$StateMachine_1_f.$A0 As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0055: ldarg.0
IL_0056: ldflda "Form1.VB$StateMachine_1_f.$Builder As System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer)"
IL_005b: ldloca.s V_2
IL_005d: ldarg.0
IL_005e: call "Sub System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer).AwaitUnsafeOnCompleted(Of System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Form1.VB$StateMachine_1_f)(ByRef System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ByRef Form1.VB$StateMachine_1_f)"
IL_0063: leave IL_00eb
IL_0068: ldarg.0
IL_0069: ldc.i4.m1
IL_006a: dup
IL_006b: stloc.1
IL_006c: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_0071: ldarg.0
IL_0072: ldfld "Form1.VB$StateMachine_1_f.$A0 As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0077: stloc.2
IL_0078: ldarg.0
IL_0079: ldflda "Form1.VB$StateMachine_1_f.$A0 As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_007e: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0084: ldloca.s V_2
IL_0086: call "Sub System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"
IL_008b: ldloca.s V_2
IL_008d: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0093: ldstr "3 "
IL_0098: call "Sub System.Console.Write(String)"
IL_009d: ldc.i4.s 123
IL_009f: stloc.0
IL_00a0: leave.s IL_00d5
IL_0010: ldc.i4.1
IL_0011: bne.un.s IL_0021
IL_0013: ldarg.0
IL_0014: ldc.i4.m1
IL_0015: dup
IL_0016: stloc.1
IL_0017: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_001c: leave IL_00e8
IL_0021: ldstr "2 "
IL_0026: call "Sub System.Console.Write(String)"
IL_002b: call "Function System.Threading.Tasks.Task.Yield() As System.Runtime.CompilerServices.YieldAwaitable"
IL_0030: stloc.3
IL_0031: ldloca.s V_3
IL_0033: call "Function System.Runtime.CompilerServices.YieldAwaitable.GetAwaiter() As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0038: stloc.2
IL_0039: ldloca.s V_2
IL_003b: call "Function System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.get_IsCompleted() As Boolean"
IL_0040: brtrue.s IL_0081
IL_0042: ldarg.0
IL_0043: ldc.i4.0
IL_0044: dup
IL_0045: stloc.1
IL_0046: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_004b: ldarg.0
IL_004c: ldloc.2
IL_004d: stfld "Form1.VB$StateMachine_1_f.$A0 As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0052: ldarg.0
IL_0053: ldflda "Form1.VB$StateMachine_1_f.$Builder As System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer)"
IL_0058: ldloca.s V_2
IL_005a: ldarg.0
IL_005b: call "Sub System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer).AwaitUnsafeOnCompleted(Of System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, Form1.VB$StateMachine_1_f)(ByRef System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter, ByRef Form1.VB$StateMachine_1_f)"
IL_0060: leave IL_00e8
IL_0065: ldarg.0
IL_0066: ldc.i4.m1
IL_0067: dup
IL_0068: stloc.1
IL_0069: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_006e: ldarg.0
IL_006f: ldfld "Form1.VB$StateMachine_1_f.$A0 As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0074: stloc.2
IL_0075: ldarg.0
IL_0076: ldflda "Form1.VB$StateMachine_1_f.$A0 As System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_007b: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0081: ldloca.s V_2
IL_0083: call "Sub System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter.GetResult()"
IL_0088: ldloca.s V_2
IL_008a: initobj "System.Runtime.CompilerServices.YieldAwaitable.YieldAwaiter"
IL_0090: ldstr "3 "
IL_0095: call "Sub System.Console.Write(String)"
IL_009a: ldc.i4.s 123
IL_009c: stloc.0
IL_009d: leave.s IL_00d2
}
finally
{
IL_00a2: ldloc.1
IL_00a3: ldc.i4.0
IL_00a4: bge.s IL_00b0
IL_00a6: ldstr "4 "
IL_00ab: call "Sub System.Console.Write(String)"
IL_00b0: endfinally
IL_009f: ldloc.1
IL_00a0: ldc.i4.0
IL_00a1: bge.s IL_00ad
IL_00a3: ldstr "4 "
IL_00a8: call "Sub System.Console.Write(String)"
IL_00ad: endfinally
}
}
catch System.Exception
{
IL_00b1: dup
IL_00b2: call "Sub Microsoft.VisualBasic.CompilerServices.ProjectData.SetProjectError(System.Exception)"
IL_00b7: stloc.s V_4
IL_00b9: ldarg.0
IL_00ba: ldc.i4.s -2
IL_00bc: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_00c1: ldarg.0
IL_00c2: ldflda "Form1.VB$StateMachine_1_f.$Builder As System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer)"
IL_00c7: ldloc.s V_4
IL_00c9: call "Sub System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer).SetException(System.Exception)"
IL_00ce: call "Sub Microsoft.VisualBasic.CompilerServices.ProjectData.ClearProjectError()"
IL_00d3: leave.s IL_00eb
IL_00ae: dup
IL_00af: call "Sub Microsoft.VisualBasic.CompilerServices.ProjectData.SetProjectError(System.Exception)"
IL_00b4: stloc.s V_4
IL_00b6: ldarg.0
IL_00b7: ldc.i4.s -2
IL_00b9: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_00be: ldarg.0
IL_00bf: ldflda "Form1.VB$StateMachine_1_f.$Builder As System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer)"
IL_00c4: ldloc.s V_4
IL_00c6: call "Sub System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer).SetException(System.Exception)"
IL_00cb: call "Sub Microsoft.VisualBasic.CompilerServices.ProjectData.ClearProjectError()"
IL_00d0: leave.s IL_00e8
}
IL_00d5: ldarg.0
IL_00d6: ldc.i4.s -2
IL_00d8: dup
IL_00d9: stloc.1
IL_00da: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_00df: ldarg.0
IL_00e0: ldflda "Form1.VB$StateMachine_1_f.$Builder As System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer)"
IL_00e5: ldloc.0
IL_00e6: call "Sub System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer).SetResult(Integer)"
IL_00eb: ret
IL_00d2: ldarg.0
IL_00d3: ldc.i4.s -2
IL_00d5: dup
IL_00d6: stloc.1
IL_00d7: stfld "Form1.VB$StateMachine_1_f.$State As Integer"
IL_00dc: ldarg.0
IL_00dd: ldflda "Form1.VB$StateMachine_1_f.$Builder As System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer)"
IL_00e2: ldloc.0
IL_00e3: call "Sub System.Runtime.CompilerServices.AsyncTaskMethodBuilder(Of Integer).SetResult(Integer)"
IL_00e8: ret
}
]]>)
End Sub
......
......@@ -628,7 +628,7 @@ End Module
CompileAndVerify(source, expectedOutput:="12345").VerifyIL("Module1.VB$StateMachine_1_Foo.MoveNext", <![CDATA[
{
// Code size 161 (0xa1)
// Code size 152 (0x98)
.maxstack 3
.locals init (Boolean V_0,
Integer V_1,
......@@ -637,84 +637,85 @@ End Module
IL_0001: ldfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: switch (
IL_001b,
IL_0024,
IL_0024)
IL_0019: ldc.i4.0
IL_001a: ret
IL_001b: ldarg.0
IL_001c: ldc.i4.m1
IL_001d: dup
IL_001e: stloc.1
IL_001f: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0024: nop
IL_0008: brfalse.s IL_0012
IL_000a: ldloc.1
IL_000b: ldc.i4.1
IL_000c: sub
IL_000d: ldc.i4.1
IL_000e: ble.un.s IL_001b
IL_0010: ldc.i4.0
IL_0011: ret
IL_0012: ldarg.0
IL_0013: ldc.i4.m1
IL_0014: dup
IL_0015: stloc.1
IL_0016: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_001b: nop
.try
{
IL_0025: ldloc.1
IL_0026: ldc.i4.1
IL_0027: beq.s IL_006d
IL_0029: ldloc.1
IL_002a: ldc.i4.2
IL_002b: bne.un.s IL_003a
IL_002d: ldarg.0
IL_002e: ldc.i4.m1
IL_002f: dup
IL_0030: stloc.1
IL_0031: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0036: ldc.i4.1
IL_0037: stloc.0
IL_0038: leave.s IL_009f
IL_003a: ldarg.0
IL_003b: ldarg.0
IL_003c: ldfld "Module1.VB$StateMachine_1_Foo.$VB$Local_x As System.Collections.Generic.IEnumerable(Of Integer)"
IL_0041: callvirt "Function System.Collections.Generic.IEnumerable(Of Integer).GetEnumerator() As System.Collections.Generic.IEnumerator(Of Integer)"
IL_0046: stfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_004b: br.s IL_0076
IL_004d: ldarg.0
IL_004e: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_0053: callvirt "Function System.Collections.Generic.IEnumerator(Of Integer).get_Current() As Integer"
IL_0058: stloc.2
IL_0059: ldarg.0
IL_005a: ldloc.2
IL_005b: stfld "Module1.VB$StateMachine_1_Foo.$Current As Integer"
IL_0060: ldarg.0
IL_0061: ldc.i4.1
IL_0062: dup
IL_0063: stloc.1
IL_0064: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0069: ldc.i4.1
IL_006a: stloc.0
IL_006b: leave.s IL_009f
IL_001c: ldloc.1
IL_001d: ldc.i4.1
IL_001e: beq.s IL_0064
IL_0020: ldloc.1
IL_0021: ldc.i4.2
IL_0022: bne.un.s IL_0031
IL_0024: ldarg.0
IL_0025: ldc.i4.m1
IL_0026: dup
IL_0027: stloc.1
IL_0028: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_002d: ldc.i4.1
IL_002e: stloc.0
IL_002f: leave.s IL_0096
IL_0031: ldarg.0
IL_0032: ldarg.0
IL_0033: ldfld "Module1.VB$StateMachine_1_Foo.$VB$Local_x As System.Collections.Generic.IEnumerable(Of Integer)"
IL_0038: callvirt "Function System.Collections.Generic.IEnumerable(Of Integer).GetEnumerator() As System.Collections.Generic.IEnumerator(Of Integer)"
IL_003d: stfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_0042: br.s IL_006d
IL_0044: ldarg.0
IL_0045: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_004a: callvirt "Function System.Collections.Generic.IEnumerator(Of Integer).get_Current() As Integer"
IL_004f: stloc.2
IL_0050: ldarg.0
IL_0051: ldloc.2
IL_0052: stfld "Module1.VB$StateMachine_1_Foo.$Current As Integer"
IL_0057: ldarg.0
IL_0058: ldc.i4.1
IL_0059: dup
IL_005a: stloc.1
IL_005b: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0060: ldc.i4.1
IL_0061: stloc.0
IL_0062: leave.s IL_0096
IL_0064: ldarg.0
IL_0065: ldc.i4.m1
IL_0066: dup
IL_0067: stloc.1
IL_0068: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_006d: ldarg.0
IL_006e: ldc.i4.m1
IL_006f: dup
IL_0070: stloc.1
IL_0071: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0076: ldarg.0
IL_0077: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_007c: callvirt "Function System.Collections.IEnumerator.MoveNext() As Boolean"
IL_0081: brtrue.s IL_004d
IL_0083: leave.s IL_009d
IL_006e: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_0073: callvirt "Function System.Collections.IEnumerator.MoveNext() As Boolean"
IL_0078: brtrue.s IL_0044
IL_007a: leave.s IL_0094
}
finally
{
IL_0085: ldloc.1
IL_0086: ldc.i4.0
IL_0087: bge.s IL_009c
IL_0089: ldarg.0
IL_008a: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_008f: brfalse.s IL_009c
IL_0091: ldarg.0
IL_0092: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_0097: callvirt "Sub System.IDisposable.Dispose()"
IL_009c: endfinally
IL_007c: ldloc.1
IL_007d: ldc.i4.0
IL_007e: bge.s IL_0093
IL_0080: ldarg.0
IL_0081: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_0086: brfalse.s IL_0093
IL_0088: ldarg.0
IL_0089: ldfld "Module1.VB$StateMachine_1_Foo.$S0 As System.Collections.Generic.IEnumerator(Of Integer)"
IL_008e: callvirt "Sub System.IDisposable.Dispose()"
IL_0093: endfinally
}
IL_009d: ldc.i4.0
IL_009e: ret
IL_009f: ldloc.0
IL_00a0: ret
IL_0094: ldc.i4.0
IL_0095: ret
IL_0096: ldloc.0
IL_0097: ret
}
]]>).VerifyIL("Module1.VB$StateMachine_1_Foo.IEnumerable.GetEnumerator", <![CDATA[
{
......@@ -858,7 +859,7 @@ End Module
CompileAndVerify(source, expectedOutput:="233").VerifyIL("Module1.VB$StateMachine_1_Foo.MoveNext", <![CDATA[
{
// Code size 168 (0xa8)
// Code size 159 (0x9f)
.maxstack 3
.locals init (Boolean V_0,
Integer V_1,
......@@ -867,88 +868,90 @@ End Module
IL_0001: ldfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: switch (
IL_001b,
IL_002b,
IL_002b)
IL_0019: ldc.i4.0
IL_001a: ret
IL_0008: brfalse.s IL_0012
IL_000a: ldloc.1
IL_000b: ldc.i4.1
IL_000c: sub
IL_000d: ldc.i4.1
IL_000e: ble.un.s IL_0022
IL_0010: ldc.i4.0
IL_0011: ret
IL_0012: ldarg.0
IL_0013: ldc.i4.m1
IL_0014: dup
IL_0015: stloc.1
IL_0016: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_001b: ldarg.0
IL_001c: ldc.i4.m1
IL_001d: dup
IL_001e: stloc.1
IL_001f: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0024: ldarg.0
IL_0025: ldc.i4.1
IL_0026: stfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_002b: nop
IL_001c: ldc.i4.1
IL_001d: stfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0022: nop
.try
{
IL_002c: ldloc.1
IL_002d: ldc.i4.1
IL_002e: beq.s IL_0068
IL_0030: ldloc.1
IL_0031: ldc.i4.2
IL_0032: bne.un.s IL_0041
IL_0034: ldarg.0
IL_0035: ldc.i4.m1
IL_0036: dup
IL_0037: stloc.1
IL_0038: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_003d: ldc.i4.1
IL_003e: stloc.0
IL_003f: leave.s IL_00a6
IL_0041: ldarg.0
IL_0042: ldarg.0
IL_0043: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0048: ldc.i4.1
IL_0049: add.ovf
IL_004a: stfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_004f: ldarg.0
IL_0050: ldarg.0
IL_0051: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0056: stfld "Module1.VB$StateMachine_1_Foo.$Current As Integer"
IL_005b: ldarg.0
IL_005c: ldc.i4.1
IL_005d: dup
IL_005e: stloc.1
IL_005f: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0064: ldc.i4.1
IL_0065: stloc.0
IL_0066: leave.s IL_00a6
IL_0068: ldarg.0
IL_0069: ldc.i4.m1
IL_006a: dup
IL_006b: stloc.1
IL_006c: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0071: leave.s IL_00a4
IL_0023: ldloc.1
IL_0024: ldc.i4.1
IL_0025: beq.s IL_005f
IL_0027: ldloc.1
IL_0028: ldc.i4.2
IL_0029: bne.un.s IL_0038
IL_002b: ldarg.0
IL_002c: ldc.i4.m1
IL_002d: dup
IL_002e: stloc.1
IL_002f: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0034: ldc.i4.1
IL_0035: stloc.0
IL_0036: leave.s IL_009d
IL_0038: ldarg.0
IL_0039: ldarg.0
IL_003a: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_003f: ldc.i4.1
IL_0040: add.ovf
IL_0041: stfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0046: ldarg.0
IL_0047: ldarg.0
IL_0048: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_004d: stfld "Module1.VB$StateMachine_1_Foo.$Current As Integer"
IL_0052: ldarg.0
IL_0053: ldc.i4.1
IL_0054: dup
IL_0055: stloc.1
IL_0056: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_005b: ldc.i4.1
IL_005c: stloc.0
IL_005d: leave.s IL_009d
IL_005f: ldarg.0
IL_0060: ldc.i4.m1
IL_0061: dup
IL_0062: stloc.1
IL_0063: stfld "Module1.VB$StateMachine_1_Foo.$State As Integer"
IL_0068: leave.s IL_009b
}
finally
{
IL_0073: ldloc.1
IL_0074: ldc.i4.0
IL_0075: bge.s IL_00a3
IL_0077: ldarg.0
IL_0078: ldarg.0
IL_0079: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_007e: ldc.i4.1
IL_007f: add.ovf
IL_0080: stfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0085: ldarg.0
IL_0086: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_008b: stloc.2
IL_008c: ldarg.0
IL_008d: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0092: call "Sub System.Console.Write(Integer)"
IL_0097: ldloca.s V_2
IL_0099: call "Function Integer.ToString() As String"
IL_009e: call "Sub System.Console.Write(String)"
IL_00a3: endfinally
IL_006a: ldloc.1
IL_006b: ldc.i4.0
IL_006c: bge.s IL_009a
IL_006e: ldarg.0
IL_006f: ldarg.0
IL_0070: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0075: ldc.i4.1
IL_0076: add.ovf
IL_0077: stfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_007c: ldarg.0
IL_007d: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0082: stloc.2
IL_0083: ldarg.0
IL_0084: ldfld "Module1.VB$StateMachine_1_Foo.$VB$ResumableLocal_x$0 As Integer"
IL_0089: call "Sub System.Console.Write(Integer)"
IL_008e: ldloca.s V_2
IL_0090: call "Function Integer.ToString() As String"
IL_0095: call "Sub System.Console.Write(String)"
IL_009a: endfinally
}
IL_00a4: ldc.i4.0
IL_00a5: ret
IL_00a6: ldloc.0
IL_00a7: ret
IL_009b: ldc.i4.0
IL_009c: ret
IL_009d: ldloc.0
IL_009e: ret
}
]]>).VerifyIL("Module1.VB$StateMachine_1_Foo.IEnumerable.GetEnumerator", <![CDATA[
{
......
......@@ -1411,22 +1411,20 @@ End Module
]]></file>
</compilation>, expectedOutput:="Success").VerifyIL("M1.Main", <![CDATA[
{
// Code size 31 (0x1f)
// Code size 28 (0x1c)
.maxstack 2
.locals init (Integer V_0)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: brfalse.s IL_0009
IL_0005: ldloc.0
IL_0006: ldc.i4.1
IL_0007: bne.un.s IL_0014
IL_0009: ldstr "Success"
IL_000e: call "Sub System.Console.WriteLine(String)"
IL_0013: ret
IL_0014: ldstr "Fail"
IL_0019: call "Sub System.Console.WriteLine(String)"
IL_001e: ret
IL_0003: ldc.i4.1
IL_0004: bgt.un.s IL_0011
IL_0006: ldstr "Success"
IL_000b: call "Sub System.Console.WriteLine(String)"
IL_0010: ret
IL_0011: ldstr "Fail"
IL_0016: call "Sub System.Console.WriteLine(String)"
IL_001b: ret
}
]]>)
VerifySynthesizedStringHashMethod(compVerifier, expected:=False)
......@@ -1453,26 +1451,27 @@ End Module
]]></file>
</compilation>, expectedOutput:="Success").VerifyIL("M1.Main", <![CDATA[
{
// Code size 55 (0x37)
.maxstack 1
// Code size 44 (0x2c)
.maxstack 2
.locals init (Integer V_0)
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: switch (
IL_0021,
IL_0016,
IL_0016)
IL_0014: br.s IL_002c
IL_0016: ldstr "Fail"
IL_0003: brfalse.s IL_0016
IL_0005: ldloc.0
IL_0006: ldc.i4.1
IL_0007: sub
IL_0008: ldc.i4.1
IL_0009: bgt.un.s IL_0021
IL_000b: ldstr "Fail"
IL_0010: call "Sub System.Console.WriteLine(String)"
IL_0015: ret
IL_0016: ldstr "Success"
IL_001b: call "Sub System.Console.WriteLine(String)"
IL_0020: ret
IL_0021: ldstr "Success"
IL_0021: ldstr "Fail"
IL_0026: call "Sub System.Console.WriteLine(String)"
IL_002b: ret
IL_002c: ldstr "Fail"
IL_0031: call "Sub System.Console.WriteLine(String)"
IL_0036: ret
}
]]>)
VerifySynthesizedStringHashMethod(compVerifier, expected:=False)
......@@ -1499,7 +1498,7 @@ End Module
]]></file>
</compilation>, expectedOutput:="Success").VerifyIL("M1.Main", <![CDATA[
{
// Code size 61 (0x3d)
// Code size 49 (0x31)
.maxstack 2
.locals init (Integer V_0)
IL_0000: ldc.i4.0
......@@ -1507,21 +1506,23 @@ End Module
IL_0002: ldloc.0
IL_0003: ldc.i4.1
IL_0004: sub
IL_0005: switch (
IL_001c,
IL_001c,
IL_0027,
IL_0027)
IL_001a: br.s IL_0032
IL_001c: ldstr "Fail"
IL_0021: call "Sub System.Console.WriteLine(String)"
IL_0026: ret
IL_0027: ldstr "Fail"
IL_002c: call "Sub System.Console.WriteLine(String)"
IL_0031: ret
IL_0032: ldstr "Success"
IL_0037: call "Sub System.Console.WriteLine(String)"
IL_003c: ret
IL_0005: ldc.i4.1
IL_0006: ble.un.s IL_0010
IL_0008: ldloc.0
IL_0009: ldc.i4.3
IL_000a: sub
IL_000b: ldc.i4.1
IL_000c: ble.un.s IL_001b
IL_000e: br.s IL_0026
IL_0010: ldstr "Fail"
IL_0015: call "Sub System.Console.WriteLine(String)"
IL_001a: ret
IL_001b: ldstr "Fail"
IL_0020: call "Sub System.Console.WriteLine(String)"
IL_0025: ret
IL_0026: ldstr "Success"
IL_002b: call "Sub System.Console.WriteLine(String)"
IL_0030: ret
}
]]>)
VerifySynthesizedStringHashMethod(compVerifier, expected:=False)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册