Skip to content
Closed
Show file tree
Hide file tree
Changes from 14 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))
4 changes: 2 additions & 2 deletions src/Compiler/CodeGen/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2867,8 +2867,8 @@ and GenExprPreSteps (cenv: cenv) (cgbuf: CodeGenBuffer) eenv expr sequel =
None

match lowering with
| Some altExpr ->
GenExpr cenv cgbuf eenv altExpr sequel
| Some initExpr ->
GenExpr cenv cgbuf eenv initExpr sequel
true
| None ->

Expand Down
77 changes: 76 additions & 1 deletion 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,15 +255,90 @@ let (|SeqToArray|_|) g expr =
| ValApp g g.seq_to_array_vref (_, [seqExpr], m) -> ValueSome (seqExpr, m)
| _ -> ValueNone

/// 1..10
[<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

/// 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
//let constListSizeThreshold = 10
//let constArrayBytesThreshold = 40

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] → if start <= finish then List.init (finish - start + 1) ((+) start) else []
| SeqToList g (OptionalCoerce (OptionalSeq g amap (Int32Range g (start, finish))), m) ->
let diff = mkAsmExpr ([AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
let range = mkAsmExpr ([AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)
let zero = mkZero g Text.Range.range0
let negRangeLtZero = mkAsmExpr ([AI_neg], [], [mkAsmExpr ([AI_clt], [], [range; zero], [g.int32_ty], Text.Range.range0)], [g.int32_ty], Text.Range.range0)
let anded = mkAsmExpr ([AI_and], [], [range; negRangeLtZero], [g.int32_ty], Text.Range.range0)
// range ^ (range & -(range < 0))
// https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
let range = mkAsmExpr ([AI_xor], [], [range; anded], [g.int32_ty], Text.Range.range0)
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
let body = mkAsmExpr ([AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
let initializer = mkLambda Text.Range.range0 v (body, g.int32_ty)
Some (mkCallListInit g m g.int32_ty range initializer)

| 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) * sizeof<int32> < constArrayBytesThreshold
// ->
// Some (mkArray (g.int32_ty, [for n in start..finish -> Expr.Const (Const.Int32 n, Text.Range.range0, g.int32_ty)], m))

// [|start..finish|] → if start <= finish then Array.init (finish - start + 1) ((+) start) else [||]
| SeqToArray g (OptionalCoerce (OptionalSeq g amap (Int32Range g (start, finish))), m) ->
let diff = mkAsmExpr ([AI_sub], [], [finish; start], [g.int32_ty], Text.Range.range0)
let range = mkAsmExpr ([AI_add], [], [diff; mkOne g Text.Range.range0], [g.int32_ty], Text.Range.range0)
let zero = mkZero g Text.Range.range0
let negRangeLtZero = mkAsmExpr ([AI_neg], [], [mkAsmExpr ([AI_clt], [], [range; zero], [g.int32_ty], Text.Range.range0)], [g.int32_ty], Text.Range.range0)
let anded = mkAsmExpr ([AI_and], [], [range; negRangeLtZero], [g.int32_ty], Text.Range.range0)
// range ^ (range & -(range < 0))
// https://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
let range = mkAsmExpr ([AI_xor], [], [range; anded], [g.int32_ty], Text.Range.range0)
let v, e = mkCompGenLocal Text.Range.range0 "i" g.int32_ty
let body = mkAsmExpr ([AI_add], [], [start; e], [g.int32_ty], Text.Range.range0)
let initializer = mkLambda Text.Range.range0 v (body, g.int32_ty)
Some (mkCallArrayInit g m g.int32_ty range initializer)

| 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 @@ -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
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