Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection expression span optimization should reuse temps when possible #73269

Open
RikkiGibson opened this issue Apr 29, 2024 · 1 comment
Open
Labels
Area-Compilers Bug Code Gen Quality Room for improvement in the quality of the compiler's generated code New Feature - ParamsCollections
Milestone

Comments

@RikkiGibson
Copy link
Contributor

RikkiGibson commented Apr 29, 2024

Currently it looks like a new temp slot is allocated in IL for each collection expression which uses the InlineArray or ref to single temp lowering strategy. We should figure out how to reuse these temps where possible.

See the following test for reference. Note that separate temps are introduced for both y and z. It should be fairly straightforward to reuse temps in disjunct blocks, but some analysis should enable reuse within the same block as well.

[Fact]
public void Span_SingleElement_TempsAreNotReused()
{
    var source = """
        using System;

        class Program
        {
            static void Main() => M(1);

            static void M(int x)
            {
                {
                    Span<int> y = [x];
                    Console.Write(y[0]);
                    y[0]++;
                }
                {
                    Span<int> y = [x];
                    Console.Write(y[0]);
                }
            }
        }
        """;

    var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11"));
    verifier.VerifyDiagnostics();

    verifier.VerifyIL("Program.M", """
        {
            // Code size       62 (0x3e)
            .maxstack  3
            .locals init (int V_0,
                        int V_1,
                        System.Span<int> V_2, //y
                        System.Span<int> V_3) //y
            IL_0000:  ldarg.0
            IL_0001:  stloc.0
            IL_0002:  ldloca.s   V_0
            IL_0004:  newobj     "System.Span<int>..ctor(ref int)"
            IL_0009:  stloc.2
            IL_000a:  ldloca.s   V_2
            IL_000c:  ldc.i4.0
            IL_000d:  call       "ref int System.Span<int>.this[int].get"
            IL_0012:  ldind.i4
            IL_0013:  call       "void System.Console.Write(int)"
            IL_0018:  ldloca.s   V_2
            IL_001a:  ldc.i4.0
            IL_001b:  call       "ref int System.Span<int>.this[int].get"
            IL_0020:  dup
            IL_0021:  ldind.i4
            IL_0022:  ldc.i4.1
            IL_0023:  add
            IL_0024:  stind.i4
            IL_0025:  ldarg.0
            IL_0026:  stloc.1
            IL_0027:  ldloca.s   V_1
            IL_0029:  newobj     "System.Span<int>..ctor(ref int)"
            IL_002e:  stloc.3
            IL_002f:  ldloca.s   V_3
            IL_0031:  ldc.i4.0
            IL_0032:  call       "ref int System.Span<int>.this[int].get"
            IL_0037:  ldind.i4
            IL_0038:  call       "void System.Console.Write(int)"
            IL_003d:  ret
        }
        """);

    verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11"));
    verifier.VerifyDiagnostics();
}

Originally posted by @jaredpar in #73086 (comment)

@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Apr 29, 2024
@jaredpar
Copy link
Member

jaredpar commented May 1, 2024

The case I'm most concerned about is the params case. It's common to call params methods in groups for example if you're using Console.WriteLine or StringBuilder.AppendFormat it a series of statements. That is a place where the temp locals could build up and a better re-use plan would be valuable.

I also think that scoping the optimization to that makes it more approachable. The advantage of params methods is it's very simple to detect if the collection expression is inaccessible after the method returns. Methods that meet the following criteria can be assured the generated collection expression value cannot live beyond the method invocation:

  1. The type of params is a ref struct
  2. The return type of the method is not a ref struct
  3. There are no parameters that are in / out / ref with a type of a ref struct

@jaredpar jaredpar added Bug Code Gen Quality Room for improvement in the quality of the compiler's generated code New Feature - ParamsCollections and removed untriaged Issues and PRs which have not yet been triaged by a lead labels May 1, 2024
@jaredpar jaredpar added this to the 17.12 milestone May 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Bug Code Gen Quality Room for improvement in the quality of the compiler's generated code New Feature - ParamsCollections
Projects
None yet
Development

No branches or pull requests

2 participants