@@ -9762,6 +9762,99 @@ var_types Compiler::impGetByRefResultType(genTreeOps oper, bool fUnsigned, GenTr
9762
9762
return type;
9763
9763
}
9764
9764
9765
+ //------------------------------------------------------------------------
9766
+ // impOptimizeCastClassOrIsInst: attempt to resolve a cast when jitting
9767
+ //
9768
+ // Arguments:
9769
+ // op1 -- value to cast
9770
+ // pResolvedToken -- resolved token for type to cast to
9771
+ // isCastClass -- true if this is a castclass, false if isinst
9772
+ //
9773
+ // Return Value:
9774
+ // tree representing optimized cast, or null if no optimization possible
9775
+
9776
+ GenTree* Compiler::impOptimizeCastClassOrIsInst(GenTree* op1, CORINFO_RESOLVED_TOKEN* pResolvedToken, bool isCastClass)
9777
+ {
9778
+ assert(op1->TypeGet() == TYP_REF);
9779
+
9780
+ // Don't optimize for minopts or debug codegen.
9781
+ if (opts.compDbgCode || opts.MinOpts())
9782
+ {
9783
+ return nullptr;
9784
+ }
9785
+
9786
+ // See what we know about the type of the object being cast.
9787
+ bool isExact = false;
9788
+ bool isNonNull = false;
9789
+ CORINFO_CLASS_HANDLE fromClass = gtGetClassHandle(op1, &isExact, &isNonNull);
9790
+ GenTree* optResult = nullptr;
9791
+
9792
+ if (fromClass != nullptr)
9793
+ {
9794
+ CORINFO_CLASS_HANDLE toClass = pResolvedToken->hClass;
9795
+ JITDUMP("\nConsidering optimization of %s from %s%p (%s) to %p (%s)\n", isCastClass ? "castclass" : "isinst",
9796
+ isExact ? "exact " : "", fromClass, info.compCompHnd->getClassName(fromClass), toClass,
9797
+ info.compCompHnd->getClassName(toClass));
9798
+
9799
+ // Perhaps we know if the cast will succeed or fail.
9800
+ TypeCompareState castResult = info.compCompHnd->compareTypesForCast(fromClass, toClass);
9801
+
9802
+ if (castResult == TypeCompareState::Must)
9803
+ {
9804
+ // Cast will succeed, result is simply op1.
9805
+ JITDUMP("Cast will succeed, optimizing to simply return input\n");
9806
+ return op1;
9807
+ }
9808
+ else if (castResult == TypeCompareState::MustNot)
9809
+ {
9810
+ // See if we can sharpen exactness by looking for final classes
9811
+ if (!isExact)
9812
+ {
9813
+ DWORD flags = info.compCompHnd->getClassAttribs(fromClass);
9814
+ DWORD flagsMask = CORINFO_FLG_FINAL | CORINFO_FLG_MARSHAL_BYREF | CORINFO_FLG_CONTEXTFUL |
9815
+ CORINFO_FLG_VARIANCE | CORINFO_FLG_ARRAY;
9816
+ isExact = ((flags & flagsMask) == CORINFO_FLG_FINAL);
9817
+ }
9818
+
9819
+ // Cast to exact type will fail. Handle case where we have
9820
+ // an exact type (that is, fromClass is not a subtype)
9821
+ // and we're not going to throw on failure.
9822
+ if (isExact && !isCastClass)
9823
+ {
9824
+ JITDUMP("Cast will fail, optimizing to return null\n");
9825
+ GenTree* result = gtNewIconNode(0, TYP_REF);
9826
+
9827
+ // If the cast was fed by a box, we can remove that too.
9828
+ if (op1->IsBoxedValue())
9829
+ {
9830
+ JITDUMP("Also removing upstream box\n");
9831
+ gtTryRemoveBoxUpstreamEffects(op1);
9832
+ }
9833
+
9834
+ return result;
9835
+ }
9836
+ else if (isExact)
9837
+ {
9838
+ JITDUMP("Not optimizing failing castclass (yet)\n");
9839
+ }
9840
+ else
9841
+ {
9842
+ JITDUMP("Can't optimize since fromClass is inexact\n");
9843
+ }
9844
+ }
9845
+ else
9846
+ {
9847
+ JITDUMP("Result of cast unknown, must generate runtime test\n");
9848
+ }
9849
+ }
9850
+ else
9851
+ {
9852
+ JITDUMP("\nCan't optimize since fromClass is unknown\n");
9853
+ }
9854
+
9855
+ return nullptr;
9856
+ }
9857
+
9765
9858
//------------------------------------------------------------------------
9766
9859
// impCastClassOrIsInstToTree: build and import castclass/isinst
9767
9860
//
@@ -10203,6 +10296,7 @@ void Compiler::impImportBlockCode(BasicBlock* block)
10203
10296
var_types lclTyp, ovflType = TYP_UNKNOWN;
10204
10297
GenTreePtr op1 = DUMMY_INIT(NULL);
10205
10298
GenTreePtr op2 = DUMMY_INIT(NULL);
10299
+ GenTree* optTree = nullptr;
10206
10300
GenTreeArgList* args = nullptr; // What good do these "DUMMY_INIT"s do?
10207
10301
GenTreePtr newObjThisPtr = DUMMY_INIT(NULL);
10208
10302
bool uns = DUMMY_INIT(false);
@@ -14262,43 +14356,54 @@ void Compiler::impImportBlockCode(BasicBlock* block)
14262
14356
14263
14357
op1 = impPopStack().val;
14264
14358
14265
- #ifdef FEATURE_READYTORUN_COMPILER
14266
- if (opts.IsReadyToRun())
14359
+ optTree = impOptimizeCastClassOrIsInst(op1, &resolvedToken, false);
14360
+
14361
+ if (optTree != nullptr)
14362
+ {
14363
+ impPushOnStack(optTree, tiRetVal);
14364
+ }
14365
+ else
14267
14366
{
14268
- GenTreeCall* opLookup =
14269
- impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_ISINSTANCEOF, TYP_REF,
14270
- gtNewArgList(op1));
14271
- usingReadyToRunHelper = (opLookup != nullptr);
14272
- op1 = (usingReadyToRunHelper ? opLookup : op1);
14273
14367
14274
- if (!usingReadyToRunHelper)
14368
+ #ifdef FEATURE_READYTORUN_COMPILER
14369
+ if (opts.IsReadyToRun())
14275
14370
{
14276
- // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
14277
- // and the isinstanceof_any call with a single call to a dynamic R2R cell that will:
14278
- // 1) Load the context
14279
- // 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub
14280
- // 3) Perform the 'is instance' check on the input object
14281
- // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
14371
+ GenTreeCall* opLookup =
14372
+ impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_ISINSTANCEOF, TYP_REF,
14373
+ gtNewArgList(op1));
14374
+ usingReadyToRunHelper = (opLookup != nullptr);
14375
+ op1 = (usingReadyToRunHelper ? opLookup : op1);
14282
14376
14283
- op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
14284
- if (op2 == nullptr)
14285
- { // compDonotInline()
14286
- return;
14377
+ if (!usingReadyToRunHelper)
14378
+ {
14379
+ // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
14380
+ // and the isinstanceof_any call with a single call to a dynamic R2R cell that will:
14381
+ // 1) Load the context
14382
+ // 2) Perform the generic dictionary lookup and caching, and generate the appropriate
14383
+ // stub
14384
+ // 3) Perform the 'is instance' check on the input object
14385
+ // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
14386
+
14387
+ op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
14388
+ if (op2 == nullptr)
14389
+ { // compDonotInline()
14390
+ return;
14391
+ }
14287
14392
}
14288
14393
}
14289
- }
14290
14394
14291
- if (!usingReadyToRunHelper)
14395
+ if (!usingReadyToRunHelper)
14292
14396
#endif
14293
- {
14294
- op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, false);
14295
- }
14296
- if (compDonotInline())
14297
- {
14298
- return;
14299
- }
14397
+ {
14398
+ op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, false);
14399
+ }
14400
+ if (compDonotInline())
14401
+ {
14402
+ return;
14403
+ }
14300
14404
14301
- impPushOnStack(op1, tiRetVal);
14405
+ impPushOnStack(op1, tiRetVal);
14406
+ }
14302
14407
14303
14408
break;
14304
14409
@@ -14796,43 +14901,55 @@ void Compiler::impImportBlockCode(BasicBlock* block)
14796
14901
// and op2 to contain code that creates the type handle corresponding to typeRef
14797
14902
CASTCLASS:
14798
14903
14799
- #ifdef FEATURE_READYTORUN_COMPILER
14800
- if (opts.IsReadyToRun())
14904
+ optTree = impOptimizeCastClassOrIsInst(op1, &resolvedToken, true);
14905
+
14906
+ if (optTree != nullptr)
14907
+ {
14908
+ impPushOnStack(optTree, tiRetVal);
14909
+ }
14910
+ else
14801
14911
{
14802
- GenTreeCall* opLookup = impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_CHKCAST,
14803
- TYP_REF, gtNewArgList(op1));
14804
- usingReadyToRunHelper = (opLookup != nullptr);
14805
- op1 = (usingReadyToRunHelper ? opLookup : op1);
14806
14912
14807
- if (!usingReadyToRunHelper)
14913
+ #ifdef FEATURE_READYTORUN_COMPILER
14914
+ if (opts.IsReadyToRun())
14808
14915
{
14809
- // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
14810
- // and the chkcastany call with a single call to a dynamic R2R cell that will:
14811
- // 1) Load the context
14812
- // 2) Perform the generic dictionary lookup and caching, and generate the appropriate stub
14813
- // 3) Check the object on the stack for the type-cast
14814
- // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
14916
+ GenTreeCall* opLookup =
14917
+ impReadyToRunHelperToTree(&resolvedToken, CORINFO_HELP_READYTORUN_CHKCAST, TYP_REF,
14918
+ gtNewArgList(op1));
14919
+ usingReadyToRunHelper = (opLookup != nullptr);
14920
+ op1 = (usingReadyToRunHelper ? opLookup : op1);
14815
14921
14816
- op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
14817
- if (op2 == nullptr)
14818
- { // compDonotInline()
14819
- return;
14922
+ if (!usingReadyToRunHelper)
14923
+ {
14924
+ // TODO: ReadyToRun: When generic dictionary lookups are necessary, replace the lookup call
14925
+ // and the chkcastany call with a single call to a dynamic R2R cell that will:
14926
+ // 1) Load the context
14927
+ // 2) Perform the generic dictionary lookup and caching, and generate the appropriate
14928
+ // stub
14929
+ // 3) Check the object on the stack for the type-cast
14930
+ // Reason: performance (today, we'll always use the slow helper for the R2R generics case)
14931
+
14932
+ op2 = impTokenToHandle(&resolvedToken, nullptr, FALSE);
14933
+ if (op2 == nullptr)
14934
+ { // compDonotInline()
14935
+ return;
14936
+ }
14820
14937
}
14821
14938
}
14822
- }
14823
14939
14824
- if (!usingReadyToRunHelper)
14940
+ if (!usingReadyToRunHelper)
14825
14941
#endif
14826
- {
14827
- op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, true);
14828
- }
14829
- if (compDonotInline())
14830
- {
14831
- return;
14832
- }
14942
+ {
14943
+ op1 = impCastClassOrIsInstToTree(op1, op2, &resolvedToken, true);
14944
+ }
14945
+ if (compDonotInline())
14946
+ {
14947
+ return;
14948
+ }
14833
14949
14834
- /* Push the result back on the stack */
14835
- impPushOnStack(op1, tiRetVal);
14950
+ /* Push the result back on the stack */
14951
+ impPushOnStack(op1, tiRetVal);
14952
+ }
14836
14953
break;
14837
14954
14838
14955
case CEE_THROW:
0 commit comments