C
C#6mo ago
Tacti Tacoz

collection expression on Span<T>

Just a quick question, wasn't something like
void TestMethod(Span<int> span);

TestMethod([1]);

//Suppose to emit something like:
var _arg = 1;
TestMethod(new(&_arg, 1));
//Or
Span<int> span = stackalloc int[1];
span[0] = 1;
TestMethod(span);
void TestMethod(Span<int> span);

TestMethod([1]);

//Suppose to emit something like:
var _arg = 1;
TestMethod(new(&_arg, 1));
//Or
Span<int> span = stackalloc int[1];
span[0] = 1;
TestMethod(span);
Did they skip anti allocation improvements like this initially? right now it just allocates an array.
5 Replies
Thinker
Thinker6mo ago
It should use stackalloc. Not sure why it's using an array, ask in #roslyn perhaps.
jcotton42
jcotton426mo ago
public class C
{
public void M(Span<int> span)
{
}

public void M2()
{
<>y__InlineArray1<int> buffer = default(<>y__InlineArray1<int>);
<PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray1<int>, int>(ref buffer, 0) = 1;
M(<PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray1<int>, int>(ref buffer, 1));
}
}
[StructLayout(LayoutKind.Auto)]
[InlineArray(1)]
internal struct <>y__InlineArray1<T>
{
[CompilerGenerated]
private T _element0;
}
public class C
{
public void M(Span<int> span)
{
}

public void M2()
{
<>y__InlineArray1<int> buffer = default(<>y__InlineArray1<int>);
<PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray1<int>, int>(ref buffer, 0) = 1;
M(<PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray1<int>, int>(ref buffer, 1));
}
}
[StructLayout(LayoutKind.Auto)]
[InlineArray(1)]
internal struct <>y__InlineArray1<T>
{
[CompilerGenerated]
private T _element0;
}
Tacti Tacoz
Tacti Tacoz6mo ago
Hmm maybe it doesn't do it when targeting .net framework? or net standard for that matter:
IL_0000: ldarg.0 // this
IL_0001: ldc.i4.1
IL_0002: newarr [netstandard]System.Int32
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldc.i4.1
IL_000a: stelem.i4
IL_000b: newobj instance void valuetype [netstandard]System.Span`1<int32>::.ctor(!0/*int32*/[])
IL_0010: call instance void Test::test(valuetype [netstandard]System.Span`1<int32>)
IL_0000: ldarg.0 // this
IL_0001: ldc.i4.1
IL_0002: newarr [netstandard]System.Int32
IL_0007: dup
IL_0008: ldc.i4.0
IL_0009: ldc.i4.1
IL_000a: stelem.i4
IL_000b: newobj instance void valuetype [netstandard]System.Span`1<int32>::.ctor(!0/*int32*/[])
IL_0010: call instance void Test::test(valuetype [netstandard]System.Span`1<int32>)
. Not in .net 7.0 either:
IL_0000: ldc.i4.1
IL_0001: newarr [System.Runtime]System.Int32
IL_0006: dup
IL_0007: ldc.i4.0
IL_0008: ldc.i4.1
IL_0009: stelem.i4
IL_000a: newobj instance void valuetype [System.Runtime]System.Span`1<int32>::.ctor(!0/*int32*/[])
IL_000f: call void Testt::Test(valuetype [System.Runtime]System.Span`1<int32>)
IL_0000: ldc.i4.1
IL_0001: newarr [System.Runtime]System.Int32
IL_0006: dup
IL_0007: ldc.i4.0
IL_0008: ldc.i4.1
IL_0009: stelem.i4
IL_000a: newobj instance void valuetype [System.Runtime]System.Span`1<int32>::.ctor(!0/*int32*/[])
IL_000f: call void Testt::Test(valuetype [System.Runtime]System.Span`1<int32>)
It's .net 8.0 only:
IL_0000: ldloca.s V_1
IL_0002: initobj valuetype '<>y__InlineArray1`1'<int32>
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.0
IL_000b: call !!1/*int32*/& '<PrivateImplementationDetails>'::InlineArrayElementRef<valuetype '<>y__InlineArray1`1'<int32>, int32>(!!0/*valuetype '<>y__InlineArray1`1'<int32>*/&, int32)
IL_0010: ldc.i4.1
IL_0011: stind.i4
IL_0012: ldloca.s V_1
IL_0014: ldc.i4.1
IL_0015: call valuetype [System.Runtime]System.Span`1<!!1/*int32*/> '<PrivateImplementationDetails>'::InlineArrayAsSpan<valuetype '<>y__InlineArray1`1'<int32>, int32>(!!0/*valuetype '<>y__InlineArray1`1'<int32>*/&, int32)
IL_001a: call void Test::Test(valuetype [System.Runtime]System.Span`1<int32>)
IL_0000: ldloca.s V_1
IL_0002: initobj valuetype '<>y__InlineArray1`1'<int32>
IL_0008: ldloca.s V_1
IL_000a: ldc.i4.0
IL_000b: call !!1/*int32*/& '<PrivateImplementationDetails>'::InlineArrayElementRef<valuetype '<>y__InlineArray1`1'<int32>, int32>(!!0/*valuetype '<>y__InlineArray1`1'<int32>*/&, int32)
IL_0010: ldc.i4.1
IL_0011: stind.i4
IL_0012: ldloca.s V_1
IL_0014: ldc.i4.1
IL_0015: call valuetype [System.Runtime]System.Span`1<!!1/*int32*/> '<PrivateImplementationDetails>'::InlineArrayAsSpan<valuetype '<>y__InlineArray1`1'<int32>, int32>(!!0/*valuetype '<>y__InlineArray1`1'<int32>*/&, int32)
IL_001a: call void Test::Test(valuetype [System.Runtime]System.Span`1<int32>)
I guess because inline array require runtime support. But was it really too much to ask for them to add a stackalloc fallback though? Seems kinda sloppy to be honest