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 2 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
62 changes: 62 additions & 0 deletions src/Compiler/Optimize/LowerComputedCollections.fs
Original file line number Diff line number Diff line change
Expand Up @@ -252,15 +252,77 @@ let (|SeqToArray|_|) g expr =
| ValApp g g.seq_to_array_vref (_, [seqExpr], m) -> Some (seqExpr, m)
| _ -> None

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

[<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
let constListSizeThreshold = 100
let constArraySizeThreshold = int System.UInt16.MaxValue

match overallExpr with
// [5..1] → []
| SeqToList g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), m) when
start > finish
->
Some (mkUnionCaseExpr (g.nil_ucref, [g.int32_ty], [], m))

// [1..5] → [1; 2; 3; 4; 5] ≡ 1 :: 2 :: 3 :: 4 :: 5 :: []
| SeqToList g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), _) when
finish - start < constListSizeThreshold
->
let rec conses acc n =
if n < start then acc
else conses (mkCons g g.int32_ty (Expr.Const (Const.Int32 n, Text.Range.range0, g.int32_ty)) acc) (n - 1)

Some (conses (mkNil g Text.Range.range0 g.int32_ty) finish)

// [start..finish] → List.init (finish - start + 1) ((+) start)
| SeqToList g (OptionalCoerce (OptionalSeq g amap (Int32Range g (start, finish))), m) ->
let diff = mkAsmExpr ([AbstractIL.IL.AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
let range = mkAsmExpr ([AbstractIL.IL.AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
let body = mkAsmExpr ([AbstractIL.IL.AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
let initializer = mkLambda Text.Range.range0 v (body, g.int32_ty)
let init = mkCallListInit g m g.int32_ty range initializer
Some init

| SeqToList g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
let collectorTy = g.mk_ListCollector_ty overallElemTy
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr

// [|5..1|] → [||]
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), m) when
start > finish
->
Some (mkArray (g.int32_ty, [], m))

// [|1..5|] → [|1; 2; 3; 4; 5|]
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (ConstInt32Range g (start, finish))), m) when
finish - start < constArraySizeThreshold
->
Some (mkArray (g.int32_ty, [for n in start..finish -> Expr.Const(Const.Int32 n, Text.Range.range0, g.int32_ty)], m))

// [|start..finish|] → Array.init (finish - start + 1) ((+) start)
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (Int32Range g (start, finish))), m) ->
let diff = mkAsmExpr ([AbstractIL.IL.AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
let range = mkAsmExpr ([AbstractIL.IL.AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
let body = mkAsmExpr ([AbstractIL.IL.AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
let initializer = mkLambda Text.Range.range0 v (body, g.int32_ty)
let init = mkCallArrayInit g m g.int32_ty range initializer
Some init

| SeqToArray g (OptionalCoerce (OptionalSeq g amap (overallSeqExpr, overallElemTy)), m) ->
let collectorTy = g.mk_ArrayCollector_ty overallElemTy
LowerComputedListOrArraySeqExpr tcVal g amap m collectorTy overallSeqExpr
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 @@ -7736,6 +7736,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 @@ -7752,6 +7754,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
12 changes: 11 additions & 1 deletion tests/FSharp.Test.Utilities/ILChecker.fs
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,19 @@ module ILChecker =
errorMsgOpt <- Some(msg + "\nExpected:\n" + ilCode + "\n")
else
for i = 0 to expectedLines.Length - 1 do
let ignorePrivateImplDeets (s: string) =
// .field static assembly valuetype '<PrivateImplementationDetails$assembly>'/T3169_40Bytes@ field3170@ at I_000028A7
let s = match s.IndexOf "PrivateImplementationDetails" with -1 -> s | i -> s[..i]
// .class explicit ansi sealed nested assembly beforefieldinit T3169_40Bytes@
let s = match s.IndexOf ".class explicit ansi sealed nested assembly beforefieldinit" with -1 -> s | i -> s[..i]
// .data cil I_000028A7 = bytearray (
let s = match s.IndexOf ".data cil" with -1 -> s | i -> s[..i]
s

let expected = expectedLines[i].Trim()
let actual = actualLines[i].Trim()
if expected <> actual then

if ignorePrivateImplDeets expected <> ignorePrivateImplDeets actual then
errors.Add $"\n==\nName: '%s{actualLines[0]}'\n\nExpected:\t %s{expected}\nActual:\t\t %s{actual}\n=="

if errors.Count > 0 then
Expand Down
Loading