提交 2021715c 编写于 作者: N Neal Gafter

Optimize a type test followed by a cast, a common situation in pattern-matching.

Fixes #20641
上级 dceb3ba2
......@@ -142,47 +142,114 @@ private void LowerDecisionDag(ImmutableArray<BoundDecisionDag> sortedNodes)
break;
}
// Call LowerDecisionDagNode with each node and its following node in the generation order.
// However, some nodes can be emitted more efficiently as a switch dispatch (possibly out
// of order).
var loweredNodes = PooledHashSet<BoundDecisionDag>.GetInstance();
BoundDecisionDag previous = null;
// Code for the when clause goes in a separate code section for the switch section.
foreach (BoundDecisionDag node in sortedNodes)
{
if (node is BoundWhenClause w)
{
// Code for the when clause goes in a separate code section for the switch section.
LowerWhenClause(w);
}
}
ImmutableArray<BoundDecisionDag> nodesToLower = sortedNodes.WhereAsArray(n => n.Kind != BoundKind.WhenClause && n.Kind != BoundKind.Decision);
var loweredNodes = PooledHashSet<BoundDecisionDag>.GetInstance();
for (int i = 0, length = nodesToLower.Length; i < length; i++)
{
BoundDecisionDag node = nodesToLower[i];
if (loweredNodes.Contains(node))
{
continue;
}
// BoundDecision nodes do not get any code generated for them.
if (node is BoundDecision || loweredNodes.Contains(node))
if (this._dagNodeLabels.TryGetValue(node, out LabelSymbol label))
{
_loweredDecisionDag.Add(_factory.Label(label));
}
// If we can generate an IL switch instruction, do so
if (GenerateSwitchDispatch(node, loweredNodes))
{
continue;
}
if (previous != null && !loweredNodes.Contains(previous))
// If we can generate a type test and cast more efficiently as an `is` followed by a null check, do so
if (GenerateTypeTestAndCast(node, loweredNodes, nodesToLower, i))
{
// Lower the node "previous".
if (!GenerateSwitchDispatch(previous, loweredNodes))
{
LowerDecisionDagNode(previous, node);
}
continue;
}
loweredNodes.Add(previous);
// We pass the node that will follow so we can permit a test to fall through if appropriate
BoundDecisionDag nextNode = ((i + 1) < length) ? nodesToLower[i + 1] : null;
if (nextNode != null && loweredNodes.Contains(nextNode))
{
nextNode = null;
}
previous = node;
LowerDecisionDagNode(node, nextNode);
}
// Lower the final node
if (previous != null && !loweredNodes.Contains(previous))
loweredNodes.Free();
}
/// <summary>
/// If we have a type decision followed by a cast to that type, and the types are reference types,
/// then we can replace the pair of them by a conversion using `as` and a null check.
/// </summary>
/// <returns>true if we generated code for the decision</returns>
private bool GenerateTypeTestAndCast(
BoundDecisionDag node,
HashSet<BoundDecisionDag> loweredNodes,
ImmutableArray<BoundDecisionDag> nodesToLower,
int indexOfNode)
{
Debug.Assert(node == nodesToLower[indexOfNode]);
if (node is BoundDecisionPoint decisionPoint &&
decisionPoint.WhenTrue is BoundEvaluationPoint evaluationPoint &&
// Even if there are other entries to the evaluation point, we need not use it here
// !this._dagNodeLabels.ContainsKey(evaluationPoint) &&
TryLowerTypeTestAndCast(decisionPoint.Decision, evaluationPoint.Evaluation, out BoundExpression sideEffect, out BoundExpression test)
)
{
LowerDecisionDagNode(previous, null);
var whenTrue = evaluationPoint.Next;
var whenFalse = decisionPoint.WhenFalse;
if (!this._dagNodeLabels.ContainsKey(evaluationPoint))
{
loweredNodes.Add(evaluationPoint);
}
var nextNode =
(indexOfNode + 2 < nodesToLower.Length) &&
nodesToLower[indexOfNode + 1] == evaluationPoint &&
!loweredNodes.Contains(nodesToLower[indexOfNode + 2]) ? nodesToLower[indexOfNode + 2] : null;
_loweredDecisionDag.Add(_factory.ExpressionStatement(sideEffect));
GenerateTest(test, whenTrue, whenFalse, nextNode);
return true;
}
loweredNodes.Free();
return false;
}
private void GenerateTest(BoundExpression test, BoundDecisionDag whenTrue, BoundDecisionDag whenFalse, BoundDecisionDag nextNode)
{
// Because we have already "optimized" away tests for a constant switch expression, the decision should be nontrivial.
Debug.Assert(test != null);
if (nextNode == whenFalse)
{
_loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(whenTrue), jumpIfTrue: true));
// fall through to false decision
}
else if (nextNode == whenTrue)
{
_loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(whenFalse), jumpIfTrue: false));
// fall through to true decision
}
else
{
_loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(whenTrue), jumpIfTrue: true));
_loweredDecisionDag.Add(_factory.Goto(GetDagNodeLabel(whenFalse)));
}
}
/// <summary>
......@@ -358,11 +425,6 @@ private void LowerWhenClause(BoundWhenClause whenClause)
/// </summary>
private void LowerDecisionDagNode(BoundDecisionDag node, BoundDecisionDag nextNode)
{
if (this._dagNodeLabels.TryGetValue(node, out LabelSymbol label))
{
_loweredDecisionDag.Add(_factory.Label(label));
}
switch (node)
{
case BoundEvaluationPoint evaluationPoint:
......@@ -383,25 +445,7 @@ private void LowerDecisionDagNode(BoundDecisionDag node, BoundDecisionDag nextNo
{
// PROTOTYPE(patterns2): should translate a chain of constant value tests into a switch instruction as before
BoundExpression test = base.LowerDecision(decisionPoint.Decision);
// Because we have already "optimized" away tests for a constant switch expression, the decision should be nontrivial.
Debug.Assert(test != null);
if (nextNode == decisionPoint.WhenFalse)
{
_loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(decisionPoint.WhenTrue), jumpIfTrue: true));
// fall through to false decision
}
else if (nextNode == decisionPoint.WhenTrue)
{
_loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(decisionPoint.WhenFalse), jumpIfTrue: false));
// fall through to true decision
}
else
{
_loweredDecisionDag.Add(_factory.ConditionalGoto(test, GetDagNodeLabel(decisionPoint.WhenTrue), jumpIfTrue: true));
_loweredDecisionDag.Add(_factory.Goto(GetDagNodeLabel(decisionPoint.WhenFalse)));
}
GenerateTest(test, decisionPoint.WhenTrue, decisionPoint.WhenFalse, nextNode);
}
break;
......
......@@ -255,6 +255,42 @@ protected BoundExpression LowerDecision(BoundDagDecision decision)
throw ExceptionUtilities.UnexpectedValue(decision);
}
}
/// <summary>
/// Lower a decision followed by an evaluation into a side-effect followed by a test. This permits us to optimize
/// a type test followed by a cast into an `as` expression followed by a null check. Returns true if the optimization
/// applies and the results are placed into <paramref name="sideEffect"/> and <paramref name="test"/>. The caller
/// should place the side-effect before the test in the generated code.
/// </summary>
/// <param name="evaluation"></param>
/// <param name="decision"></param>
/// <param name="sideEffect"></param>
/// <param name="test"></param>
/// <returns>true if the optimization is applied</returns>
protected bool TryLowerTypeTestAndCast(
BoundDagDecision decision,
BoundDagEvaluation evaluation,
out BoundExpression sideEffect,
out BoundExpression test)
{
if (decision is BoundTypeDecision typeDecision &&
evaluation is BoundDagTypeEvaluation typeEvaluation &&
typeDecision.Type.IsReferenceType &&
typeDecision.Input.Type.IsReferenceType &&
typeEvaluation.Type == typeDecision.Type &&
typeEvaluation.Input == typeDecision.Input
)
{
BoundExpression input = _tempAllocator.GetTemp(decision.Input);
BoundExpression output = _tempAllocator.GetTemp(new BoundDagTemp(evaluation.Syntax, typeEvaluation.Type, evaluation, 0));
sideEffect = _factory.AssignmentExpression(output, _factory.As(input, typeEvaluation.Type));
test = _factory.ObjectNotEqual(output, _factory.Null(output.Type));
return true;
}
sideEffect = test = null;
return false;
}
}
private class IsPatternExpressionLocalRewriter : PatternLocalRewriter
......@@ -348,18 +384,32 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
}
// We follow the "good" path in the decision dag. We depend on it being nicely linear in structure.
// If we add "or" patterns that assumption breaks down.
while (dag.Kind != BoundKind.Decision && dag.Kind != BoundKind.WhenClause)
{
switch (dag)
{
case BoundEvaluationPoint e:
LowerOneDecision(e.Evaluation);
dag = e.Next;
{
LowerOneDecision(e.Evaluation);
dag = e.Next;
}
break;
case BoundDecisionPoint d:
LowerOneDecision(d.Decision);
Debug.Assert(d.WhenFalse is BoundDecision x && x.Label == failureLabel);
dag = d.WhenTrue;
{
Debug.Assert(d.WhenFalse is BoundDecision x && x.Label == failureLabel);
if (d.WhenTrue is BoundEvaluationPoint e && TryLowerTypeTestAndCast(d.Decision, e.Evaluation, out BoundExpression sideEffect, out BoundExpression test))
{
_sideEffectBuilder.Add(sideEffect);
AddConjunct(test);
dag = e.Next;
}
else
{
LowerOneDecision(d.Decision);
dag = d.WhenTrue;
}
}
break;
}
}
......@@ -422,6 +472,7 @@ public BoundExpression LowerIsPattern(BoundPattern pattern, CSharpCompilation co
}
}
private BoundExpression MakeEqual(BoundExpression loweredLiteral, BoundExpression input)
{
Debug.Assert(loweredLiteral.Type == input.Type);
......
......@@ -345,14 +345,14 @@ static void Main(string[] args)
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
compVerifier.VerifyIL("Program.Main",
@"{
// Code size 106 (0x6a)
// Code size 96 (0x60)
.maxstack 2
.locals init (Base<object> V_0, //x
Derived V_1, //y
Derived V_2,
Derived V_3, //z
Base<object> V_4,
Derived V_5,
Derived V_4,
Base<object> V_5,
Base<object> V_6)
IL_0000: nop
IL_0001: newobj ""Derived..ctor()""
......@@ -365,42 +365,40 @@ .maxstack 2
IL_0015: nop
IL_0016: ldloc.0
IL_0017: isinst ""Derived""
IL_001c: brfalse.s IL_002a
IL_001e: ldloc.0
IL_001f: castclass ""Derived""
IL_0024: stloc.2
IL_0025: ldloc.2
IL_0026: stloc.1
IL_0027: ldc.i4.1
IL_0028: br.s IL_002b
IL_002a: ldc.i4.0
IL_002b: call ""void System.Console.WriteLine(bool)""
IL_0030: nop
IL_0031: ldloc.0
IL_0032: stloc.s V_6
IL_0034: ldloc.s V_6
IL_0036: stloc.s V_4
IL_0038: ldloc.s V_4
IL_003a: isinst ""Derived""
IL_003f: brfalse.s IL_005a
IL_0041: ldloc.s V_4
IL_0043: castclass ""Derived""
IL_0048: stloc.s V_5
IL_004a: br.s IL_004c
IL_004c: ldloc.s V_5
IL_004e: stloc.3
IL_004f: br.s IL_0051
IL_0051: ldc.i4.1
IL_0052: call ""void System.Console.WriteLine(bool)""
IL_0057: nop
IL_0058: br.s IL_005a
IL_005a: ldloc.0
IL_005b: isinst ""Derived""
IL_0060: ldnull
IL_0061: cgt.un
IL_0063: call ""void System.Console.WriteLine(bool)""
IL_0068: nop
IL_0069: ret
IL_001c: stloc.2
IL_001d: ldloc.2
IL_001e: brfalse.s IL_0025
IL_0020: ldloc.2
IL_0021: stloc.1
IL_0022: ldc.i4.1
IL_0023: br.s IL_0026
IL_0025: ldc.i4.0
IL_0026: call ""void System.Console.WriteLine(bool)""
IL_002b: nop
IL_002c: ldloc.0
IL_002d: stloc.s V_6
IL_002f: ldloc.s V_6
IL_0031: stloc.s V_5
IL_0033: ldloc.s V_5
IL_0035: isinst ""Derived""
IL_003a: stloc.s V_4
IL_003c: ldloc.s V_4
IL_003e: brtrue.s IL_0042
IL_0040: br.s IL_0050
IL_0042: ldloc.s V_4
IL_0044: stloc.3
IL_0045: br.s IL_0047
IL_0047: ldc.i4.1
IL_0048: call ""void System.Console.WriteLine(bool)""
IL_004d: nop
IL_004e: br.s IL_0050
IL_0050: ldloc.0
IL_0051: isinst ""Derived""
IL_0056: ldnull
IL_0057: cgt.un
IL_0059: call ""void System.Console.WriteLine(bool)""
IL_005e: nop
IL_005f: ret
}");
}
......@@ -752,26 +750,22 @@ .locals init (bool? V_0)
}");
compVerifier.VerifyIL("C.M2",
@"{
// Code size 26 (0x1a)
// Code size 19 (0x13)
.maxstack 1
.locals init (object V_0,
string V_1)
.locals init (string V_0)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0003: isinst ""string""
IL_0008: brfalse.s IL_0018
IL_000a: ldloc.0
IL_000b: castclass ""string""
IL_0010: stloc.1
IL_0011: ldarg.1
IL_0012: brfalse.s IL_0016
IL_0014: ldc.i4.1
IL_0015: ret
IL_0016: ldc.i4.2
IL_0017: ret
IL_0018: ldc.i4.3
IL_0019: ret
IL_0001: isinst ""string""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0011
IL_000a: ldarg.1
IL_000b: brfalse.s IL_000f
IL_000d: ldc.i4.1
IL_000e: ret
IL_000f: ldc.i4.2
IL_0010: ret
IL_0011: ldc.i4.3
IL_0012: ret
}");
}
......@@ -825,42 +819,41 @@ public void SwitchBasedPatternMatching(object o)
var compVerifier = CompileAndVerify(compilation);
compVerifier.VerifyIL("C.SwitchBasedPatternMatching",
@"{
// Code size 77 (0x4d)
// Code size 71 (0x47)
.maxstack 2
.locals init (object V_0,
int V_1,
string V_2)
.locals init (int V_0,
string V_1,
object V_2)
IL_0000: ldarg.1
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0001: stloc.2
IL_0002: ldloc.2
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0013
IL_000a: ldloc.0
IL_000a: ldloc.2
IL_000b: unbox.any ""int""
IL_0010: stloc.1
IL_0011: br.s IL_0024
IL_0013: ldloc.0
IL_0010: stloc.0
IL_0011: br.s IL_001e
IL_0013: ldloc.2
IL_0014: isinst ""string""
IL_0019: brfalse.s IL_004c
IL_001b: ldloc.0
IL_001c: castclass ""string""
IL_0021: stloc.2
IL_0022: br.s IL_0033
IL_0024: ldloc.1
IL_0025: ldc.i4.1
IL_0026: bne.un.s IL_003e
IL_0028: ldstr ""1""
IL_002d: call ""void System.Console.WriteLine(string)""
IL_0032: ret
IL_0033: ldstr ""s""
IL_0038: call ""void System.Console.WriteLine(string)""
IL_003d: ret
IL_003e: ldloc.1
IL_003f: ldc.i4.2
IL_0040: bne.un.s IL_004c
IL_0042: ldstr ""2""
IL_0047: call ""void System.Console.WriteLine(string)""
IL_004c: ret
IL_0019: stloc.1
IL_001a: ldloc.1
IL_001b: brtrue.s IL_002d
IL_001d: ret
IL_001e: ldloc.0
IL_001f: ldc.i4.1
IL_0020: bne.un.s IL_0038
IL_0022: ldstr ""1""
IL_0027: call ""void System.Console.WriteLine(string)""
IL_002c: ret
IL_002d: ldstr ""s""
IL_0032: call ""void System.Console.WriteLine(string)""
IL_0037: ret
IL_0038: ldloc.0
IL_0039: ldc.i4.2
IL_003a: bne.un.s IL_0046
IL_003c: ldstr ""2""
IL_0041: call ""void System.Console.WriteLine(string)""
IL_0046: ret
}");
}
......@@ -971,6 +964,201 @@ .locals init (U V_0)
IL_001a: box ""T""
IL_001f: call ""void System.Console.Write(object)""
IL_0024: ret
}");
}
[Fact, WorkItem(20641, "https://github.com/dotnet/roslyn/issues/20641")]
public void PatternsVsAs01()
{
var source = @"using System.Collections;
using System.Collections.Generic;
class Program
{
static void Main() { }
internal static bool TryGetCount1<T>(IEnumerable<T> source, out int count)
{
ICollection nonGeneric = source as ICollection;
if (nonGeneric != null)
{
count = nonGeneric.Count;
return true;
}
ICollection<T> generic = source as ICollection<T>;
if (generic != null)
{
count = generic.Count;
return true;
}
count = -1;
return false;
}
internal static bool TryGetCount2<T>(IEnumerable<T> source, out int count)
{
switch (source)
{
case ICollection nonGeneric:
count = nonGeneric.Count;
return true;
case ICollection<T> generic:
count = generic.Count;
return true;
default:
count = -1;
return false;
}
}
}";
var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll);
compilation.VerifyDiagnostics();
var compVerifier = CompileAndVerify(compilation);
compVerifier.VerifyIL("Program.TryGetCount1<T>",
@"{
// Code size 45 (0x2d)
.maxstack 2
.locals init (System.Collections.ICollection V_0, //nonGeneric
System.Collections.Generic.ICollection<T> V_1) //generic
IL_0000: ldarg.0
IL_0001: isinst ""System.Collections.ICollection""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0014
IL_000a: ldarg.1
IL_000b: ldloc.0
IL_000c: callvirt ""int System.Collections.ICollection.Count.get""
IL_0011: stind.i4
IL_0012: ldc.i4.1
IL_0013: ret
IL_0014: ldarg.0
IL_0015: isinst ""System.Collections.Generic.ICollection<T>""
IL_001a: stloc.1
IL_001b: ldloc.1
IL_001c: brfalse.s IL_0028
IL_001e: ldarg.1
IL_001f: ldloc.1
IL_0020: callvirt ""int System.Collections.Generic.ICollection<T>.Count.get""
IL_0025: stind.i4
IL_0026: ldc.i4.1
IL_0027: ret
IL_0028: ldarg.1
IL_0029: ldc.i4.m1
IL_002a: stind.i4
IL_002b: ldc.i4.0
IL_002c: ret
}");
compVerifier.VerifyIL("Program.TryGetCount2<T>",
@"{
// Code size 56 (0x38)
.maxstack 2
.locals init (System.Collections.ICollection V_0, //nonGeneric
System.Collections.Generic.ICollection<T> V_1, //generic
System.Collections.ICollection V_2,
System.Collections.Generic.ICollection<T> V_3,
System.Collections.Generic.IEnumerable<T> V_4)
IL_0000: ldarg.0
IL_0001: stloc.s V_4
IL_0003: ldloc.s V_4
IL_0005: isinst ""System.Collections.ICollection""
IL_000a: stloc.2
IL_000b: ldloc.2
IL_000c: brtrue.s IL_001b
IL_000e: ldloc.s V_4
IL_0010: isinst ""System.Collections.Generic.ICollection<T>""
IL_0015: stloc.3
IL_0016: ldloc.3
IL_0017: brtrue.s IL_0027
IL_0019: br.s IL_0033
IL_001b: ldloc.2
IL_001c: stloc.0
IL_001d: ldarg.1
IL_001e: ldloc.0
IL_001f: callvirt ""int System.Collections.ICollection.Count.get""
IL_0024: stind.i4
IL_0025: ldc.i4.1
IL_0026: ret
IL_0027: ldloc.3
IL_0028: stloc.1
IL_0029: ldarg.1
IL_002a: ldloc.1
IL_002b: callvirt ""int System.Collections.Generic.ICollection<T>.Count.get""
IL_0030: stind.i4
IL_0031: ldc.i4.1
IL_0032: ret
IL_0033: ldarg.1
IL_0034: ldc.i4.m1
IL_0035: stind.i4
IL_0036: ldc.i4.0
IL_0037: ret
}");
}
[Fact, WorkItem(20641, "https://github.com/dotnet/roslyn/issues/20641")]
public void PatternsVsAs02()
{
var source = @"using System.Collections;
class Program
{
static void Main() { }
internal static bool IsEmpty1(IEnumerable source)
{
var c = source as ICollection;
return c != null && c.Count > 0;
}
internal static bool IsEmpty2(IEnumerable source)
{
return source is ICollection c && c.Count > 0;
}
}";
var compilation = CreateCompilation(source, options: TestOptions.ReleaseDll);
compilation.VerifyDiagnostics();
var compVerifier = CompileAndVerify(compilation);
compVerifier.VerifyIL("Program.IsEmpty1",
@"{
// Code size 22 (0x16)
.maxstack 2
.locals init (System.Collections.ICollection V_0) //c
IL_0000: ldarg.0
IL_0001: isinst ""System.Collections.ICollection""
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: brfalse.s IL_0014
IL_000a: ldloc.0
IL_000b: callvirt ""int System.Collections.ICollection.Count.get""
IL_0010: ldc.i4.0
IL_0011: cgt
IL_0013: ret
IL_0014: ldc.i4.0
IL_0015: ret
}");
compVerifier.VerifyIL("Program.IsEmpty2",
@"{
// Code size 24 (0x18)
.maxstack 2
.locals init (System.Collections.ICollection V_0, //c
System.Collections.ICollection V_1)
IL_0000: ldarg.0
IL_0001: isinst ""System.Collections.ICollection""
IL_0006: stloc.1
IL_0007: ldloc.1
IL_0008: brfalse.s IL_0016
IL_000a: ldloc.1
IL_000b: stloc.0
IL_000c: ldloc.0
IL_000d: callvirt ""int System.Collections.ICollection.Count.get""
IL_0012: ldc.i4.0
IL_0013: cgt
IL_0015: ret
IL_0016: ldc.i4.0
IL_0017: ret
}");
}
}
......
......@@ -7941,22 +7941,22 @@ .locals init (int? V_0)
// Code size 51 (0x33)
.maxstack 1
.locals init (int V_0, //i
int? V_1,
int V_2,
int V_1,
int? V_2,
int? V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloca.s V_1
IL_0004: stloc.2
IL_0005: ldloca.s V_2
IL_0007: call ""bool int?.HasValue.get""
IL_000c: brfalse.s IL_0025
IL_000e: ldloca.s V_1
IL_000e: ldloca.s V_2
IL_0010: call ""int int?.GetValueOrDefault()""
IL_0015: stloc.2
IL_0015: stloc.1
IL_0016: br.s IL_0018
IL_0018: ldloc.2
IL_0018: ldloc.1
IL_0019: stloc.0
IL_001a: br.s IL_001c
IL_001c: ldloc.0
......@@ -8088,22 +8088,22 @@ .locals init (int? V_0)
// Code size 37 (0x25)
.maxstack 1
.locals init (System.IComparable V_0, //i
int? V_1,
System.IComparable V_2,
System.IComparable V_1,
int? V_2,
int? V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloca.s V_1
IL_0004: stloc.2
IL_0005: ldloca.s V_2
IL_0007: call ""bool int?.HasValue.get""
IL_000c: brfalse.s IL_0024
IL_000e: ldloc.1
IL_000e: ldloc.2
IL_000f: box ""int?""
IL_0014: stloc.2
IL_0014: stloc.1
IL_0015: br.s IL_0017
IL_0017: ldloc.2
IL_0017: ldloc.1
IL_0018: stloc.0
IL_0019: br.s IL_001b
IL_001b: ldloc.0
......@@ -8165,22 +8165,22 @@ .locals init (object V_0)
// Code size 36 (0x24)
.maxstack 1
.locals init (int V_0, //i
object V_1,
int V_2,
int V_1,
object V_2,
object V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloc.1
IL_0004: stloc.2
IL_0005: ldloc.2
IL_0006: isinst ""int""
IL_000b: brfalse.s IL_0023
IL_000d: ldloc.1
IL_000d: ldloc.2
IL_000e: unbox.any ""int""
IL_0013: stloc.2
IL_0013: stloc.1
IL_0014: br.s IL_0016
IL_0016: ldloc.2
IL_0016: ldloc.1
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
......@@ -8245,22 +8245,22 @@ .locals init (object V_0)
// Code size 41 (0x29)
.maxstack 1
.locals init (T V_0, //i
object V_1,
T V_2,
T V_1,
object V_2,
object V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloc.1
IL_0004: stloc.2
IL_0005: ldloc.2
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0028
IL_000d: ldloc.1
IL_000d: ldloc.2
IL_000e: unbox.any ""T""
IL_0013: stloc.2
IL_0013: stloc.1
IL_0014: br.s IL_0016
IL_0016: ldloc.2
IL_0016: ldloc.1
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
......@@ -8326,22 +8326,22 @@ .locals init (System.IComparable V_0)
// Code size 41 (0x29)
.maxstack 1
.locals init (T V_0, //i
System.IComparable V_1,
T V_2,
T V_1,
System.IComparable V_2,
System.IComparable V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloc.1
IL_0004: stloc.2
IL_0005: ldloc.2
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0028
IL_000d: ldloc.1
IL_000d: ldloc.2
IL_000e: unbox.any ""T""
IL_0013: stloc.2
IL_0013: stloc.1
IL_0014: br.s IL_0016
IL_0016: ldloc.2
IL_0016: ldloc.1
IL_0017: stloc.0
IL_0018: br.s IL_001a
IL_001a: ldloc.0
......@@ -8409,24 +8409,24 @@ .locals init (U V_0)
// Code size 51 (0x33)
.maxstack 1
.locals init (T V_0, //i
U V_1,
T V_2,
T V_1,
U V_2,
U V_3)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloc.1
IL_0004: stloc.2
IL_0005: ldloc.2
IL_0006: box ""U""
IL_000b: isinst ""T""
IL_0010: brfalse.s IL_0032
IL_0012: ldloc.1
IL_0012: ldloc.2
IL_0013: box ""U""
IL_0018: unbox.any ""T""
IL_001d: stloc.2
IL_001d: stloc.1
IL_001e: br.s IL_0020
IL_0020: ldloc.2
IL_0020: ldloc.1
IL_0021: stloc.0
IL_0022: br.s IL_0024
IL_0024: ldloc.0
......@@ -8603,8 +8603,8 @@ .maxstack 1
// Code size 53 (0x35)
.maxstack 1
.locals init (T V_0, //t
System.ValueType V_1,
T V_2,
T V_1,
System.ValueType V_2,
System.ValueType V_3,
T V_4,
T V_5)
......@@ -8612,16 +8612,16 @@ .maxstack 1
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloc.1
IL_0004: stloc.2
IL_0005: ldloc.2
IL_0006: isinst ""T""
IL_000b: brfalse.s IL_0024
IL_000d: ldloc.1
IL_000d: ldloc.2
IL_000e: isinst ""T""
IL_0013: unbox.any ""T""
IL_0018: stloc.2
IL_0018: stloc.1
IL_0019: br.s IL_001b
IL_001b: ldloc.2
IL_001b: ldloc.1
IL_001c: stloc.0
IL_001d: br.s IL_001f
IL_001f: ldloc.0
......@@ -8758,26 +8758,26 @@ .maxstack 1
// Code size 54 (0x36)
.maxstack 1
.locals init (int V_0, //t
T V_1,
int V_2,
int V_1,
T V_2,
T V_3,
int V_4)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.3
IL_0003: ldloc.3
IL_0004: stloc.1
IL_0005: ldloc.1
IL_0004: stloc.2
IL_0005: ldloc.2
IL_0006: box ""T""
IL_000b: isinst ""int""
IL_0010: brfalse.s IL_002e
IL_0012: ldloc.1
IL_0012: ldloc.2
IL_0013: box ""T""
IL_0018: isinst ""int""
IL_001d: unbox.any ""int""
IL_0022: stloc.2
IL_0022: stloc.1
IL_0023: br.s IL_0025
IL_0025: ldloc.2
IL_0025: ldloc.1
IL_0026: stloc.0
IL_0027: br.s IL_0029
IL_0029: ldloc.0
......@@ -9302,16 +9302,16 @@ public static void M(object o)
@"{
// Code size 106 (0x6a)
.maxstack 1
.locals init (object V_0,
int V_1)
.locals init (int V_0,
object V_1)
IL_0000: ldarg.0
IL_0001: stloc.0
IL_0002: ldloc.0
IL_0001: stloc.1
IL_0002: ldloc.1
IL_0003: isinst ""int""
IL_0008: brfalse.s IL_0021
IL_000a: ldloc.0
IL_000a: ldloc.1
IL_000b: unbox.any ""int""
IL_0010: stloc.1
IL_0010: stloc.0
IL_0011: ldsfld ""bool Program.b""
IL_0016: brtrue.s IL_0069
IL_0018: ldsfld ""bool Program.b""
......@@ -9355,22 +9355,22 @@ .maxstack 1
int V_1, //i
int V_2, //i
int V_3, //i
object V_4,
int V_5,
int V_4,
object V_5,
object V_6)
IL_0000: nop
IL_0001: ldarg.0
IL_0002: stloc.s V_6
IL_0004: ldloc.s V_6
IL_0006: stloc.s V_4
IL_0008: ldloc.s V_4
IL_0006: stloc.s V_5
IL_0008: ldloc.s V_5
IL_000a: isinst ""int""
IL_000f: brfalse.s IL_0033
IL_0011: ldloc.s V_4
IL_0011: ldloc.s V_5
IL_0013: unbox.any ""int""
IL_0018: stloc.s V_5
IL_0018: stloc.s V_4
IL_001a: br.s IL_001c
IL_001c: ldloc.s V_5
IL_001c: ldloc.s V_4
IL_001e: stloc.0
IL_001f: ldsfld ""bool Program.b""
IL_0024: brtrue.s IL_0028
......@@ -9383,7 +9383,7 @@ .maxstack 1
IL_0038: brtrue.s IL_003c
IL_003a: br.s IL_0055
IL_003c: br.s IL_009b
IL_003e: ldloc.s V_5
IL_003e: ldloc.s V_4
IL_0040: stloc.1
IL_0041: ldsfld ""bool Program.b""
IL_0046: brtrue.s IL_004a
......@@ -9396,7 +9396,7 @@ .maxstack 1
IL_005a: brtrue.s IL_005e
IL_005c: br.s IL_0077
IL_005e: br.s IL_009b
IL_0060: ldloc.s V_5
IL_0060: ldloc.s V_4
IL_0062: stloc.2
IL_0063: ldsfld ""bool Program.b""
IL_0068: brtrue.s IL_006c
......@@ -9409,7 +9409,7 @@ .maxstack 1
IL_007c: brtrue.s IL_0080
IL_007e: br.s IL_0090
IL_0080: br.s IL_009b
IL_0082: ldloc.s V_5
IL_0082: ldloc.s V_4
IL_0084: stloc.3
IL_0085: ldsfld ""bool Program.b""
IL_008a: brtrue.s IL_008e
......
......@@ -2442,44 +2442,44 @@ .maxstack 2
int V_1, //i
int V_2, //j
object V_3, //o
object V_4,
int V_5,
byte V_6,
int V_4,
byte V_5,
object V_6,
object V_7)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_4
IL_000c: ldloc.s V_4
IL_000a: stloc.s V_6
IL_000c: ldloc.s V_6
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_4
IL_0015: ldloc.s V_6
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_5
IL_001c: stloc.s V_4
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_5
IL_001f: ldloc.s V_4
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_4
IL_0025: ldloc.s V_6
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_4
IL_002e: ldloc.s V_6
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_6
IL_0035: stloc.s V_5
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_6
IL_003a: ldloc.s V_5
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_4
IL_0040: ldloc.s V_6
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_6
IL_0053: ldloc.s V_5
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
......@@ -2488,7 +2488,7 @@ .maxstack 2
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_5
IL_0068: ldloc.s V_4
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
......@@ -2501,14 +2501,14 @@ .maxstack 2
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_5
IL_008a: ldloc.s V_4
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_4
IL_0098: ldloc.s V_6
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
......@@ -2534,47 +2534,47 @@ .maxstack 2
int V_1, //i
int V_2, //j
object V_3, //o
[object] V_4,
[int] V_5,
[unchanged] V_6,
[int] V_4,
[unchanged] V_5,
[object] V_6,
object V_7,
object V_8,
int V_9,
byte V_10)
int V_8,
byte V_9,
object V_10)
IL_0000: nop
IL_0001: call ""object C.F()""
IL_0006: stloc.s V_7
IL_0008: ldloc.s V_7
IL_000a: stloc.s V_8
IL_000c: ldloc.s V_8
IL_000a: stloc.s V_10
IL_000c: ldloc.s V_10
IL_000e: isinst ""int""
IL_0013: brfalse.s IL_0025
IL_0015: ldloc.s V_8
IL_0015: ldloc.s V_10
IL_0017: unbox.any ""int""
IL_001c: stloc.s V_9
IL_001c: stloc.s V_8
IL_001e: ldc.i4.1
IL_001f: ldloc.s V_9
IL_001f: ldloc.s V_8
IL_0021: beq.s IL_0046
IL_0023: br.s IL_0068
IL_0025: ldloc.s V_8
IL_0025: ldloc.s V_10
IL_0027: isinst ""byte""
IL_002c: brfalse.s IL_0040
IL_002e: ldloc.s V_8
IL_002e: ldloc.s V_10
IL_0030: unbox.any ""byte""
IL_0035: stloc.s V_10
IL_0035: stloc.s V_9
IL_0037: br.s IL_0053
IL_0039: ldc.i4.1
IL_003a: ldloc.s V_10
IL_003a: ldloc.s V_9
IL_003c: beq.s IL_007d
IL_003e: br.s IL_0098
IL_0040: ldloc.s V_8
IL_0040: ldloc.s V_10
IL_0042: brtrue.s IL_0098
IL_0044: br.s IL_00a6
IL_0046: ldstr ""int 1""
IL_004b: call ""void System.Console.WriteLine(string)""
IL_0050: nop
IL_0051: br.s IL_00a6
IL_0053: ldloc.s V_10
IL_0053: ldloc.s V_9
IL_0055: stloc.0
IL_0056: call ""bool C.P()""
IL_005b: brtrue.s IL_005f
......@@ -2583,7 +2583,7 @@ .maxstack 2
IL_0060: call ""void System.Console.WriteLine(int)""
IL_0065: nop
IL_0066: br.s IL_00a6
IL_0068: ldloc.s V_9
IL_0068: ldloc.s V_8
IL_006a: stloc.1
IL_006b: call ""bool C.P()""
IL_0070: brtrue.s IL_0074
......@@ -2596,14 +2596,14 @@ .maxstack 2
IL_0082: call ""void System.Console.WriteLine(string)""
IL_0087: nop
IL_0088: br.s IL_00a6
IL_008a: ldloc.s V_9
IL_008a: ldloc.s V_8
IL_008c: stloc.2
IL_008d: br.s IL_008f
IL_008f: ldloc.2
IL_0090: call ""void System.Console.WriteLine(int)""
IL_0095: nop
IL_0096: br.s IL_00a6
IL_0098: ldloc.s V_8
IL_0098: ldloc.s V_10
IL_009a: stloc.3
IL_009b: br.s IL_009d
IL_009d: ldloc.3
......
......@@ -3417,8 +3417,8 @@ static void M2<T>()
.maxstack 1
.locals init (T V_0, //t
int V_1, //i
int V_2,
T V_3,
T V_2,
int V_3,
int V_4)
// sequence point: {
IL_0000: nop
......@@ -3426,18 +3426,18 @@ .maxstack 1
IL_0001: ldc.i4.1
IL_0002: stloc.s V_4
IL_0004: ldc.i4.1
IL_0005: stloc.2
IL_0006: ldloc.2
IL_0005: stloc.3
IL_0006: ldloc.3
IL_0007: box ""int""
IL_000c: isinst ""T""
IL_0011: brfalse.s IL_0033
IL_0013: ldloc.2
IL_0013: ldloc.3
IL_0014: box ""int""
IL_0019: isinst ""T""
IL_001e: unbox.any ""T""
IL_0023: stloc.3
IL_0023: stloc.2
IL_0024: br.s IL_0026
IL_0026: ldloc.3
IL_0026: ldloc.2
IL_0027: stloc.0
IL_0028: br.s IL_002a
// sequence point: Console.Write(1);
......@@ -3446,7 +3446,7 @@ .maxstack 1
IL_0030: nop
// sequence point: break;
IL_0031: br.s IL_0040
IL_0033: ldloc.2
IL_0033: ldloc.3
IL_0034: stloc.1
IL_0035: br.s IL_0037
// sequence point: Console.Write(2);
......@@ -3457,15 +3457,16 @@ .maxstack 1
IL_003e: br.s IL_0040
// sequence point: }
IL_0040: ret
}");
}
");
verifier.VerifyIL(qualifiedMethodName: "Program.M2<T>", sequencePoints: "Program.M2", source: source,
expectedIL: @"{
// Code size 63 (0x3f)
.maxstack 1
.locals init (T V_0, //t
string V_1, //s
string V_2,
T V_3,
T V_2,
string V_3,
string V_4)
// sequence point: {
IL_0000: nop
......@@ -3473,16 +3474,16 @@ .maxstack 1
IL_0001: ldstr ""M2""
IL_0006: stloc.s V_4
IL_0008: ldstr ""M2""
IL_000d: stloc.2
IL_000e: ldloc.2
IL_000d: stloc.3
IL_000e: ldloc.3
IL_000f: isinst ""T""
IL_0014: brfalse.s IL_0031
IL_0016: ldloc.2
IL_0016: ldloc.3
IL_0017: isinst ""T""
IL_001c: unbox.any ""T""
IL_0021: stloc.3
IL_0021: stloc.2
IL_0022: br.s IL_0024
IL_0024: ldloc.3
IL_0024: ldloc.2
IL_0025: stloc.0
IL_0026: br.s IL_0028
// sequence point: Console.Write(3);
......@@ -3491,7 +3492,7 @@ .maxstack 1
IL_002e: nop
// sequence point: break;
IL_002f: br.s IL_003e
IL_0031: ldloc.2
IL_0031: ldloc.3
IL_0032: stloc.1
IL_0033: br.s IL_0035
// sequence point: Console.Write(4);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册