Skip to content

Commit d6507d6

Browse files
Optimize simple mappings with binds and/or sequentials before single yield (#17419)
1 parent 13c658f commit d6507d6

19 files changed

+4566
-25
lines changed

docs/release-notes/.FSharp.Compiler.Service/9.0.100.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
* Compiler hangs when compiling inline recursive invocation ([Issue #17376](https://github.com/dotnet/fsharp/issues/17376), [PR #17394](https://github.com/dotnet/fsharp/pull/17394))
44
* Fix reporting IsFromComputationExpression only for CE builder type constructors and let bindings. ([PR #17375](https://github.com/dotnet/fsharp/pull/17375))
5+
* Optimize simple mappings in comprehensions when the body of the mapping has `let`-bindings and/or sequential expressions before a single yield. ([PR #17419](https://github.com/dotnet/fsharp/pull/17419))
56

67
### Added
78

src/Compiler/Optimize/LowerComputedCollections.fs

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -524,22 +524,46 @@ module Array =
524524
)
525525
)
526526

527-
/// f (); …; Seq.singleton x
527+
/// Matches Seq.singleton and returns the body expression.
528+
[<return: Struct>]
529+
let (|SeqSingleton|_|) g expr : Expr voption =
530+
match expr with
531+
| ValApp g g.seq_singleton_vref (_, [body], _) -> ValueSome body
532+
| _ -> ValueNone
533+
534+
/// Matches the compiled representation of the mapping in
535+
///
536+
/// for … in … do f (); …; yield …
537+
/// for … in … do let … = … in yield …
538+
/// for … in … do f (); …; …
539+
/// for … in … do let … = … in …
528540
///
529-
/// E.g., in [for x in … do f (); …; yield x]
541+
/// i.e.,
542+
///
543+
/// f (); …; Seq.singleton …
544+
/// let … = … in Seq.singleton …
530545
[<return: Struct>]
531-
let (|SimpleSequential|_|) g expr : Expr voption =
546+
let (|SingleYield|_|) g expr : Expr voption =
532547
let rec loop expr cont =
533548
match expr with
534-
| Expr.Sequential (expr1, DebugPoints (ValApp g g.seq_singleton_vref (_, [body], _), debug), kind, m) ->
535-
ValueSome (cont (expr1, debug body, kind, m))
549+
| Expr.Let (binding, DebugPoints (SeqSingleton g body, debug), m, frees) ->
550+
ValueSome (cont (Expr.Let (binding, debug body, m, frees)))
551+
552+
| Expr.Let (binding, DebugPoints (body, debug), m, frees) ->
553+
loop body (cont << fun body -> Expr.Let (binding, debug body, m, frees))
554+
555+
| Expr.Sequential (expr1, DebugPoints (SeqSingleton g body, debug), kind, m) ->
556+
ValueSome (cont (Expr.Sequential (expr1, debug body, kind, m)))
536557

537558
| Expr.Sequential (expr1, DebugPoints (body, debug), kind, m) ->
538-
loop body (cont >> fun body -> Expr.Sequential (expr1, debug body, kind, m))
559+
loop body (cont << fun body -> Expr.Sequential (expr1, debug body, kind, m))
560+
561+
| SeqSingleton g body ->
562+
ValueSome (cont body)
539563

540564
| _ -> ValueNone
541565

542-
loop expr Expr.Sequential
566+
loop expr id
543567

544568
/// Extracts any let-bindings or sequential
545569
/// expressions that directly precede the specified mapping application, e.g.,
@@ -573,11 +597,9 @@ let gatherPrelude ((|App|_|) : _ -> _ voption) expr =
573597

574598
/// The representation used for
575599
///
576-
/// for … in … -> …
577-
///
578-
/// and
579-
///
580-
/// for … in … do yield …
600+
/// for … in … -> …
601+
/// for … in … do yield …
602+
/// for … in … do …
581603
[<return: Struct>]
582604
let (|SeqMap|_|) g =
583605
gatherPrelude (function
@@ -592,30 +614,41 @@ let (|SeqMap|_|) g =
592614

593615
/// The representation used for
594616
///
595-
/// for … in … do f (); …; yield …
617+
/// for … in … do f (); …; yield …
618+
/// for … in … do let … = … in yield …
619+
/// for … in … do f (); …; …
620+
/// for … in … do let … = … in …
596621
[<return: Struct>]
597622
let (|SeqCollectSingle|_|) g =
598623
gatherPrelude (function
599-
| ValApp g g.seq_collect_vref ([ty1; _; ty2], [Expr.Lambda (valParams = [loopVal]; bodyExpr = SimpleSequential g body; range = mIn) as mapping; input], mFor) ->
624+
| ValApp g g.seq_collect_vref ([ty1; _; ty2], [Expr.Lambda (valParams = [loopVal]; bodyExpr = DebugPoints (SingleYield g body, debug); range = mIn) as mapping; input], mFor) ->
600625
let spIn = match mIn.NotedSourceConstruct with NotedSourceConstruct.InOrTo -> DebugPointAtInOrTo.Yes mIn | _ -> DebugPointAtInOrTo.No
601626
let spFor = DebugPointAtBinding.Yes mFor
602627
let spInWhile = match spIn with DebugPointAtInOrTo.Yes m -> DebugPointAtWhile.Yes m | DebugPointAtInOrTo.No -> DebugPointAtWhile.No
603628
let ranges = body.Range, spFor, spIn, mFor, mIn, spInWhile
604-
ValueSome (ty1, ty2, input, mapping, loopVal, body, ranges)
629+
ValueSome (ty1, ty2, input, mapping, loopVal, debug body, ranges)
605630

606631
| _ -> ValueNone)
607632

608633
/// for … in … -> …
609634
/// for … in … do yield …
635+
/// for … in … do …
610636
/// for … in … do f (); …; yield …
637+
/// for … in … do let … = … in yield …
638+
/// for … in … do f (); …; …
639+
/// for … in … do let … = … in …
611640
[<return: Struct>]
612641
let (|SimpleMapping|_|) g expr =
613642
match expr with
614643
// for … in … -> …
615644
// for … in … do yield …
645+
// for … in … do …
616646
| ValApp g g.seq_delay_vref (_, [Expr.Lambda (bodyExpr = DebugPoints (SeqMap g (cont, (ty1, ty2, input, mapping, loopVal, body, ranges)), debug))], _)
617647

618648
// for … in … do f (); …; yield …
649+
// for … in … do let … = … in yield …
650+
// for … in … do f (); …; …
651+
// for … in … do let … = … in …
619652
| ValApp g g.seq_delay_vref (_, [Expr.Lambda (bodyExpr = DebugPoints (SeqCollectSingle g (cont, (ty1, ty2, input, mapping, loopVal, body, ranges)), debug))], _) ->
620653
ValueSome (debug >> cont, (ty1, ty2, input, mapping, loopVal, body, ranges))
621654

src/Compiler/TypedTree/TypedTreeOps.fs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,11 +1271,14 @@ let rec stripDebugPoints expr =
12711271
| Expr.DebugPoint (_, innerExpr) -> stripDebugPoints innerExpr
12721272
| expr -> expr
12731273

1274-
// Strip debug points and remember how to recrete them
1274+
// Strip debug points and remember how to recreate them
12751275
let (|DebugPoints|) expr =
1276-
match stripExpr expr with
1277-
| Expr.DebugPoint (dp, innerExpr) -> innerExpr, (fun e -> Expr.DebugPoint(dp, e))
1278-
| expr -> expr, id
1276+
let rec loop expr debug =
1277+
match stripExpr expr with
1278+
| Expr.DebugPoint (dp, innerExpr) -> loop innerExpr (debug << fun e -> Expr.DebugPoint (dp, e))
1279+
| expr -> expr, debug
1280+
1281+
loop expr id
12791282

12801283
let mkCase (a, b) = TCase(a, b)
12811284

tests/FSharp.Compiler.ComponentTests/EmittedIL/ComputedCollections/ForNInRangeArrays.fs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22
let f00 f g = [|for n in 1..10 do f (); g (); yield n|]
33
let f000 f = [|for n in 1..10 do f (); yield n; yield n + 1|]
44
let f0000 () = [|for n in 1..10 do yield n|]
5+
let f00000 () = [|for n in 1..10 do n|]
6+
let f000000 () = [|for n in 1..10 do let n = n in n|]
7+
let f0000000 () = [|for n in 1..10 do let n = n in yield n|]
8+
let f00000000 () = [|for n in 1..10 do let n = n in let n = n in yield n|]
9+
let f000000000 x y = [|for n in 1..10 do let foo = n + x in let bar = n + y in yield n + foo + bar|]
10+
let f0000000000 f g = [|for n in 1..10 do f (); g (); n|]
11+
let f00000000000 (f : unit -> int) (g : unit -> int) = [|for n in 1..10 do f (); g (); n|]
512
let f1 () = [|for n in 1..10 -> n|]
613
let f2 () = [|for n in 10..1 -> n|]
714
let f3 () = [|for n in 1..1..10 -> n|]

0 commit comments

Comments
 (0)