Skip to content

Commit db21cf2

Browse files
DedSec256psfinaki
andauthored
Optimize metadata reading for extension methods (#16168)
Co-authored-by: Petr <[email protected]>
1 parent 54a1997 commit db21cf2

File tree

12 files changed

+221
-80
lines changed

12 files changed

+221
-80
lines changed

docs/release-notes/.FSharp.Compiler.Service/8.0.400.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,4 @@
3535
* Reduce allocations in compiler checking via `ValueOption` usage ([PR #16822](https://github.com/dotnet/fsharp/pull/16822))
3636
* Use AsyncLocal instead of ThreadStatic to hold Cancellable.Token ([PR #17156](https://github.com/dotnet/fsharp/pull/17156))
3737
* Showing and inserting correct name of entities from unopened namespace/module ([Issue #14375](https://github.com/dotnet/fsharp/issues/14375), [PR #17261](https://github.com/dotnet/fsharp/pull/17261))
38+
* Support lazy custom attributes calculation for `ILTypeDef` public API, improve `ExtensionAttribute` presence detecting perf. ([PR #16168](https://github.com/dotnet/fsharp/pull/16168))

src/Compiler/AbstractIL/il.fs

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,6 +1258,9 @@ let storeILCustomAttrs (attrs: ILAttributes) =
12581258
else
12591259
ILAttributesStored.Given attrs
12601260

1261+
let mkILCustomAttrsComputed f =
1262+
ILAttributesStored.Reader(fun _ -> f ())
1263+
12611264
let mkILCustomAttrsReader f = ILAttributesStored.Reader f
12621265

12631266
type ILCodeLabel = int
@@ -2611,6 +2614,14 @@ let convertInitSemantics (init: ILTypeInit) =
26112614
| ILTypeInit.BeforeField -> TypeAttributes.BeforeFieldInit
26122615
| ILTypeInit.OnAny -> enum 0
26132616

2617+
[<Flags>]
2618+
type ILTypeDefAdditionalFlags =
2619+
| None = 0
2620+
| IsKnownToBeAttribute = 1
2621+
/// The type can contain extension methods,
2622+
/// or this information may not be available at the time the ILTypeDef is created
2623+
| CanContainExtensionMethods = 2
2624+
26142625
[<NoComparison; NoEquality; StructuredFormatDisplay("{DebugText}")>]
26152626
type ILTypeDef
26162627
(
@@ -2626,14 +2637,16 @@ type ILTypeDef
26262637
methodImpls: ILMethodImplDefs,
26272638
events: ILEventDefs,
26282639
properties: ILPropertyDefs,
2629-
isKnownToBeAttribute: bool,
2640+
additionalFlags: ILTypeDefAdditionalFlags,
26302641
securityDeclsStored: ILSecurityDeclsStored,
26312642
customAttrsStored: ILAttributesStored,
26322643
metadataIndex: int32
26332644
) =
26342645

26352646
let mutable customAttrsStored = customAttrsStored
26362647

2648+
let hasFlag flag = additionalFlags &&& flag = flag
2649+
26372650
new(name,
26382651
attributes,
26392652
layout,
@@ -2646,7 +2659,7 @@ type ILTypeDef
26462659
methodImpls,
26472660
events,
26482661
properties,
2649-
isKnownToBeAttribute,
2662+
additionalFlags,
26502663
securityDecls,
26512664
customAttrs) =
26522665
ILTypeDef(
@@ -2662,9 +2675,9 @@ type ILTypeDef
26622675
methodImpls,
26632676
events,
26642677
properties,
2665-
isKnownToBeAttribute,
2678+
additionalFlags,
26662679
storeILSecurityDecls securityDecls,
2667-
storeILCustomAttrs customAttrs,
2680+
customAttrs,
26682681
NoMetadataIdx
26692682
)
26702683

@@ -2694,7 +2707,10 @@ type ILTypeDef
26942707

26952708
member _.Properties = properties
26962709

2697-
member _.IsKnownToBeAttribute = isKnownToBeAttribute
2710+
member _.IsKnownToBeAttribute = hasFlag ILTypeDefAdditionalFlags.IsKnownToBeAttribute
2711+
2712+
member _.CanContainExtensionMethods =
2713+
hasFlag ILTypeDefAdditionalFlags.CanContainExtensionMethods
26982714

26992715
member _.CustomAttrsStored = customAttrsStored
27002716

@@ -2714,7 +2730,7 @@ type ILTypeDef
27142730
?methodImpls,
27152731
?events,
27162732
?properties,
2717-
?isKnownToBeAttribute,
2733+
?newAdditionalFlags,
27182734
?customAttrs,
27192735
?securityDecls
27202736
) =
@@ -2732,11 +2748,11 @@ type ILTypeDef
27322748
methodImpls = defaultArg methodImpls x.MethodImpls,
27332749
events = defaultArg events x.Events,
27342750
properties = defaultArg properties x.Properties,
2735-
isKnownToBeAttribute = defaultArg isKnownToBeAttribute x.IsKnownToBeAttribute,
2736-
customAttrs = defaultArg customAttrs x.CustomAttrs
2751+
additionalFlags = defaultArg newAdditionalFlags additionalFlags,
2752+
customAttrs = defaultArg customAttrs (storeILCustomAttrs x.CustomAttrs)
27372753
)
27382754

2739-
member x.CustomAttrs =
2755+
member x.CustomAttrs: ILAttributes =
27402756
match customAttrsStored with
27412757
| ILAttributesStored.Reader f ->
27422758
let res = ILAttributes(f x.MetadataIndex)
@@ -4220,11 +4236,11 @@ let mkILGenericClass (nm, access, genparams, extends, impl, methods, fields, nes
42204236
methods = methods,
42214237
fields = fields,
42224238
nestedTypes = nestedTypes,
4223-
customAttrs = attrs,
4239+
customAttrs = storeILCustomAttrs attrs,
42244240
methodImpls = emptyILMethodImpls,
42254241
properties = props,
42264242
events = events,
4227-
isKnownToBeAttribute = false,
4243+
additionalFlags = ILTypeDefAdditionalFlags.None,
42284244
securityDecls = emptyILSecurityDecls
42294245
)
42304246

@@ -4244,11 +4260,11 @@ let mkRawDataValueTypeDef (iltyp_ValueType: ILType) (nm, size, pack) =
42444260
methods = emptyILMethods,
42454261
fields = emptyILFields,
42464262
nestedTypes = emptyILTypeDefs,
4247-
customAttrs = emptyILCustomAttrs,
4263+
customAttrs = emptyILCustomAttrsStored,
42484264
methodImpls = emptyILMethodImpls,
42494265
properties = emptyILProperties,
42504266
events = emptyILEvents,
4251-
isKnownToBeAttribute = false,
4267+
additionalFlags = ILTypeDefAdditionalFlags.None,
42524268
securityDecls = emptyILSecurityDecls
42534269
)
42544270

src/Compiler/AbstractIL/il.fsi

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
55
module rec FSharp.Compiler.AbstractIL.IL
66

7+
open System
78
open FSharp.Compiler.IO
89
open System.Collections.Generic
910
open System.Reflection
@@ -1481,6 +1482,12 @@ type ILTypeDefs =
14811482
/// Calls to <c>ExistsByName</c> will result in all the ILPreTypeDefs being read.
14821483
member internal ExistsByName: string -> bool
14831484

1485+
[<Flags>]
1486+
type ILTypeDefAdditionalFlags =
1487+
| None = 0
1488+
| IsKnownToBeAttribute = 1
1489+
| CanContainExtensionMethods = 2
1490+
14841491
/// Represents IL Type Definitions.
14851492
[<NoComparison; NoEquality>]
14861493
type ILTypeDef =
@@ -1499,7 +1506,7 @@ type ILTypeDef =
14991506
methodImpls: ILMethodImplDefs *
15001507
events: ILEventDefs *
15011508
properties: ILPropertyDefs *
1502-
isKnownToBeAttribute: bool *
1509+
additionalFlags: ILTypeDefAdditionalFlags *
15031510
securityDeclsStored: ILSecurityDeclsStored *
15041511
customAttrsStored: ILAttributesStored *
15051512
metadataIndex: int32 ->
@@ -1519,9 +1526,9 @@ type ILTypeDef =
15191526
methodImpls: ILMethodImplDefs *
15201527
events: ILEventDefs *
15211528
properties: ILPropertyDefs *
1522-
isKnownToBeAttribute: bool *
1529+
additionalFlags: ILTypeDefAdditionalFlags *
15231530
securityDecls: ILSecurityDecls *
1524-
customAttrs: ILAttributes ->
1531+
customAttrs: ILAttributesStored ->
15251532
ILTypeDef
15261533

15271534
member Name: string
@@ -1556,6 +1563,7 @@ type ILTypeDef =
15561563
member HasSecurity: bool
15571564
member Encoding: ILDefaultPInvokeEncoding
15581565
member IsKnownToBeAttribute: bool
1566+
member CanContainExtensionMethods: bool
15591567

15601568
member internal WithAccess: ILTypeDefAccess -> ILTypeDef
15611569
member internal WithNestedAccess: ILMemberAccess -> ILTypeDef
@@ -1584,8 +1592,8 @@ type ILTypeDef =
15841592
?methodImpls: ILMethodImplDefs *
15851593
?events: ILEventDefs *
15861594
?properties: ILPropertyDefs *
1587-
?isKnownToBeAttribute: bool *
1588-
?customAttrs: ILAttributes *
1595+
?newAdditionalFlags: ILTypeDefAdditionalFlags *
1596+
?customAttrs: ILAttributesStored *
15891597
?securityDecls: ILSecurityDecls ->
15901598
ILTypeDef
15911599

@@ -2212,8 +2220,10 @@ val internal mkILTypeForGlobalFunctions: ILScopeRef -> ILType
22122220
val mkILCustomAttrs: ILAttribute list -> ILAttributes
22132221
val mkILCustomAttrsFromArray: ILAttribute[] -> ILAttributes
22142222
val storeILCustomAttrs: ILAttributes -> ILAttributesStored
2223+
val mkILCustomAttrsComputed: (unit -> ILAttribute[]) -> ILAttributesStored
22152224
val internal mkILCustomAttrsReader: (int32 -> ILAttribute[]) -> ILAttributesStored
22162225
val emptyILCustomAttrs: ILAttributes
2226+
val emptyILCustomAttrsStored: ILAttributesStored
22172227

22182228
val mkILSecurityDecls: ILSecurityDecl list -> ILSecurityDecls
22192229
val emptyILSecurityDecls: ILSecurityDecls

src/Compiler/AbstractIL/ilmorph.fs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ let rec tdef_ty2ty_ilmbody2ilmbody_mdefs2mdefs isInKnownSet enc fs (tdef: ILType
378378
methodImpls = mimpls_ty2ty fTyInCtxtR tdef.MethodImpls,
379379
events = edefs_ty2ty fTyInCtxtR tdef.Events,
380380
properties = pdefs_ty2ty fTyInCtxtR tdef.Properties,
381-
customAttrs = cattrs_ty2ty fTyInCtxtR tdef.CustomAttrs
381+
customAttrs = storeILCustomAttrs (cattrs_ty2ty fTyInCtxtR tdef.CustomAttrs)
382382
)
383383

384384
and tdefs_ty2ty_ilmbody2ilmbody_mdefs2mdefs isInKnownSet enc fs tdefs =

src/Compiler/AbstractIL/ilread.fs

Lines changed: 97 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -858,10 +858,10 @@ let hsCompare (TaggedIndex(t1: HasSemanticsTag, idx1: int)) (TaggedIndex(t2: Has
858858
elif idx1 > idx2 then 1
859859
else compare t1.Tag t2.Tag
860860

861-
let hcaCompare (TaggedIndex(t1: HasCustomAttributeTag, idx1: int)) (TaggedIndex(t2: HasCustomAttributeTag, idx2)) =
862-
if idx1 < idx2 then -1
863-
elif idx1 > idx2 then 1
864-
else compare t1.Tag t2.Tag
861+
let inline hcaCompare (t1: TaggedIndex<HasCustomAttributeTag>) (t2: TaggedIndex<HasCustomAttributeTag>) =
862+
if t1.index < t2.index then -1
863+
elif t1.index > t2.index then 1
864+
else compare t1.tag t2.tag
865865

866866
let mfCompare (TaggedIndex(t1: MemberForwardedTag, idx1: int)) (TaggedIndex(t2: MemberForwardedTag, idx2)) =
867867
if idx1 < idx2 then -1
@@ -2112,9 +2112,82 @@ and typeDefReader ctxtH : ILTypeDefStored =
21122112
let layout = typeLayoutOfFlags ctxt mdv flags idx
21132113

21142114
let hasLayout =
2115-
(match layout with
2116-
| ILTypeDefLayout.Explicit _ -> true
2117-
| _ -> false)
2115+
match layout with
2116+
| ILTypeDefLayout.Explicit _ -> true
2117+
| _ -> false
2118+
2119+
let containsExtensionMethods =
2120+
let mutable containsExtensionMethods = false
2121+
let searchedKey = TaggedIndex(hca_TypeDef, idx)
2122+
2123+
let attributesSearcher =
2124+
{ new ISeekReadIndexedRowReader<int, int, int> with
2125+
member _.GetRow(i, rowIndex) = rowIndex <- i
2126+
member _.GetKey(rowIndex) = rowIndex
2127+
2128+
member _.CompareKey(rowIndex) =
2129+
let mutable addr = ctxt.rowAddr TableNames.CustomAttribute rowIndex
2130+
// read parentIndex
2131+
let key = seekReadHasCustomAttributeIdx ctxt mdv &addr
2132+
hcaCompare searchedKey key
2133+
2134+
member _.ConvertRow(i) = i
2135+
}
2136+
2137+
let attrsStartIdx, attrsEndIdx =
2138+
seekReadIndexedRowsRange
2139+
(ctxt.getNumRows TableNames.CustomAttribute)
2140+
(isSorted ctxt TableNames.CustomAttribute)
2141+
attributesSearcher
2142+
2143+
if attrsStartIdx <= 0 || attrsEndIdx < attrsStartIdx then
2144+
false
2145+
else
2146+
let mutable attrIdx = attrsStartIdx
2147+
2148+
let looksLikeSystemAssembly =
2149+
ctxt.fileName.EndsWith("System.Runtime.dll")
2150+
|| ctxt.fileName.EndsWith("mscorlib.dll")
2151+
|| ctxt.fileName.EndsWith("netstandard.dll")
2152+
2153+
while attrIdx <= attrsEndIdx && not containsExtensionMethods do
2154+
let mutable addr = ctxt.rowAddr TableNames.CustomAttribute attrIdx
2155+
// skip parentIndex to read typeIndex
2156+
seekReadHasCustomAttributeIdx ctxt mdv &addr |> ignore
2157+
let attrTypeIndex = seekReadCustomAttributeTypeIdx ctxt mdv &addr
2158+
let attrCtorIdx = attrTypeIndex.index
2159+
2160+
let name =
2161+
if attrTypeIndex.tag = cat_MethodDef then
2162+
// the ExtensionAttribute constructor can be cat_MethodDef if the metadata is read from the assembly
2163+
// in which the corresponding attribute is defined -- from the system library
2164+
if not looksLikeSystemAssembly then
2165+
""
2166+
else
2167+
let _, (_, nameIdx, namespaceIdx, _, _, _) = seekMethodDefParent ctxt attrCtorIdx
2168+
readBlobHeapAsTypeName ctxt (nameIdx, namespaceIdx)
2169+
else
2170+
let mutable addr = ctxt.rowAddr TableNames.MemberRef attrCtorIdx
2171+
let mrpTag = seekReadMemberRefParentIdx ctxt mdv &addr
2172+
2173+
if mrpTag.tag <> mrp_TypeRef then
2174+
""
2175+
else
2176+
let _, nameIdx, namespaceIdx = seekReadTypeRefRow ctxt mdv mrpTag.index
2177+
readBlobHeapAsTypeName ctxt (nameIdx, namespaceIdx)
2178+
2179+
if name = "System.Runtime.CompilerServices.ExtensionAttribute" then
2180+
containsExtensionMethods <- true
2181+
2182+
attrIdx <- attrIdx + 1
2183+
2184+
containsExtensionMethods
2185+
2186+
let additionalFlags =
2187+
if containsExtensionMethods then
2188+
ILTypeDefAdditionalFlags.CanContainExtensionMethods
2189+
else
2190+
ILTypeDefAdditionalFlags.None
21182191

21192192
let mdefs = seekReadMethods ctxt numTypars methodsIdx endMethodsIdx
21202193
let fdefs = seekReadFields ctxt (numTypars, hasLayout) fieldsIdx endFieldsIdx
@@ -2138,7 +2211,7 @@ and typeDefReader ctxtH : ILTypeDefStored =
21382211
methodImpls = mimpls,
21392212
events = events,
21402213
properties = props,
2141-
isKnownToBeAttribute = false,
2214+
additionalFlags = additionalFlags,
21422215
customAttrsStored = ctxt.customAttrsReader_TypeDef,
21432216
metadataIndex = idx
21442217
))
@@ -2797,22 +2870,26 @@ and seekReadMemberRefAsFieldSpecUncached ctxtH (MemberRefAsFspecIdx(numTypars, i
27972870
// method-range and field-range start/finish indexes
27982871
and seekReadMethodDefAsMethodData ctxt idx = ctxt.seekReadMethodDefAsMethodData idx
27992872

2873+
and seekMethodDefParent (ctxt: ILMetadataReader) methodIdx =
2874+
seekReadIndexedRow (
2875+
ctxt.getNumRows TableNames.TypeDef,
2876+
(fun i -> i, seekReadTypeDefRow ctxt i),
2877+
id,
2878+
(fun (i, (_, _, _, _, _, methodsIdx as info)) ->
2879+
if methodsIdx > methodIdx then
2880+
-1
2881+
else
2882+
let struct (_, endMethodsIdx) = seekReadTypeDefRowExtents ctxt info i
2883+
if endMethodsIdx <= methodIdx then 1 else 0),
2884+
true,
2885+
id
2886+
)
2887+
28002888
and seekReadMethodDefAsMethodDataUncached ctxtH idx =
28012889
let (ctxt: ILMetadataReader) = getHole ctxtH
28022890
let mdv = ctxt.mdfile.GetView()
28032891
// Look for the method def parent.
2804-
let tidx =
2805-
seekReadIndexedRow (
2806-
ctxt.getNumRows TableNames.TypeDef,
2807-
(fun i -> i, seekReadTypeDefRowWithExtents ctxt i),
2808-
id,
2809-
(fun (_, ((_, _, _, _, _, methodsIdx), (_, endMethodsIdx))) ->
2810-
if endMethodsIdx <= idx then 1
2811-
elif methodsIdx <= idx && idx < endMethodsIdx then 0
2812-
else -1),
2813-
true,
2814-
fst
2815-
)
2892+
let tidx, _ = seekMethodDefParent ctxt idx
28162893
// Create a formal instantiation if needed
28172894
let typeGenericArgs = seekReadGenericParams ctxt 0 (tomd_TypeDef, tidx)
28182895
let typeGenericArgsCount = typeGenericArgs.Length

src/Compiler/Checking/NameResolution.fs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -524,7 +524,10 @@ let NextExtensionMethodPriority() = uint64 (newStamp())
524524
/// Checks if the type is used for C# style extension members.
525525
let IsTyconRefUsedForCSharpStyleExtensionMembers g m (tcref: TyconRef) =
526526
// Type must be non-generic and have 'Extension' attribute
527-
isNil(tcref.Typars m) && TyconRefHasAttribute g m g.attrib_ExtensionAttribute tcref
527+
match metadataOfTycon tcref.Deref with
528+
| ILTypeMetadata(TILObjectReprData(_, _, tdef)) -> tdef.CanContainExtensionMethods
529+
| _ -> true
530+
&& isNil(tcref.Typars m) && TyconRefHasAttribute g m g.attrib_ExtensionAttribute tcref
528531

529532
/// Checks if the type is used for C# style extension members.
530533
let IsTypeUsedForCSharpStyleExtensionMembers g m ty =

0 commit comments

Comments
 (0)