Add support for multi-dimensional array initialization
authorMike Danes <onemihaid@hotmail.com>
Tue, 22 Mar 2016 21:14:38 +0000 (23:14 +0200)
committerMike Danes <onemihaid@hotmail.com>
Wed, 8 Jun 2016 16:32:13 +0000 (19:32 +0300)
This extends impInitializeArrayIntrinsic to support MD arrays in addition to SD arrays. This includes support for SD arrays which are created via CORINFO_HELP_NEW_MDARR when the lower bound is not specified or known to be 0.

The generated code is similar to the code generated for the SD arrays, for example:
  call CORINFO_HELP_NEW_MDARR
  mov rdx, 0x202AB822050
  lea rcx, bword ptr [rax+32]
  vmovdqu ymm0, qword ptr [rdx]
  vmovdqu qword ptr [rcx], ymm0
  mov r8d, dword ptr [rdx+16]
  mov dword ptr [rcx+16], r8d

Commit migrated from https://github.com/dotnet/coreclr/commit/9c635bde59d979cd3359e5e12d743839481b38be

src/coreclr/src/jit/compiler.h
src/coreclr/src/jit/ee_il_dll.cpp
src/coreclr/src/jit/importer.cpp [changed mode: 0755->0644]
src/coreclr/src/jit/instr.cpp
src/coreclr/src/jit/lower.cpp
src/coreclr/tests/src/JIT/Methodical/Arrays/misc/_il_dbginitializearray.ilproj [new file with mode: 0644]
src/coreclr/tests/src/JIT/Methodical/Arrays/misc/_il_relinitializearray.ilproj [new file with mode: 0644]
src/coreclr/tests/src/JIT/Methodical/Arrays/misc/initializearray.il [new file with mode: 0644]

index 2a0d4ee..81a2bd0 100644 (file)
@@ -6397,7 +6397,10 @@ public :
 
     CORINFO_EE_INFO *           eeGetEEInfo();
 
+    // Gets the offset of a SDArray's first element
     unsigned                    eeGetArrayDataOffset(var_types type);
+    // Gets the offset of a MDArray's first element
+    unsigned                    eeGetMDArrayDataOffset(var_types type, unsigned rank);
 
     GenTreePtr                  eeGetPInvokeCookie(CORINFO_SIG_INFO *szMetaSig);
 
index 787c311..f51e452 100755 (executable)
@@ -494,7 +494,14 @@ GenTreePtr          Compiler::eeGetPInvokeCookie(CORINFO_SIG_INFO *szMetaSig)
     return gtNewIconEmbHndNode(cookie, pCookie, GTF_ICON_PINVKI_HDL);
 }
 
-/*****************************************************************************/
+//------------------------------------------------------------------------
+// eeGetArrayDataOffset: Gets the offset of a SDArray's first element
+//
+// Arguments:
+//    type - The array element type
+//
+// Return Value:
+//    The offset to the first array element. 
 
 unsigned           Compiler::eeGetArrayDataOffset(var_types type)
 {
@@ -502,6 +509,27 @@ unsigned           Compiler::eeGetArrayDataOffset(var_types type)
                                                  : offsetof(CORINFO_Array, u1Elems);
 }
 
+//------------------------------------------------------------------------
+// eeGetMDArrayDataOffset: Gets the offset of a MDArray's first element
+//
+// Arguments:
+//    type - The array element type
+//    rank - The array rank
+//
+// Return Value:
+//    The offset to the first array element.
+//
+// Assumptions:
+//    The rank should be greater than 0.
+
+unsigned           Compiler::eeGetMDArrayDataOffset(var_types type, unsigned rank)
+{
+    assert(rank > 0);
+    // Note that below we're specifically using genTypeSize(TYP_INT) because array 
+    // indices are not native int.
+    return eeGetArrayDataOffset(type) + 2 * genTypeSize(TYP_INT) * rank;
+}
+
 /*****************************************************************************/
 
 void                Compiler::eeGetStmtOffsets()
old mode 100755 (executable)
new mode 100644 (file)
index 5a3bcf8..3e67afd
@@ -2666,23 +2666,30 @@ BOOL            Compiler::impLocAllocOnStack()
     return(FALSE);
 }
 
-/*****************************************************************************/
+//------------------------------------------------------------------------
+// impInitializeArrayIntrinsic: Attempts to replace a call to InitializeArray
+//    with a GT_COPYBLK node.
+//
+// Arguments:
+//    sig - The InitializeArray signature.
+//
+// Return Value:
+//    A pointer to the newly create GT_COPYBLK node if the replacement succeeds or
+//    nullptr otherwise.
+//
+// Notes:
+//    The function recognizes the following IL pattern:
+//      ldc <length> or a list of ldc <lower bound>/<length>
+//      newarr or newobj
+//      dup
+//      ldtoken <field handle>
+//      call InitializeArray
+//    The lower bounds need not be constant except when the array rank is 1.
+//    The function recognizes all kinds of arrays thus enabling a small runtime 
+//    such as CoreRT to skip providing an implementation for InitializeArray.
 
 GenTreePtr      Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO * sig)
 {
-    //
-    // The IL for array initialization looks like the following:
-    //
-    // ldc <number of elements>
-    // newarr
-    // dup
-    // ldtoken <field handle>
-    // call InitializeArray
-    //
-    // We will try to implement it using a GT_COPYBLK node to avoid the
-    // runtime call to the helper.
-    //
-
     assert(sig->numArgs == 2);
 
     GenTreePtr fieldTokenNode = impStackTop(0).val;
@@ -2767,6 +2774,9 @@ GenTreePtr      Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO * sig)
     //
     // Verify that it is one of the new array helpers.
     //
+
+    bool isMDArray = false;
+
     if (newArrayCall->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_DIRECT) &&
         newArrayCall->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_OBJ)    &&
         newArrayCall->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEWARR_1_VC)     &&
@@ -2776,57 +2786,177 @@ GenTreePtr      Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO * sig)
 #endif
         )
     {
-        //
-        // In order to simplify the code, we won't support the multi-dim
-        // case.  Instead we simply return NULL, and the caller will insert 
-        // a run-time call to the helper.  Note that we can't assert
-        // failure here, because this is a valid case.
-        //
+        if (newArrayCall->gtCall.gtCallMethHnd != eeFindHelper(CORINFO_HELP_NEW_MDARR))
+            return nullptr;
 
-        return NULL;
+        isMDArray = true;
     }
 
+    CORINFO_CLASS_HANDLE arrayClsHnd = (CORINFO_CLASS_HANDLE) newArrayCall->gtCall.compileTimeHelperArgumentHandle;
 
     //
-    // Make sure there are exactly two arguments:  the array class and
-    // the number of elements.
+    // Make sure we found a compile time handle to the array
     //
 
-    GenTreePtr arrayLengthNode;
+    if (!arrayClsHnd)
+        return nullptr;
 
-    GenTreeArgList* args = newArrayCall->gtCall.gtCallArgs;
-#ifdef FEATURE_READYTORUN_COMPILER
-    if (newArrayCall->gtCall.gtCallMethHnd == eeFindHelper(CORINFO_HELP_READYTORUN_NEWARR_1))
+    unsigned rank;
+    S_UINT32 numElements;
+
+    if (isMDArray)
     {
-        // Array length is 1st argument for readytorun helper
-        arrayLengthNode = args->Current();
+        rank = info.compCompHnd->getArrayRank(arrayClsHnd);
+
+        if (rank == 0)
+            return nullptr;
+
+        GenTreeArgList* args = newArrayCall->gtCall.gtCallArgs;
+        GenTreeArgList* numArgsArg = nullptr;
+        GenTreeArgList* beginArgs = nullptr;
+        GenTreeArgList* endArgs = nullptr;
+
+        if (Target::g_tgtArgOrder == Target::ARG_ORDER_R2L)
+        {
+            numArgsArg = args->Rest();
+
+            if (numArgsArg != nullptr)
+                beginArgs = numArgsArg->Rest();
+        }
+        else
+        {
+            beginArgs = args;
+
+            for (GenTreeArgList* arg = args; arg != nullptr; arg = arg->Rest())
+            {
+                bool isSecondToLast = (arg->Rest() != nullptr) && (arg->Rest()->Rest() == nullptr);
+
+                if (isSecondToLast)
+                {
+                    numArgsArg = arg;
+                    endArgs = arg;
+                    break;
+                }
+            }
+        }
+
+        //
+        // The number of arguments should be a constant between 1 and 64. The rank can't be 0
+        // so at least one length must be present and the rank can't exceed 32 so there can
+        // be at most 64 arguments - 32 lengths and 32 lower bounds.
+        //
+
+        if ((beginArgs == nullptr) ||
+            (numArgsArg == nullptr) ||
+            (numArgsArg->Current()->OperGet() != GT_CNS_INT) ||
+            (numArgsArg->Current()->AsIntCon()->IconValue() < 1) ||
+            (numArgsArg->Current()->AsIntCon()->IconValue() > 64))
+        {
+            return nullptr;
+        }
+
+        unsigned numArgs = static_cast<unsigned>(numArgsArg->Current()->AsIntCon()->IconValue());
+        bool lowerBoundsSpecified;
+
+        if (numArgs == rank * 2)
+        {
+            lowerBoundsSpecified = true;
+        }
+        else if (numArgs == rank)
+        {
+            lowerBoundsSpecified = false;
+
+            //
+            // If the rank is 1 and a lower bound isn't specified then the runtime creates
+            // a SDArray. Note that even if a lower bound is specified it can be 0 and then
+            // we get a SDArray as well, see the for loop below.
+            //
+
+            if (rank == 1)
+                isMDArray = false;
+        }
+        else
+        {
+            return nullptr;
+        }
+
+        //
+        // The rank is known to be at least 1 so we can start with numElements being 1
+        // to avoid the need to special case the first dimension.
+        //
+
+        numElements = S_UINT32(1);
+
+        for (GenTreeArgList* arg = beginArgs; arg != endArgs; arg = arg->Rest())
+        {
+            if (lowerBoundsSpecified)
+            {
+                //
+                // In general lower bounds can be ignored because they're not needed to
+                // calculate the total number of elements. But for single dimensional arrays
+                // we need to know if the lower bound is 0 because in this case the runtime
+                // creates a SDArray and this affects the way the array data offset is calculated.
+                //
+
+                if (rank == 1)
+                {
+                    GenTree* lowerBoundNode = arg->Current();
+
+                    if (lowerBoundNode->OperGet() != GT_CNS_INT)
+                        return nullptr;
+
+                    if (lowerBoundNode->AsIntCon()->IconValue() == 0)
+                        isMDArray = false;
+                }
+
+                arg = arg->Rest();
+            }
+
+            GenTree* lengthNode = arg->Current();
+
+            if (lengthNode->OperGet() != GT_CNS_INT)
+                return nullptr;
+
+            numElements *= S_SIZE_T(lengthNode->AsIntCon()->IconValue());
+        }
     }
     else
-#endif
     {
-        // Array length is 2nd argument for regular helper
-        arrayLengthNode = args->Rest()->Current();
-    }
+        //
+        // Make sure there are exactly two arguments:  the array class and
+        // the number of elements.
+        //
 
-    //
-    // Make sure that the number of elements look valid.
-    //
-    if (arrayLengthNode->gtOper != GT_CNS_INT)
-    {
-        return NULL;
-    }
+        GenTreePtr arrayLengthNode;
 
-    S_UINT32 numElements             = S_SIZE_T(arrayLengthNode->gtIntCon.gtIconVal);
-    CORINFO_CLASS_HANDLE arrayClsHnd = (CORINFO_CLASS_HANDLE) newArrayCall->gtCall.compileTimeHelperArgumentHandle;
+        GenTreeArgList* args = newArrayCall->gtCall.gtCallArgs;
+#ifdef FEATURE_READYTORUN_COMPILER
+        if (newArrayCall->gtCall.gtCallMethHnd == eeFindHelper(CORINFO_HELP_READYTORUN_NEWARR_1))
+        {
+            // Array length is 1st argument for readytorun helper
+            arrayLengthNode = args->Current();
+        }
+        else
+#endif
+        {
+            // Array length is 2nd argument for regular helper
+            arrayLengthNode = args->Rest()->Current();
+        }
 
-    //
-    // Make sure we found a compile time handle to the array
-    //
+        //
+        // Make sure that the number of elements look valid.
+        //
+        if (arrayLengthNode->gtOper != GT_CNS_INT)
+        {
+            return NULL;
+        }
 
-    if (!arrayClsHnd                              ||
-        !info.compCompHnd->isSDArray(arrayClsHnd))
-    {
-        return NULL;
+        numElements = S_SIZE_T(arrayLengthNode->gtIntCon.gtIconVal);
+    
+        if (!info.compCompHnd->isSDArray(arrayClsHnd))
+        {
+            return NULL;
+        }
     }
 
     CORINFO_CLASS_HANDLE elemClsHnd;
@@ -2868,10 +2998,24 @@ GenTreePtr      Compiler::impInitializeArrayIntrinsic(CORINFO_SIG_INFO * sig)
     impPopStack();
     impPopStack();
 
-    GenTreePtr dst = gtNewOperNode(GT_ADDR, 
-                                   TYP_BYREF,
-                                   gtNewIndexRef(elementType, arrayLocalNode, gtNewIconNode(0)));
+    GenTreePtr dst;
 
+    if (isMDArray)
+    {
+        unsigned dataOffset = eeGetMDArrayDataOffset(elementType, rank);
+
+        dst = gtNewOperNode(GT_ADD, 
+                            TYP_BYREF, 
+                            arrayLocalNode, 
+                            gtNewIconNode(dataOffset, TYP_I_IMPL));
+    }
+    else
+    {
+        dst = gtNewOperNode(GT_ADDR,
+                            TYP_BYREF,
+                            gtNewIndexRef(elementType, arrayLocalNode, gtNewIconNode(0)));
+    }
     return gtNewBlkOpNode(GT_COPYBLK,
                           dst,                                                          // dst
                           gtNewIconHandleNode((size_t) initData, GTF_ICON_STATIC_HDL),  // src
index 3dd10f2..4fb23f6 100644 (file)
@@ -580,8 +580,7 @@ void        CodeGen::instGetAddrMode(GenTreePtr    addr,
         else
             *indScale = 0;
 
-        *cns = compiler->eeGetArrayDataOffset(addr->gtArrElem.gtArrElemType)
-            + 2 * sizeof(int) * addr->gtArrElem.gtArrRank;
+        *cns = compiler->eeGetMDArrayDataOffset(addr->gtArrElem.gtArrElemType, addr->gtArrElem.gtArrRank);
     }
     else if (addr->gtOper == GT_LEA)
     {
index d76ce8c..da34404 100755 (executable)
@@ -3689,8 +3689,7 @@ Lowering::LowerArrElem(GenTree **ppTree, Compiler::fgWalkData* data)
     // Generate the LEA and make it reverse evaluation, because we want to evaluate the index expression before the base.
     GenTreePtr leaBase = comp->gtClone(arrObjNode);
     unsigned scale = arrElem->gtArrElem.gtArrElemSize;
-    // Note that below we're specifically using genTypeSize(TYP_INT) because array indices are not native int.
-    unsigned offset = comp->eeGetArrayDataOffset(arrElem->gtArrElem.gtArrElemType) + 2 * genTypeSize(TYP_INT) * arrElem->gtArrElem.gtArrRank;
+    unsigned offset = comp->eeGetMDArrayDataOffset(arrElem->gtArrElem.gtArrElemType, arrElem->gtArrElem.gtArrRank);
     GenTreePtr leaIndexNode = prevArrOffs;
     if (!jitIsScaleIndexMul(scale))
     {
diff --git a/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/_il_dbginitializearray.ilproj b/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/_il_dbginitializearray.ilproj
new file mode 100644 (file)
index 0000000..466865d
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+    <DebugType>Full</DebugType>
+    <Optimize>false</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="initializearray.il" />
+    <None Include="app.config" />
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
\ No newline at end of file
diff --git a/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/_il_relinitializearray.ilproj b/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/_il_relinitializearray.ilproj
new file mode 100644 (file)
index 0000000..689b3e0
--- /dev/null
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <AssemblyName>$(MSBuildProjectName)</AssemblyName>
+    <ProjectGuid>{95DFC527-4DC1-495E-97D7-E94EE1F7140D}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+    <ReferencePath>$(ProgramFiles)\Common Files\microsoft shared\VSTT\11.0\UITestExtensionPackages</ReferencePath>
+    <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\..\</SolutionDir>
+    <NuGetPackageImportStamp>7a9bfb7d</NuGetPackageImportStamp>
+    <Optimize>true</Optimize>
+  </PropertyGroup>
+  <ItemGroup>
+    <Compile Include="initializearray.il" />
+    <None Include="app.config" />
+    <Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
+  </ItemGroup>
+  <Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
+</Project>
\ No newline at end of file
diff --git a/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/initializearray.il b/src/coreclr/tests/src/JIT/Methodical/Arrays/misc/initializearray.il
new file mode 100644 (file)
index 0000000..256c2be
--- /dev/null
@@ -0,0 +1,640 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+.assembly extern mscorlib
+{
+  .publickeytoken = (B7 7A 5C 56 19 34 E0 89)
+  .ver 4:0:0:0
+}
+.assembly initializearray
+{
+  .hash algorithm 0x00008004
+  .ver 0:0:0:0
+}
+.module initializearray.exe
+.imagebase 0x00400000
+.file alignment 0x00000200
+.stackreserve 0x00100000
+.subsystem 0x0003       // WINDOWS_CUI
+.corflags 0x00000001    // ILONLY
+
+.class private auto ansi beforefieldinit InitializeArray extends [mscorlib]System.Object
+{
+  .method private hidebysig static int32 Main() cil managed
+  {
+    .entrypoint
+    .maxstack 8
+
+    call bool InitializeArray::SZArray_Int64_5_ValidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::SZArray_Int64_6_InvalidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Int16_10_ValidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Int16_0_10_ValidInitializationTest()
+    brfalse.s fail
+
+    call bool InitializeArray::MDArray_Int16_11_InvalidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Int8_8x10_ValidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Int8_10x10_InvalidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Float32_4x5_ValidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Float32_5x5_InvalidInitializationTest()
+    brfalse.s fail
+    
+    call bool InitializeArray::MDArray_Int8_2_4x3_5_ValidInitializationTest()
+    brfalse.s fail
+
+    call bool InitializeArray::MDArray_Int8_2_5x3_5_InvalidInitializationTest()
+    brfalse.s fail
+
+    ldc.i4.s 100
+    ret
+    
+  fail:
+    ldc.i4.s 0
+    ret
+  }
+
+  .method private hidebysig static bool SZArray_Int64_5_ValidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    call int64[] InitializeArray::SZArray_Initialize_Int64_5()
+    call int64[] InitializeArray::SZArray_Initialize_Int64_5_Optimized()
+    call bool InitializeArray::ArrayEquals(class [mscorlib]System.Array, class [mscorlib]System.Array)
+    ret
+  }
+  
+  .method private hidebysig static int64[] SZArray_Initialize_Int64_5() cil managed noinlining nooptimization
+  {
+    .maxstack 8
+    ldc.i4 5
+    newarr int64
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize40' 'ArrayInitializers'::'InitializerField40'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static int64[] SZArray_Initialize_Int64_5_Optimized() cil managed noinlining
+  {
+    .maxstack 8
+    ldc.i4 5
+    newarr int64
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize40' 'ArrayInitializers'::'InitializerField40'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static bool SZArray_Int64_6_InvalidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    .locals init (int32 exitCode)
+    .try
+    {
+      ldc.i4 11
+      newarr int64
+      dup
+      ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+      call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+      pop
+      ldc.i4 0
+      stloc.0
+      leave exit
+    }
+    catch [mscorlib]System.ArgumentException
+    {
+      pop
+      ldc.i4 1
+      stloc.0
+      leave exit
+    }
+    
+  exit:
+    ldloc.0
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Int16_10_ValidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    call int16[0...] InitializeArray::MDArray_Initialize_Int16_10()
+    call int16[0...] InitializeArray::MDArray_Initialize_Int16_10_Optimized()
+    call bool InitializeArray::ArrayEquals(class [mscorlib]System.Array, class [mscorlib]System.Array)
+    ret
+  }
+  
+  .method private hidebysig static int16[0...] MDArray_Initialize_Int16_10() cil managed noinlining nooptimization
+  {
+    .maxstack 8
+    ldc.i4 10
+    newobj instance void int16[0...]::.ctor(int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static int16[0...] MDArray_Initialize_Int16_10_Optimized() cil managed noinlining
+  {
+    .maxstack 8
+    ldc.i4 10
+    newobj instance void int16[0...]::.ctor(int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Int16_11_InvalidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    .locals init (int32 exitCode)
+    .try
+    {
+      ldc.i4 11
+      newobj instance void int16[0...]::.ctor(int32)
+      dup
+      ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+      call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+      pop
+      ldc.i4 0
+      stloc.0
+      leave exit
+    }
+    catch [mscorlib]System.ArgumentException
+    {
+      pop
+      ldc.i4 1
+      stloc.0
+      leave exit
+    }
+    
+  exit:
+    ldloc.0
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Int16_0_10_ValidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    call int16[...] InitializeArray::MDArray_Initialize_Int16_0_10()
+    call int16[...] InitializeArray::MDArray_Initialize_Int16_0_10_Optimized()
+    call bool InitializeArray::ArrayEquals(class [mscorlib]System.Array, class [mscorlib]System.Array)
+    ret
+  }
+  
+  .method private hidebysig static int16[...] MDArray_Initialize_Int16_0_10() cil managed noinlining nooptimization
+  {
+    .maxstack 8
+    ldc.i4 0
+    ldc.i4 10
+    newobj instance void int16[...]::.ctor(int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static int16[...] MDArray_Initialize_Int16_0_10_Optimized() cil managed noinlining
+  {
+    .maxstack 8
+    ldc.i4 0
+    ldc.i4 10
+    newobj instance void int16[...]::.ctor(int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+
+  .method private hidebysig static bool MDArray_Int8_8x10_ValidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    call int8[0..., 0...] InitializeArray::MDArray_Initialize_Int8_8x10()
+    call int8[0..., 0...] InitializeArray::MDArray_Initialize_Int8_8x10_Optimized()
+    call bool InitializeArray::ArrayEquals(class [mscorlib]System.Array, class [mscorlib]System.Array)
+    ret
+  }
+  
+  .method private hidebysig static int8[0..., 0...] MDArray_Initialize_Int8_8x10() cil managed noinlining nooptimization
+  {
+    .maxstack 8
+    ldc.i4 8
+    ldc.i4 10
+    newobj instance void int8 [0..., 0...]::.ctor(int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'ArrayInitializers'::'InitializerField80'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static int8[0..., 0...] MDArray_Initialize_Int8_8x10_Optimized() cil managed noinlining
+  {
+    .maxstack 8
+    ldc.i4 8
+    ldc.i4 10
+    newobj instance void int8[0..., 0...]::.ctor(int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'ArrayInitializers'::'InitializerField80'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Int8_10x10_InvalidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    .locals init (int32 exitCode)
+    .try
+    {
+      ldc.i4 10
+      ldc.i4 10
+      newobj instance void int8[0..., 0...]::.ctor(int32, int32)
+      dup
+      ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'ArrayInitializers'::'InitializerField80'
+      call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+      pop
+      ldc.i4 0
+      stloc.0
+      leave exit
+    }
+    catch [mscorlib]System.ArgumentException
+    {
+      pop
+      ldc.i4 1
+      stloc.0
+      leave exit
+    }
+    
+  exit:
+    ldloc.0
+    ret
+  }
+  
+  .method private hidebysig static bool MDArray_Float32_4x5_ValidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    call float32[0..., 0...] InitializeArray::MDArray_Initialize_Float32_4x5()
+    call float32[0..., 0...] InitializeArray::MDArray_Initialize_Float32_4x5_Optimized()
+    call bool InitializeArray::ArrayEquals(class [mscorlib]System.Array, class [mscorlib]System.Array)
+    ret
+  }
+  
+  .method private hidebysig static float32[0..., 0...] MDArray_Initialize_Float32_4x5_Optimized() cil managed noinlining
+  {
+    .maxstack 8
+    ldc.i4.4
+    ldc.i4.5
+    newobj instance void float32[0..., 0...]::.ctor(int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'ArrayInitializers'::'InitializerField80'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static float32[0..., 0...] MDArray_Initialize_Float32_4x5() cil managed noinlining nooptimization
+  {
+    .maxstack 8
+    ldc.i4 4
+    ldc.i4 5
+    newobj instance void float32[0..., 0...]::.ctor(int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'ArrayInitializers'::'InitializerField80'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Float32_5x5_InvalidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    .locals init (int32 exitCode)
+    .try
+    {
+      ldc.i4 5
+      ldc.i4 5
+      newobj instance void float32[0..., 0...]::.ctor(int32, int32)
+      dup
+      ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'ArrayInitializers'::'InitializerField80'
+      call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+      pop
+      ldc.i4 0
+      stloc.0
+      leave exit
+    }
+    catch [mscorlib]System.ArgumentException
+    {
+      pop
+      ldc.i4 1
+      stloc.0
+      leave exit
+    }
+    
+  exit:
+    ldloc.0
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Int8_2_4x3_5_ValidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    call int8[..., ...] InitializeArray::MDArray_Initialize_Int8_2_4x3_5()
+    call int8[..., ...] InitializeArray::MDArray_Initialize_Int8_2_4x3_5_Optimized()
+    call bool InitializeArray::ArrayEquals(class [mscorlib]System.Array, class [mscorlib]System.Array)
+    ret
+  }
+  
+  .method private hidebysig static int8[..., ...] MDArray_Initialize_Int8_2_4x3_5() cil managed noinlining nooptimization
+  {
+    .maxstack 8
+    ldc.i4.2
+    ldc.i4.4
+    ldc.i4.3
+    ldc.i4.5
+    newobj instance void int8 [..., ...]::.ctor(int32, int32, int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static int8[..., ...] MDArray_Initialize_Int8_2_4x3_5_Optimized() cil managed noinlining
+  {
+    .maxstack 8
+    ldc.i4.2
+    ldc.i4.4
+    ldc.i4.3
+    ldc.i4.5
+    newobj instance void int8[..., ...]::.ctor(int32, int32, int32, int32)
+    dup
+    ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+    call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+    ret
+  }
+
+  .method private hidebysig static bool MDArray_Int8_2_5x3_5_InvalidInitializationTest() cil managed noinlining
+  {
+    .maxstack 8
+    .locals init (int32 exitCode)
+    .try
+    {
+      ldc.i4.2
+      ldc.i4.5
+      ldc.i4.3
+      ldc.i4.5
+      newobj instance void int8[..., ...]::.ctor(int32, int32, int32, int32)
+      dup
+      ldtoken field valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'ArrayInitializers'::'InitializerField20'
+      call void[mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::InitializeArray(class [mscorlib]System.Array, valuetype[mscorlib] System.RuntimeFieldHandle)
+      pop
+      ldc.i4 0
+      stloc.0
+      leave exit
+    }
+    catch [mscorlib]System.ArgumentException
+    {
+      pop
+      ldc.i4 1
+      stloc.0
+      leave exit
+    }
+    
+  exit:
+    ldloc.0
+    ret
+  }
+
+//
+// ArrayEquals was generated from the following C# code:
+//
+//static bool ArrayEquals(Array x, Array y)
+//{
+//    if (x.Rank != y.Rank)
+//        return false;
+
+//    var lower = new int[x.Rank];
+//    var indices = new int[x.Rank];
+//    var upper = new int[x.Rank];
+
+//    for (int i = 0; i < indices.Length; i++)
+//    {
+//        if (x.GetLowerBound(i) != y.GetLowerBound(i))
+//            return false;
+//        if (x.GetLength(i) != y.GetLength(i))
+//            return false;
+//        lower[i] = x.GetLowerBound(i);
+//        indices[i] = lower[i];
+//        upper[i] = lower[i] + x.GetLength(i);
+//    }
+
+//    while (true)
+//    {
+//        if (!object.Equals(x.GetValue(indices), y.GetValue(indices)))
+//            return false;
+
+//        int i = 0;
+//        indices[i]++;
+
+//        while (indices[i] >= upper[i])
+//        {
+//            indices[i] = lower[i];
+//            i++;
+
+//            if (i >= indices.Length)
+//                return true;
+
+//            indices[i]++;
+//        }
+//    }
+//}
+  
+  .method private hidebysig static bool ArrayEquals(class [mscorlib]System.Array x, class [mscorlib]System.Array y) cil managed
+  {
+      .maxstack 5
+      .locals init (
+          [0] int32[] lower,
+          [1] int32[] indices,
+          [2] int32[] upper,
+          [3] int32 i,
+          [4] int32 V_4)
+      L_0000: ldarg.0 
+      L_0001: callvirt instance int32 [mscorlib]System.Array::get_Rank()
+      L_0006: ldarg.1 
+      L_0007: callvirt instance int32 [mscorlib]System.Array::get_Rank()
+      L_000c: beq.s L_0010
+      L_000e: ldc.i4.0 
+      L_000f: ret 
+      L_0010: ldarg.0 
+      L_0011: callvirt instance int32 [mscorlib]System.Array::get_Rank()
+      L_0016: newarr int32
+      L_001b: stloc.0 
+      L_001c: ldarg.0 
+      L_001d: callvirt instance int32 [mscorlib]System.Array::get_Rank()
+      L_0022: newarr int32
+      L_0027: stloc.1 
+      L_0028: ldarg.0 
+      L_0029: callvirt instance int32 [mscorlib]System.Array::get_Rank()
+      L_002e: newarr int32
+      L_0033: stloc.2 
+      L_0034: ldc.i4.0 
+      L_0035: stloc.3 
+      L_0036: br.s L_007e
+      L_0038: ldarg.0 
+      L_0039: ldloc.3 
+      L_003a: callvirt instance int32 [mscorlib]System.Array::GetLowerBound(int32)
+      L_003f: ldarg.1 
+      L_0040: ldloc.3 
+      L_0041: callvirt instance int32 [mscorlib]System.Array::GetLowerBound(int32)
+      L_0046: beq.s L_004a
+      L_0048: ldc.i4.0 
+      L_0049: ret 
+      L_004a: ldarg.0 
+      L_004b: ldloc.3 
+      L_004c: callvirt instance int32 [mscorlib]System.Array::GetLength(int32)
+      L_0051: ldarg.1 
+      L_0052: ldloc.3 
+      L_0053: callvirt instance int32 [mscorlib]System.Array::GetLength(int32)
+      L_0058: beq.s L_005c
+      L_005a: ldc.i4.0 
+      L_005b: ret 
+      L_005c: ldloc.0 
+      L_005d: ldloc.3 
+      L_005e: ldarg.0 
+      L_005f: ldloc.3 
+      L_0060: callvirt instance int32 [mscorlib]System.Array::GetLowerBound(int32)
+      L_0065: stelem.i4 
+      L_0066: ldloc.1 
+      L_0067: ldloc.3 
+      L_0068: ldloc.0 
+      L_0069: ldloc.3 
+      L_006a: ldelem.i4 
+      L_006b: stelem.i4 
+      L_006c: ldloc.2 
+      L_006d: ldloc.3 
+      L_006e: ldloc.0 
+      L_006f: ldloc.3 
+      L_0070: ldelem.i4 
+      L_0071: ldarg.0 
+      L_0072: ldloc.3 
+      L_0073: callvirt instance int32 [mscorlib]System.Array::GetLength(int32)
+      L_0078: add 
+      L_0079: stelem.i4 
+      L_007a: ldloc.3 
+      L_007b: ldc.i4.1 
+      L_007c: add 
+      L_007d: stloc.3 
+      L_007e: ldloc.3 
+      L_007f: ldloc.1 
+      L_0080: ldlen 
+      L_0081: conv.i4 
+      L_0082: blt.s L_0038
+      L_0084: ldarg.0 
+      L_0085: ldloc.1 
+      L_0086: callvirt instance object [mscorlib]System.Array::GetValue(int32[])
+      L_008b: ldarg.1 
+      L_008c: ldloc.1 
+      L_008d: callvirt instance object [mscorlib]System.Array::GetValue(int32[])
+      L_0092: call bool [mscorlib]System.Object::Equals(object, object)
+      L_0097: brtrue.s L_009b
+      L_0099: ldc.i4.0 
+      L_009a: ret 
+      L_009b: ldc.i4.0 
+      L_009c: stloc.s V_4
+      L_009e: ldloc.1 
+      L_009f: ldloc.s V_4
+      L_00a1: ldelema int32
+      L_00a6: dup 
+      L_00a7: ldind.i4 
+      L_00a8: ldc.i4.1 
+      L_00a9: add 
+      L_00aa: stind.i4 
+      L_00ab: br.s L_00d1
+      L_00ad: ldloc.1 
+      L_00ae: ldloc.s V_4
+      L_00b0: ldloc.0 
+      L_00b1: ldloc.s V_4
+      L_00b3: ldelem.i4 
+      L_00b4: stelem.i4 
+      L_00b5: ldloc.s V_4
+      L_00b7: ldc.i4.1 
+      L_00b8: add 
+      L_00b9: stloc.s V_4
+      L_00bb: ldloc.s V_4
+      L_00bd: ldloc.1 
+      L_00be: ldlen 
+      L_00bf: conv.i4 
+      L_00c0: blt.s L_00c4
+      L_00c2: ldc.i4.1 
+      L_00c3: ret 
+      L_00c4: ldloc.1 
+      L_00c5: ldloc.s V_4
+      L_00c7: ldelema int32
+      L_00cc: dup 
+      L_00cd: ldind.i4 
+      L_00ce: ldc.i4.1 
+      L_00cf: add 
+      L_00d0: stind.i4 
+      L_00d1: ldloc.1 
+      L_00d2: ldloc.s V_4
+      L_00d4: ldelem.i4 
+      L_00d5: ldloc.2 
+      L_00d6: ldloc.s V_4
+      L_00d8: ldelem.i4 
+      L_00d9: bge.s L_00ad
+      L_00db: br.s L_0084
+  }
+
+  .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
+  {
+    .maxstack 8
+    ldarg.0
+    call instance void [mscorlib]System.Object::.ctor()
+    ret
+  }
+}
+
+.class private auto ansi 'ArrayInitializers' extends [mscorlib]System.Object
+{
+  .class explicit ansi sealed nested private 'StaticArrayInitTypeSize20' extends [mscorlib]System.ValueType
+  {
+    .pack 1
+    .size 20
+  }
+
+  .class explicit ansi sealed nested private 'StaticArrayInitTypeSize40' extends [mscorlib]System.ValueType
+  {
+    .pack 1
+    .size 40
+  }
+  
+  .class explicit ansi sealed nested private 'StaticArrayInitTypeSize80' extends [mscorlib]System.ValueType
+  {
+    .pack 1
+    .size 80
+  }
+  
+  .field static assembly valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize20' 'InitializerField20' at InitializerField20Data
+  .field static assembly valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize40' 'InitializerField40' at InitializerField40Data
+  .field static assembly valuetype 'ArrayInitializers'/'StaticArrayInitTypeSize80' 'InitializerField80' at InitializerField80Data
+}
+
+.data cil InitializerField20Data = bytearray (01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14)
+
+.data cil InitializerField40Data = bytearray (01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14
+                                              15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28)
+
+.data cil InitializerField80Data = bytearray (01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14
+                                              15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28
+                                              29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C
+                                              3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50)