Enable promotion of structs containing fields of structs with a single pointer-sized...
authorsivarv <sivarv@microsoft.com>
Thu, 23 Feb 2017 18:59:28 +0000 (10:59 -0800)
committersivarv <sivarv@microsoft.com>
Thu, 23 Feb 2017 21:29:52 +0000 (13:29 -0800)
Commit migrated from https://github.com/dotnet/coreclr/commit/cc35e361b1c1ddeb9e36dbb293f1456a211f5216

src/coreclr/src/jit/lclvars.cpp
src/coreclr/src/jit/lower.cpp
src/coreclr/src/jit/rationalize.cpp

index fa1ba82..56fbe07 100644 (file)
@@ -1543,8 +1543,60 @@ void Compiler::lvaCanPromoteStructType(CORINFO_CLASS_HANDLE    typeHnd,
 
             if (pFieldInfo->fldSize == 0)
             {
+                // Size of TYP_BLK, TYP_FUNC, TYP_VOID and TYP_STRUCT is zero.
+                // Early out if field type is other than TYP_STRUCT.
+                // This is a defensive check as we don't expect a struct to have
+                // fields of TYP_BLK, TYP_FUNC or TYP_VOID.
+                if (pFieldInfo->fldType != TYP_STRUCT)
+                {
+                    return;
+                }
+
                 // Non-primitive struct field.
-                return;
+                // Try to promote structs of single field of scalar types aligned at their
+                // natural boundary.
+
+                // Do Not promote if the struct field in turn has more than one field.
+                if (info.compCompHnd->getClassNumInstanceFields(pFieldInfo->fldTypeHnd) != 1)
+                {
+                    return;
+                }
+
+                // Do not promote if the single field is not aligned at its natural boundary within
+                // the struct field.
+                CORINFO_FIELD_HANDLE fHnd    = info.compCompHnd->getFieldInClass(pFieldInfo->fldTypeHnd, 0);
+                unsigned             fOffset = info.compCompHnd->getFieldOffset(fHnd);
+                if (fOffset != 0)
+                {
+                    return;
+                }
+
+                CORINFO_CLASS_HANDLE cHnd;
+                CorInfoType          fieldCorType = info.compCompHnd->getFieldType(fHnd, &cHnd);
+                var_types            fieldVarType = JITtype2varType(fieldCorType);
+                unsigned             fieldSize    = genTypeSize(fieldVarType);
+
+                // Do not promote if either not a primitive type or size equal to ptr size on
+                // target or a struct containing a single floating-point field.
+                //
+                // TODO-PERF: Structs containing a single floating-point field on Amd64
+                // needs to be passed in integer registers. Right now LSRA doesn't support
+                // passing of floating-point LCL_VARS in integer registers.  Enabling promotion
+                // of such structs results in an assert in lsra right now.
+                //
+                // TODO-PERF: Right now promotion is confined to struct containing a ptr sized
+                // field (int/uint/ref/byref on 32-bits and long/ulong/ref/byref on 64-bits).
+                // Though this would serve the purpose of promoting Span<T> containing ByReference<T>,
+                // this can be extended to other primitive types as long as they are aligned at their
+                // natural boundary.
+                if (fieldSize == 0 || fieldSize != TARGET_POINTER_SIZE || varTypeIsFloating(fieldVarType))
+                {
+                    return;
+                }
+
+                // Retype the field as the type of the single field of the struct
+                pFieldInfo->fldType = fieldVarType;
+                pFieldInfo->fldSize = fieldSize;
             }
 
             if ((pFieldInfo->fldOffset % pFieldInfo->fldSize) != 0)
index b3a674e..7d672d4 100644 (file)
@@ -971,6 +971,43 @@ GenTreePtr Lowering::NewPutArg(GenTreeCall* call, GenTreePtr arg, fgArgTabEntryP
                 assert(!varTypeIsSIMD(arg));
                 numRefs = comp->info.compCompHnd->getClassGClayout(arg->gtObj.gtClass, gcLayout);
                 putArg->AsPutArgStk()->setGcPointers(numRefs, gcLayout);
+
+#ifdef _TARGET_X86_
+                // On x86 VM lies about the type of a struct containing a pointer sized
+                // integer field by returning the type of its field as the type of struct.
+                // Such struct can be passed in a register depending its position in
+                // parameter list.  VM does this unwrapping only one level and therefore
+                // a type like Struct Foo { Struct Bar { int f}} awlays needs to be
+                // passed on stack.  Also, VM doesn't lie about type of such a struct
+                // when it is a field of another struct.  That is VM doesn't lie about
+                // the type of Foo.Bar
+                //
+                // We now support the promotion of fields that are of type struct.
+                // However we only support a limited case where the struct field has a
+                // single field and that single field must be a scalar type. Say Foo.Bar
+                // field is getting passed as a parameter to a call, Since it is a TYP_STRUCT,
+                // as per x86 ABI it should always be passed on stack.  Therefore GenTree
+                // node under a PUTARG_STK could be GT_OBJ(GT_LCL_VAR_ADDR(v1)), where
+                // local v1 could be a promoted field standing for Foo.Bar.  Note that
+                // the type of v1 will be the type of field of Foo.Bar.f when Foo is
+                // promoted.  That is v1 will be a scalar type.  In this case we need to
+                // pass v1 on stack instead of in a register.
+                //
+                // TODO-PERF: replace GT_OBJ(GT_LCL_VAR_ADDR(v1)) with v1 if v1 is
+                // a scalar type and the width of GT_OBJ matches the type size of v1.
+                // Note that this cannot be done till call node arguments are morphed
+                // because we should not lose the fact that the type of argument is
+                // a struct so that the arg gets correctly marked to be passed on stack.
+                GenTree* objOp1 = arg->gtGetOp1();
+                if (objOp1->OperGet() == GT_LCL_VAR_ADDR)
+                {
+                    unsigned lclNum = objOp1->AsLclVarCommon()->GetLclNum();
+                    if (comp->lvaTable[lclNum].lvType != TYP_STRUCT)
+                    {
+                        comp->lvaSetVarDoNotEnregister(lclNum DEBUGARG(Compiler::DNER_VMNeedsStackAddr));
+                    }
+                }
+#endif // _TARGET_X86_
             }
         }
 #endif // FEATURE_PUT_STRUCT_ARG_STK
index 00e0bec..1bc3a61 100644 (file)
@@ -732,6 +732,35 @@ Compiler::fgWalkResult Rationalizer::RewriteNode(GenTree** useEdge, ArrayStack<G
             {
                 RewriteSIMDOperand(use, false);
             }
+            else
+            {
+                // Due to promotion of structs containing fields of type struct with a
+                // single scalar type field, we could potentially see IR nodes of the
+                // form GT_IND(GT_ADD(lclvarAddr, 0)) where 0 is an offset representing
+                // a field-seq. These get folded here.
+                //
+                // TODO: This code can be removed once JIT implements recursive struct
+                // promotion instead of lying about the type of struct field as the type
+                // of its single scalar field.
+                GenTree* addr = node->AsIndir()->Addr();
+                if (addr->OperGet() == GT_ADD && addr->gtGetOp1()->OperGet() == GT_LCL_VAR_ADDR &&
+                    addr->gtGetOp2()->IsIntegralConst(0))
+                {
+                    GenTreeLclVarCommon* lclVarNode = addr->gtGetOp1()->AsLclVarCommon();
+                    unsigned             lclNum     = lclVarNode->GetLclNum();
+                    LclVarDsc*           varDsc     = comp->lvaTable + lclNum;
+                    if (node->TypeGet() == varDsc->TypeGet())
+                    {
+                        JITDUMP("Rewriting GT_IND(GT_ADD(LCL_VAR_ADDR,0)) to LCL_VAR\n");
+                        lclVarNode->SetOper(GT_LCL_VAR);
+                        lclVarNode->gtType = node->TypeGet();
+                        use.ReplaceWith(comp, lclVarNode);
+                        BlockRange().Remove(addr);
+                        BlockRange().Remove(addr->gtGetOp2());
+                        BlockRange().Remove(node);
+                    }
+                }
+            }
             break;
 
         case GT_NOP: