Skip to content

Extraneous temporary and copy in implicit byref parameter #3057

Description

@asik

Using the tuple form of a call that has a byref parameter, if the surrounding code is sufficiently complex, the compiler emits an extraneous temporary and copy.

open System
open System.Collections.Generic

[<EntryPoint>]
let main argv = 
    let dict = Dictionary<int, DateTime>()
    let result, elem = dict.TryGetValue(0)
    let b = 
        if result then
            if elem.Day > 0 then
                Console.WriteLine ""
                true
            else false
        else false
    0

Expected behavior

Only one local is generated and no extra copy happens, i.e. generated code is equivalent to:

    let mutable V_1 = DateTime()
    let result = dict.TryGetValue(0, &V_1)
    let b = 
        if result then
            if V_1.Day > 0 then ...

Actual behavior

Looking at the IL, this generates 2 DateTime locals:

       [1] valuetype [mscorlib]System.DateTime V_1,
       [2] valuetype [mscorlib]System.DateTime V_2,

V_1 is passed into the call, then V_1 is copied into V_2, then V_2 is used. In effect this is equivalent to the following code:

    let mutable V_1 = DateTime()
    let result = dict.TryGetValue(0, &V_1)
    let V_2 = V_1
    let b = 
        if result then
            if V_2.Day > 0 then ...

I tried simplifying this example as much as possible, but at this point removing anything results in good generated code, without the copy, so it seems to depend a lot on the surrounding logic.

This makes a big difference in code that heavily relies on passing large structs by reference, e.g. System.Numerics.Vectors.Matrix4x4 etc; it wastes stack space and time copying. The JIT doesn't optimize this away.

Known workarounds

Don't use the tupled form, use a mutable local and pass it explicitly.

Related information

Visual Studio 2017 15.2 (26430.6).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions