Skip to content

Commit

Permalink
Better array pattern index codegen (#38988)
Browse files Browse the repository at this point in the history
Fixes #38942
  • Loading branch information
agocke committed Oct 8, 2019
1 parent 3a235d1 commit 5a24d13
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -581,23 +581,25 @@ public override BoundNode VisitArrayAccess(BoundArrayAccess node)
_compilation.GetWellKnownType(WellKnownType.System_Index),
TypeCompareKind.ConsiderEverything))
{
// array[Index] is compiled to:
// array[Index.GetOffset(array.Length)]
// array[Index] is treated like a pattern-based System.Index indexing
// expression, except that array indexers don't actually exist (they
// don't have symbols)

var arrayLocal = F.StoreToTemp(
VisitExpression(node.Expression),
out BoundAssignmentOperator arrayAssign);

var indexOffsetExpr = MakePatternIndexOffsetExpression(
node.Indices[0],
F.ArrayLength(arrayLocal),
out _);

resultExpr = F.Sequence(
ImmutableArray.Create(arrayLocal.LocalSymbol),
ImmutableArray.Create<BoundExpression>(arrayAssign),
F.ArrayAccess(
arrayLocal,
ImmutableArray.Create<BoundExpression>(
F.Call(
VisitExpression(node.Indices[0]),
WellKnownMember.System_Index__GetOffset,
F.ArrayLength(arrayLocal)))));
ImmutableArray.Create(indexOffsetExpr)));
}
else if (TypeSymbol.Equals(
indexType,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,9 +254,25 @@ private BoundSequence VisitIndexPatternIndexerAccess(
isLeftOfAssignment));
}

/// <summary>
/// Used to construct a pattern index offset expression, of the form
/// `unloweredExpr.GetOffset(lengthAccess)`
/// where unloweredExpr is an expression of type System.Index and the
/// lengthAccess retrieves the length of the indexing target.
/// </summary>
/// <param name="unloweredExpr">The unlowered argument to the indexing expression</param>
/// <param name="lengthAccess">
/// An expression accessing the length of the indexing target. This should
/// be a non-side-effecting operation.
/// </param>
/// <param name="usedLength">
/// True if we were able to optimize the <paramref name="unloweredExpr"/>
/// to use the <paramref name="lengthAccess"/> operation directly on the receiver, instead of
/// using System.Index helpers.
/// </param>
private BoundExpression MakePatternIndexOffsetExpression(
BoundExpression unloweredExpr,
BoundLocal lengthAccess,
BoundExpression lengthAccess,
out bool usedLength)
{
Debug.Assert(TypeSymbol.Equals(
Expand Down
135 changes: 83 additions & 52 deletions src/Compilers/CSharp/Test/Emit/CodeGen/IndexAndRangeTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,51 @@ private static void VerifyIndexCall(IMethodSymbol symbol, string methodName, str
Assert.Equal(containingTypeName, symbol.ContainingType.Name);
}

[Fact]
public void PatternIndexArray()
{
var src = @"
class C
{
static int M1(int[] arr) => arr[^1];
static char M2(string s) => s[^1];
}
";
var verifier = CompileAndVerifyWithIndexAndRange(src);
// Code gen for the following two should look basically
// the same, except that string will use Length/indexer
// and array will use ldlen/ldelem, and string may have
// more temporaries
verifier.VerifyIL("C.M1", @"
{
// Code size 8 (0x8)
.maxstack 3
IL_0000: ldarg.0
IL_0001: dup
IL_0002: ldlen
IL_0003: conv.i4
IL_0004: ldc.i4.1
IL_0005: sub
IL_0006: ldelem.i4
IL_0007: ret
}");
verifier.VerifyIL("C.M2", @"
{
// Code size 17 (0x11)
.maxstack 3
.locals init (int V_0)
IL_0000: ldarg.0
IL_0001: dup
IL_0002: callvirt ""int string.Length.get""
IL_0007: ldc.i4.1
IL_0008: sub
IL_0009: stloc.0
IL_000a: ldloc.0
IL_000b: callvirt ""char string.this[int].get""
IL_0010: ret
}");
}

[Fact]
[WorkItem(37789, "https://github.com/dotnet/roslyn/issues/37789")]
public void PatternIndexAndRangeCompoundOperatorRefIndexer()
Expand Down Expand Up @@ -1400,52 +1445,44 @@ static void M(int[] x)
5");
verifier.VerifyIL("C.M", @"
{
// Code size 82 (0x52)
// Code size 67 (0x43)
.maxstack 4
.locals init (int& V_0, //r2
int[] V_1,
System.Index V_2)
.locals init (int& V_0) //r2
IL_0000: ldarg.0
IL_0001: ldc.i4.2
IL_0002: ldelema ""int""
IL_0007: dup
IL_0008: ldind.i4
IL_0009: call ""void System.Console.WriteLine(int)""
IL_000e: ldarg.0
IL_000f: stloc.1
IL_0010: ldloc.1
IL_0011: ldc.i4.2
IL_0012: ldc.i4.1
IL_0013: newobj ""System.Index..ctor(int, bool)""
IL_0018: stloc.2
IL_0019: ldloca.s V_2
IL_001b: ldloc.1
IL_001c: ldlen
IL_001d: conv.i4
IL_001e: call ""int System.Index.GetOffset(int)""
IL_0023: ldelema ""int""
IL_0028: stloc.0
IL_0029: ldloc.0
IL_002a: ldind.i4
IL_002b: call ""void System.Console.WriteLine(int)""
IL_0030: ldloc.0
IL_0031: ldc.i4.7
IL_0032: stind.i4
IL_0033: dup
IL_0034: ldind.i4
IL_0035: call ""void System.Console.WriteLine(int)""
IL_003a: ldloc.0
IL_003b: ldind.i4
IL_003c: call ""void System.Console.WriteLine(int)""
IL_0041: dup
IL_0042: ldc.i4.5
IL_0043: stind.i4
IL_0044: ldind.i4
IL_0045: call ""void System.Console.WriteLine(int)""
IL_004a: ldloc.0
IL_004b: ldind.i4
IL_004c: call ""void System.Console.WriteLine(int)""
IL_0051: ret
IL_000f: dup
IL_0010: ldlen
IL_0011: conv.i4
IL_0012: ldc.i4.2
IL_0013: sub
IL_0014: ldelema ""int""
IL_0019: stloc.0
IL_001a: ldloc.0
IL_001b: ldind.i4
IL_001c: call ""void System.Console.WriteLine(int)""
IL_0021: ldloc.0
IL_0022: ldc.i4.7
IL_0023: stind.i4
IL_0024: dup
IL_0025: ldind.i4
IL_0026: call ""void System.Console.WriteLine(int)""
IL_002b: ldloc.0
IL_002c: ldind.i4
IL_002d: call ""void System.Console.WriteLine(int)""
IL_0032: dup
IL_0033: ldc.i4.5
IL_0034: stind.i4
IL_0035: ldind.i4
IL_0036: call ""void System.Console.WriteLine(int)""
IL_003b: ldloc.0
IL_003c: ldind.i4
IL_003d: call ""void System.Console.WriteLine(int)""
IL_0042: ret
}");
}

Expand Down Expand Up @@ -1678,7 +1715,7 @@ public static void M(int[] array)
11");
verifier.VerifyIL("C.M", @"
{
// Code size 55 (0x37)
// Code size 40 (0x28)
.maxstack 3
.locals init (int[] V_0,
System.Index V_1)
Expand All @@ -1697,20 +1734,14 @@ .locals init (int[] V_0,
IL_0015: ldelem.i4
IL_0016: call ""void System.Console.WriteLine(int)""
IL_001b: ldarg.0
IL_001c: stloc.0
IL_001d: ldloc.0
IL_001e: ldc.i4.1
IL_001c: dup
IL_001d: ldlen
IL_001e: conv.i4
IL_001f: ldc.i4.1
IL_0020: newobj ""System.Index..ctor(int, bool)""
IL_0025: stloc.1
IL_0026: ldloca.s V_1
IL_0028: ldloc.0
IL_0029: ldlen
IL_002a: conv.i4
IL_002b: call ""int System.Index.GetOffset(int)""
IL_0030: ldelem.i4
IL_0031: call ""void System.Console.WriteLine(int)""
IL_0036: ret
IL_0020: sub
IL_0021: ldelem.i4
IL_0022: call ""void System.Console.WriteLine(int)""
IL_0027: ret
}");
}

Expand Down

0 comments on commit 5a24d13

Please sign in to comment.