Skip to content

Commit 596f3d7

Browse files
authored
Compiler can generate delegate with direct pointer when referencing static members (#9017)
* Added unit tests for delegates on static members * Changed expectations for static member invocation - Use static members directly * Compiler can generate delegate with direct pointer when referencing static members * Updated existing baseline files
1 parent 2cdf31c commit 596f3d7

9 files changed

Lines changed: 507 additions & 252 deletions

src/fsharp/IlxGen.fs

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4396,7 +4396,15 @@ and GenClosureTypeDefs cenv (tref: ILTypeRef, ilGenParams, attrs, ilCloAllFreeVa
43964396

43974397
let tdefs = EraseClosures.convIlxClosureDef g.ilxPubCloEnv tref.Enclosing tdef cloInfo
43984398
tdefs
4399-
4399+
4400+
and GenStaticClosureTypeDefs cenv (tref: ILTypeRef, ilGenParams, attrs, ilCloAllFreeVars, ilCloLambdas, ilCtorBody, mdefs, mimpls, ext, ilIntfTys) =
4401+
let tdefs = GenClosureTypeDefs cenv (tref, ilGenParams, attrs, ilCloAllFreeVars, ilCloLambdas, ilCtorBody, mdefs, mimpls, ext, ilIntfTys)
4402+
4403+
// Apply the abstract attribute, turning the sealed class into abstract sealed (i.e. static class).
4404+
// Remove the redundant constructor.
4405+
tdefs |> List.map (fun td -> td.WithAbstract(true)
4406+
.With(methods= mkILMethodsFromArray (td.Methods.AsArray |> Array.filter (fun m -> not m.IsConstructor))))
4407+
44004408
and GenGenericParams cenv eenv tps =
44014409
tps |> DropErasedTypars |> List.map (GenGenericParam cenv eenv)
44024410

@@ -4781,54 +4789,72 @@ and GenDelegateExpr cenv cgbuf eenvouter expr (TObjExprMethod((TSlotSig(_, deleg
47814789
false
47824790
with _ ->
47834791
false
4784-
4792+
47854793
// Work out the free type variables for the morphing thunk
47864794
let takenNames = List.map nameOfVal tmvs
47874795
let (cloAttribs, _, _, cloFreeTyvars, cloFreeVars, ilDelegeeTypeRef, ilCloFreeVars, eenvinner) =
47884796
GetIlxClosureFreeVars cenv m [] eenvouter takenNames expr
47894797
let ilDelegeeGenericParams = GenGenericParams cenv eenvinner cloFreeTyvars
47904798
let ilDelegeeGenericActualsInner = mkILFormalGenericArgs 0 ilDelegeeGenericParams
47914799

4800+
// When creating a delegate that does not capture any variables, we can instead create a static closure and directly reference the method.
4801+
let useStaticClosure = cloFreeVars.IsEmpty
4802+
47924803
// Create a new closure class with a single "delegee" method that implements the delegate.
47934804
let delegeeMethName = "Invoke"
47944805
let ilDelegeeTyInner = mkILBoxedTy ilDelegeeTypeRef ilDelegeeGenericActualsInner
4795-
4806+
47964807
let envForDelegeeUnderTypars = AddTyparsToEnv methTyparsOfOverridingMethod eenvinner
4797-
4798-
let numthis = 1
4808+
4809+
let numthis = if useStaticClosure then 0 else 1
47994810
let tmvs, body = BindUnitVars g (tmvs, List.replicate (List.concat slotsig.FormalParams).Length ValReprInfo.unnamedTopArg1, body)
4800-
4811+
48014812
// The slot sig contains a formal instantiation. When creating delegates we're only
48024813
// interested in the actual instantiation since we don't have to emit a method impl.
48034814
let ilDelegeeParams, ilDelegeeRet = GenActualSlotsig m cenv envForDelegeeUnderTypars slotsig methTyparsOfOverridingMethod tmvs
4804-
4815+
48054816
let envForDelegeeMeth = AddStorageForLocalVals g (List.mapi (fun i v -> (v, Arg (i+numthis))) tmvs) envForDelegeeUnderTypars
48064817
let ilMethodBody = CodeGenMethodForExpr cenv cgbuf.mgbuf (SPAlways, [], delegeeMethName, envForDelegeeMeth, 1, body, (if slotSigHasVoidReturnTy slotsig then discardAndReturnVoid else Return))
48074818
let delegeeInvokeMeth =
4808-
mkILNonGenericInstanceMethod
4809-
(delegeeMethName, ILMemberAccess.Assembly,
4819+
(if useStaticClosure then mkILNonGenericStaticMethod else mkILNonGenericInstanceMethod)
4820+
(delegeeMethName,
4821+
ILMemberAccess.Assembly,
48104822
ilDelegeeParams,
48114823
ilDelegeeRet,
48124824
MethodBody.IL ilMethodBody)
48134825
let delegeeCtorMeth = mkILSimpleStorageCtor(None, Some g.ilg.typ_Object.TypeSpec, ilDelegeeTyInner, [], [], ILMemberAccess.Assembly)
48144826
let ilCtorBody = delegeeCtorMeth.MethodBody
4815-
4827+
48164828
let ilCloLambdas = Lambdas_return ilCtxtDelTy
48174829
let ilAttribs = GenAttrs cenv eenvinner cloAttribs
4818-
let cloTypeDefs = GenClosureTypeDefs cenv (ilDelegeeTypeRef, ilDelegeeGenericParams, ilAttribs, ilCloFreeVars, ilCloLambdas, ilCtorBody, [delegeeInvokeMeth], [], g.ilg.typ_Object, [])
4830+
let cloTypeDefs =
4831+
(if useStaticClosure then GenStaticClosureTypeDefs else GenClosureTypeDefs)
4832+
cenv (ilDelegeeTypeRef, ilDelegeeGenericParams, ilAttribs, ilCloFreeVars, ilCloLambdas, ilCtorBody, [delegeeInvokeMeth], [], g.ilg.typ_Object, [])
48194833
for cloTypeDef in cloTypeDefs do
48204834
cgbuf.mgbuf.AddTypeDef(ilDelegeeTypeRef, cloTypeDef, false, false, None)
48214835
CountClosure()
48224836

4837+
// Push the constructor for the delegee
48234838
let ctxtGenericArgsForDelegee = GenGenericArgs m eenvouter.tyenv cloFreeTyvars
4824-
let ilxCloSpec = IlxClosureSpec.Create(IlxClosureRef(ilDelegeeTypeRef, ilCloLambdas, ilCloFreeVars), ctxtGenericArgsForDelegee)
4825-
GenGetLocalVals cenv cgbuf eenvouter m cloFreeVars
4826-
CG.EmitInstr cgbuf (pop ilCloFreeVars.Length) (Push [EraseClosures.mkTyOfLambdas g.ilxPubCloEnv ilCloLambdas]) (I_newobj (ilxCloSpec.Constructor, None))
4827-
4839+
if useStaticClosure then
4840+
GenUnit cenv eenvouter m cgbuf
4841+
else
4842+
let ilxCloSpec = IlxClosureSpec.Create(IlxClosureRef(ilDelegeeTypeRef, ilCloLambdas, ilCloFreeVars), ctxtGenericArgsForDelegee)
4843+
GenGetLocalVals cenv cgbuf eenvouter m cloFreeVars
4844+
CG.EmitInstr cgbuf (pop ilCloFreeVars.Length) (Push [EraseClosures.mkTyOfLambdas g.ilxPubCloEnv ilCloLambdas]) (I_newobj (ilxCloSpec.Constructor, None))
4845+
4846+
// Push the function pointer to the Invoke method of the delegee
48284847
let ilDelegeeTyOuter = mkILBoxedTy ilDelegeeTypeRef ctxtGenericArgsForDelegee
4829-
let ilDelegeeInvokeMethOuter = mkILNonGenericInstanceMethSpecInTy (ilDelegeeTyOuter, "Invoke", typesOfILParams ilDelegeeParams, ilDelegeeRet.Type)
4830-
let ilDelegeeCtorMethOuter = mkCtorMethSpecForDelegate g.ilg (ilCtxtDelTy, useUIntPtrForDelegateCtor)
4848+
let ilDelegeeInvokeMethOuter =
4849+
(if useStaticClosure then mkILNonGenericStaticMethSpecInTy else mkILNonGenericInstanceMethSpecInTy)
4850+
(ilDelegeeTyOuter,
4851+
"Invoke",
4852+
typesOfILParams ilDelegeeParams,
4853+
ilDelegeeRet.Type)
48314854
CG.EmitInstr cgbuf (pop 0) (Push [g.ilg.typ_IntPtr]) (I_ldftn ilDelegeeInvokeMethOuter)
4855+
4856+
// Instantiate the delegate
4857+
let ilDelegeeCtorMethOuter = mkCtorMethSpecForDelegate g.ilg (ilCtxtDelTy, useUIntPtrForDelegateCtor)
48324858
CG.EmitInstr cgbuf (pop 2) (Push [ilCtxtDelTy]) (I_newobj(ilDelegeeCtorMethOuter, None))
48334859
GenSequel cenv eenvouter.cloc cgbuf sequel
48344860

0 commit comments

Comments
 (0)