From 2a48068a7bd924a4b79ac68e1bc44a0ccb1044e8 Mon Sep 17 00:00:00 2001 From: kerams Date: Tue, 23 Mar 2021 21:19:12 +0100 Subject: [PATCH 1/4] Tuple elimination --- src/fsharp/Optimizer.fs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index d7d2d827cce..86e0241ba56 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -1628,6 +1628,10 @@ let rec RearrangeTupleBindings expr fin = | Some b -> Some (mkLetBind m bind b) | None -> None | Expr.Op (TOp.Tuple tupInfo, _, _, _) when not (evalTupInfoIsStruct tupInfo) -> Some (fin expr) + | Expr.Sequential (e1, e2, kind, sp, m) -> + match RearrangeTupleBindings e2 fin with + | Some b -> Some (Expr.Sequential (e1, b, kind, sp, m)) + | None -> None | _ -> None let ExpandStructuralBinding cenv expr = From 26899092bca894a8c9cfc2948270df4268eb0d39 Mon Sep 17 00:00:00 2001 From: kerams Date: Fri, 26 Mar 2021 10:05:40 +0100 Subject: [PATCH 2/4] Add tuple elimination test --- .../EmittedIL/TupleElimination.fs | 132 ++++++++++++++++++ .../FSharp.Compiler.ComponentTests.fsproj | 1 + 2 files changed, 133 insertions(+) create mode 100644 tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs new file mode 100644 index 00000000000..a6b9d33852b --- /dev/null +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs @@ -0,0 +1,132 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.ComponentTests.EmittedIL + +open Xunit +open FSharp.Test.Utilities.Compiler + +module ``TupleElimination`` = + + [] + let ``Sequence expressions with potential side effects do not prevent tuple elimination``() = + FSharp """ +module TupleElimination +open System.Runtime.CompilerServices + +[] +let f () = 3 + +let x () = + let a, b = + "".ToString () |> ignore + System.DateTime.Now |> ignore + "3".ToString () |> ignore + 2, f () + System.DateTime.Now |> ignore + a + b + +let y () = + let a, b, c = + let a = f () + System.Console.ReadKey () |> ignore + a, f (), f () + a + b + c + +let z () = + let a, b, c = + let u, v = 3, 4 + System.Console.ReadKey () |> ignore + f (), f () + u, f () + v + a + b + c + """ + |> compile + |> shouldSucceed + |> verifyIL [ + +// public static int x() +// { +// string text = "".ToString(); +// DateTime now = DateTime.Now; +// text = "3".ToString(); +// int num = TupleElimination.f(); +// now = DateTime.Now; +// return 2 + num; +// } + """ +.method public static int32 x() cil managed +{ + + .maxstack 4 + .locals init (string V_0, + valuetype [runtime]System.DateTime V_1, + int32 V_2) + IL_0000: ldstr "" + IL_0005: callvirt instance string [runtime]System.Object::ToString() + IL_000a: stloc.0 + IL_000b: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0010: stloc.1 + IL_0011: ldstr "3" + IL_0016: callvirt instance string [runtime]System.Object::ToString() + IL_001b: stloc.0 + IL_001c: call int32 TupleElimination::f() + IL_0021: stloc.2 + IL_0022: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0027: stloc.1 + IL_0028: ldc.i4.2 + IL_0029: ldloc.2 + IL_002a: add + IL_002b: ret +} +""" + +// public static int y() +// { +// int num = TupleElimination.f(); +// ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(); +// return num + TupleElimination.f() + TupleElimination.f(); +// } + """ +.method public static int32 y() cil managed +{ + + .maxstack 4 + .locals init (int32 V_0, + valuetype [runtime]System.ConsoleKeyInfo V_1) + IL_0000: call int32 TupleElimination::f() + IL_0005: stloc.0 + IL_0006: call valuetype [runtime]System.ConsoleKeyInfo [runtime]System.Console::ReadKey() + IL_000b: stloc.1 + IL_000c: ldloc.0 + IL_000d: call int32 TupleElimination::f() + IL_0012: add + IL_0013: call int32 TupleElimination::f() + IL_0018: add + IL_0019: ret +} +""" + +// public static int z() +// { +// ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(); +// return TupleElimination.f() + (TupleElimination.f() + 3) + (TupleElimination.f() + 4); +// } + """ +.method public static int32 z() cil managed +{ + + .maxstack 5 + .locals init (valuetype [runtime]System.ConsoleKeyInfo V_0) + IL_0000: call valuetype [runtime]System.ConsoleKeyInfo [runtime]System.Console::ReadKey() + IL_0005: stloc.0 + IL_0006: call int32 TupleElimination::f() + IL_000b: call int32 TupleElimination::f() + IL_0010: ldc.i4.3 + IL_0011: add + IL_0012: add + IL_0013: call int32 TupleElimination::f() + IL_0018: ldc.i4.4 + IL_0019: add + IL_001a: add + IL_001b: ret +} +"""] diff --git a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj index 32b9781dd62..05fc99b3fb7 100644 --- a/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj +++ b/tests/FSharp.Compiler.ComponentTests/FSharp.Compiler.ComponentTests.fsproj @@ -22,6 +22,7 @@ + From bccc04949e42aa00f45431863e3aeb1c55bfa858 Mon Sep 17 00:00:00 2001 From: kerams Date: Fri, 26 Mar 2021 10:23:59 +0100 Subject: [PATCH 3/4] Test fix --- .../EmittedIL/TupleElimination.fs | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs index a6b9d33852b..1a6fee1c486 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs @@ -16,6 +16,9 @@ open System.Runtime.CompilerServices [] let f () = 3 +[] +let sideEffect () = () + let x () = let a, b = "".ToString () |> ignore @@ -28,14 +31,14 @@ let x () = let y () = let a, b, c = let a = f () - System.Console.ReadKey () |> ignore + sideEffect () a, f (), f () a + b + c let z () = let a, b, c = let u, v = 3, 4 - System.Console.ReadKey () |> ignore + sideEffect () f (), f () + u, f () + v a + b + c """ @@ -82,7 +85,7 @@ let z () = // public static int y() // { // int num = TupleElimination.f(); -// ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(); +// TupleElimination.sideEffect(); // return num + TupleElimination.f() + TupleElimination.f(); // } """ @@ -90,43 +93,39 @@ let z () = { .maxstack 4 - .locals init (int32 V_0, - valuetype [runtime]System.ConsoleKeyInfo V_1) + .locals init (int32 V_0) IL_0000: call int32 TupleElimination::f() IL_0005: stloc.0 - IL_0006: call valuetype [runtime]System.ConsoleKeyInfo [runtime]System.Console::ReadKey() - IL_000b: stloc.1 - IL_000c: ldloc.0 - IL_000d: call int32 TupleElimination::f() - IL_0012: add - IL_0013: call int32 TupleElimination::f() - IL_0018: add - IL_0019: ret + IL_0006: call void TupleElimination::sideEffect() + IL_000b: ldloc.0 + IL_000c: call int32 TupleElimination::f() + IL_0011: add + IL_0012: call int32 TupleElimination::f() + IL_0017: add + IL_0018: ret } """ // public static int z() // { -// ConsoleKeyInfo consoleKeyInfo = Console.ReadKey(); +// TupleElimination.sideEffect(); // return TupleElimination.f() + (TupleElimination.f() + 3) + (TupleElimination.f() + 4); // } """ .method public static int32 z() cil managed { - .maxstack 5 - .locals init (valuetype [runtime]System.ConsoleKeyInfo V_0) - IL_0000: call valuetype [runtime]System.ConsoleKeyInfo [runtime]System.Console::ReadKey() - IL_0005: stloc.0 - IL_0006: call int32 TupleElimination::f() - IL_000b: call int32 TupleElimination::f() - IL_0010: ldc.i4.3 + .maxstack 8 + IL_0000: call void TupleElimination::sideEffect() + IL_0005: call int32 TupleElimination::f() + IL_000a: call int32 TupleElimination::f() + IL_000f: ldc.i4.3 + IL_0010: add IL_0011: add - IL_0012: add - IL_0013: call int32 TupleElimination::f() - IL_0018: ldc.i4.4 + IL_0012: call int32 TupleElimination::f() + IL_0017: ldc.i4.4 + IL_0018: add IL_0019: add - IL_001a: add - IL_001b: ret + IL_001a: ret } """] From fe9acea80956527d1a541f7f91c6e32be3468378 Mon Sep 17 00:00:00 2001 From: kerams Date: Mon, 5 Apr 2021 21:05:50 +0200 Subject: [PATCH 4/4] Add more tuple elimination tests --- .../EmittedIL/TupleElimination.fs | 186 +++++++++++++++++- 1 file changed, 185 insertions(+), 1 deletion(-) diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs index 1a6fee1c486..3cfcc2a3b13 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs @@ -19,6 +19,29 @@ let f () = 3 [] let sideEffect () = () +type Test = + [] + static member test(x: int32 * int32) = x + +let v () = + let a, b = + "".ToString () |> ignore + System.DateTime.Now |> ignore + "3".ToString () |> ignore + 2L, f () + System.DateTime.Now |> ignore + a, b + +let w () = + let (a, b) as t = + "".ToString () |> ignore + System.DateTime.Now |> ignore + "3".ToString () |> ignore + 2, f () + System.DateTime.Now |> ignore + let _ = Test.test(t) + a + b + let x () = let a, b = "".ToString () |> ignore @@ -45,6 +68,92 @@ let z () = |> compile |> shouldSucceed |> verifyIL [ + +// public static Tuple v() +// { +// string text = "".ToString(); +// DateTime now = DateTime.Now; +// text = "3".ToString(); +// int item = TupleElimination.f(); +// now = DateTime.Now; +// return new Tuple(2L, item); +// } + """ +.method public static class [runtime]System.Tuple`2 + v() cil managed +{ + + .maxstack 4 + .locals init (string V_0, + valuetype [runtime]System.DateTime V_1, + int32 V_2) + IL_0000: ldstr "" + IL_0005: callvirt instance string [runtime]System.Object::ToString() + IL_000a: stloc.0 + IL_000b: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0010: stloc.1 + IL_0011: ldstr "3" + IL_0016: callvirt instance string [runtime]System.Object::ToString() + IL_001b: stloc.0 + IL_001c: call int32 TupleElimination::f() + IL_0021: stloc.2 + IL_0022: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0027: stloc.1 + IL_0028: ldc.i4.2 + IL_0029: conv.i8 + IL_002a: ldloc.2 + IL_002b: newobj instance void class [runtime]System.Tuple`2::.ctor(!0, + !1) + IL_0030: ret +} +""" + +// public static int w() +// { +// string text = "".ToString(); +// DateTime now = DateTime.Now; +// text = "3".ToString(); +// int num = TupleElimination.f(); +// Tuple x = new Tuple(2, num); +// now = DateTime.Now; +// TupleElimination.Test.test(x); +// return 2 + num; +// } + """ +.method public static int32 w() cil managed +{ + + .maxstack 4 + .locals init (string V_0, + valuetype [runtime]System.DateTime V_1, + int32 V_2, + class [runtime]System.Tuple`2 V_3) + IL_0000: ldstr "" + IL_0005: callvirt instance string [runtime]System.Object::ToString() + IL_000a: stloc.0 + IL_000b: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0010: stloc.1 + IL_0011: ldstr "3" + IL_0016: callvirt instance string [runtime]System.Object::ToString() + IL_001b: stloc.0 + IL_001c: call int32 TupleElimination::f() + IL_0021: stloc.2 + IL_0022: ldc.i4.2 + IL_0023: ldloc.2 + IL_0024: newobj instance void class [runtime]System.Tuple`2::.ctor(!0, + !1) + IL_0029: stloc.3 + IL_002a: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_002f: stloc.1 + IL_0030: ldloc.3 + IL_0031: call class [runtime]System.Tuple`2 TupleElimination/Test::test(class [runtime]System.Tuple`2) + IL_0036: pop + IL_0037: ldc.i4.2 + IL_0038: ldloc.2 + IL_0039: add + IL_003a: ret +} +""" // public static int x() // { @@ -128,4 +237,79 @@ let z () = IL_0019: add IL_001a: ret } -"""] +""" ] + + [] + let ``First class use of tuple prevents elimination``() = + FSharp """ +module TupleElimination +open System.Runtime.CompilerServices + +[] +let f () = 3 + +type Test = + [] + static member test(x: int32 * int32) = x + +let z () = + let a, b = + "".ToString () |> ignore + System.DateTime.Now |> ignore + "3".ToString () |> ignore + Test.test(2, f ()) + System.DateTime.Now |> ignore + a + b + """ + |> compile + |> shouldSucceed + |> verifyIL [ + +// public static int z() +// { +// string text = "".ToString(); +// DateTime now = DateTime.Now; +// text = "3".ToString(); +// Tuple tuple = TupleElimination.Test.test(new Tuple(2, TupleElimination.f())); +// int item = tuple.Item2; +// int item2 = tuple.Item1; +// now = DateTime.Now; +// return item2 + item; +// } + """ +.method public static int32 z() cil managed +{ + + .maxstack 4 + .locals init (class [runtime]System.Tuple`2 V_0, + string V_1, + valuetype [runtime]System.DateTime V_2, + int32 V_3, + int32 V_4) + IL_0000: ldstr "" + IL_0005: callvirt instance string [runtime]System.Object::ToString() + IL_000a: stloc.1 + IL_000b: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0010: stloc.2 + IL_0011: ldstr "3" + IL_0016: callvirt instance string [runtime]System.Object::ToString() + IL_001b: stloc.1 + IL_001c: ldc.i4.2 + IL_001d: call int32 TupleElimination::f() + IL_0022: newobj instance void class [runtime]System.Tuple`2::.ctor(!0, + !1) + IL_0027: call class [runtime]System.Tuple`2 TupleElimination/Test::test(class [runtime]System.Tuple`2) + IL_002c: stloc.0 + IL_002d: ldloc.0 + IL_002e: call instance !1 class [runtime]System.Tuple`2::get_Item2() + IL_0033: stloc.3 + IL_0034: ldloc.0 + IL_0035: call instance !0 class [runtime]System.Tuple`2::get_Item1() + IL_003a: stloc.s V_4 + IL_003c: call valuetype [runtime]System.DateTime [runtime]System.DateTime::get_Now() + IL_0041: stloc.2 + IL_0042: ldloc.s V_4 + IL_0044: ldloc.3 + IL_0045: add + IL_0046: ret +}""" ]