Skip to content

Better lowering of [start..finish] & [|start..finish|] #16577

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
cc670d0
Better lowering of `[lo..hi]` & `[|lo..hi|]`
brianrourkeboll Jan 23, 2024
4c3da4a
Not much point to that
brianrourkeboll Jan 23, 2024
09f4023
Fix path casing in fsproj
brianrourkeboll Jan 24, 2024
dcc970d
Add comments
brianrourkeboll Jan 24, 2024
720a241
Use smaller, byte-based const arr threshold
brianrourkeboll Jan 24, 2024
0a23a5e
Dynamically return empty if start > finish
brianrourkeboll Jan 24, 2024
3f99298
Merge branch 'main' of https://github.com/dotnet/fsharp into collecti…
brianrourkeboll Jan 24, 2024
dd40c16
Better place to put the range, probably
brianrourkeboll Jan 24, 2024
936ea57
Merge branch 'main' of https://github.com/dotnet/fsharp into collecti…
brianrourkeboll Jan 25, 2024
0136a96
Properly handle branching
brianrourkeboll Jan 25, 2024
283ad75
Go branchless
brianrourkeboll Jan 26, 2024
a972f2c
Update release notes
brianrourkeboll Jan 26, 2024
5d2e907
Will it be green?
brianrourkeboll Jan 26, 2024
a52587d
Merge branch 'main' of https://github.com/dotnet/fsharp into collecti…
brianrourkeboll Jan 26, 2024
f21305a
Remove optimizations for const ranges
brianrourkeboll Jan 26, 2024
49e738c
Update comments
brianrourkeboll Jan 26, 2024
38ccefe
Revert that
brianrourkeboll Jan 26, 2024
3a67589
Closer
brianrourkeboll Jan 26, 2024
e55cc55
Bind exprs to locals when needed
brianrourkeboll Jan 27, 2024
09511a7
Nest for clarity
brianrourkeboll Jan 27, 2024
e2f2ef6
Be consistent
brianrourkeboll Jan 27, 2024
2437e93
Extra space
brianrourkeboll Jan 27, 2024
b0aa902
Update baselines
brianrourkeboll Jan 27, 2024
1165eaf
Might as well do that at compile time
brianrourkeboll Jan 27, 2024
4b67e5d
Update baselines
brianrourkeboll Jan 27, 2024
34a2214
Add tests for non-const, non-val args
brianrourkeboll Jan 27, 2024
b5596d0
Simplify tests
brianrourkeboll Jan 27, 2024
c0494eb
Fix baselines
brianrourkeboll Jan 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/release-notes/.FSharp.Compiler.Service/8.0.300.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16323](https://github.com/dotnet/fsharp/pull/16323), [PR #16567](https://github.com/dotnet/fsharp/pull/16567))
* Reverted [#16348](https://github.com/dotnet/fsharp/pull/16348) `ThreadStatic` `CancellationToken` changes to improve test stability and prevent potential unwanted cancellations. ([PR #16536](https://github.com/dotnet/fsharp/pull/16536))
* Refactored parenthesization API. ([PR #16461])(https://github.com/dotnet/fsharp/pull/16461))
* Improved lowering of `[start..finish]` and `[|start..finish|]`. ([PR #16577](https://github.com/dotnet/fsharp/pull/16577))
153 changes: 148 additions & 5 deletions src/Compiler/Optimize/LowerComputedCollections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

module internal FSharp.Compiler.LowerComputedCollectionExpressions

open Internal.Utilities.Library
open FSharp.Compiler.AbstractIL.IL
open FSharp.Compiler.AccessibilityLogic
open FSharp.Compiler.DiagnosticsLogger
open FSharp.Compiler.InfoReader
Expand Down Expand Up @@ -255,18 +255,161 @@ let (|SeqToArray|_|) g expr =
| ValApp g g.seq_to_array_vref (_, [seqExpr], m) -> ValueSome (seqExpr, m)
| _ -> ValueNone

/// start..finish
[<return: Struct>]
let (|Int32Range|_|) g expr =
match expr with
| ValApp g g.range_int32_op_vref ([], [start; Expr.Const (value = Const.Int32 1); finish], _) -> ValueSome (start, finish)
| _ -> ValueNone

let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap overallExpr =
// If ListCollector is in FSharp.Core then this optimization kicks in
if g.ListCollector_tcr.CanDeref then
/// Make an expression holding the count/length
/// to initialize the collection with.
let mkCount start finish =
let diff = mkAsmExpr ([AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
mkAsmExpr ([AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)

/// Make a lambda expression to pass into
/// the Array.init/List.init call.
let mkInitializer start =
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
let body = mkAsmExpr ([AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
mkLambda Text.Range.range0 v (body, g.int32_ty)

/// Make an expression that initializes a list
/// with the values from start through finish.
let mkListInit m start finish =
mkCond
DebugPointAtBinding.NoneAtInvisible
m
(mkListTy g g.int32_ty)
(mkILAsmClt g Text.Range.range0 finish start)
(mkNil g Text.Range.range0 g.int32_ty)
(mkCallListInit g Text.Range.range0 g.int32_ty (mkCount start finish) (mkInitializer start))

/// Make an expression that initializes an array
/// with the values from start through finish.
let mkArrayInit m start finish =
mkCond
DebugPointAtBinding.NoneAtInvisible
m
(mkArrayType g g.int32_ty)
(mkILAsmClt g Text.Range.range0 finish start)
(mkArray (g.int32_ty, [], Text.Range.range0))
(mkCallArrayInit g Text.Range.range0 g.int32_ty (mkCount start finish) (mkInitializer start))

match overallExpr with
// […]
| SeqToList g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
let collectorTy = g.mk_ListCollector_ty overallElemTy
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr
match overallSeqExpr with
// [5..1] → []
| Int32Range g (Expr.Const (value = Const.Int32 start), Expr.Const (value = Const.Int32 finish)) when
finish < start
->
Some (mkUnionCaseExpr (g.nil_ucref, [g.int32_ty], [], m))

// [1..5] → List.init 5 ((+) 5)
| Int32Range g (start & Expr.Const (value = Const.Int32 startVal), Expr.Const (value = Const.Int32 finishVal))
->
Some (mkCallListInit g Text.Range.range0 g.int32_ty (Expr.Const (Const.Int32 (finishVal - startVal + 1), Text.Range.range0, g.int32_ty)) (mkInitializer start))

// [start..finish] → if finish < start then [] else List.init (finish - start + 1) ((+) start)
| Int32Range g (start & (Expr.Const _ | Expr.Val _), finish & (Expr.Const _ | Expr.Val _)) ->
Some (mkListInit m start finish)

// [start..finishExpr] →
// let finish = finishExpr
// if finish < start then [] else List.init (finish - start + 1) ((+) start)
| Int32Range g (start & (Expr.Const _ | Expr.Val _), finish) ->
let expr =
mkCompGenLetIn Text.Range.range0 (nameof finish) g.int32_ty finish (fun (_, finish) ->
mkListInit m start finish)

Some expr

// [startExpr..finish] →
// let start = startExpr
// if finish < start then [] else List.init (finish - start + 1) ((+) start)
| Int32Range g (start, finish & (Expr.Const _ | Expr.Val _)) ->
let expr =
mkCompGenLetIn Text.Range.range0 (nameof start) g.int32_ty start (fun (_, start) ->
mkListInit m start finish)

Some expr

// [startExpr..finishExpr] →
// let start = startExpr
// let finish = finishExpr
// if finish < start then [] else List.init (finish - start + 1) ((+) start)
| Int32Range g (start, finish) ->
let expr =
mkCompGenLetIn Text.Range.range0 (nameof start) g.int32_ty start (fun (_, start) ->
mkCompGenLetIn Text.Range.range0 (nameof finish) g.int32_ty finish (fun (_, finish) ->
mkListInit m start finish))

Some expr

// [(* Anything more complex. *)]
| _ ->
let collectorTy = g.mk_ListCollector_ty overallElemTy
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr

// [|…|]
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
let collectorTy = g.mk_ArrayCollector_ty overallElemTy
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr
match overallSeqExpr with
// [|5..1|] → [||]
| Int32Range g (Expr.Const (value = Const.Int32 start), Expr.Const (value = Const.Int32 finish)) when
finish < start
->
Some (mkArray (g.int32_ty, [], m))

// [|1..5|] → Array.init 5 ((+) 5)
| Int32Range g (start & Expr.Const (value = Const.Int32 startVal), Expr.Const (value = Const.Int32 finishVal))
->
Some (mkCallArrayInit g Text.Range.range0 g.int32_ty (Expr.Const (Const.Int32 (finishVal - startVal + 1), Text.Range.range0, g.int32_ty)) (mkInitializer start))

// [|start..finish|] → if finish < start then [||] else Array.init (finish - start + 1) ((+) start)
| Int32Range g (start & (Expr.Const _ | Expr.Val _), finish & (Expr.Const _ | Expr.Val _)) ->
Some (mkArrayInit m start finish)

// [|start..finishExpr|] →
// let finish = finishExpr
// if finish < start then [||] else Array.init (finish - start + 1) ((+) start)
| Int32Range g (start & (Expr.Const _ | Expr.Val _), finish) ->
let expr =
mkCompGenLetIn Text.Range.range0 (nameof finish) g.int32_ty finish (fun (_, finish) ->
mkArrayInit m start finish)

Some expr

// [|startExpr..finish|] →
// let start = startExpr
// if finish < start then [||] else Array.init (finish - start + 1) ((+) start)
| Int32Range g (start, finish & (Expr.Const _ | Expr.Val _)) ->
let expr =
mkCompGenLetIn Text.Range.range0 (nameof start) g.int32_ty start (fun (_, start) ->
mkArrayInit m start finish)

Some expr

// [|startExpr..finishExpr|] →
// let start = startExpr
// let finish = finishExpr
// if finish < start then [||] else Array.init (finish - start + 1) ((+) start)
| Int32Range g (start, finish) ->
let expr =
mkCompGenLetIn Text.Range.range0 (nameof start) g.int32_ty start (fun (_, start) ->
mkCompGenLetIn Text.Range.range0 (nameof finish) g.int32_ty finish (fun (_, finish) ->
mkArrayInit m start finish))

Some expr

// [|(* Anything more complex. *)|]
| _ ->
let collectorTy = g.mk_ArrayCollector_ty overallElemTy
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr

| _ -> None
else
Expand Down
6 changes: 6 additions & 0 deletions src/Compiler/TypedTree/TcGlobals.fs
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,7 @@ type TcGlobals(
let v_range_int32_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeInt32" , None , None , [], ([[v_int_ty];[v_int_ty];[v_int_ty]], mkSeqTy v_int_ty))

let v_array_length_info = makeIntrinsicValRef(fslib_MFArrayModule_nleref, "length" , None , Some "Length" , [vara], ([[mkArrayType 1 varaTy]], v_int_ty))
let v_array_init_info = makeIntrinsicValRef(fslib_MFArrayModule_nleref, "init" , None , Some "Initialize", [vara], ([[v_int32_ty]; [v_int32_ty --> varaTy]], mkArrayType 1 varaTy))
let v_array_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray" , None , None , [vara], ([[mkArrayType 1 varaTy]; [v_int_ty]], varaTy))
let v_array2D_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray2D" , None , None , [vara], ([[mkArrayType 2 varaTy];[v_int_ty]; [v_int_ty]], varaTy))
let v_array3D_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray3D" , None , None , [vara], ([[mkArrayType 3 varaTy];[v_int_ty]; [v_int_ty]; [v_int_ty]], varaTy))
Expand All @@ -820,6 +821,8 @@ type TcGlobals(
let v_array3D_set_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "SetArray3D" , None , None , [vara], ([[mkArrayType 3 varaTy];[v_int_ty]; [v_int_ty]; [v_int_ty]; [varaTy]], v_unit_ty))
let v_array4D_set_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "SetArray4D" , None , None , [vara], ([[mkArrayType 4 varaTy];[v_int_ty]; [v_int_ty]; [v_int_ty]; [v_int_ty]; [varaTy]], v_unit_ty))

let v_list_init_info = makeIntrinsicValRef(fslib_MFListModule_nleref, "init" , None , Some "Initialize", [vara], ([[v_int32_ty]; [v_int32_ty --> varaTy]], mkListTy varaTy))

let v_option_toNullable_info = makeIntrinsicValRef(fslib_MFOptionModule_nleref, "toNullable" , None , Some "ToNullable" , [vara], ([[mkOptionTy varaTy]], mkNullableTy varaTy))
let v_option_defaultValue_info = makeIntrinsicValRef(fslib_MFOptionModule_nleref, "defaultValue" , None , Some "DefaultValue" , [vara], ([[varaTy]; [mkOptionTy varaTy]], varaTy))

Expand Down Expand Up @@ -1751,6 +1754,7 @@ type TcGlobals(
member _.seq_to_array_info = v_seq_to_array_info

member _.array_length_info = v_array_length_info
member _.array_init_info = v_array_init_info
member _.array_get_info = v_array_get_info
member _.array2D_get_info = v_array2D_get_info
member _.array3D_get_info = v_array3D_get_info
Expand All @@ -1760,6 +1764,8 @@ type TcGlobals(
member _.array3D_set_info = v_array3D_set_info
member _.array4D_set_info = v_array4D_set_info

member _.list_init_info = v_list_init_info

member val option_toNullable_info = v_option_toNullable_info
member val option_defaultValue_info = v_option_defaultValue_info

Expand Down
4 changes: 4 additions & 0 deletions src/Compiler/TypedTree/TypedTreeOps.fs
Original file line number Diff line number Diff line change
Expand Up @@ -7737,6 +7737,8 @@ let mkCallToEnumOperator (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsi

let mkCallArrayLength (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g m g.array_length_info, [[ty]], [e1], m)

let mkCallArrayInit (g: TcGlobals) m ty e1 e2 = mkApps g (typedExprForIntrinsic g m g.array_init_info, [[ty]], [e1; e2], m)

let mkCallArrayGet (g: TcGlobals) m ty e1 idx1 = mkApps g (typedExprForIntrinsic g m g.array_get_info, [[ty]], [ e1 ; idx1 ], m)

let mkCallArray2DGet (g: TcGlobals) m ty e1 idx1 idx2 = mkApps g (typedExprForIntrinsic g m g.array2D_get_info, [[ty]], [ e1 ; idx1; idx2 ], m)
Expand All @@ -7753,6 +7755,8 @@ let mkCallArray3DSet (g: TcGlobals) m ty e1 idx1 idx2 idx3 v = mkApps g (typedEx

let mkCallArray4DSet (g: TcGlobals) m ty e1 idx1 idx2 idx3 idx4 v = mkApps g (typedExprForIntrinsic g m g.array4D_set_info, [[ty]], [ e1 ; idx1; idx2; idx3; idx4; v ], m)

let mkCallListInit (g: TcGlobals) m ty e1 e2 = mkApps g (typedExprForIntrinsic g m g.list_init_info, [[ty]], [e1; e2], m)

let mkCallHash (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g m g.hash_info, [[ty]], [ e1 ], m)

let mkCallBox (g: TcGlobals) m ty e1 = mkApps g (typedExprForIntrinsic g m g.box_info, [[ty]], [ e1 ], m)
Expand Down
4 changes: 4 additions & 0 deletions src/Compiler/TypedTree/TypedTreeOps.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -2015,6 +2015,8 @@ val mkCallCreateEvent: TcGlobals -> range -> TType -> TType -> Expr -> Expr -> E

val mkCallArrayLength: TcGlobals -> range -> TType -> Expr -> Expr

val mkCallArrayInit: TcGlobals -> range -> TType -> Expr -> Expr -> Expr

val mkCallArrayGet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr

val mkCallArray2DGet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Expr
Expand All @@ -2031,6 +2033,8 @@ val mkCallArray3DSet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Exp

val mkCallArray4DSet: TcGlobals -> range -> TType -> Expr -> Expr -> Expr -> Expr -> Expr -> Expr -> Expr

val mkCallListInit: TcGlobals -> range -> TType -> Expr -> Expr -> Expr

val mkCallHash: TcGlobals -> range -> TType -> Expr -> Expr

val mkCallBox: TcGlobals -> range -> TType -> Expr -> Expr
Expand Down
Loading