JIT: allow slightly more general promotion of structs with struct fields (#22867)
authorAndy Ayers <andya@microsoft.com>
Thu, 28 Feb 2019 20:15:19 +0000 (12:15 -0800)
committerGitHub <noreply@github.com>
Thu, 28 Feb 2019 20:15:19 +0000 (12:15 -0800)
For a while now the jit has been able to promote an outer struct A with an
inner struct field B that itself has a single non-struct field C, provided
that C occupies all of B and that C and B are pointer-sized.

For example, this comes up when supporting promotion of `Span<T>`, as a span
contains a `ByReference<T>` field that itself contains a pointer-sized field.

This change relaxes the constraints slightly, allowing B and C to be less than
pointer sized, provided C still occupies all of B, and B is suitably aligned
within A.

Doing so allows promotion of the new `Range` type, which contains two `Index`
fields that each wrap an `int`. This improves performance for uses of `Range`
for simple examples like those in #22079.

src/jit/lclvars.cpp

index 5e82ab7..7454bf7 100644 (file)
@@ -2020,13 +2020,26 @@ bool Compiler::StructPromotionHelper::TryPromoteStructField(lvaStructFieldInfo&
     //
     // TODO-CQ: Right now we only promote an actual SIMD typed field, which would cause
     // a nested SIMD type to fail promotion.
-    if (fieldSize == 0 || fieldSize != TARGET_POINTER_SIZE || varTypeIsFloating(fieldVarType))
+    if (fieldSize == 0 || fieldSize > TARGET_POINTER_SIZE || varTypeIsFloating(fieldVarType))
     {
         JITDUMP("Promotion blocked: struct contains struct field with one field,"
                 " but that field has invalid size or type");
         return false;
     }
 
+    if (fieldSize != TARGET_POINTER_SIZE)
+    {
+        unsigned outerFieldOffset = compHandle->getFieldOffset(fieldInfo.fldHnd);
+
+        if ((outerFieldOffset % fieldSize) != 0)
+        {
+            JITDUMP("Promotion blocked: struct contains struct field with one field,"
+                    " but the outer struct offset %u is not a multiple of the inner field size %u",
+                    outerFieldOffset, fieldSize);
+            return false;
+        }
+    }
+
     // Insist this wrapped field occupy all of its parent storage.
     unsigned innerStructSize = compHandle->getClassSize(fieldInfo.fldTypeHnd);