Skip to content

Commit

Permalink
compiled list/array computations
Browse files Browse the repository at this point in the history
  • Loading branch information
Don Syme committed May 21, 2021
1 parent 7f22a41 commit 08b0a8c
Show file tree
Hide file tree
Showing 7 changed files with 465 additions and 71 deletions.
1 change: 1 addition & 0 deletions src/fsharp/CheckComputationExpressions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -1965,6 +1965,7 @@ let TcArrayOrListSequenceExpression (cenv: cenv) env overallTy tpenv (isArray, c

let expr =
if cenv.g.compilingFslib then
//warning(Error(FSComp.SR.fslibUsingComputedListOrArray(), expr.Range))
expr
else
// We add a call to 'seq ... ' to make sure sequence expression compilation gets applied to the contents of the
Expand Down
111 changes: 111 additions & 0 deletions src/fsharp/FSharp.Core/seqcore.fs
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,10 @@ namespace Microsoft.FSharp.Core.CompilerServices
{ new System.IDisposable with
member x.Dispose() = removeHandler h } }

let inline SetFreshConsTail cons tail = cons.( :: ).1 <- tail

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
let inline FreshConsNoTail head = head :: (# "ldnull" : 'T list #)

[<AbstractClass>]
type GeneratedSequenceBase<'T>() =
Expand Down Expand Up @@ -400,3 +404,110 @@ namespace Microsoft.FSharp.Core.CompilerServices
member x.MoveNext() = x.MoveNextImpl()

member _.Reset() = raise <| new System.NotSupportedException()


[<Struct; NoEquality; NoComparison>]
type ListCollector<'T> =
[<DefaultValue(false)>]
val mutable Result : 'T list

[<DefaultValue(false)>]
val mutable LastCons : 'T list

member this.Add (value: 'T) =
match box this.Result with
| null ->
let ra = RuntimeHelpers.FreshConsNoTail value
this.Result <- ra
this.LastCons <- ra
| _ ->
let ra = RuntimeHelpers.FreshConsNoTail value
RuntimeHelpers.SetFreshConsTail this.LastCons ra
this.LastCons <- ra

member this.AddMany (values: seq<'T>) =
// cook a faster iterator for lists and arrays
match values with
| :? ('T[]) as valuesAsArray ->
for v in valuesAsArray do
this.Add v
| :? ('T list) as valuesAsList ->
for v in valuesAsList do
this.Add v
| _ ->
for v in values do
this.Add v

member this.Close() =
match box this.Result with
| null -> []
| _ ->
RuntimeHelpers.SetFreshConsTail this.LastCons []
let res = this.Result
this.Result <- Unchecked.defaultof<_>
this.LastCons <- Unchecked.defaultof<_>
res

// Optimized for 0, 1 and 2 sized arrays
[<Struct; NoEquality; NoComparison>]
type ArrayCollector<'T> =
[<DefaultValue(false)>]
val mutable ResizeArray: ResizeArray<'T>
[<DefaultValue(false)>]
val mutable First: 'T
[<DefaultValue(false)>]
val mutable Second: 'T
[<DefaultValue(false)>]
val mutable Count: int

member this.Add (value: 'T) =
match this.Count with
| 0 ->
this.Count <- 1
this.First <- value
| 1 ->
this.Count <- 2
this.Second <- value
| 2 ->
let ra = ResizeArray()
ra.Add(this.First)
ra.Add(this.Second)
ra.Add(value)
this.Count <- 3
this.ResizeArray <- ra
| _ ->
this.ResizeArray.Add(value)

member this.AddMany (values: seq<'T>) =
if this.Count > 2 then
this.ResizeArray.AddRange(values)
else
// cook a faster iterator for lists and arrays
match values with
| :? ('T[]) as valuesAsArray ->
for v in valuesAsArray do
this.Add v
| :? ('T list) as valuesAsList ->
for v in valuesAsList do
this.Add v
| _ ->
for v in values do
this.Add v

member this.Close() =
match this.Count with
| 0 -> Array.Empty<'T>()
| 1 ->
let res = [| this.First |]
this.First <- Unchecked.defaultof<_>
res
| 2 ->
let res = [| this.First; this.Second |]
this.First <- Unchecked.defaultof<_>
this.Second <- Unchecked.defaultof<_>
res
| _ ->
let res = this.ResizeArray.ToArray()
this <- ArrayCollector<'T>()
res

48 changes: 48 additions & 0 deletions src/fsharp/FSharp.Core/seqcore.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -149,3 +149,51 @@ namespace Microsoft.FSharp.Core.CompilerServices
interface IEnumerator<'T>
interface IEnumerator
interface IDisposable

[<Struct; NoEquality; NoComparison>]
[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
type ListCollector<'T> =
[<DefaultValue(false)>]
val mutable internal Result: 'T list

[<DefaultValue(false)>]
val mutable internal LastCons: 'T list

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
member Add: value: 'T -> unit

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
member AddMany: values: seq<'T> -> unit

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
member Close: unit -> 'T list

[<Struct; NoEquality; NoComparison>]
[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
type ArrayCollector<'T> =
[<DefaultValue(false)>]
val mutable internal ResizeArray: ResizeArray<'T>
[<DefaultValue(false)>]
val mutable internal First: 'T
[<DefaultValue(false)>]
val mutable internal Second: 'T
[<DefaultValue(false)>]
val mutable internal Count: int

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
member Add: value: 'T -> unit

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
member AddMany: values: seq<'T> -> unit

[<Experimental("Experimental library feature, requires '--langversion:preview'")>]
[<CompilerMessage("This is for use by compiled F# code and should not be used directly", 1204, IsHidden=true)>]
member Close: unit -> 'T[]

8 changes: 7 additions & 1 deletion src/fsharp/IlxGen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2346,7 +2346,13 @@ and GenExprPreSteps (cenv: cenv) (cgbuf: CodeGenBuffer) eenv sp expr sequel =

ProcessDebugPointForExpr cenv cgbuf sp expr

match (if compileSequenceExpressions then ConvertSequenceExprToObject g cenv.amap expr else None) with
match (if compileSequenceExpressions then LowerComputedListOrArrayExpr cenv.tcVal g cenv.amap expr else None) with
| Some altExpr ->
GenExpr cenv cgbuf eenv sp altExpr sequel
true
| None ->

match (if compileSequenceExpressions && IsPossibleSequenceExpr g expr then ConvertSequenceExprToObject g cenv.amap expr else None) with
| Some info ->
GenSequenceExpr cenv cgbuf eenv info sequel
true
Expand Down
Loading

0 comments on commit 08b0a8c

Please sign in to comment.