Skip to content
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

Better integral range lowering: start..finish, start..step..finish #16650

Merged
merged 86 commits into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from 48 commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
105cc21
Lower integral ranges to fast while-loops
brianrourkeboll Feb 5, 2024
7ead59f
Add int32 tests
brianrourkeboll Feb 5, 2024
46ee617
Update baselines
brianrourkeboll Feb 5, 2024
ae4f97e
Update release notes
brianrourkeboll Feb 5, 2024
97924fd
Fmt
brianrourkeboll Feb 5, 2024
3e335f7
Update more baselines
brianrourkeboll Feb 5, 2024
3b58f81
Typo
brianrourkeboll Feb 5, 2024
8142df1
Merge branch 'main' into range-lowering
vzarytovskii Feb 5, 2024
fa515e8
Update comments
brianrourkeboll Feb 5, 2024
e1f4356
Refactor for clarity
brianrourkeboll Feb 5, 2024
9c60cc7
Merge branch 'range-lowering' of https://github.com/brianrourkeboll/f…
brianrourkeboll Feb 5, 2024
963a85f
Fix debug assert
brianrourkeboll Feb 6, 2024
9355c1f
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 7, 2024
e44dc37
Just precompute count for all scenarios
brianrourkeboll Feb 13, 2024
195ae17
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 13, 2024
5c49feb
Add missing detail to comment
brianrourkeboll Feb 13, 2024
f8a2b6f
Don't need to expose that
brianrourkeboll Feb 13, 2024
ff1d3db
Remove bad & unneeded comparison
brianrourkeboll Feb 13, 2024
e419414
Comments
brianrourkeboll Feb 13, 2024
89c6903
Better ranges
brianrourkeboll Feb 13, 2024
7668549
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 13, 2024
e1a315c
Update baselines
brianrourkeboll Feb 13, 2024
b99c4a6
Use `Seq.toArray`
brianrourkeboll Feb 13, 2024
8f59853
Better name
brianrourkeboll Feb 13, 2024
5d70a48
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 14, 2024
1b15640
Hmm
brianrourkeboll Feb 14, 2024
5ebb561
Hmm
brianrourkeboll Feb 14, 2024
66f131b
Update baselines
brianrourkeboll Feb 14, 2024
6ae6005
Meh
brianrourkeboll Feb 14, 2024
01b9b6c
That was it
brianrourkeboll Feb 14, 2024
b602519
Better comments
brianrourkeboll Feb 14, 2024
9296f18
Clarity
brianrourkeboll Feb 14, 2024
3f2736f
Update net472 baseline
brianrourkeboll Feb 14, 2024
89401bc
Use simpler abs
brianrourkeboll Feb 14, 2024
6c0cc22
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 14, 2024
d171669
Use correctly typed (& sized) one
brianrourkeboll Feb 15, 2024
e45dd55
Handle
brianrourkeboll Feb 15, 2024
e5e4112
Handle conversions like C# does
brianrourkeboll Feb 15, 2024
29f5ca8
Update baselines
brianrourkeboll Feb 15, 2024
458f14a
Expose `mkTypedZero` & `mkTypedOne`
brianrourkeboll Feb 15, 2024
31d80cc
Update baselines
brianrourkeboll Feb 15, 2024
99e9986
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 15, 2024
d5341f5
integral → numeric
brianrourkeboll Feb 16, 2024
a98fcde
Use `mkTypedOne`
brianrourkeboll Feb 16, 2024
7ba11fc
Handle unspecialized cases
brianrourkeboll Feb 16, 2024
2eb28fe
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 16, 2024
574595a
Add comments to range tests
brianrourkeboll Feb 19, 2024
bc3068f
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 19, 2024
6056c45
Add `LowerIntegralRangesToFastLoops` lang feature
brianrourkeboll Feb 20, 2024
d92366a
Use better name
brianrourkeboll Feb 20, 2024
6ed642e
Missed a spot
brianrourkeboll Feb 20, 2024
469b8c5
Handle MinValue..MaxValue, MaxValue..-1..MinValue
brianrourkeboll Feb 21, 2024
966d0ae
Add tests for MinValue..MaxValue, &c.
brianrourkeboll Feb 21, 2024
5b10546
Update baselines
brianrourkeboll Feb 21, 2024
8aab7da
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 21, 2024
e35cf4d
Parameterize zero & one
brianrourkeboll Feb 21, 2024
e263702
Add more tests for range edge cases
brianrourkeboll Feb 21, 2024
b558079
Don't check for ovf when not needed
brianrourkeboll Feb 21, 2024
13a2d46
Update baselines
brianrourkeboll Feb 21, 2024
da5f114
Add some debug stepping samples
brianrourkeboll Feb 22, 2024
d27cab1
Merge main
brianrourkeboll Feb 22, 2024
2323b20
Add comment to help future contributors
brianrourkeboll Feb 22, 2024
1293e33
Handle ranges with count 2⁶⁴ + 1
brianrourkeboll Feb 27, 2024
c045c97
Add more comprehensive IL tests
brianrourkeboll Feb 27, 2024
0acf40f
Remove FSharpSuite tests
brianrourkeboll Feb 27, 2024
39c0927
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 27, 2024
602ca6d
Fix types
brianrourkeboll Feb 27, 2024
bd892f1
Update baselines
brianrourkeboll Feb 27, 2024
3064807
More sensible
brianrourkeboll Feb 28, 2024
71318dc
Update baselines
brianrourkeboll Feb 28, 2024
404905f
Minor cleanup & clarification
brianrourkeboll Feb 28, 2024
77e04b4
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Feb 28, 2024
d91b092
Only emit runtime check for zero step once
brianrourkeboll Feb 28, 2024
ae9624a
Need that
brianrourkeboll Feb 28, 2024
65f7a1b
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Mar 1, 2024
07185d5
Update baseline
brianrourkeboll Mar 1, 2024
7022cbb
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Mar 1, 2024
1d83dbc
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Mar 1, 2024
7dd79e5
Missed in merge
brianrourkeboll Mar 1, 2024
d355c8d
Update baseline
brianrourkeboll Mar 1, 2024
2fd3413
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Mar 2, 2024
eb2b4e1
Update baselines
brianrourkeboll Mar 2, 2024
3fd075e
One more
brianrourkeboll Mar 2, 2024
c185329
Merge branch 'main' into range-lowering
T-Gro Mar 4, 2024
e79ac50
Emit a ~better approximation of a do-while loop
brianrourkeboll Mar 5, 2024
e4fca01
Merge branch 'main' of https://github.com/dotnet/fsharp into range-lo…
brianrourkeboll Mar 5, 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 @@ -29,3 +29,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))
* Integral range optimizations. ([PR #16650](https://github.com/dotnet/fsharp/pull/16650))
156 changes: 149 additions & 7 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,160 @@ let (|SeqToArray|_|) g expr =
| ValApp g g.seq_to_array_vref (_, [seqExpr], m) -> ValueSome (seqExpr, m)
| _ -> ValueNone

module List =
/// Makes an expression that will build a list from an integral range.
let mkFromIntegralRange tcVal (g: TcGlobals) amap m overallElemTy overallSeqExpr start step finish =
let collectorTy = g.mk_ListCollector_ty overallElemTy

/// let collector = ListCollector () in
/// <initialization loop>
/// collector.Close ()
let mkListInit mkLoop =
mkCompGenLetMutableIn m "collector" collectorTy (mkDefault (m, collectorTy)) (fun (_, collector) ->
let reader = InfoReader (g, amap)
let loop = mkLoop (fun _idxVar loopVar -> mkCallCollectorAdd tcVal g reader m collector loopVar)
let close = mkCallCollectorClose tcVal g reader m collector
mkSequential m loop close
)

mkOptimizedRangeLoop
g
(m, m, m, DebugPointAtWhile.No)
(overallElemTy, overallSeqExpr)
(start, step, finish)
(fun count mkLoop ->
match count with
| Expr.Const (value = IntegralConst.Zero) ->
mkNil g m overallElemTy

| Expr.Const (value = _nonzeroConstant) ->
mkListInit mkLoop

| _dynamicCount ->
mkListInit mkLoop
)

module Array =
/// Makes an expression that will build an array from an integral range.
let mkFromIntegralRange g m overallElemTy overallSeqExpr start step finish =
let arrayTy = mkArrayType g overallElemTy

let convToNativeInt expr =
let ty = stripMeasuresFromTy g (tyOfExpr g expr)

if typeEquiv g ty g.int64_ty then
mkAsmExpr ([AI_conv_ovf DT_I], [], [expr], [g.nativeint_ty], m)
elif typeEquiv g ty g.nativeint_ty then
mkAsmExpr ([AI_conv_ovf DT_I], [], [mkAsmExpr ([AI_conv DT_I8], [], [expr], [g.int64_ty], m)], [g.nativeint_ty], m)
elif typeEquiv g ty g.uint64_ty then
mkAsmExpr ([AI_conv_ovf_un DT_I], [], [expr], [g.nativeint_ty], m)
elif typeEquiv g ty g.unativeint_ty then
mkAsmExpr ([AI_conv_ovf_un DT_I], [], [mkAsmExpr ([AI_conv DT_U8], [], [expr], [g.uint64_ty], m)], [g.nativeint_ty], m)
else
expr

let ilTy, ilBasicTy =
let ty = stripMeasuresFromTy g overallElemTy

if typeEquiv g ty g.int32_ty then g.ilg.typ_Int32, DT_I4
elif typeEquiv g ty g.int64_ty then g.ilg.typ_Int64, DT_I8
elif typeEquiv g ty g.uint64_ty then g.ilg.typ_UInt64, DT_U8
elif typeEquiv g ty g.uint32_ty then g.ilg.typ_UInt32, DT_U4
elif typeEquiv g ty g.nativeint_ty then g.ilg.typ_IntPtr, DT_I
elif typeEquiv g ty g.unativeint_ty then g.ilg.typ_UIntPtr, DT_U
elif typeEquiv g ty g.int16_ty then g.ilg.typ_Int16, DT_I2
elif typeEquiv g ty g.uint16_ty then g.ilg.typ_UInt16, DT_U2
elif typeEquiv g ty g.sbyte_ty then g.ilg.typ_SByte, DT_I1
elif typeEquiv g ty g.byte_ty then g.ilg.typ_Byte, DT_U1
elif typeEquiv g ty g.char_ty then g.ilg.typ_Char, DT_U2
else error (InternalError ($"Unable to find IL type for integral type '{overallElemTy}'.", m))

/// (# "newarr !0" type ('T) count : 'T array #)
let mkNewArray count =
mkAsmExpr
(
[I_newarr (ILArrayShape.SingleDimensional, ilTy)],
[],
[convToNativeInt count],
[arrayTy],
m
)

/// let array = (# "newarr !0" type ('T) count : 'T array #) in
/// <initialization loop>
/// array
let mkArrayInit count mkLoop =
mkCompGenLetIn m "array" arrayTy (mkNewArray count) (fun (_, array) ->
let loop = mkLoop (fun idxVar loopVar -> mkAsmExpr ([I_stelem ilBasicTy], [], [array; convToNativeInt idxVar; loopVar], [], m))
mkSequential m loop array)

mkOptimizedRangeLoop
g
(m, m, m, DebugPointAtWhile.No)
(overallElemTy, overallSeqExpr)
(start, step, finish)
(fun count mkLoop ->
match count with
| Expr.Const (value = IntegralConst.Zero) ->
mkArray (overallElemTy, [], m)

| Expr.Const (value = _nonzeroConstant) ->
mkArrayInit count mkLoop

| _dynamicCount ->
let countTy = tyOfExpr g count

// count < 1
let countLtOne =
if isSignedIntegerTy g countTy then
mkILAsmClt g m count (mkTypedOne g m countTy)
else
mkAsmExpr ([AI_clt_un], [], [count; mkTypedOne g m countTy], [g.bool_ty], m)

// if count < 1 then
// [||]
// else
// let array = (# "newarr !0" type ('T) count : 'T array #) in
// <initialization loop>
// array
mkCond
DebugPointAtBinding.NoneAtInvisible
m
arrayTy
countLtOne
(mkArray (overallElemTy, [], m))
(mkArrayInit count mkLoop)
)

let LowerComputedListOrArrayExpr tcVal (g: TcGlobals) amap overallExpr =
// If ListCollector is in FSharp.Core then this optimization kicks in
if g.ListCollector_tcr.CanDeref then

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
// [start..finish]
// [start..step..finish]
| IntegralRange g (_, (start, step, finish)) ->
Some (List.mkFromIntegralRange tcVal g amap m overallElemTy overallSeqExpr start step finish)

// [(* 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
// [|start..finish|]
// [|start..step..finish|]
| IntegralRange g (_, (start, step, finish)) ->
Some (Array.mkFromIntegralRange g m overallElemTy overallSeqExpr start step finish)

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

| _ -> None
else
Expand Down
24 changes: 24 additions & 0 deletions src/Compiler/TypedTree/TcGlobals.fs
Original file line number Diff line number Diff line change
Expand Up @@ -809,6 +809,18 @@ type TcGlobals(
let v_range_op_info = makeIntrinsicValRef(fslib_MFOperators_nleref, "op_Range" , None , None , [vara], ([[varaTy];[varaTy]], mkSeqTy varaTy))
let v_range_step_op_info = makeIntrinsicValRef(fslib_MFOperators_nleref, "op_RangeStep" , None , None , [vara;varb], ([[varaTy];[varbTy];[varaTy]], mkSeqTy varaTy))
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_range_int64_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeInt64" , None , None , [], ([[v_int64_ty];[v_int64_ty];[v_int64_ty]], mkSeqTy v_int64_ty))
let v_range_uint64_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeUInt64" , None , None , [], ([[v_uint64_ty];[v_uint64_ty];[v_uint64_ty]], mkSeqTy v_uint64_ty))
let v_range_uint32_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeUInt32" , None , None , [], ([[v_uint32_ty];[v_uint32_ty];[v_uint32_ty]], mkSeqTy v_uint32_ty))
let v_range_nativeint_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeIntPtr" , None , None , [], ([[v_nativeint_ty];[v_nativeint_ty];[v_nativeint_ty]], mkSeqTy v_nativeint_ty))
let v_range_unativeint_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeUIntPtr" , None , None , [], ([[v_unativeint_ty];[v_unativeint_ty];[v_unativeint_ty]], mkSeqTy v_unativeint_ty))
let v_range_int16_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeInt16" , None , None , [], ([[v_int16_ty];[v_int16_ty];[v_int16_ty]], mkSeqTy v_int16_ty))
let v_range_uint16_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeUInt16" , None , None , [], ([[v_uint16_ty];[v_uint16_ty];[v_uint16_ty]], mkSeqTy v_uint16_ty))
let v_range_sbyte_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeSByte" , None , None , [], ([[v_sbyte_ty];[v_sbyte_ty];[v_sbyte_ty]], mkSeqTy v_sbyte_ty))
let v_range_byte_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeByte" , None , None , [], ([[v_byte_ty];[v_byte_ty];[v_byte_ty]], mkSeqTy v_byte_ty))
let v_range_char_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeChar" , None , None , [], ([[v_char_ty];[v_char_ty];[v_char_ty]], mkSeqTy v_char_ty))
let v_range_generic_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeGeneric" , None , None , [vara], ([[varaTy];[varaTy]], mkSeqTy varaTy))
let v_range_step_generic_op_info = makeIntrinsicValRef(fslib_MFOperatorIntrinsics_nleref, "RangeStepGeneric" , None , None , [vara;varb], ([[varaTy];[varbTy];[varaTy]], mkSeqTy varaTy))

let v_array_length_info = makeIntrinsicValRef(fslib_MFArrayModule_nleref, "length" , None , Some "Length" , [vara], ([[mkArrayType 1 varaTy]], v_int_ty))
let v_array_get_info = makeIntrinsicValRef(fslib_MFIntrinsicFunctions_nleref, "GetArray" , None , None , [vara], ([[mkArrayType 1 varaTy]; [v_int_ty]], varaTy))
Expand Down Expand Up @@ -1687,6 +1699,18 @@ type TcGlobals(
member val range_op_vref = ValRefForIntrinsic v_range_op_info
member val range_step_op_vref = ValRefForIntrinsic v_range_step_op_info
member val range_int32_op_vref = ValRefForIntrinsic v_range_int32_op_info
member val range_int64_op_vref = ValRefForIntrinsic v_range_int64_op_info
member val range_uint64_op_vref = ValRefForIntrinsic v_range_uint64_op_info
member val range_uint32_op_vref = ValRefForIntrinsic v_range_uint32_op_info
member val range_nativeint_op_vref = ValRefForIntrinsic v_range_nativeint_op_info
member val range_unativeint_op_vref = ValRefForIntrinsic v_range_unativeint_op_info
member val range_int16_op_vref = ValRefForIntrinsic v_range_int16_op_info
member val range_uint16_op_vref = ValRefForIntrinsic v_range_uint16_op_info
member val range_sbyte_op_vref = ValRefForIntrinsic v_range_sbyte_op_info
member val range_byte_op_vref = ValRefForIntrinsic v_range_byte_op_info
member val range_char_op_vref = ValRefForIntrinsic v_range_char_op_info
member val range_generic_op_vref = ValRefForIntrinsic v_range_generic_op_info
member val range_step_generic_op_vref = ValRefForIntrinsic v_range_step_generic_op_info
member val array_get_vref = ValRefForIntrinsic v_array_get_info
member val array2D_get_vref = ValRefForIntrinsic v_array2D_get_info
member val array3D_get_vref = ValRefForIntrinsic v_array3D_get_info
Expand Down
Loading
Loading