未验证 提交 b2bb7267 编写于 作者: N Neal Gafter 提交者: GitHub

Merge pull request #25992 from alrz/lifted-op

Branchless codegen for lifted nullable operators
......@@ -874,25 +874,23 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
//
// tempx = x;
// tempy = y;
// result = tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ?
// tempx.HasValue == tempy.HasValue :
// false;
// result = (tempx.GetValueOrDefault() == tempy.GetValueOrDefault()) &
// (tempx.HasValue == tempy.HasValue);
//
// and x != y as
//
// tempx = x;
// tempy = y;
// result = tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ?
// tempx.HasValue != tempy.HasValue :
// true;
// result = !((tempx.GetValueOrDefault() == tempy.GetValueOrDefault()) &
// (tempx.HasValue == tempy.HasValue));
//
// Otherwise, we rewrite x OP y as
//
// tempx = x;
// tempy = y;
// result = tempx.GetValueOrDefault() OP tempy.GetValueOrDefault() ?
// tempx.HasValue & tempy.HasValue :
// false;
// result = (tempx.GetValueOrDefault() OP tempy.GetValueOrDefault()) &
// (tempx.HasValue & tempy.HasValue);
//
//
// Note that there is no reason to generate "&&" over "&"; the cost of
// the added code for the conditional branch would be about the same as simply doing
......@@ -908,79 +906,77 @@ private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression
BoundExpression xNonNull = NullableAlwaysHasValue(loweredLeft);
BoundExpression yNonNull = NullableAlwaysHasValue(loweredRight);
BoundAssignmentOperator tempAssignmentX;
BoundLocal boundTempX = _factory.StoreToTemp(xNonNull ?? loweredLeft, out tempAssignmentX);
BoundAssignmentOperator tempAssignmentY;
BoundLocal boundTempY = _factory.StoreToTemp(yNonNull ?? loweredRight, out tempAssignmentY);
BoundLocal boundTempX = _factory.StoreToTemp(xNonNull ?? loweredLeft, out BoundAssignmentOperator tempAssignmentX);
BoundLocal boundTempY = _factory.StoreToTemp(yNonNull ?? loweredRight, out BoundAssignmentOperator tempAssignmentY);
BoundExpression callX_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempX);
BoundExpression callY_GetValueOrDefault = MakeOptimizedGetValueOrDefault(syntax, boundTempY);
BoundExpression callX_HasValue = MakeOptimizedHasValue(syntax, boundTempX);
BoundExpression callY_HasValue = MakeOptimizedHasValue(syntax, boundTempY);
// tempx.GetValueOrDefault() == tempy.GetValueOrDefault()
BinaryOperatorKind operatorKind = kind.Operator();
BinaryOperatorKind conditionOperator = operatorKind == BinaryOperatorKind.NotEqual ?
BinaryOperatorKind.Equal :
operatorKind;
TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean);
BoundExpression condition = MakeBinaryOperator(
syntax: syntax,
operatorKind: conditionOperator.WithType(kind.OperandTypes()),
loweredLeft: callX_GetValueOrDefault,
loweredRight: callY_GetValueOrDefault,
type: boolType,
method: null);
BinaryOperatorKind leftOperator;
BinaryOperatorKind rightOperator;
BinaryOperatorKind consequenceOperator;
BinaryOperatorKind operatorKind = kind.Operator();
switch (operatorKind)
{
case BinaryOperatorKind.Equal:
consequenceOperator = BinaryOperatorKind.BoolEqual;
break;
case BinaryOperatorKind.NotEqual:
consequenceOperator = BinaryOperatorKind.BoolNotEqual;
leftOperator = BinaryOperatorKind.Equal;
rightOperator = BinaryOperatorKind.BoolEqual;
break;
default:
consequenceOperator = BinaryOperatorKind.BoolAnd;
leftOperator = operatorKind;
rightOperator = BinaryOperatorKind.BoolAnd;
break;
}
// tempx.HasValue == tempy.HasValue
BoundExpression consequence = MakeBinaryOperator(
TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean);
// (tempx.GetValueOrDefault() OP tempy.GetValueOrDefault())
BoundExpression leftExpression = MakeBinaryOperator(
syntax: syntax,
operatorKind: leftOperator.WithType(kind.OperandTypes()),
loweredLeft: callX_GetValueOrDefault,
loweredRight: callY_GetValueOrDefault,
type: boolType,
method: null);
// (tempx.HasValue OP tempy.HasValue)
BoundExpression rightExpression = MakeBinaryOperator(
syntax: syntax,
operatorKind: consequenceOperator,
operatorKind: rightOperator,
loweredLeft: callX_HasValue,
loweredRight: callY_HasValue,
type: boolType,
method: null);
// false
BoundExpression alternative = this.MakeBooleanConstant(syntax, operatorKind == BinaryOperatorKind.NotEqual);
// tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ?
// tempx.HasValue == tempy.HasValue :
// false;
BoundExpression conditionalExpression = RewriteConditionalOperator(
// result = (tempx.GetValueOrDefault() OP tempy.GetValueOrDefault()) &
// (tempx.HasValue OP tempy.HasValue)
BoundExpression binaryExpression = MakeBinaryOperator(
syntax: syntax,
rewrittenCondition: condition,
rewrittenConsequence: consequence,
rewrittenAlternative: alternative,
constantValueOpt: null,
rewrittenType: boolType,
isRef: false);
operatorKind: BinaryOperatorKind.BoolAnd,
loweredLeft: leftExpression,
loweredRight: rightExpression,
type: boolType,
method: null);
// result = !((tempx.GetValueOrDefault() == tempy.GetValueOrDefault()) &
// (tempx.HasValue == tempy.HasValue));
if (operatorKind == BinaryOperatorKind.NotEqual)
{
binaryExpression = _factory.Not(binaryExpression);
}
// tempx = x;
// tempy = y;
// result = tempx.GetValueOrDefault() == tempy.GetValueOrDefault() ?
// tempx.HasValue == tempy.HasValue :
// false;
// result = (tempx.GetValueOrDefault() == tempy.GetValueOrDefault()) &
// (tempx.HasValue == tempy.HasValue);
return new BoundSequence(
syntax: syntax,
locals: ImmutableArray.Create<LocalSymbol>(boundTempX.LocalSymbol, boundTempY.LocalSymbol),
sideEffects: ImmutableArray.Create<BoundExpression>(tempAssignmentX, tempAssignmentY),
value: conditionalExpression,
value: binaryExpression,
type: boolType);
}
......
......@@ -1368,10 +1368,10 @@ static bool M5()
";
string expectedOutput = "";
string expectedIL1 = @"{
// Code size 32 (0x20)
// Code size 31 (0x1f)
.maxstack 2
.locals init (int? V_0,
int V_1)
int V_1)
IL_0000: call ""int? Program.N1()""
IL_0005: stloc.0
IL_0006: call ""short Program.V2()""
......@@ -1379,23 +1379,22 @@ .maxstack 2
IL_000c: ldloca.s V_0
IL_000e: call ""int int?.GetValueOrDefault()""
IL_0013: ldloc.1
IL_0014: beq.s IL_0018
IL_0016: ldc.i4.0
IL_0017: ret
IL_0018: ldloca.s V_0
IL_001a: call ""bool int?.HasValue.get""
IL_001f: ret
IL_0014: ceq
IL_0016: ldloca.s V_0
IL_0018: call ""bool int?.HasValue.get""
IL_001d: and
IL_001e: ret
}";
// TODO: We do a worse job than the native compiler here. Find out why.
string expectedIL2 = @"{
// Code size 75 (0x4b)
// Code size 72 (0x48)
.maxstack 2
.locals init (decimal? V_0,
decimal V_1,
int? V_2,
decimal? V_3)
decimal V_1,
int? V_2,
decimal? V_3)
IL_0000: call ""int? Program.N1()""
IL_0005: stloc.2
IL_0006: ldloca.s V_2
......@@ -1416,12 +1415,10 @@ .maxstack 2
IL_0034: call ""decimal decimal?.GetValueOrDefault()""
IL_0039: ldloc.1
IL_003a: call ""bool decimal.op_LessThan(decimal, decimal)""
IL_003f: brtrue.s IL_0043
IL_0041: ldc.i4.0
IL_0042: ret
IL_0043: ldloca.s V_0
IL_0045: call ""bool decimal?.HasValue.get""
IL_004a: ret
IL_003f: ldloca.s V_0
IL_0041: call ""bool decimal?.HasValue.get""
IL_0046: and
IL_0047: ret
}";
string expectedIL3 = @"{
// Code size 37 (0x25)
......
......@@ -5966,7 +5966,7 @@ .maxstack 2
IL_000f: ret
}").VerifyIL("C.HasLengthChecked", @"
{
// Code size 49 (0x31)
// Code size 48 (0x30)
.maxstack 2
.locals init (int? V_0,
int V_1,
......@@ -5987,12 +5987,11 @@ .maxstack 2
IL_001d: ldloca.s V_0
IL_001f: call ""int int?.GetValueOrDefault()""
IL_0024: ldloc.1
IL_0025: beq.s IL_0029
IL_0027: ldc.i4.0
IL_0028: ret
IL_0029: ldloca.s V_0
IL_002b: call ""bool int?.HasValue.get""
IL_0030: ret
IL_0025: ceq
IL_0027: ldloca.s V_0
IL_0029: call ""bool int?.HasValue.get""
IL_002e: and
IL_002f: ret
}");
}
......@@ -6103,7 +6102,7 @@ static bool NotHasLength(string s, int? len)
verifier.VerifyIL("C.NotHasLength", @"
{
// Code size 43 (0x2b)
// Code size 42 (0x2a)
.maxstack 2
.locals init (int? V_0)
IL_0000: ldarg.0
......@@ -6119,14 +6118,13 @@ .locals init (int? V_0)
IL_0014: stloc.0
IL_0015: ldloca.s V_0
IL_0017: call ""int int?.GetValueOrDefault()""
IL_001c: beq.s IL_0020
IL_001e: ldc.i4.1
IL_001f: ret
IL_0020: ldloca.s V_0
IL_0022: call ""bool int?.HasValue.get""
IL_0027: ldc.i4.0
IL_0028: ceq
IL_002a: ret
IL_001c: ceq
IL_001e: ldloca.s V_0
IL_0020: call ""bool int?.HasValue.get""
IL_0025: and
IL_0026: ldc.i4.0
IL_0027: ceq
IL_0029: ret
}");
}
......@@ -6816,7 +6814,7 @@ static bool NotHasLength(int? s, int? len)
verifier.VerifyIL("C.NotHasLength", @"
{
// Code size 58 (0x3a)
// Code size 57 (0x39)
.maxstack 2
.locals init (int? V_0,
int V_1)
......@@ -6837,14 +6835,13 @@ .maxstack 2
IL_0023: stloc.0
IL_0024: ldloca.s V_0
IL_0026: call ""int int?.GetValueOrDefault()""
IL_002b: beq.s IL_002f
IL_002d: ldc.i4.1
IL_002e: ret
IL_002f: ldloca.s V_0
IL_0031: call ""bool int?.HasValue.get""
IL_0036: ldc.i4.0
IL_0037: ceq
IL_0039: ret
IL_002b: ceq
IL_002d: ldloca.s V_0
IL_002f: call ""bool int?.HasValue.get""
IL_0034: and
IL_0035: ldc.i4.0
IL_0036: ceq
IL_0038: ret
}");
}
......
......@@ -2266,7 +2266,7 @@ public static void Eval(object obj1, object obj2)
compilation.VerifyIL("NullableTest.EqualEqual",
@"
{
// Code size 120 (0x78)
// Code size 112 (0x70)
.maxstack 2
.locals init (decimal? V_0)
IL_0000: ldc.i4.0
......@@ -2280,31 +2280,27 @@ .locals init (decimal? V_0)
IL_001c: ldloca.s V_0
IL_001e: call ""decimal decimal?.GetValueOrDefault()""
IL_0023: call ""bool decimal.op_Equality(decimal, decimal)""
IL_0028: brtrue.s IL_002d
IL_002a: ldc.i4.0
IL_002b: br.s IL_0034
IL_002d: ldloca.s V_0
IL_002f: call ""bool decimal?.HasValue.get""
IL_0034: box ""bool""
IL_0039: ldc.i4.0
IL_003a: box ""bool""
IL_003f: call ""void Test.Eval(object, object)""
IL_0044: ldsfld ""decimal decimal.Zero""
IL_0049: ldsfld ""decimal? NullableTest.NULL""
IL_004e: stloc.0
IL_004f: ldloca.s V_0
IL_0051: call ""decimal decimal?.GetValueOrDefault()""
IL_0056: call ""bool decimal.op_Equality(decimal, decimal)""
IL_005b: brtrue.s IL_0060
IL_005d: ldc.i4.0
IL_005e: br.s IL_0067
IL_0060: ldloca.s V_0
IL_0062: call ""bool decimal?.HasValue.get""
IL_0067: box ""bool""
IL_006c: ldc.i4.0
IL_006d: box ""bool""
IL_0072: call ""void Test.Eval(object, object)""
IL_0077: ret
IL_0028: ldloca.s V_0
IL_002a: call ""bool decimal?.HasValue.get""
IL_002f: and
IL_0030: box ""bool""
IL_0035: ldc.i4.0
IL_0036: box ""bool""
IL_003b: call ""void Test.Eval(object, object)""
IL_0040: ldsfld ""decimal decimal.Zero""
IL_0045: ldsfld ""decimal? NullableTest.NULL""
IL_004a: stloc.0
IL_004b: ldloca.s V_0
IL_004d: call ""decimal decimal?.GetValueOrDefault()""
IL_0052: call ""bool decimal.op_Equality(decimal, decimal)""
IL_0057: ldloca.s V_0
IL_0059: call ""bool decimal?.HasValue.get""
IL_005e: and
IL_005f: box ""bool""
IL_0064: ldc.i4.0
IL_0065: box ""bool""
IL_006a: call ""void Test.Eval(object, object)""
IL_006f: ret
}
");
}
......
......@@ -536,7 +536,7 @@ static bool M((int?, bool?) t1)
comp.VerifyDiagnostics();
comp.VerifyIL("C.M", @"{
// Code size 66 (0x42)
// Code size 63 (0x3f)
.maxstack 2
.locals init (System.ValueTuple<int?, bool?> V_0,
int? V_1,
......@@ -553,28 +553,26 @@ .maxstack 2
IL_000b: ldloca.s V_1
IL_000d: call ""int int?.GetValueOrDefault()""
IL_0012: ldloc.2
IL_0013: beq.s IL_0018
IL_0015: ldc.i4.0
IL_0016: br.s IL_001f
IL_0018: ldloca.s V_1
IL_001a: call ""bool int?.HasValue.get""
IL_001f: brfalse.s IL_0040
IL_0021: ldloc.0
IL_0022: ldfld ""bool? System.ValueTuple<int?, bool?>.Item2""
IL_0027: stloc.3
IL_0028: ldc.i4.1
IL_0029: stloc.s V_4
IL_002b: ldloca.s V_3
IL_002d: call ""bool bool?.GetValueOrDefault()""
IL_0032: ldloc.s V_4
IL_0034: beq.s IL_0038
IL_0036: ldc.i4.0
IL_0037: ret
IL_0038: ldloca.s V_3
IL_003a: call ""bool bool?.HasValue.get""
IL_003f: ret
IL_0040: ldc.i4.0
IL_0041: ret
IL_0013: ceq
IL_0015: ldloca.s V_1
IL_0017: call ""bool int?.HasValue.get""
IL_001c: and
IL_001d: brfalse.s IL_003d
IL_001f: ldloc.0
IL_0020: ldfld ""bool? System.ValueTuple<int?, bool?>.Item2""
IL_0025: stloc.3
IL_0026: ldc.i4.1
IL_0027: stloc.s V_4
IL_0029: ldloca.s V_3
IL_002b: call ""bool bool?.GetValueOrDefault()""
IL_0030: ldloc.s V_4
IL_0032: ceq
IL_0034: ldloca.s V_3
IL_0036: call ""bool bool?.HasValue.get""
IL_003b: and
IL_003c: ret
IL_003d: ldc.i4.0
IL_003e: ret
}");
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册