提交 13adbac9 编写于 作者: V vsadov

Refer directly to static data when ReadOnlySpan wraps arrays of byte literals.

No need to allocate anything in this case.
上级 ce5a3f11
......@@ -347,5 +347,148 @@ private static bool IsMultidimensionalInitializer(ImmutableArray<BoundExpression
return inits.Length != 0 && inits[0].Kind == BoundKind.ArrayInitialization;
}
private bool TryEmitReadonlySpanAsBlobWrapper(NamedTypeSymbol spanType, BoundExpression wrappedExprerssion, bool used, bool inPlace)
{
ImmutableArray<byte> data = default;
int elementCount = -1;
TypeSymbol elementType = null;
if (!_module.SupportsPrivateImplClass)
{
return false;
}
var ctor = ((MethodSymbol)this._module.Compilation.GetWellKnownTypeMember(WellKnownMember.System_ReadOnlySpan_T__ctor));
if (ctor == null)
{
return false;
}
if (wrappedExprerssion is BoundArrayCreation ac)
{
var arrayType = (ArrayTypeSymbol)ac.Type;
elementType = arrayType.ElementType.EnumUnderlyingType();
if (elementType.SpecialType.SizeInBytes() != 1)
{
return false;
}
data = TryGetRawDataForArrayInit(ac.InitializerOpt, out elementCount);
}
if (elementCount < 0)
{
return false;
}
if (!inPlace && !used)
{
// emitting a value that noone will see
return true;
}
if(elementCount == 0)
{
if (inPlace)
{
_builder.EmitOpCode(ILOpCode.Initobj);
EmitSymbolToken(spanType, wrappedExprerssion.Syntax);
}
else
{
EmitDefaultValue(spanType, used, wrappedExprerssion.Syntax);
}
}
else
{
if(EnablePEVerifyCompat())
{
return false;
}
_builder.EmitArrayBlockFieldRef(data, elementType, wrappedExprerssion.Syntax, _diagnostics);
_builder.EmitIntConstant(elementCount);
if (inPlace)
{
// consumes target ref, data ptr and size, pushes nothing
_builder.EmitOpCode(ILOpCode.Call, stackAdjustment: -3);
}
else
{
// consumes data ptr and size, pushes the instance
_builder.EmitOpCode(ILOpCode.Newobj, stackAdjustment: -1);
}
EmitSymbolToken(ctor.AsMember(spanType), wrappedExprerssion.Syntax, optArgList: null);
}
return true;
}
/// <summary>
/// Returns a byte blob that matches serialized content of the string.
/// elementCount is set to the char count and is -1 if the string is 'null' or empty.
/// </summary>
private ImmutableArray<byte> TryGetRawDataForStringLiteral(string stringValue, out int elementCount)
{
if (string.IsNullOrEmpty(stringValue))
{
elementCount = -1;
return default;
}
elementCount = stringValue.Length;
if (elementCount == 0)
{
return ImmutableArray<byte>.Empty;
}
var writer = new BlobBuilder(stringValue.Length * 2);
foreach (var ch in stringValue)
{
writer.WriteInt16((short)ch);
}
return writer.ToImmutableArray();
}
/// <summary>
/// Returns a byte blob that matches serialized content of single array initializer.
/// elementCount is set to -1 if the initializer is null or not an array of literals
/// </summary>
private ImmutableArray<byte> TryGetRawDataForArrayInit(BoundArrayInitialization initializer, out int elementCount)
{
elementCount = -1;
if (initializer == null)
{
return default;
}
var initializers = initializer.Initializers;
if (initializers.Any(init => init.ConstantValue == null))
{
return default;
}
elementCount = initializers.Length;
if (elementCount == 0)
{
return ImmutableArray<byte>.Empty;
}
var writer = new BlobBuilder(initializers.Length * 4);
foreach (var init in initializer.Initializers)
{
init.ConstantValue.Serialize(writer);
}
return writer.ToImmutableArray();
}
}
}
......@@ -26,18 +26,45 @@ private void EmitConversionExpression(BoundConversion conversion, bool used)
return;
}
var operand = conversion.Operand;
if (conversion.ConversionKind.IsUserDefinedConversion())
{
EmitSpecialUserDefinedConversion(conversion, used, operand);
return;
}
if (!used && !conversion.ConversionHasSideEffects())
{
EmitExpression(conversion.Operand, false); // just do expr side effects
EmitExpression(operand, false); // just do expr side effects
return;
}
EmitExpression(conversion.Operand, true);
EmitExpression(operand, true);
EmitConversion(conversion);
EmitPopIfUnused(used);
}
private void EmitSpecialUserDefinedConversion(BoundConversion conversion, bool used, BoundExpression operand)
{
var typeTo = conversion.Type;
Debug.Assert((operand.Type.IsArray()) &&
this._module.Compilation.IsReadOnlySpanType(typeTo),
"only special kinds of conversions involvling ReadOnlySpan may be handled in emit");
if (!TryEmitReadonlySpanAsBlobWrapper((NamedTypeSymbol)typeTo, operand, used, inPlace: false))
{
EmitExpression(operand, used);
if (used)
{
_builder.EmitOpCode(ILOpCode.Call, 0);
EmitSymbolToken(conversion.SymbolOpt, conversion.Syntax, optArgList: null);
}
}
}
private void EmitConversion(BoundConversion conversion)
{
switch (conversion.ConversionKind)
......
......@@ -1900,27 +1900,39 @@ private void EmitObjectCreationExpression(BoundObjectCreationExpression expressi
}
else
{
// check if need to construct at all
if (!used && ConstructorNotSideEffecting(constructor))
{
// creating nullable has no side-effects, so we will just evaluate the arguments
// ctor has no side-effects, so we will just evaluate the arguments
foreach (var arg in expression.Arguments)
{
EmitExpression(arg, used: false);
}
return;
}
else
// ReadOnlySpan may just refer to the blob, if possible.
if (this._module.Compilation.IsReadOnlySpanType(expression.Type) &&
expression.Arguments.Length == 1)
{
EmitArguments(expression.Arguments, constructor.Parameters, expression.ArgumentRefKindsOpt);
if (TryEmitReadonlySpanAsBlobWrapper((NamedTypeSymbol)expression.Type, expression.Arguments[0], used, inPlace: false))
{
return;
}
}
var stackAdjustment = GetObjCreationStackBehavior(expression);
_builder.EmitOpCode(ILOpCode.Newobj, stackAdjustment);
// none of the above cases, so just create an instance
EmitArguments(expression.Arguments, constructor.Parameters, expression.ArgumentRefKindsOpt);
// for variadic ctors emit expanded ctor token
EmitSymbolToken(constructor, expression.Syntax,
constructor.IsVararg ? (BoundArgListOperator)expression.Arguments[expression.Arguments.Length - 1] : null);
var stackAdjustment = GetObjCreationStackBehavior(expression);
_builder.EmitOpCode(ILOpCode.Newobj, stackAdjustment);
EmitPopIfUnused(used);
}
// for variadic ctors emit expanded ctor token
EmitSymbolToken(constructor, expression.Syntax,
constructor.IsVararg ? (BoundArgListOperator)expression.Arguments[expression.Arguments.Length - 1] : null);
EmitPopIfUnused(used);
}
}
......@@ -2124,9 +2136,24 @@ private void InPlaceInit(BoundExpression target, bool used)
private void InPlaceCtorCall(BoundExpression target, BoundObjectCreationExpression objCreation, bool used)
{
Debug.Assert(TargetIsNotOnHeap(target), "in-place construction target should not be on heap");
var temp = EmitAddress(target, AddressKind.Writeable);
Debug.Assert(temp == null, "in-place ctor target should not create temps");
// ReadOnlySpan may just refer to the blob, if possible.
if (this._module.Compilation.IsReadOnlySpanType(objCreation.Type) && objCreation.Arguments.Length == 1)
{
if (TryEmitReadonlySpanAsBlobWrapper((NamedTypeSymbol)objCreation.Type, objCreation.Arguments[0], used, inPlace: true))
{
if (used)
{
EmitExpression(target, used: true);
}
return;
}
}
var constructor = objCreation.Constructor;
EmitArguments(objCreation.Arguments, constructor.Parameters, objCreation.ArgumentRefKindsOpt);
// -2 to adjust for consumed target address and not produced value.
......@@ -2138,7 +2165,6 @@ private void InPlaceCtorCall(BoundExpression target, BoundObjectCreationExpressi
if (used)
{
Debug.Assert(TargetIsNotOnHeap(target), "cannot read-back the target since it could have been modified");
EmitExpression(target, used: true);
}
}
......
......@@ -1014,8 +1014,15 @@ private static BoundExpression NullableAlwaysHasValue(BoundExpression expression
}
}
// do not rewrite user defined conversion
// - in expression trees or
// - special situations when converting to ReadOnlySpan, which has special support in codegen
var doNotLowerToCall = _inExpressionLambda ||
(rewrittenOperand.Type.IsArray()) &&
_compilation.IsReadOnlySpanType(rewrittenType);
BoundExpression result =
_inExpressionLambda
doNotLowerToCall
? BoundConversion.Synthesized(syntax, rewrittenOperand, conversion, false, true, default(ConstantValue), rewrittenType)
: (BoundExpression)BoundCall.Synthesized(syntax, null, conversion.Method, rewrittenOperand);
Debug.Assert(result.Type == rewrittenType);
......
......@@ -206,6 +206,11 @@ internal bool IsExceptionType(TypeSymbol type, ref HashSet<DiagnosticInfo> useSi
return IsEqualOrDerivedFromWellKnownClass(type, WellKnownType.System_Exception, ref useSiteDiagnostics);
}
internal bool IsReadOnlySpanType(TypeSymbol type)
{
return type.OriginalDefinition == GetWellKnownType(WellKnownType.System_ReadOnlySpan_T);
}
internal bool IsEqualOrDerivedFromWellKnownClass(TypeSymbol type, WellKnownType wellKnownType, ref HashSet<DiagnosticInfo> useSiteDiagnostics)
{
Debug.Assert(wellKnownType == WellKnownType.System_Attribute ||
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen
{
public class CodeGenReadOnlySpanConstructionTest : CSharpTestBase
{
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void EmptyOrNullStringConv()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
var s1 = (ReadOnlySpan<char>)"""";
var s2 = (ReadOnlySpan<char>)"""";
Console.Write(s1.Length == s2.Length);
s1 = (ReadOnlySpan<char>)(string)null;
s2 = (ReadOnlySpan<char>)(string)null;
Console.Write(s1.Length == s2.Length);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "TrueTrue", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 79 (0x4f)
.maxstack 2
.locals init (System.ReadOnlySpan<char> V_0, //s1
System.ReadOnlySpan<char> V_1) //s2
IL_0000: ldstr """"
IL_0005: call ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.op_Implicit(string)""
IL_000a: stloc.0
IL_000b: ldstr """"
IL_0010: call ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.op_Implicit(string)""
IL_0015: stloc.1
IL_0016: ldloca.s V_0
IL_0018: call ""int System.ReadOnlySpan<char>.Length.get""
IL_001d: ldloca.s V_1
IL_001f: call ""int System.ReadOnlySpan<char>.Length.get""
IL_0024: ceq
IL_0026: call ""void System.Console.Write(bool)""
IL_002b: ldnull
IL_002c: call ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.op_Implicit(string)""
IL_0031: stloc.0
IL_0032: ldnull
IL_0033: call ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.op_Implicit(string)""
IL_0038: stloc.1
IL_0039: ldloca.s V_0
IL_003b: call ""int System.ReadOnlySpan<char>.Length.get""
IL_0040: ldloca.s V_1
IL_0042: call ""int System.ReadOnlySpan<char>.Length.get""
IL_0047: ceq
IL_0049: call ""void System.Console.Write(bool)""
IL_004e: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void EmptyOrNullArrayConv()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
var s1 = (ReadOnlySpan<byte>)new byte[]{};
var s2 = (ReadOnlySpan<byte>)new byte[]{};
Console.Write(s1.Length == s2.Length);
s1 = (ReadOnlySpan<byte>)(byte[])null;
s2 = (ReadOnlySpan<byte>)(byte[])null;
Console.Write(s1.Length == s2.Length);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "TrueTrue", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 77 (0x4d)
.maxstack 2
.locals init (System.ReadOnlySpan<byte> V_0, //s1
System.ReadOnlySpan<byte> V_1, //s2
System.ReadOnlySpan<byte> V_2)
IL_0000: ldloca.s V_2
IL_0002: initobj ""System.ReadOnlySpan<byte>""
IL_0008: ldloc.2
IL_0009: stloc.0
IL_000a: ldloca.s V_2
IL_000c: initobj ""System.ReadOnlySpan<byte>""
IL_0012: ldloc.2
IL_0013: stloc.1
IL_0014: ldloca.s V_0
IL_0016: call ""int System.ReadOnlySpan<byte>.Length.get""
IL_001b: ldloca.s V_1
IL_001d: call ""int System.ReadOnlySpan<byte>.Length.get""
IL_0022: ceq
IL_0024: call ""void System.Console.Write(bool)""
IL_0029: ldnull
IL_002a: call ""System.ReadOnlySpan<byte> System.ReadOnlySpan<byte>.op_Implicit(byte[])""
IL_002f: stloc.0
IL_0030: ldnull
IL_0031: call ""System.ReadOnlySpan<byte> System.ReadOnlySpan<byte>.op_Implicit(byte[])""
IL_0036: stloc.1
IL_0037: ldloca.s V_0
IL_0039: call ""int System.ReadOnlySpan<byte>.Length.get""
IL_003e: ldloca.s V_1
IL_0040: call ""int System.ReadOnlySpan<byte>.Length.get""
IL_0045: ceq
IL_0047: call ""void System.Console.Write(bool)""
IL_004c: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void EmptyArrayCtor()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
// inplace inits
var s1 = new ReadOnlySpan<sbyte>(new sbyte[]{});
var s2 = new ReadOnlySpan<sbyte>(new sbyte[]{});
Console.Write(s1.Length == s2.Length);
// make an instance
Console.Write(s1.Length == new ReadOnlySpan<sbyte>(new sbyte[]{}).Length);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "TrueTrue", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 69 (0x45)
.maxstack 2
.locals init (System.ReadOnlySpan<sbyte> V_0, //s1
System.ReadOnlySpan<sbyte> V_1, //s2
System.ReadOnlySpan<sbyte> V_2)
IL_0000: ldloca.s V_0
IL_0002: initobj ""System.ReadOnlySpan<sbyte>""
IL_0008: ldloca.s V_1
IL_000a: initobj ""System.ReadOnlySpan<sbyte>""
IL_0010: ldloca.s V_0
IL_0012: call ""int System.ReadOnlySpan<sbyte>.Length.get""
IL_0017: ldloca.s V_1
IL_0019: call ""int System.ReadOnlySpan<sbyte>.Length.get""
IL_001e: ceq
IL_0020: call ""void System.Console.Write(bool)""
IL_0025: ldloca.s V_0
IL_0027: call ""int System.ReadOnlySpan<sbyte>.Length.get""
IL_002c: ldloca.s V_2
IL_002e: initobj ""System.ReadOnlySpan<sbyte>""
IL_0034: ldloc.2
IL_0035: stloc.2
IL_0036: ldloca.s V_2
IL_0038: call ""int System.ReadOnlySpan<sbyte>.Length.get""
IL_003d: ceq
IL_003f: call ""void System.Console.Write(bool)""
IL_0044: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void NotConstArrayCtor()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
static int[] arr = new int[]{1, 2, int.Parse(""3""), 4};
public static void Main()
{
var s1 = new ReadOnlySpan<int>(new int[]{1, 2, int.Parse(""3""), 4});
var s2 = new ReadOnlySpan<int>(arr);
Console.Write(s1[2] == s2[2]);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "True", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 75 (0x4b)
.maxstack 5
.locals init (System.ReadOnlySpan<int> V_0, //s1
System.ReadOnlySpan<int> V_1) //s2
IL_0000: ldloca.s V_0
IL_0002: ldc.i4.4
IL_0003: newarr ""int""
IL_0008: dup
IL_0009: ldtoken ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=16 <PrivateImplementationDetails>.0304DE2B7DF2D15400D2997C7318A0237A5E33D3""
IL_000e: call ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
IL_0013: dup
IL_0014: ldc.i4.2
IL_0015: ldstr ""3""
IL_001a: call ""int int.Parse(string)""
IL_001f: stelem.i4
IL_0020: call ""System.ReadOnlySpan<int>..ctor(int[])""
IL_0025: ldloca.s V_1
IL_0027: ldsfld ""int[] Test.arr""
IL_002c: call ""System.ReadOnlySpan<int>..ctor(int[])""
IL_0031: ldloca.s V_0
IL_0033: ldc.i4.2
IL_0034: call ""ref readonly int System.ReadOnlySpan<int>.this[int].get""
IL_0039: ldind.i4
IL_003a: ldloca.s V_1
IL_003c: ldc.i4.2
IL_003d: call ""ref readonly int System.ReadOnlySpan<int>.this[int].get""
IL_0042: ldind.i4
IL_0043: ceq
IL_0045: call ""void System.Console.Write(bool)""
IL_004a: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void NotBlittableArrayConv()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
var s1 = (ReadOnlySpan<object>)new object[]{1, 2, int.Parse(""3""), 4};
Console.Write(s1[2]);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "3", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 72 (0x48)
.maxstack 4
.locals init (System.ReadOnlySpan<object> V_0) //s1
IL_0000: ldc.i4.4
IL_0001: newarr ""object""
IL_0006: dup
IL_0007: ldc.i4.0
IL_0008: ldc.i4.1
IL_0009: box ""int""
IL_000e: stelem.ref
IL_000f: dup
IL_0010: ldc.i4.1
IL_0011: ldc.i4.2
IL_0012: box ""int""
IL_0017: stelem.ref
IL_0018: dup
IL_0019: ldc.i4.2
IL_001a: ldstr ""3""
IL_001f: call ""int int.Parse(string)""
IL_0024: box ""int""
IL_0029: stelem.ref
IL_002a: dup
IL_002b: ldc.i4.3
IL_002c: ldc.i4.4
IL_002d: box ""int""
IL_0032: stelem.ref
IL_0033: call ""System.ReadOnlySpan<object> System.ReadOnlySpan<object>.op_Implicit(object[])""
IL_0038: stloc.0
IL_0039: ldloca.s V_0
IL_003b: ldc.i4.2
IL_003c: call ""ref readonly object System.ReadOnlySpan<object>.this[int].get""
IL_0041: ldind.ref
IL_0042: call ""void System.Console.Write(object)""
IL_0047: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void EnumArrayCtor()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
// inplace
var s1 = new ReadOnlySpan<Color>(new Color[] {Color.Red, Color.Green, Color.Blue});
Console.Write(s1[2]);
// new instance
Console.Write(s1[1] == new ReadOnlySpan<Color>(new Color[] { Color.Red, Color.Green, Color.Blue })[1]);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "BlueTrue", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
// Code size 70 (0x46)
.maxstack 3
.locals init (System.ReadOnlySpan<System.Color> V_0, //s1
System.ReadOnlySpan<System.Color> V_1)
IL_0000: ldloca.s V_0
IL_0002: ldsflda ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.0C7A623FD2BBC05B06423BE359E4021D36E721AD""
IL_0007: ldc.i4.3
IL_0008: call ""System.ReadOnlySpan<System.Color>..ctor(void*, int)""
IL_000d: ldloca.s V_0
IL_000f: ldc.i4.2
IL_0010: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_0015: ldind.i1
IL_0016: box ""System.Color""
IL_001b: call ""void System.Console.Write(object)""
IL_0020: ldloca.s V_0
IL_0022: ldc.i4.1
IL_0023: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_0028: ldind.i1
IL_0029: ldsflda ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.0C7A623FD2BBC05B06423BE359E4021D36E721AD""
IL_002e: ldc.i4.3
IL_002f: newobj ""System.ReadOnlySpan<System.Color>..ctor(void*, int)""
IL_0034: stloc.1
IL_0035: ldloca.s V_1
IL_0037: ldc.i4.1
IL_0038: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_003d: ldind.i1
IL_003e: ceq
IL_0040: call ""void System.Console.Write(bool)""
IL_0045: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void EnumArrayCtorPEverify()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
// inplace
var s1 = new ReadOnlySpan<Color>(new Color[] {Color.Red, Color.Green, Color.Blue});
Console.Write(s1[2]);
// new instance
Console.Write(s1[1] == new ReadOnlySpan<Color>(new Color[] { Color.Red, Color.Green, Color.Blue })[1]);
}
}
", TestOptions.ReleaseExe, parseOptions: TestOptions.Regular.WithPEVerifyCompatFeature());
CompileAndVerify(comp, expectedOutput: "BlueTrue", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 86 (0x56)
.maxstack 5
.locals init (System.ReadOnlySpan<System.Color> V_0, //s1
System.ReadOnlySpan<System.Color> V_1)
IL_0000: ldloca.s V_0
IL_0002: ldc.i4.3
IL_0003: newarr ""System.Color""
IL_0008: dup
IL_0009: ldc.i4.1
IL_000a: ldc.i4.1
IL_000b: stelem.i1
IL_000c: dup
IL_000d: ldc.i4.2
IL_000e: ldc.i4.2
IL_000f: stelem.i1
IL_0010: call ""System.ReadOnlySpan<System.Color>..ctor(System.Color[])""
IL_0015: ldloca.s V_0
IL_0017: ldc.i4.2
IL_0018: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_001d: ldind.i1
IL_001e: box ""System.Color""
IL_0023: call ""void System.Console.Write(object)""
IL_0028: ldloca.s V_0
IL_002a: ldc.i4.1
IL_002b: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_0030: ldind.i1
IL_0031: ldc.i4.3
IL_0032: newarr ""System.Color""
IL_0037: dup
IL_0038: ldc.i4.1
IL_0039: ldc.i4.1
IL_003a: stelem.i1
IL_003b: dup
IL_003c: ldc.i4.2
IL_003d: ldc.i4.2
IL_003e: stelem.i1
IL_003f: newobj ""System.ReadOnlySpan<System.Color>..ctor(System.Color[])""
IL_0044: stloc.1
IL_0045: ldloca.s V_1
IL_0047: ldc.i4.1
IL_0048: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_004d: ldind.i1
IL_004e: ceq
IL_0050: call ""void System.Console.Write(bool)""
IL_0055: ret
}");
}
[Fact]
[WorkItem(23358, "https://github.com/dotnet/roslyn/issues/23358")]
public void ConvInMethodCall()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
Test1<char, byte>(""QWERTYUIOP"", new byte[]{1,2,3,4,5,6,7,8,9,10});
}
public static void Test1<T, U>(ReadOnlySpan<T> arg1, ReadOnlySpan<U> arg2)
{
Console.Write(arg1[arg1.Length - 1]);
Console.Write(arg2[arg1.Length - 1]);
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "P10", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
// Code size 28 (0x1c)
.maxstack 3
IL_0000: ldstr ""QWERTYUIOP""
IL_0005: call ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.op_Implicit(string)""
IL_000a: ldsflda ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=10 <PrivateImplementationDetails>.C5391E308AF25B42D5934D6A201A34E898D255C6""
IL_000f: ldc.i4.s 10
IL_0011: newobj ""System.ReadOnlySpan<byte>..ctor(void*, int)""
IL_0016: call ""void Test.Test1<char, byte>(System.ReadOnlySpan<char>, System.ReadOnlySpan<byte>)""
IL_001b: ret
}");
}
}
}
......@@ -431,7 +431,7 @@ class Test
{
public static void Main()
{
var sp = new ReadOnlySpan<int>(new[] {1, 2, 3});
var sp = new ReadOnlySpan<Color>(new [] {Color.Red, Color.Green, Color.Blue});
foreach(var i in sp)
{
Console.Write(i);
......@@ -441,36 +441,132 @@ public static void Main()
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "123").VerifyIL("Test.Main", @"
CompileAndVerify(comp, expectedOutput: "RedGreenBlue", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
// Code size 56 (0x38)
.maxstack 3
.locals init (System.ReadOnlySpan<int> V_0,
// Code size 50 (0x32)
.maxstack 2
.locals init (System.ReadOnlySpan<System.Color> V_0,
int V_1)
IL_0000: ldc.i4.3
IL_0001: newarr ""int""
IL_0006: dup
IL_0007: ldtoken ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.E429CCA3F703A39CC5954A6572FEC9086135B34E""
IL_000c: call ""void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)""
IL_0011: newobj ""System.ReadOnlySpan<int>..ctor(int[])""
IL_0016: stloc.0
IL_0017: ldc.i4.0
IL_0018: stloc.1
IL_0019: br.s IL_002d
IL_001b: ldloca.s V_0
IL_0000: ldsflda ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.0C7A623FD2BBC05B06423BE359E4021D36E721AD""
IL_0005: ldc.i4.3
IL_0006: newobj ""System.ReadOnlySpan<System.Color>..ctor(void*, int)""
IL_000b: stloc.0
IL_000c: ldc.i4.0
IL_000d: stloc.1
IL_000e: br.s IL_0027
IL_0010: ldloca.s V_0
IL_0012: ldloc.1
IL_0013: call ""ref readonly System.Color System.ReadOnlySpan<System.Color>.this[int].get""
IL_0018: ldind.i1
IL_0019: box ""System.Color""
IL_001e: call ""void System.Console.Write(object)""
IL_0023: ldloc.1
IL_0024: ldc.i4.1
IL_0025: add
IL_0026: stloc.1
IL_0027: ldloc.1
IL_0028: ldloca.s V_0
IL_002a: call ""int System.ReadOnlySpan<System.Color>.Length.get""
IL_002f: blt.s IL_0010
IL_0031: ret
}");
}
[Fact]
public void TestReadOnlySpanString()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
var sp = (ReadOnlySpan<char>)""hello"";
foreach(var i in sp)
{
Console.Write(i);
}
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "hello", verify: Verification.Passes).VerifyIL("Test.Main", @"
{
// Code size 44 (0x2c)
.maxstack 2
.locals init (System.ReadOnlySpan<char> V_0,
int V_1)
IL_0000: ldstr ""hello""
IL_0005: call ""System.ReadOnlySpan<char> System.ReadOnlySpan<char>.op_Implicit(string)""
IL_000a: stloc.0
IL_000b: ldc.i4.0
IL_000c: stloc.1
IL_000d: br.s IL_0021
IL_000f: ldloca.s V_0
IL_0011: ldloc.1
IL_0012: call ""ref readonly char System.ReadOnlySpan<char>.this[int].get""
IL_0017: ldind.u2
IL_0018: call ""void System.Console.Write(char)""
IL_001d: ldloc.1
IL_001e: call ""ref readonly int System.ReadOnlySpan<int>.this[int].get""
IL_0023: ldind.i4
IL_0024: call ""void System.Console.Write(int)""
IL_0029: ldloc.1
IL_002a: ldc.i4.1
IL_002b: add
IL_002c: stloc.1
IL_002d: ldloc.1
IL_002e: ldloca.s V_0
IL_0030: call ""int System.ReadOnlySpan<int>.Length.get""
IL_0035: blt.s IL_001b
IL_0037: ret
IL_001e: ldc.i4.1
IL_001f: add
IL_0020: stloc.1
IL_0021: ldloc.1
IL_0022: ldloca.s V_0
IL_0024: call ""int System.ReadOnlySpan<char>.Length.get""
IL_0029: blt.s IL_000f
IL_002b: ret
}");
}
[Fact]
public void TestReadOnlySpan2()
{
var comp = CreateCompilationWithMscorlibAndSpan(@"
using System;
class Test
{
public static void Main()
{
foreach(var i in (ReadOnlySpan<byte>)new byte[] {1, 2, 3})
{
Console.Write(i);
}
}
}
", TestOptions.ReleaseExe);
CompileAndVerify(comp, expectedOutput: "123", verify: Verification.Fails).VerifyIL("Test.Main", @"
{
// Code size 45 (0x2d)
.maxstack 2
.locals init (System.ReadOnlySpan<byte> V_0,
int V_1)
IL_0000: ldsflda ""<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.7037807198C22A7D2B0807371D763779A84FDFCF""
IL_0005: ldc.i4.3
IL_0006: newobj ""System.ReadOnlySpan<byte>..ctor(void*, int)""
IL_000b: stloc.0
IL_000c: ldc.i4.0
IL_000d: stloc.1
IL_000e: br.s IL_0022
IL_0010: ldloca.s V_0
IL_0012: ldloc.1
IL_0013: call ""ref readonly byte System.ReadOnlySpan<byte>.this[int].get""
IL_0018: ldind.u1
IL_0019: call ""void System.Console.Write(int)""
IL_001e: ldloc.1
IL_001f: ldc.i4.1
IL_0020: add
IL_0021: stloc.1
IL_0022: ldloc.1
IL_0023: ldloca.s V_0
IL_0025: call ""int System.ReadOnlySpan<byte>.Length.get""
IL_002a: blt.s IL_0010
IL_002c: ret
}");
}
......
......@@ -863,6 +863,7 @@ public void AllWellKnownTypeMembers()
case WellKnownMember.System_Span_T__ctor:
case WellKnownMember.System_Span_T__get_Item:
case WellKnownMember.System_Span_T__get_Length:
case WellKnownMember.System_ReadOnlySpan_T__ctor:
case WellKnownMember.System_ReadOnlySpan_T__get_Item:
case WellKnownMember.System_ReadOnlySpan_T__get_Length:
// Not yet in the platform.
......
......@@ -87,6 +87,15 @@ internal void EmitArrayBlockInitializer(ImmutableArray<byte> data, SyntaxNode sy
EmitToken(initializeArray, syntaxNode, diagnostics);
}
internal void EmitArrayBlockFieldRef(ImmutableArray<byte> data, ITypeSymbol elementType, SyntaxNode syntaxNode, DiagnosticBag diagnostics)
{
// map a field to the block (that makes it addressable)
var field = module.GetFieldForData(data, syntaxNode, diagnostics);
EmitOpCode(ILOpCode.Ldsflda);
EmitToken(field, syntaxNode, diagnostics);
}
/// <summary>
/// Mark current IL position with a label
/// </summary>
......
......@@ -427,6 +427,7 @@ internal enum WellKnownMember
System_Span_T__get_Item,
System_Span_T__get_Length,
System_ReadOnlySpan_T__ctor,
System_ReadOnlySpan_T__get_Item,
System_ReadOnlySpan_T__get_Length,
......
......@@ -2945,6 +2945,15 @@ static WellKnownMembers()
0, // Method Signature
(byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32,
// System_ReadOnlySpan__ctor
(byte)(MemberFlags.Constructor), // Flags
(byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), // DeclaringTypeId
0, // Arity
2, // Method Signature
(byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void,
(byte)SignatureTypeCode.Pointer, (byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Void,
(byte)SignatureTypeCode.TypeHandle, (byte)SpecialType.System_Int32,
// System_ReadOnlySpan__get_Item
(byte)(MemberFlags.PropertyGet), // Flags
(byte)WellKnownType.ExtSentinel, (byte)(WellKnownType.System_ReadOnlySpan_T - WellKnownType.ExtSentinel), // DeclaringTypeId
......@@ -3326,6 +3335,7 @@ static WellKnownMembers()
".ctor", // System_Span__ctor
"get_Item", // System_Span__get_Item
"get_Length", // System_Span__get_Length
".ctor", // System_ReadOnlySpan__ctor
"get_Item", // System_ReadOnlySpan__get_Item
"get_Length", // System_ReadOnlySpan__get_Length
};
......
......@@ -1228,7 +1228,7 @@ namespace System
unsafe public Span(void* pointer, int length)
{
this.arr = null;
this.arr = Helpers.ToArray<T>(pointer, length);
this.Length = length;
}
......@@ -1278,6 +1278,8 @@ public bool MoveNext()
get => ref _span[_index];
}
}
public static implicit operator Span<T>(T[] array) => new Span<T>(array);
}
public readonly ref struct ReadOnlySpan<T>
......@@ -1288,6 +1290,12 @@ public bool MoveNext()
public override int GetHashCode() => 2;
public int Length { get; }
unsafe public ReadOnlySpan(void* pointer, int length)
{
this.arr = Helpers.ToArray<T>(pointer, length);
this.Length = length;
}
public ReadOnlySpan(T[] arr)
{
this.arr = arr;
......@@ -1334,12 +1342,80 @@ public bool MoveNext()
get => ref _span[_index];
}
}
public static implicit operator ReadOnlySpan<T>(T[] array) => array == null? default: new ReadOnlySpan<T>(array);
public static implicit operator ReadOnlySpan<T>(string stringValue) => string.IsNullOrEmpty(stringValue)? default: new ReadOnlySpan<T>((T[])(object)stringValue.ToCharArray());
}
public readonly ref struct SpanLike<T>
{
public readonly Span<T> field;
}
public enum Color: sbyte
{
Red,
Green,
Blue
}
public static unsafe class Helpers
{
public static T[] ToArray<T>(void* ptr, int count)
{
if (ptr == null)
{
return null;
}
if (typeof(T) == typeof(int))
{
var arr = new int[count];
for(int i = 0; i < count; i++)
{
arr[i] = ((int*)ptr)[i];
}
return (T[])(object)arr;
}
if (typeof(T) == typeof(byte))
{
var arr = new byte[count];
for(int i = 0; i < count; i++)
{
arr[i] = ((byte*)ptr)[i];
}
return (T[])(object)arr;
}
if (typeof(T) == typeof(char))
{
var arr = new char[count];
for(int i = 0; i < count; i++)
{
arr[i] = ((char*)ptr)[i];
}
return (T[])(object)arr;
}
if (typeof(T) == typeof(Color))
{
var arr = new Color[count];
for(int i = 0; i < count; i++)
{
arr[i] = ((Color*)ptr)[i];
}
return (T[])(object)arr;
}
throw new Exception(""add a case for: "" + typeof(T));
}
}
}";
#endregion
}
......
......@@ -589,6 +589,7 @@ End Namespace
WellKnownMember.System_Span_T__ctor,
WellKnownMember.System_Span_T__get_Item,
WellKnownMember.System_Span_T__get_Length,
WellKnownMember.System_ReadOnlySpan_T__ctor,
WellKnownMember.System_ReadOnlySpan_T__get_Item,
WellKnownMember.System_ReadOnlySpan_T__get_Length
' Not available yet, but will be in upcoming release.
......@@ -677,6 +678,7 @@ End Namespace
WellKnownMember.System_Span_T__ctor,
WellKnownMember.System_Span_T__get_Item,
WellKnownMember.System_Span_T__get_Length,
WellKnownMember.System_ReadOnlySpan_T__ctor,
WellKnownMember.System_ReadOnlySpan_T__get_Item,
WellKnownMember.System_ReadOnlySpan_T__get_Length
' Not available yet, but will be in upcoming release.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册