From c7ebffe5853386fc3cd0b4605b3bb90cf685659a Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Fri, 20 Oct 2023 11:53:42 +0200 Subject: [PATCH 1/3] IL: make ILTypeDefs, ILMethodDefs thread-safe --- src/Compiler/AbstractIL/il.fs | 71 ++++++++++++++------------------ src/Compiler/AbstractIL/il.fsi | 8 +++- src/Compiler/Utilities/illib.fs | 57 ++++++++++++++++++------- src/Compiler/Utilities/illib.fsi | 13 +++--- 4 files changed, 86 insertions(+), 63 deletions(-) diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 2c96d10f528..55741939b04 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -2142,24 +2142,21 @@ type ILMethodDef type MethodDefMap = Map [] -type ILMethodDefs(f: unit -> ILMethodDef[]) = +type ILMethodDefs(f) = + inherit DelayInitArrayMap(f) - let mutable array = InlineDelayInit<_>(f) + override this.CreateDictionary(arr) = + let t = Dictionary(arr.Length) - let mutable dict = - InlineDelayInit<_>(fun () -> - let arr = array.Value - let t = Dictionary<_, _>() + for i = arr.Length - 1 downto 0 do + let y = arr[i] + let key = y.Name - for i = arr.Length - 1 downto 0 do - let y = arr[i] - let key = y.Name + match t.TryGetValue key with + | true, m -> t[key] <- y :: m + | _ -> t[key] <- [ y ] - match t.TryGetValue key with - | true, m -> t[key] <- y :: m - | _ -> t[key] <- [ y ] - - t) + t interface IEnumerable with member x.GetEnumerator() = @@ -2167,14 +2164,13 @@ type ILMethodDefs(f: unit -> ILMethodDef[]) = interface IEnumerable with member x.GetEnumerator() = - (array.Value :> IEnumerable).GetEnumerator() - - member x.AsArray() = array.Value + (x.GetArray() :> IEnumerable).GetEnumerator() - member x.AsList() = array.Value |> Array.toList + member x.AsArray() = x.GetArray() + member x.AsList() = x.GetArray() |> Array.toList member x.FindByName nm = - match dict.Value.TryGetValue nm with + match x.GetDictionary().TryGetValue nm with | true, m -> m | _ -> [] @@ -2830,25 +2826,22 @@ type ILTypeDef override x.ToString() = "type " + x.Name and [] ILTypeDefs(f: unit -> ILPreTypeDef[]) = + inherit DelayInitArrayMap(f) - let mutable array = InlineDelayInit<_>(f) + override this.CreateDictionary(arr) = + let t = Dictionary(arr.Length, HashIdentity.Structural) - let mutable dict = - InlineDelayInit<_>(fun () -> - let arr = array.Value - let t = Dictionary<_, _>(HashIdentity.Structural) + for pre in arr do + let key = pre.Namespace, pre.Name + t[key] <- pre - for pre in arr do - let key = pre.Namespace, pre.Name - t[key] <- pre + ReadOnlyDictionary t - ReadOnlyDictionary t) + member x.AsArray() = + [| for pre in x.GetArray() -> pre.GetTypeDef() |] - member _.AsArray() = - [| for pre in array.Value -> pre.GetTypeDef() |] - - member _.AsList() = - [ for pre in array.Value -> pre.GetTypeDef() ] + member x.AsList() = + [ for pre in x.GetArray() -> pre.GetTypeDef() ] interface IEnumerable with member x.GetEnumerator() = @@ -2856,17 +2849,17 @@ and [] ILTypeDefs(f: unit -> ILPreTypeDef[]) = interface IEnumerable with member x.GetEnumerator() = - (seq { for pre in array.Value -> pre.GetTypeDef() }).GetEnumerator() + (seq { for pre in x.GetArray() -> pre.GetTypeDef() }).GetEnumerator() - member _.AsArrayOfPreTypeDefs() = array.Value + member x.AsArrayOfPreTypeDefs() = x.GetArray() - member _.FindByName nm = + member x.FindByName nm = let ns, n = splitILTypeName nm - dict.Value[ (ns, n) ].GetTypeDef() + x.GetDictionary().[ (ns, n) ].GetTypeDef() - member _.ExistsByName nm = + member x.ExistsByName nm = let ns, n = splitILTypeName nm - dict.Value.ContainsKey((ns, n)) + x.GetDictionary().ContainsKey((ns, n)) and [] ILPreTypeDef = abstract Namespace: string list diff --git a/src/Compiler/AbstractIL/il.fsi b/src/Compiler/AbstractIL/il.fsi index 34e7d8584e2..37bfd87e4ae 100644 --- a/src/Compiler/AbstractIL/il.fsi +++ b/src/Compiler/AbstractIL/il.fsi @@ -7,6 +7,7 @@ module rec FSharp.Compiler.AbstractIL.IL open FSharp.Compiler.IO open System.Collections.Generic open System.Reflection +open Internal.Utilities.Library /// Represents the target primary assembly [] @@ -1179,8 +1180,9 @@ type ILMethodDef = /// Tables of methods. Logically equivalent to a list of methods but /// the table is kept in a form optimized for looking up methods by /// name and arity. -[] +[] type ILMethodDefs = + inherit DelayInitArrayMap interface IEnumerable @@ -1458,8 +1460,10 @@ type ILTypeDefKind = | Delegate /// Tables of named type definitions. -[] +[] type ILTypeDefs = + inherit DelayInitArrayMap + interface IEnumerable member internal AsArray: unit -> ILTypeDef[] diff --git a/src/Compiler/Utilities/illib.fs b/src/Compiler/Utilities/illib.fs index a0db105d145..c88d592b883 100644 --- a/src/Compiler/Utilities/illib.fs +++ b/src/Compiler/Utilities/illib.fs @@ -118,25 +118,50 @@ module internal PervasiveAutoOpens = task.Result -/// An efficient lazy for inline storage in a class type. Results in fewer thunks. -[] -type InlineDelayInit<'T when 'T: not struct> = - new(f: unit -> 'T) = - { - store = Unchecked.defaultof<'T> - func = Func<_>(f) - } +[] +type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue>(f: unit -> 'T[]) = + let syncObj = obj() - val mutable store: 'T - val mutable func: Func<'T> MaybeNull + let mutable arrayStore = null + let mutable dictStore = null + + let mutable func = f + + member this.GetArray() = + match arrayStore with + | NonNull value -> value + | _ -> + Monitor.Enter(syncObj) + try + match arrayStore with + | NonNull value -> value + | _ -> + + arrayStore <- func () + + func <- Unchecked.defaultof<_> + arrayStore + finally + Monitor.Exit(syncObj) - member x.Value = - match x.func with - | null -> x.store + member this.GetDictionary() = + match dictStore with + | NonNull value -> value | _ -> - let res = LazyInitializer.EnsureInitialized(&x.store, x.func) - x.func <- null - res + let array = this.GetArray() + Monitor.Enter(syncObj) + try + match dictStore with + | NonNull value -> value + | _ -> + + dictStore <- this.CreateDictionary(array) + dictStore + finally + Monitor.Exit(syncObj) + + abstract CreateDictionary: 'T[] -> IDictionary<'TDictKey, 'TDictValue> + //------------------------------------------------------------------------- // Library: projections diff --git a/src/Compiler/Utilities/illib.fsi b/src/Compiler/Utilities/illib.fsi index bb41d9c490f..ade6e3205cd 100644 --- a/src/Compiler/Utilities/illib.fsi +++ b/src/Compiler/Utilities/illib.fsi @@ -70,13 +70,14 @@ module internal PervasiveAutoOpens = val notFound: unit -> 'a -[] -type internal InlineDelayInit<'T when 'T: not struct> = +[] +type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue> = + new: f: (unit -> 'T[]) -> DelayInitArrayMap<'T, 'TDictKey, 'TDictValue> + + member GetArray: unit -> 'T[] + member GetDictionary: unit -> IDictionary<'TDictKey, 'TDictValue> - new: f: (unit -> 'T) -> InlineDelayInit<'T> - val mutable store: 'T - val mutable func: Func<'T> - member Value: 'T + abstract CreateDictionary: 'T[] -> IDictionary<'TDictKey, 'TDictValue> module internal Order = From b3096fd9955a487a929b7c72dc65b4f206eb719f Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Fri, 20 Oct 2023 12:44:22 +0200 Subject: [PATCH 2/3] Fantomas --- src/Compiler/AbstractIL/il.fs | 2 +- src/Compiler/Utilities/illib.fs | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Compiler/AbstractIL/il.fs b/src/Compiler/AbstractIL/il.fs index 55741939b04..93ada135102 100644 --- a/src/Compiler/AbstractIL/il.fs +++ b/src/Compiler/AbstractIL/il.fs @@ -2855,7 +2855,7 @@ and [] ILTypeDefs(f: unit -> ILPreTypeDef[]) = member x.FindByName nm = let ns, n = splitILTypeName nm - x.GetDictionary().[ (ns, n) ].GetTypeDef() + x.GetDictionary().[(ns, n)].GetTypeDef() member x.ExistsByName nm = let ns, n = splitILTypeName nm diff --git a/src/Compiler/Utilities/illib.fs b/src/Compiler/Utilities/illib.fs index c88d592b883..776525e32b0 100644 --- a/src/Compiler/Utilities/illib.fs +++ b/src/Compiler/Utilities/illib.fs @@ -120,7 +120,7 @@ module internal PervasiveAutoOpens = [] type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue>(f: unit -> 'T[]) = - let syncObj = obj() + let syncObj = obj () let mutable arrayStore = null let mutable dictStore = null @@ -132,15 +132,16 @@ type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue>(f: unit -> 'T[]) = | NonNull value -> value | _ -> Monitor.Enter(syncObj) + try match arrayStore with | NonNull value -> value | _ -> - - arrayStore <- func () - func <- Unchecked.defaultof<_> - arrayStore + arrayStore <- func () + + func <- Unchecked.defaultof<_> + arrayStore finally Monitor.Exit(syncObj) @@ -150,19 +151,19 @@ type DelayInitArrayMap<'T, 'TDictKey, 'TDictValue>(f: unit -> 'T[]) = | _ -> let array = this.GetArray() Monitor.Enter(syncObj) + try match dictStore with | NonNull value -> value | _ -> - - dictStore <- this.CreateDictionary(array) - dictStore + + dictStore <- this.CreateDictionary(array) + dictStore finally Monitor.Exit(syncObj) abstract CreateDictionary: 'T[] -> IDictionary<'TDictKey, 'TDictValue> - //------------------------------------------------------------------------- // Library: projections //------------------------------------------------------------------------ From da1221879ebbe6cc20edb3b1f15cf93c23765f8d Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Fri, 20 Oct 2023 13:24:24 +0200 Subject: [PATCH 3/3] Update surface area --- ...p.Compiler.Service.SurfaceArea.netstandard20.debug.bsl | 8 +++++++- ...Compiler.Service.SurfaceArea.netstandard20.release.bsl | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl index e5d58f2ed14..3ed7352ab36 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.debug.bsl @@ -799,6 +799,7 @@ FSharp.Compiler.AbstractIL.IL+ILMethodDefs: ILMethodDef[] AsArray() FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] AsList() FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] FindByName(System.String) FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] TryFindInstanceByNameAndCallingSignature(System.String, ILCallingSignature) +FSharp.Compiler.AbstractIL.IL+ILMethodDefs: System.Collections.Generic.IDictionary`2[System.String,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef]] CreateDictionary(ILMethodDef[]) FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(ILMethodImplDef) FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object) FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object, System.Collections.IEqualityComparer) @@ -1619,6 +1620,7 @@ FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 GetHashCode(System.Collecti FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 Tag FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 get_Tag() FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: System.String ToString() +FSharp.Compiler.AbstractIL.IL+ILTypeDefs: System.Collections.Generic.IDictionary`2[System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],System.String],FSharp.Compiler.AbstractIL.IL+ILPreTypeDef] CreateDictionary(ILPreTypeDef[]) FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 BeforeField FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 OnAny FSharp.Compiler.AbstractIL.IL+ILTypeInit: Boolean Equals(ILTypeInit) @@ -11707,4 +11709,8 @@ FSharp.Compiler.Xml.XmlDoc: System.String GetXmlText() FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines() FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines() -FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range) \ No newline at end of file +FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range) +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] CreateDictionary(T[]) +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] GetDictionary() +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: T[] GetArray() +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: Void .ctor(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T[]]) \ No newline at end of file diff --git a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl index e5d58f2ed14..3ed7352ab36 100644 --- a/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl +++ b/tests/FSharp.Compiler.Service.Tests/FSharp.Compiler.Service.SurfaceArea.netstandard20.release.bsl @@ -799,6 +799,7 @@ FSharp.Compiler.AbstractIL.IL+ILMethodDefs: ILMethodDef[] AsArray() FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] AsList() FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] FindByName(System.String) FSharp.Compiler.AbstractIL.IL+ILMethodDefs: Microsoft.FSharp.Core.FSharpOption`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef] TryFindInstanceByNameAndCallingSignature(System.String, ILCallingSignature) +FSharp.Compiler.AbstractIL.IL+ILMethodDefs: System.Collections.Generic.IDictionary`2[System.String,Microsoft.FSharp.Collections.FSharpList`1[FSharp.Compiler.AbstractIL.IL+ILMethodDef]] CreateDictionary(ILMethodDef[]) FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(ILMethodImplDef) FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object) FSharp.Compiler.AbstractIL.IL+ILMethodImplDef: Boolean Equals(System.Object, System.Collections.IEqualityComparer) @@ -1619,6 +1620,7 @@ FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 GetHashCode(System.Collecti FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 Tag FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: Int32 get_Tag() FSharp.Compiler.AbstractIL.IL+ILTypeDefLayout: System.String ToString() +FSharp.Compiler.AbstractIL.IL+ILTypeDefs: System.Collections.Generic.IDictionary`2[System.Tuple`2[Microsoft.FSharp.Collections.FSharpList`1[System.String],System.String],FSharp.Compiler.AbstractIL.IL+ILPreTypeDef] CreateDictionary(ILPreTypeDef[]) FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 BeforeField FSharp.Compiler.AbstractIL.IL+ILTypeInit+Tags: Int32 OnAny FSharp.Compiler.AbstractIL.IL+ILTypeInit: Boolean Equals(ILTypeInit) @@ -11707,4 +11709,8 @@ FSharp.Compiler.Xml.XmlDoc: System.String GetXmlText() FSharp.Compiler.Xml.XmlDoc: System.String[] GetElaboratedXmlLines() FSharp.Compiler.Xml.XmlDoc: System.String[] UnprocessedLines FSharp.Compiler.Xml.XmlDoc: System.String[] get_UnprocessedLines() -FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range) \ No newline at end of file +FSharp.Compiler.Xml.XmlDoc: Void .ctor(System.String[], FSharp.Compiler.Text.Range) +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] CreateDictionary(T[]) +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: System.Collections.Generic.IDictionary`2[TDictKey,TDictValue] GetDictionary() +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: T[] GetArray() +Internal.Utilities.Library.DelayInitArrayMap`3[T,TDictKey,TDictValue]: Void .ctor(Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,T[]]) \ No newline at end of file