Skip to content

Commit 9afe6f6

Browse files
committed
Inline properties during importation
When we see a call to something in the shape of a property, we check if it does the same thing as an auto property and then pretend that we saw the LD(S)FLD or ST(S)FLD instead of the CALL in the caller - resolve tokens in the scope and with the generic context of the callee instead of the caller - call getFieldInfo with the scope of the callee to make visibility checks happy - make sure the callee is non virtual - respect basic no inlining instructions - getMethodInfo returns a success bool but might also throw
1 parent a55a21f commit 9afe6f6

File tree

3 files changed

+177
-15
lines changed

3 files changed

+177
-15
lines changed

src/coreclr/jit/compiler.h

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4542,6 +4542,8 @@ class Compiler
45424542
void impHandleAccessAllowed(CorInfoIsAccessAllowedResult result, CORINFO_HELPER_DESC* helperCall);
45434543
void impHandleAccessAllowedInternal(CorInfoIsAccessAllowedResult result, CORINFO_HELPER_DESC* helperCall);
45444544

4545+
bool impTryFindField(CORINFO_METHOD_HANDLE methHnd, CORINFO_RESOLVED_TOKEN* pResolvedToken, OPCODE* opcode);
4546+
45454547
var_types impImportCall(OPCODE opcode,
45464548
CORINFO_RESOLVED_TOKEN* pResolvedToken,
45474549
CORINFO_RESOLVED_TOKEN* pConstrainedResolvedToken, // Is this a "constrained." call on a
@@ -11092,8 +11094,8 @@ class Compiler
1109211094

1109311095
void compProcessScopesUntil(unsigned offset,
1109411096
VARSET_TP* inScope,
11095-
void (Compiler::*enterScopeFn)(VARSET_TP* inScope, VarScopeDsc*),
11096-
void (Compiler::*exitScopeFn)(VARSET_TP* inScope, VarScopeDsc*));
11097+
void (Compiler::*enterScopeFn)(VARSET_TP* inScope, VarScopeDsc*),
11098+
void (Compiler::*exitScopeFn)(VARSET_TP* inScope, VarScopeDsc*));
1109711099

1109811100
#ifdef DEBUG
1109911101
void compDispScopeLists();

src/coreclr/jit/importer.cpp

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6494,6 +6494,8 @@ void Compiler::impImportBlockCode(BasicBlock* block)
64946494
return;
64956495
}
64966496

6497+
CORINFO_METHOD_HANDLE callerProp = nullptr;
6498+
64976499
/* Get the size of additional parameters */
64986500

64996501
signed int sz = opcodeSizes[opcode];
@@ -8989,6 +8991,26 @@ void Compiler::impImportBlockCode(BasicBlock* block)
89898991
combine(combine(CORINFO_CALLINFO_ALLOWINSTPARAM, CORINFO_CALLINFO_SECURITYCHECKS),
89908992
(opcode == CEE_CALLVIRT) ? CORINFO_CALLINFO_CALLVIRT : CORINFO_CALLINFO_NONE),
89918993
&callInfo);
8994+
8995+
if (!opts.compDbgCode && callInfo.kind == CORINFO_CALL &&
8996+
!(callInfo.methodFlags & (CORINFO_FLG_DONT_INLINE | CORINFO_FLG_DONT_INLINE_CALLER)) &&
8997+
!(prefixFlags & PREFIX_CONSTRAINED) &&
8998+
((callInfo.sig.retType == CORINFO_TYPE_VOID && callInfo.sig.numArgs == 1) || // possible setter
8999+
(callInfo.sig.retType > CORINFO_TYPE_VOID && callInfo.sig.numArgs == 0))) // possible getter
9000+
{
9001+
if (impTryFindField(callInfo.hMethod, &resolvedToken, &opcode))
9002+
{
9003+
callerProp = callInfo.hMethod;
9004+
if (opcode == CEE_LDFLD || opcode == CEE_LDSFLD)
9005+
{
9006+
goto LOADFIELD;
9007+
}
9008+
else
9009+
{
9010+
goto STOREFIELD;
9011+
}
9012+
}
9013+
}
89929014
}
89939015
else
89949016
{
@@ -9155,19 +9177,19 @@ void Compiler::impImportBlockCode(BasicBlock* block)
91559177
case CEE_LDFLDA:
91569178
case CEE_LDSFLDA:
91579179
{
9158-
bool isLoadAddress = (opcode == CEE_LDFLDA || opcode == CEE_LDSFLDA);
9159-
bool isLoadStatic = (opcode == CEE_LDSFLD || opcode == CEE_LDSFLDA);
9160-
91619180
/* Get the CP_Fieldref index */
91629181
assertImp(sz == sizeof(unsigned));
91639182

91649183
_impResolveToken(CORINFO_TOKENKIND_Field);
91659184

9185+
LOADFIELD:
91669186
JITDUMP(" %08X", resolvedToken.token);
91679187

9168-
GenTreeFlags indirFlags = impPrefixFlagsToIndirFlags(prefixFlags);
9169-
int aflags = isLoadAddress ? CORINFO_ACCESS_ADDRESS : CORINFO_ACCESS_GET;
9170-
GenTree* obj = nullptr;
9188+
bool isLoadAddress = (opcode == CEE_LDFLDA || opcode == CEE_LDSFLDA);
9189+
bool isLoadStatic = (opcode == CEE_LDSFLD || opcode == CEE_LDSFLDA);
9190+
GenTreeFlags indirFlags = impPrefixFlagsToIndirFlags(prefixFlags);
9191+
int aflags = isLoadAddress ? CORINFO_ACCESS_ADDRESS : CORINFO_ACCESS_GET;
9192+
GenTree* obj = nullptr;
91719193

91729194
if ((opcode == CEE_LDFLD) || (opcode == CEE_LDFLDA))
91739195
{
@@ -9179,7 +9201,12 @@ void Compiler::impImportBlockCode(BasicBlock* block)
91799201
}
91809202
}
91819203

9182-
eeGetFieldInfo(&resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo);
9204+
auto callerHandle = callerProp;
9205+
if (!callerHandle)
9206+
{
9207+
callerHandle = info.compMethodHnd;
9208+
}
9209+
info.compCompHnd->getFieldInfo(&resolvedToken, callerHandle, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo);
91839210

91849211
// Note we avoid resolving the normalized (struct) type just yet; we may not need it (for ld[s]flda).
91859212
lclTyp = JITtype2varType(fieldInfo.fieldType);
@@ -9425,21 +9452,26 @@ void Compiler::impImportBlockCode(BasicBlock* block)
94259452
case CEE_STFLD:
94269453
case CEE_STSFLD:
94279454
{
9428-
bool isStoreStatic = (opcode == CEE_STSFLD);
9429-
94309455
/* Get the CP_Fieldref index */
94319456

94329457
assertImp(sz == sizeof(unsigned));
94339458

94349459
_impResolveToken(CORINFO_TOKENKIND_Field);
94359460

9461+
STOREFIELD:
94369462
JITDUMP(" %08X", resolvedToken.token);
94379463

9438-
GenTreeFlags indirFlags = impPrefixFlagsToIndirFlags(prefixFlags);
9439-
int aflags = CORINFO_ACCESS_SET;
9440-
GenTree* obj = nullptr;
9464+
bool isStoreStatic = (opcode == CEE_STSFLD);
9465+
GenTreeFlags indirFlags = impPrefixFlagsToIndirFlags(prefixFlags);
9466+
int aflags = CORINFO_ACCESS_SET;
9467+
GenTree* obj = nullptr;
94419468

9442-
eeGetFieldInfo(&resolvedToken, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo);
9469+
auto callerHandle = callerProp;
9470+
if (!callerHandle)
9471+
{
9472+
callerHandle = info.compMethodHnd;
9473+
}
9474+
info.compCompHnd->getFieldInfo(&resolvedToken, callerHandle, (CORINFO_ACCESS_FLAGS)aflags, &fieldInfo);
94439475

94449476
ClassLayout* layout;
94459477
lclTyp = TypeHandleToVarType(fieldInfo.fieldType, fieldInfo.structType, &layout);

src/coreclr/jit/importercalls.cpp

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8819,6 +8819,134 @@ bool Compiler::impTailCallRetTypeCompatible(bool allowWideni
88198819
return false;
88208820
}
88218821

8822+
//------------------------------------------------------------------------
8823+
// impTryFindField: try to find and resolve the backing field of an auto property
8824+
//
8825+
// Arguments:
8826+
// methHnd - inline candidate
8827+
// pResolvedToken - resolved field info will be stored here
8828+
// opcode - opcode that was used against the field will be stored here
8829+
//
8830+
// Notes:
8831+
// Will update inlineResult with observations and possible failure
8832+
// status (if method cannot be inlined)
8833+
//
8834+
bool Compiler::impTryFindField(CORINFO_METHOD_HANDLE methHnd, CORINFO_RESOLVED_TOKEN* pResolvedToken, OPCODE* opcode)
8835+
{
8836+
// Either EE or JIT might throw exceptions below.
8837+
// If that happens, just don't inline the method.
8838+
//
8839+
struct Param
8840+
{
8841+
Compiler* pThis;
8842+
CORINFO_METHOD_HANDLE fncHandle;
8843+
CORINFO_CONTEXT_HANDLE exactContextHnd;
8844+
CORINFO_RESOLVED_TOKEN resolvedToken;
8845+
OPCODE opcode;
8846+
bool success;
8847+
} param;
8848+
memset(&param, 0, sizeof(param));
8849+
8850+
param.pThis = this;
8851+
param.fncHandle = methHnd;
8852+
param.exactContextHnd = MAKE_METHODCONTEXT(methHnd);
8853+
param.opcode = CEE_ILLEGAL;
8854+
param.success = false;
8855+
8856+
info.compCompHnd->beginInlining(info.compMethodHnd, methHnd);
8857+
8858+
bool success = eeRunWithErrorTrap<Param>(
8859+
[](Param* pParam) {
8860+
// Cache some frequently accessed state.
8861+
//
8862+
Compiler* const compiler = pParam->pThis;
8863+
COMP_HANDLE compCompHnd = compiler->info.compCompHnd;
8864+
CORINFO_METHOD_HANDLE ftn = pParam->fncHandle;
8865+
8866+
#ifdef DEBUG
8867+
if (JitConfig.JitNoInline())
8868+
{
8869+
compCompHnd->reportInliningDecision(compiler->info.compMethodHnd, ftn, INLINE_FAIL, "JitNoInline is set");
8870+
return;
8871+
}
8872+
#endif
8873+
8874+
// Fetch method info. This may fail, if the method doesn't have IL.
8875+
//
8876+
CORINFO_METHOD_INFO methInfo;
8877+
if (!compCompHnd->getMethodInfo(pParam->fncHandle, &methInfo, pParam->exactContextHnd))
8878+
{
8879+
compCompHnd->reportInliningDecision(compiler->info.compMethodHnd, ftn, INLINE_FAIL, "getMethodInfo failed");
8880+
return;
8881+
}
8882+
8883+
pParam->resolvedToken.tokenContext = pParam->exactContextHnd;
8884+
pParam->resolvedToken.tokenScope = methInfo.scope;
8885+
pParam->resolvedToken.tokenType = CORINFO_TOKENKIND_Field;
8886+
8887+
auto code = methInfo.ILCode;
8888+
auto size = methInfo.ILCodeSize;
8889+
// instance getter
8890+
if (size == 7 && code[0] == CEE_LDARG_0 && code[1] == CEE_LDFLD && code[6] == CEE_RET)
8891+
{
8892+
pParam->resolvedToken.token = getU4LittleEndian(&code[2]);
8893+
pParam->opcode = CEE_LDFLD;
8894+
}
8895+
// instance setter
8896+
else if (size == 8 && code[0] == CEE_LDARG_0 && code[1] == CEE_LDARG_1 && code[2] == CEE_STFLD &&
8897+
code[7] == CEE_RET)
8898+
{
8899+
pParam->resolvedToken.token = getU4LittleEndian(&code[3]);
8900+
pParam->opcode = CEE_STFLD;
8901+
}
8902+
else
8903+
{
8904+
compCompHnd->reportInliningDecision(compiler->info.compMethodHnd, ftn, INLINE_FAIL, "Not a property");
8905+
return;
8906+
}
8907+
8908+
// Speculatively check if initClass() can be done.
8909+
// If it can be done, we will try to inline the method.
8910+
CorInfoInitClassResult const initClassResult =
8911+
compCompHnd->initClass(nullptr /* field */, ftn /* method */, pParam->exactContextHnd /* context */);
8912+
8913+
if (initClassResult & CORINFO_INITCLASS_DONT_INLINE)
8914+
{
8915+
compCompHnd->reportInliningDecision(compiler->info.compMethodHnd, ftn, INLINE_FAIL,
8916+
"InitClass reported don't inline");
8917+
return;
8918+
}
8919+
8920+
// Given the VM the final say in whether to inline or not.
8921+
// This should be last since for verifiable code, this can be expensive
8922+
// This will call reportInliningDecision for us if inlining is not allowed
8923+
CorInfoInline const vmResult = compCompHnd->canInline(compiler->info.compMethodHnd, ftn);
8924+
if (vmResult == INLINE_FAIL || vmResult == INLINE_NEVER)
8925+
{
8926+
return;
8927+
}
8928+
8929+
compCompHnd->resolveToken(&pParam->resolvedToken);
8930+
pParam->success = true;
8931+
},
8932+
&param);
8933+
8934+
if (success && param.success)
8935+
{
8936+
*opcode = param.opcode;
8937+
*pResolvedToken = param.resolvedToken;
8938+
info.compCompHnd->reportInliningDecision(info.compMethodHnd, methHnd, INLINE_PASS,
8939+
"Trivial property backing field has been inlined");
8940+
return true;
8941+
}
8942+
if (!success)
8943+
{
8944+
info.compCompHnd->reportInliningDecision(info.compMethodHnd, methHnd, INLINE_FAIL,
8945+
"Failed to inline trivial property");
8946+
}
8947+
return false;
8948+
}
8949+
88228950
//------------------------------------------------------------------------
88238951
// impCheckCanInline: do more detailed checks to determine if a method can
88248952
// be inlined, and collect information that will be needed later

0 commit comments

Comments
 (0)