diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index 88bc0e86ad8..b821928acf4 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -1252,12 +1252,9 @@ let AbstractAndRemapModulInfo msg g m (repackage, hidden) info = // Misc helpers //------------------------------------------------------------------------- -/// Mark some variables (the ones we introduce via abstractBigTargets) as don't-eliminate +// Mark some variables (the ones we introduce via abstractBigTargets) as don't-eliminate let [] suffixForVariablesThatMayNotBeEliminated = "$cont" -/// Indicates a ValRef generated to facilitate tuple eliminations -let [] suffixForTupleElementAssignmentTarget = "tupleElem" - /// Type applications of F# "type functions" may cause side effects, e.g. /// let x<'a> = printfn "hello"; typeof<'a> /// In this case do not treat them as constants. @@ -1278,8 +1275,6 @@ let ValueOfExpr expr = ConstExprValue(0, expr) else UnknownValue -let IsMutableStructuralBindingForTupleElement (vref: ValRef) = vref.IsCompilerGenerated && vref.DisplayName.EndsWith suffixForTupleElementAssignmentTarget - //------------------------------------------------------------------------- // Dead binding elimination //------------------------------------------------------------------------- @@ -1580,12 +1575,7 @@ let MakeStructuralBindingTemp (v: Val) i (arg: Expr) argTy = let name = v.LogicalName + "_" + string i let v, ve = mkCompGenLocal arg.Range name argTy ve, mkCompGenBind v arg - -let MakeMutableStructuralBindingForTupleElement (v: Val) i (arg: Expr) argTy = - let name = sprintf "%s_%d_%s" v.LogicalName i suffixForTupleElementAssignmentTarget - let v, ve = mkMutableCompGenLocal arg.Range name argTy - ve, mkCompGenBind v arg - + let ExpandStructuralBindingRaw cenv expr = assert cenv.settings.ExpandStructuralValues() match expr with @@ -1625,82 +1615,6 @@ let rec RearrangeTupleBindings expr fin = | None -> None | _ -> None -// Attempts to rewrite tuple bindings containing ifs/matches by introducing a mutable local for each tuple element. -// These are assigned to exactly once from each branch in order to eliminate tuple allocations. The tuple binding -// is also rearranged such that OptimizeTupleFieldGet may kick in (see RearrangeTupleBindings comment above). -// First class use of a tuple at the end of any branch prevents this rewrite. -// -// Roughly speaking, the following expression: -// -// let a, b = -// if cond () then -// 1, 2 -// elif cond2 () then -// 3, 4 -// else -// 5, 6 -// in ... -// -// becomes -// -// let mutable a = Unchecked.defaultof<_> -// let mutable b = Unchecked.defaultof<_> -// -// if cond () then -// a <- 1 -// b <- 2 -// elif cond2 () then -// a <- 3 -// b <- 4 -// else -// a <- 5 -// b <- 6 -// in ... -let TryRewriteBranchingTupleBinding g (v: Val) rhs tgtSeqPtOpt body m = - let rec dive g m (requisites: Lazy<_>) expr = - match expr with - | Expr.Match (sp, inputRange, decision, targets, fullRange, ty) -> - // Recurse down every if/match branch - let rewrittenTargets = targets |> Array.choose (fun (TTarget (vals, targetExpr, sp)) -> - match dive g m requisites targetExpr with - | Some rewritten -> TTarget (vals, rewritten, sp) |> Some - | _ -> None) - - // If not all branches can be rewritten, keep the original expression as it is - if rewrittenTargets.Length <> targets.Length then - None - else - Expr.Match (sp, inputRange, decision, rewrittenTargets, fullRange, ty) |> Some - | Expr.Op (TOp.Tuple tupInfo, _, tupleElements, m) when not (evalTupInfoIsStruct tupInfo) -> - // Replace tuple allocation with mutations of locals - let _, _, _, vrefs = requisites.Value - List.map2 (mkValSet m) vrefs tupleElements - |> mkSequentials DebugPointAtSequential.StmtOnly g m - |> Some - | Expr.Sequential (e1, e2, kind, sp, m) -> - match dive g m requisites e2 with - | Some rewritten -> Expr.Sequential (e1, rewritten, kind, sp, m) |> Some - | _ -> None - | Expr.Let (bind, body, m, _) -> - match dive g m requisites body with - | Some rewritten -> mkLetBind m bind rewritten |> Some - | _ -> None - | _ -> None - - let requisites = lazy ( - let argTys = destRefTupleTy g v.Type - let inits = argTys |> List.map (mkNull m) - let ves, binds = List.mapi2 (MakeMutableStructuralBindingForTupleElement v) inits argTys |> List.unzip - let vrefs = binds |> List.map (fun (TBind (v, _, _)) -> mkLocalValRef v) - argTys, ves, binds, vrefs) - - match dive g m requisites rhs with - | Some rewrittenRhs -> - let argTys, ves, binds, _ = requisites.Value - let rhsAndTupleBinding = mkCompGenSequential m rewrittenRhs (mkRefTupled g m ves argTys) - mkLetsBind m binds (mkLet tgtSeqPtOpt m v rhsAndTupleBinding body) |> Some - | _ -> None - let ExpandStructuralBinding cenv expr = assert cenv.settings.ExpandStructuralValues() match expr with @@ -1710,9 +1624,7 @@ let ExpandStructuralBinding cenv expr = CanExpandStructuralBinding v) -> match RearrangeTupleBindings rhs (fun top -> mkLet tgtSeqPtOpt m v top body) with | Some e -> ExpandStructuralBindingRaw cenv e - | None -> - // RearrangeTupleBindings could have failed because the rhs branches - TryRewriteBranchingTupleBinding cenv.g v rhs tgtSeqPtOpt body m |> Option.defaultValue expr + | None -> expr // Expand 'let v = Some arg in ...' to 'let tmp = arg in let v = Some tp in ...' // Used to give names to values of optional arguments prior as we inline. @@ -2591,13 +2503,11 @@ and AddValEqualityInfo g m (v: ValRef) info = // ValValue is information that v = v2, where v2 does not change // So we can't record this information for mutable values. An exception can be made // for "outArg" values arising from method calls since they are only temporarily mutable - // when their address is passed to the method call. Another exception are mutable variables - // created for tuple elimination in branching tuple bindings because they are assigned to - // exactly once. - if not v.IsMutable || IsMutableStructuralBindingForTupleElement v || (v.IsCompilerGenerated && v.DisplayName.StartsWith(PrettyNaming.outArgCompilerGeneratedName)) then - { info with Info = MakeValueInfoForValue g m v info.Info } - else + // when their address is passed to the method call. + if v.IsMutable && not (v.IsCompilerGenerated && v.DisplayName.StartsWith(PrettyNaming.outArgCompilerGeneratedName)) then info + else + {info with Info= MakeValueInfoForValue g m v info.Info} /// Optimize/analyze a use of a value and OptimizeVal cenv env expr (v: ValRef, m) = diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs index 5aaf72f4b69..3cfcc2a3b13 100644 --- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs +++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs @@ -69,17 +69,15 @@ let z () = |> 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); -} -*) +// 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 @@ -110,19 +108,17 @@ public static Tuple v() } """ -(* -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; -} -*) +// 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 { @@ -158,18 +154,16 @@ public static int w() IL_003a: ret } """ - -(* -public static int x() -{ - string text = "".ToString(); - DateTime now = DateTime.Now; - text = "3".ToString(); - int num = TupleElimination.f(); - now = DateTime.Now; - return 2 + num; -} -*) + +// 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 { @@ -197,14 +191,12 @@ public static int x() } """ -(* -public static int y() -{ - int num = TupleElimination.f(); - TupleElimination.sideEffect(); - return num + TupleElimination.f() + TupleElimination.f(); -} -*) +// public static int y() +// { +// int num = TupleElimination.f(); +// TupleElimination.sideEffect(); +// return num + TupleElimination.f() + TupleElimination.f(); +// } """ .method public static int32 y() cil managed { @@ -223,13 +215,11 @@ public static int y() } """ -(* -public static int z() -{ - TupleElimination.sideEffect(); - return TupleElimination.f() + (TupleElimination.f() + 3) + (TupleElimination.f() + 4); -} -*) +// public static int z() +// { +// TupleElimination.sideEffect(); +// return TupleElimination.f() + (TupleElimination.f() + 3) + (TupleElimination.f() + 4); +// } """ .method public static int32 z() cil managed { @@ -258,21 +248,10 @@ open System.Runtime.CompilerServices [] let f () = 3 -[] -let cond () = true - type Test = [] static member test(x: int32 * int32) = x -let y () = - let a, b = - if cond () then - 1, 2 - else - Test.test(3, 4) - a + b - let z () = let a, b = "".ToString () |> ignore @@ -286,56 +265,17 @@ let z () = |> shouldSucceed |> verifyIL [ -(* -public static int y() -{ - Tuple tuple = (!TupleElimination.cond()) ? TupleElimination.Test.test(new Tuple(3, 4)) : new Tuple(1, 2); - return tuple.Item1 + tuple.Item2; -} -*) - """ -.method public static int32 y() cil managed -{ - - .maxstack 4 - .locals init (class [runtime]System.Tuple`2 V_0) - IL_0000: call bool TupleElimination::cond() - IL_0005: brfalse.s IL_0010 - - IL_0007: ldc.i4.1 - IL_0008: ldc.i4.2 - IL_0009: newobj instance void class [runtime]System.Tuple`2::.ctor(!0, - !1) - IL_000e: br.s IL_001c - - IL_0010: ldc.i4.3 - IL_0011: ldc.i4.4 - IL_0012: newobj instance void class [runtime]System.Tuple`2::.ctor(!0, - !1) - IL_0017: call class [runtime]System.Tuple`2 TupleElimination/Test::test(class [runtime]System.Tuple`2) - IL_001c: stloc.0 - IL_001d: ldloc.0 - IL_001e: call instance !0 class [runtime]System.Tuple`2::get_Item1() - IL_0023: ldloc.0 - IL_0024: call instance !1 class [runtime]System.Tuple`2::get_Item2() - IL_0029: add - IL_002a: ret -} -""" - -(* -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; -} -*) +// 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 { @@ -373,337 +313,3 @@ public static int z() IL_0045: add IL_0046: ret }""" ] - - [] - let ``Branching let binding rhs does not prevent tuple elimination``() = - FSharp """ -module TupleElimination -open System.Runtime.CompilerServices - -[] -let f () = 3 - -[] -let sideEffect () = () - -[] -let cond () = true - -type Test = - [] - static member test(x: int32 * int32) = x - -let x () = - let a, b = - sideEffect () - if cond () then - let v = "yep" - let v2 = if cond () then 1 else 3 - v, v2 - else - "", f () - a, b - -let rec y () = - let a, b, c = - if cond () then - "".ToString () |> ignore - 1, f (), 3 - else - if cond () then - 2, 2, 3 - else - match 1 / 0 with - | 1 -> - if 2 / 3 = 1 then - 5, 6, 7 - else - "".ToString () |> ignore - 6, 5, 4 - | 2 -> 6, 6, 6 - | 3 -> f (), 7, f () - | _ -> 8, y (), y () - - a + b + (2 * c) - -let z () = - let a, b = - if cond () then - 1, 3 - else - 3, 4 - a + b - """ - |> compile - |> shouldSucceed - |> verifyIL [ - -(* -public static Tuple x() -{ - TupleElimination.sideEffect(); - string item; - int item2; - if (TupleElimination.cond()) - { - int num = (!TupleElimination.cond()) ? 3 : 1; - item = "yep"; - item2 = num; - } - else - { - item = ""; - item2 = TupleElimination.f(); - } - return new Tuple(item, item2); -} -*) - """ -.method public static class [runtime]System.Tuple`2 - x() cil managed -{ - - .maxstack 4 - .locals init (string V_0, - int32 V_1, - int32 V_2) - IL_0000: call void TupleElimination::sideEffect() - IL_0005: call bool TupleElimination::cond() - IL_000a: brfalse.s IL_0022 - - IL_000c: call bool TupleElimination::cond() - IL_0011: brfalse.s IL_0016 - - IL_0013: ldc.i4.1 - IL_0014: br.s IL_0017 - - IL_0016: ldc.i4.3 - IL_0017: stloc.2 - IL_0018: ldstr "yep" - IL_001d: stloc.0 - IL_001e: ldloc.2 - IL_001f: stloc.1 - IL_0020: br.s IL_002e - - IL_0022: ldstr "" - IL_0027: stloc.0 - IL_0028: call int32 TupleElimination::f() - IL_002d: stloc.1 - IL_002e: ldloc.0 - IL_002f: ldloc.1 - IL_0030: newobj instance void class [runtime]System.Tuple`2::.ctor(!0, - !1) - IL_0035: ret -} -""" - -(* -public static int y() -{ - int num; - int num2; - int num3; - if (TupleElimination.cond()) - { - string text = "".ToString(); - num = 1; - num2 = TupleElimination.f(); - num3 = 3; - } - else if (TupleElimination.cond()) - { - num = 2; - num2 = 2; - num3 = 3; - } - else - { - switch (1 / 0) - { - case 1: - if (2 / 3 == 1) - { - num = 5; - num2 = 6; - num3 = 7; - } - else - { - string text = "".ToString(); - num = 6; - num2 = 5; - num3 = 4; - } - break; - case 2: - num = 6; - num2 = 6; - num3 = 6; - break; - case 3: - num = TupleElimination.f(); - num2 = 7; - num3 = TupleElimination.f(); - break; - default: - num = 8; - num2 = TupleElimination.y(); - num3 = TupleElimination.y(); - break; - } - } - return num + num2 + 2 * num3; -} -*) - """ -.method public static int32 y() cil managed -{ - - .maxstack 5 - .locals init (int32 V_0, - int32 V_1, - int32 V_2, - string V_3) - IL_0000: ldc.i4.0 - IL_0001: stloc.0 - IL_0002: ldc.i4.0 - IL_0003: stloc.1 - IL_0004: ldc.i4.0 - IL_0005: stloc.2 - IL_0006: call bool TupleElimination::cond() - IL_000b: brfalse.s IL_0027 - - IL_000d: ldstr "" - IL_0012: callvirt instance string [runtime]System.Object::ToString() - IL_0017: stloc.3 - IL_0018: ldc.i4.1 - IL_0019: stloc.0 - IL_001a: call int32 TupleElimination::f() - IL_001f: stloc.1 - IL_0020: ldc.i4.3 - IL_0021: stloc.2 - IL_0022: br IL_0095 - - IL_0027: call bool TupleElimination::cond() - IL_002c: brfalse.s IL_0036 - - IL_002e: ldc.i4.2 - IL_002f: stloc.0 - IL_0030: ldc.i4.2 - IL_0031: stloc.1 - IL_0032: ldc.i4.3 - IL_0033: stloc.2 - IL_0034: br.s IL_0095 - - IL_0036: ldc.i4.1 - IL_0037: ldc.i4.0 - IL_0038: div - IL_0039: ldc.i4.1 - IL_003a: sub - IL_003b: switch ( - IL_004e, - IL_006f, - IL_0077) - IL_004c: br.s IL_0087 - - IL_004e: ldc.i4.2 - IL_004f: ldc.i4.3 - IL_0050: div - IL_0051: ldc.i4.1 - IL_0052: bne.un.s IL_005c - - IL_0054: ldc.i4.5 - IL_0055: stloc.0 - IL_0056: ldc.i4.6 - IL_0057: stloc.1 - IL_0058: ldc.i4.7 - IL_0059: stloc.2 - IL_005a: br.s IL_0095 - - IL_005c: ldstr "" - IL_0061: callvirt instance string [runtime]System.Object::ToString() - IL_0066: stloc.3 - IL_0067: ldc.i4.6 - IL_0068: stloc.0 - IL_0069: ldc.i4.5 - IL_006a: stloc.1 - IL_006b: ldc.i4.4 - IL_006c: stloc.2 - IL_006d: br.s IL_0095 - - IL_006f: ldc.i4.6 - IL_0070: stloc.0 - IL_0071: ldc.i4.6 - IL_0072: stloc.1 - IL_0073: ldc.i4.6 - IL_0074: stloc.2 - IL_0075: br.s IL_0095 - - IL_0077: call int32 TupleElimination::f() - IL_007c: stloc.0 - IL_007d: ldc.i4.7 - IL_007e: stloc.1 - IL_007f: call int32 TupleElimination::f() - IL_0084: stloc.2 - IL_0085: br.s IL_0095 - - IL_0087: ldc.i4.8 - IL_0088: stloc.0 - IL_0089: call int32 TupleElimination::y() - IL_008e: stloc.1 - IL_008f: call int32 TupleElimination::y() - IL_0094: stloc.2 - IL_0095: ldloc.0 - IL_0096: ldloc.1 - IL_0097: add - IL_0098: ldc.i4.2 - IL_0099: ldloc.2 - IL_009a: mul - IL_009b: add - IL_009c: ret -} -""" - -(* -public static int z() -{ - int num; - int num2; - if (TupleElimination.cond()) - { - num = 1; - num2 = 3; - } - else - { - num = 3; - num2 = 4; - } - return num + num2; -} -*) - """ -.method public static int32 z() cil managed -{ - - .maxstack 4 - .locals init (int32 V_0, - int32 V_1) - IL_0000: call bool TupleElimination::cond() - IL_0005: brfalse.s IL_000d - - IL_0007: ldc.i4.1 - IL_0008: stloc.0 - IL_0009: ldc.i4.3 - IL_000a: stloc.1 - IL_000b: br.s IL_0011 - - IL_000d: ldc.i4.3 - IL_000e: stloc.0 - IL_000f: ldc.i4.4 - IL_0010: stloc.1 - IL_0011: ldloc.0 - IL_0012: ldloc.1 - IL_0013: add - IL_0014: ret -}""" ] \ No newline at end of file