From d6c0396590744d3c18273a4460b13e5b4c948254 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 12 Aug 2026 14:52:40 +0200 Subject: [PATCH 1/2] IL: create empty tables for members when possible --- src/Compiler/AbstractIL/ilread.fs | 174 +++++++++++++++--------------- 1 file changed, 88 insertions(+), 86 deletions(-) diff --git a/src/Compiler/AbstractIL/ilread.fs b/src/Compiler/AbstractIL/ilread.fs index 09fc311367a..5b915112234 100644 --- a/src/Compiler/AbstractIL/ilread.fs +++ b/src/Compiler/AbstractIL/ilread.fs @@ -1542,6 +1542,26 @@ let seekReadMethodImplRow (ctxt: ILMetadataReader) mdv idx = let mdeclIdx = seekReadMethodDefOrRefIdx ctxt mdv &addr (tidx, mbodyIdx, mdeclIdx) +/// Rows of a table keyed by type def in its first column: at most one per type in EventMap and +/// PropertyMap, pointing at its member range, and the overrides themselves in MethodImpl. Called per +/// type def rather than when the table it feeds is forced, so the types with no rows - most of them - +/// can share the empty table. +let seekReadRowRangeForTypeDef (ctxt: ILMetadataReader) mdv (table: TableName) tidx = + let searcher = + { new ISeekReadIndexedRowReader with + member _.GetRow(i, rowIndex) = rowIndex <- i + member _.GetKey(rowIndex) = rowIndex + + member _.CompareKey(rowIndex) = + let mutable addr = ctxt.rowAddr table rowIndex + let rowTidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr + simpleIndexCompare tidx rowTidx + + member _.ConvertRow(rowIndex) = rowIndex + } + + seekReadIndexedRowsRange (ctxt.getNumRows table) true searcher + /// Read Table ILModuleRef. let seekReadModuleRefRow (ctxt: ILMetadataReader) mdv idx = let mutable addr = ctxt.rowAddr TableNames.ModuleRef idx @@ -2210,9 +2230,9 @@ and typeDefReader ctxtH : ILTypeDefStored = let impls = seekReadInterfaceImpls ctxt mdv numTypars idx - let mimpls = seekReadMethodImpls ctxt numTypars idx - let props = seekReadProperties ctxt numTypars idx - let events = seekReadEvents ctxt numTypars idx + let mimpls = seekReadMethodImpls ctxt mdv numTypars idx + let props = seekReadProperties ctxt mdv numTypars idx + let events = seekReadEvents ctxt mdv numTypars idx ILTypeDef( name = nm, @@ -2551,26 +2571,30 @@ and seekReadField ctxt mdv (numTypars, hasLayout) (idx: int) = ) and seekReadFields (ctxt: ILMetadataReader) (numTypars, hasLayout) fidx1 fidx2 = - mkILFieldsLazy ( - InterruptibleLazy(fun _ -> - let mdv = ctxt.mdfile.GetView() + if fidx1 <= 0 || fidx2 <= fidx1 then + emptyILFields + else + mkILFieldsLazy ( + InterruptibleLazy(fun _ -> + let mdv = ctxt.mdfile.GetView() - [ - if fidx1 > 0 then + [ for i = fidx1 to fidx2 - 1 do yield seekReadField ctxt mdv (numTypars, hasLayout) i - ]) - ) + ]) + ) and seekReadMethods (ctxt: ILMetadataReader) numTypars midx1 midx2 = - mkILMethodsComputed (fun () -> - let mdv = ctxt.mdfile.GetView() + if midx1 <= 0 || midx2 <= midx1 then + emptyILMethods + else + mkILMethodsComputed (fun () -> + let mdv = ctxt.mdfile.GetView() - [| - if midx1 > 0 then + [| for i = midx1 to midx2 - 1 do yield seekReadMethod ctxt mdv numTypars i - |]) + |]) and sigptrGetTypeDefOrRefOrSpecIdx bytes sigptr = let struct (n, sigptr) = sigptrGetZInt32 bytes sigptr @@ -3117,40 +3141,37 @@ and seekReadParamExtras (ctxt: ILMetadataReader) mdv (retRes: byref, p MetadataIndex = idx } -and seekReadMethodImpls (ctxt: ILMetadataReader) numTypars tidx = - mkILMethodImplsLazy ( - lazy - let mdv = ctxt.mdfile.GetView() +and seekReadMethodImpls (ctxt: ILMetadataReader) mdv numTypars tidx = + let startIdx, endIdx = + seekReadRowRangeForTypeDef ctxt mdv TableNames.MethodImpl tidx - let mimpls = - seekReadIndexedRows ( - ctxt.getNumRows TableNames.MethodImpl, - id, - id, - (fun i -> - let mutable addr = ctxt.rowAddr TableNames.MethodImpl i - let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr - simpleIndexCompare tidx _tidx), - isSorted ctxt TableNames.MethodImpl, - seekReadMethodImplRow ctxt mdv - ) + if startIdx <= 0 || endIdx < startIdx then + emptyILMethodImpls + else + mkILMethodImplsLazy ( + lazy + let mdv = ctxt.mdfile.GetView() - mimpls - |> List.map (fun (_, b, c) -> - { - OverrideBy = - let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) = - seekReadMethodDefOrRefNoVarargs ctxt numTypars b + [ + for i in startIdx..endIdx do + let _, b, c = seekReadMethodImplRow ctxt mdv i + + yield + { + OverrideBy = + let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) = + seekReadMethodDefOrRefNoVarargs ctxt numTypars b - mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst) - Overrides = - let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) = - seekReadMethodDefOrRefNoVarargs ctxt numTypars c + mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst) + Overrides = + let (MethodData(enclTy, cc, nm, argTys, retTy, methInst)) = + seekReadMethodDefOrRefNoVarargs ctxt numTypars c - let mspec = mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst) - OverridesSpec(mspec.MethodRef, mspec.DeclaringType) - }) - ) + let mspec = mkILMethSpecInTy (enclTy, cc, nm, argTys, retTy, methInst) + OverridesSpec(mspec.MethodRef, mspec.DeclaringType) + } + ] + ) and seekReadMultipleMethodSemantics (ctxt: ILMetadataReader) (flags, id) = seekReadIndexedRows ( @@ -3196,27 +3217,17 @@ and seekReadEvent ctxt mdv numTypars idx = metadataIndex = idx ) -(* REVIEW: can substantially reduce numbers of EventMap and PropertyMap reads by first checking if the whole table mdv sorted according to ILTypeDef tokens and then doing a binary chop *) -and seekReadEvents (ctxt: ILMetadataReader) numTypars tidx = - mkILEventsLazy ( - InterruptibleLazy(fun _ -> - let mdv = ctxt.mdfile.GetView() +and seekReadEvents (ctxt: ILMetadataReader) mdv numTypars tidx = + let rowNum, _ = seekReadRowRangeForTypeDef ctxt mdv TableNames.EventMap tidx + + if rowNum <= 0 then + emptyILEvents + else + mkILEventsLazy ( + InterruptibleLazy(fun _ -> + let mdv = ctxt.mdfile.GetView() + let _, beginEventIdx = seekReadEventMapRow ctxt mdv rowNum - match - seekReadOptionalIndexedRow ( - ctxt.getNumRows TableNames.EventMap, - id, - id, - (fun i -> - let mutable addr = ctxt.rowAddr TableNames.EventMap i - let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr - simpleIndexCompare tidx _tidx), - false, - (fun i -> i, seekReadEventMapRow ctxt mdv i |> snd) - ) - with - | None -> [] - | Some(rowNum, beginEventIdx) -> let endEventIdx = if rowNum >= ctxt.getNumRows TableNames.EventMap then ctxt.getNumRows TableNames.Event + 1 @@ -3229,7 +3240,7 @@ and seekReadEvents (ctxt: ILMetadataReader) numTypars tidx = for i in beginEventIdx .. endEventIdx - 1 do yield seekReadEvent ctxt mdv numTypars i ]) - ) + ) and seekReadProperty ctxt mdv numTypars idx = let flags, nameIdx, typIdx = seekReadPropertyRow ctxt mdv idx @@ -3267,26 +3278,17 @@ and seekReadProperty ctxt mdv numTypars idx = metadataIndex = idx ) -and seekReadProperties (ctxt: ILMetadataReader) numTypars tidx = - mkILPropertiesLazy ( - InterruptibleLazy(fun _ -> - let mdv = ctxt.mdfile.GetView() +and seekReadProperties (ctxt: ILMetadataReader) mdv numTypars tidx = + let rowNum, _ = seekReadRowRangeForTypeDef ctxt mdv TableNames.PropertyMap tidx + + if rowNum <= 0 then + emptyILProperties + else + mkILPropertiesLazy ( + InterruptibleLazy(fun _ -> + let mdv = ctxt.mdfile.GetView() + let _, beginPropIdx = seekReadPropertyMapRow ctxt mdv rowNum - match - seekReadOptionalIndexedRow ( - ctxt.getNumRows TableNames.PropertyMap, - id, - id, - (fun i -> - let mutable addr = ctxt.rowAddr TableNames.PropertyMap i - let _tidx = seekReadUntaggedIdx TableNames.TypeDef ctxt mdv &addr - simpleIndexCompare tidx _tidx), - false, - (fun i -> i, seekReadPropertyMapRow ctxt mdv i |> snd) - ) - with - | None -> [] - | Some(rowNum, beginPropIdx) -> let endPropIdx = if rowNum >= ctxt.getNumRows TableNames.PropertyMap then ctxt.getNumRows TableNames.Property + 1 @@ -3299,7 +3301,7 @@ and seekReadProperties (ctxt: ILMetadataReader) numTypars tidx = for i in beginPropIdx .. endPropIdx - 1 do yield seekReadProperty ctxt mdv numTypars i ]) - ) + ) and customAttrsReaderFn ctxtH tag : int32 -> ILAttribute[] = fun idx -> From c6e86440357bf2b2de3d4de88fe2ba2e4a7d5845 Mon Sep 17 00:00:00 2001 From: Eugene Auduchinok Date: Wed, 12 Aug 2026 15:20:08 +0200 Subject: [PATCH 2/2] Release notes --- docs/release-notes/.FSharp.Compiler.Service/11.0.100.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md index e11e34a5c88..548cac86546 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/11.0.100.md @@ -162,6 +162,7 @@ * Support for the `` XML documentation tag: at compile time, documentation is copied from an external XML file selected by an XPath query and emitted into the generated documentation file. `` remains unsupported. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19186](https://github.com/dotnet/fsharp/pull/19186)) * Expand `` at tooling time. In IDE tooltips, completion, and signature help, documentation is inherited from base classes, interfaces, overridden members, and constructors (matched by parameter signature). The FCS Symbols API (`FSharpSymbol.XmlDoc`) additionally resolves explicit `cref` targets, but does not expand constructor inheritance. The compiler emits the tag verbatim into generated XML documentation files, matching C#; `` is not implemented. ([Issue #19175](https://github.com/dotnet/fsharp/issues/19175), [PR #19188](https://github.com/dotnet/fsharp/pull/19188)) * Add symbol and type highlighting to F# diagnostics ([PR #20097](https://github.com/dotnet/fsharp/pull/20097)) +* IL: use empty tables for members when possible ([PR #20249](https://github.com/dotnet/fsharp/pull/20249)) ### Improved