From d153f2ba35eacb7c8f360aa7c669dd14477c50bc Mon Sep 17 00:00:00 2001 From: Andy Ayers Date: Wed, 12 Dec 2018 16:56:07 -0800 Subject: [PATCH] JIT: optimize unbox/unbox.any when type is known (dotnet/coreclr#21501) Optimize away the unbox type test when the jit knows the type of object being unboxed and can resolve the type comparison at jit time. Closes dotnet/coreclr#14473. Commit migrated from https://github.com/dotnet/coreclr/commit/d279e47a9bc6a9d44484c3143a5f8dd9bc57579c --- src/coreclr/src/jit/importer.cpp | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/src/coreclr/src/jit/importer.cpp b/src/coreclr/src/jit/importer.cpp index 1b101d0..cff3e8d 100644 --- a/src/coreclr/src/jit/importer.cpp +++ b/src/coreclr/src/jit/importer.cpp @@ -15395,6 +15395,59 @@ void Compiler::impImportBlockCode(BasicBlock* block) if (canExpandInline && shouldExpandInline) { + // See if we know anything about the type of op1, the object being unboxed. + bool isExact = false; + bool isNonNull = false; + CORINFO_CLASS_HANDLE clsHnd = gtGetClassHandle(op1, &isExact, &isNonNull); + + // We can skip the "exact" bit here as we are comparing to a value class. + // compareTypesForEquality should bail on comparisions for shared value classes. + if (clsHnd != NO_CLASS_HANDLE) + { + const TypeCompareState compare = + info.compCompHnd->compareTypesForEquality(resolvedToken.hClass, clsHnd); + + if (compare == TypeCompareState::Must) + { + JITDUMP("\nOptimizing %s (%s) -- type test will succeed\n", + opcode == CEE_UNBOX ? "UNBOX" : "UNBOX.ANY", eeGetClassName(clsHnd)); + + // For UNBOX, null check (if necessary), and then leave the box payload byref on the stack. + if (opcode == CEE_UNBOX) + { + GenTree* cloneOperand; + op1 = impCloneExpr(op1, &cloneOperand, NO_CLASS_HANDLE, (unsigned)CHECK_SPILL_ALL, + nullptr DEBUGARG("optimized unbox clone")); + + GenTree* boxPayloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL); + GenTree* boxPayloadAddress = + gtNewOperNode(GT_ADD, TYP_BYREF, cloneOperand, boxPayloadOffset); + GenTree* nullcheck = gtNewOperNode(GT_NULLCHECK, TYP_I_IMPL, op1); + GenTree* result = gtNewOperNode(GT_COMMA, TYP_BYREF, nullcheck, boxPayloadAddress); + impPushOnStack(result, tiRetVal); + break; + } + + // For UNBOX.ANY load the struct from the box payload byref (the load will nullcheck) + assert(opcode == CEE_UNBOX_ANY); + GenTree* boxPayloadOffset = gtNewIconNode(TARGET_POINTER_SIZE, TYP_I_IMPL); + GenTree* boxPayloadAddress = gtNewOperNode(GT_ADD, TYP_BYREF, op1, boxPayloadOffset); + impPushOnStack(boxPayloadAddress, tiRetVal); + oper = GT_OBJ; + goto OBJ; + } + else + { + JITDUMP("\nUnable to optimize %s -- can't resolve type comparison\n", + opcode == CEE_UNBOX ? "UNBOX" : "UNBOX.ANY"); + } + } + else + { + JITDUMP("\nUnable to optimize %s -- class for [%06u] not known\n", + opcode == CEE_UNBOX ? "UNBOX" : "UNBOX.ANY", dspTreeID(op1)); + } + JITDUMP("\n Importing %s as inline sequence\n", opcode == CEE_UNBOX ? "UNBOX" : "UNBOX.ANY"); // we are doing normal unboxing // inline the common case of the unbox helper -- 2.7.4