Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/corert.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichal Strehovský <MichalStrehovsky@users.noreply.github.com>2017-12-24 17:27:47 +0300
committerGitHub <noreply@github.com>2017-12-24 17:27:47 +0300
commit4846700d032ca59a17e9ecbbd884f21cf85130be (patch)
tree6c7eac2e7144ef126cb3ddd6e7788e032b0c421e
parentf0706feddf13d7c9b491b7ea2cb24da85378c3f5 (diff)
parentc04d41a4509535d995d92dec0cb8eb1026445cf7 (diff)
Merge pull request #5149 from MichalStrehovsky/maxStack
Add algorithm to compute the MaxStack value
-rw-r--r--src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs328
-rw-r--r--src/Common/src/TypeSystem/IL/ILStackHelper.cs474
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs44
-rw-r--r--src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs4
-rw-r--r--src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs4
-rw-r--r--src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj6
-rw-r--r--src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj2
7 files changed, 854 insertions, 8 deletions
diff --git a/src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs b/src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs
new file mode 100644
index 000000000..b051e205f
--- /dev/null
+++ b/src/Common/src/TypeSystem/IL/ILOpcodeHelper.cs
@@ -0,0 +1,328 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Debug = System.Diagnostics.Debug;
+
+namespace Internal.IL
+{
+ public static class ILOpcodeHelper
+ {
+ private const byte VariableSize = 0xFF;
+ private const byte Invalid = 0xFE;
+
+ /// <summary>
+ /// Gets the size, in bytes, of the opcode and its arguments.
+ /// Note: if '<paramref name="opcode"/>' is <see cref="ILOpcode.switch_"/>,
+ /// the caller must handle it differently as that opcode is variable sized.
+ /// </summary>
+ public static int GetSize(this ILOpcode opcode)
+ {
+ Debug.Assert((uint)opcode < (uint)s_opcodeSizes.Length);
+ byte result = s_opcodeSizes[(int)opcode];
+ Debug.Assert(result != VariableSize);
+ Debug.Assert(result != Invalid);
+ return result;
+ }
+
+ /// <summary>
+ /// Returns true if '<paramref name="opcode"/>' is a valid opcode.
+ /// </summary>
+ public static bool IsValid(this ILOpcode opcode)
+ {
+ return (uint)opcode < (uint)s_opcodeSizes.Length
+ && s_opcodeSizes[(int)opcode] != Invalid;
+ }
+
+ private static readonly byte[] s_opcodeSizes = new byte[]
+ {
+ 1, // nop = 0x00,
+ 1, // break_ = 0x01,
+ 1, // ldarg_0 = 0x02,
+ 1, // ldarg_1 = 0x03,
+ 1, // ldarg_2 = 0x04,
+ 1, // ldarg_3 = 0x05,
+ 1, // ldloc_0 = 0x06,
+ 1, // ldloc_1 = 0x07,
+ 1, // ldloc_2 = 0x08,
+ 1, // ldloc_3 = 0x09,
+ 1, // stloc_0 = 0x0a,
+ 1, // stloc_1 = 0x0b,
+ 1, // stloc_2 = 0x0c,
+ 1, // stloc_3 = 0x0d,
+ 2, // ldarg_s = 0x0e,
+ 2, // ldarga_s = 0x0f,
+ 2, // starg_s = 0x10,
+ 2, // ldloc_s = 0x11,
+ 2, // ldloca_s = 0x12,
+ 2, // stloc_s = 0x13,
+ 1, // ldnull = 0x14,
+ 1, // ldc_i4_m1 = 0x15,
+ 1, // ldc_i4_0 = 0x16,
+ 1, // ldc_i4_1 = 0x17,
+ 1, // ldc_i4_2 = 0x18,
+ 1, // ldc_i4_3 = 0x19,
+ 1, // ldc_i4_4 = 0x1a,
+ 1, // ldc_i4_5 = 0x1b,
+ 1, // ldc_i4_6 = 0x1c,
+ 1, // ldc_i4_7 = 0x1d,
+ 1, // ldc_i4_8 = 0x1e,
+ 2, // ldc_i4_s = 0x1f,
+ 5, // ldc_i4 = 0x20,
+ 9, // ldc_i8 = 0x21,
+ 5, // ldc_r4 = 0x22,
+ 9, // ldc_r8 = 0x23,
+ Invalid, // = 0x24,
+ 1, // dup = 0x25,
+ 1, // pop = 0x26,
+ 5, // jmp = 0x27,
+ 5, // call = 0x28,
+ 5, // calli = 0x29,
+ 1, // ret = 0x2a,
+ 2, // br_s = 0x2b,
+ 2, // brfalse_s = 0x2c,
+ 2, // brtrue_s = 0x2d,
+ 2, // beq_s = 0x2e,
+ 2, // bge_s = 0x2f,
+ 2, // bgt_s = 0x30,
+ 2, // ble_s = 0x31,
+ 2, // blt_s = 0x32,
+ 2, // bne_un_s = 0x33,
+ 2, // bge_un_s = 0x34,
+ 2, // bgt_un_s = 0x35,
+ 2, // ble_un_s = 0x36,
+ 2, // blt_un_s = 0x37,
+ 5, // br = 0x38,
+ 5, // brfalse = 0x39,
+ 5, // brtrue = 0x3a,
+ 5, // beq = 0x3b,
+ 5, // bge = 0x3c,
+ 5, // bgt = 0x3d,
+ 5, // ble = 0x3e,
+ 5, // blt = 0x3f,
+ 5, // bne_un = 0x40,
+ 5, // bge_un = 0x41,
+ 5, // bgt_un = 0x42,
+ 5, // ble_un = 0x43,
+ 5, // blt_un = 0x44,
+ VariableSize, // switch_ = 0x45,
+ 1, // ldind_i1 = 0x46,
+ 1, // ldind_u1 = 0x47,
+ 1, // ldind_i2 = 0x48,
+ 1, // ldind_u2 = 0x49,
+ 1, // ldind_i4 = 0x4a,
+ 1, // ldind_u4 = 0x4b,
+ 1, // ldind_i8 = 0x4c,
+ 1, // ldind_i = 0x4d,
+ 1, // ldind_r4 = 0x4e,
+ 1, // ldind_r8 = 0x4f,
+ 1, // ldind_ref = 0x50,
+ 1, // stind_ref = 0x51,
+ 1, // stind_i1 = 0x52,
+ 1, // stind_i2 = 0x53,
+ 1, // stind_i4 = 0x54,
+ 1, // stind_i8 = 0x55,
+ 1, // stind_r4 = 0x56,
+ 1, // stind_r8 = 0x57,
+ 1, // add = 0x58,
+ 1, // sub = 0x59,
+ 1, // mul = 0x5a,
+ 1, // div = 0x5b,
+ 1, // div_un = 0x5c,
+ 1, // rem = 0x5d,
+ 1, // rem_un = 0x5e,
+ 1, // and = 0x5f,
+ 1, // or = 0x60,
+ 1, // xor = 0x61,
+ 1, // shl = 0x62,
+ 1, // shr = 0x63,
+ 1, // shr_un = 0x64,
+ 1, // neg = 0x65,
+ 1, // not = 0x66,
+ 1, // conv_i1 = 0x67,
+ 1, // conv_i2 = 0x68,
+ 1, // conv_i4 = 0x69,
+ 1, // conv_i8 = 0x6a,
+ 1, // conv_r4 = 0x6b,
+ 1, // conv_r8 = 0x6c,
+ 1, // conv_u4 = 0x6d,
+ 1, // conv_u8 = 0x6e,
+ 5, // callvirt = 0x6f,
+ 5, // cpobj = 0x70,
+ 5, // ldobj = 0x71,
+ 5, // ldstr = 0x72,
+ 5, // newobj = 0x73,
+ 5, // castclass = 0x74,
+ 5, // isinst = 0x75,
+ 1, // conv_r_un = 0x76,
+ Invalid, // = 0x77,
+ Invalid, // = 0x78,
+ 5, // unbox = 0x79,
+ 1, // throw_ = 0x7a,
+ 5, // ldfld = 0x7b,
+ 5, // ldflda = 0x7c,
+ 5, // stfld = 0x7d,
+ 5, // ldsfld = 0x7e,
+ 5, // ldsflda = 0x7f,
+ 5, // stsfld = 0x80,
+ 5, // stobj = 0x81,
+ 1, // conv_ovf_i1_un = 0x82,
+ 1, // conv_ovf_i2_un = 0x83,
+ 1, // conv_ovf_i4_un = 0x84,
+ 1, // conv_ovf_i8_un = 0x85,
+ 1, // conv_ovf_u1_un = 0x86,
+ 1, // conv_ovf_u2_un = 0x87,
+ 1, // conv_ovf_u4_un = 0x88,
+ 1, // conv_ovf_u8_un = 0x89,
+ 1, // conv_ovf_i_un = 0x8a,
+ 1, // conv_ovf_u_un = 0x8b,
+ 5, // box = 0x8c,
+ 5, // newarr = 0x8d,
+ 1, // ldlen = 0x8e,
+ 5, // ldelema = 0x8f,
+ 1, // ldelem_i1 = 0x90,
+ 1, // ldelem_u1 = 0x91,
+ 1, // ldelem_i2 = 0x92,
+ 1, // ldelem_u2 = 0x93,
+ 1, // ldelem_i4 = 0x94,
+ 1, // ldelem_u4 = 0x95,
+ 1, // ldelem_i8 = 0x96,
+ 1, // ldelem_i = 0x97,
+ 1, // ldelem_r4 = 0x98,
+ 1, // ldelem_r8 = 0x99,
+ 1, // ldelem_ref = 0x9a,
+ 1, // stelem_i = 0x9b,
+ 1, // stelem_i1 = 0x9c,
+ 1, // stelem_i2 = 0x9d,
+ 1, // stelem_i4 = 0x9e,
+ 1, // stelem_i8 = 0x9f,
+ 1, // stelem_r4 = 0xa0,
+ 1, // stelem_r8 = 0xa1,
+ 1, // stelem_ref = 0xa2,
+ 5, // ldelem = 0xa3,
+ 5, // stelem = 0xa4,
+ 5, // unbox_any = 0xa5,
+ Invalid, // = 0xa6,
+ Invalid, // = 0xa7,
+ Invalid, // = 0xa8,
+ Invalid, // = 0xa9,
+ Invalid, // = 0xaa,
+ Invalid, // = 0xab,
+ Invalid, // = 0xac,
+ Invalid, // = 0xad,
+ Invalid, // = 0xae,
+ Invalid, // = 0xaf,
+ Invalid, // = 0xb0,
+ Invalid, // = 0xb1,
+ Invalid, // = 0xb2,
+ 1, // conv_ovf_i1 = 0xb3,
+ 1, // conv_ovf_u1 = 0xb4,
+ 1, // conv_ovf_i2 = 0xb5,
+ 1, // conv_ovf_u2 = 0xb6,
+ 1, // conv_ovf_i4 = 0xb7,
+ 1, // conv_ovf_u4 = 0xb8,
+ 1, // conv_ovf_i8 = 0xb9,
+ 1, // conv_ovf_u8 = 0xba,
+ Invalid, // = 0xbb,
+ Invalid, // = 0xbc,
+ Invalid, // = 0xbd,
+ Invalid, // = 0xbe,
+ Invalid, // = 0xbf,
+ Invalid, // = 0xc0,
+ Invalid, // = 0xc1,
+ 5, // refanyval = 0xc2,
+ 1, // ckfinite = 0xc3,
+ Invalid, // = 0xc4,
+ Invalid, // = 0xc5,
+ 5, // mkrefany = 0xc6,
+ Invalid, // = 0xc7,
+ Invalid, // = 0xc8,
+ Invalid, // = 0xc9,
+ Invalid, // = 0xca,
+ Invalid, // = 0xcb,
+ Invalid, // = 0xcc,
+ Invalid, // = 0xcd,
+ Invalid, // = 0xce,
+ Invalid, // = 0xcf,
+ 5, // ldtoken = 0xd0,
+ 1, // conv_u2 = 0xd1,
+ 1, // conv_u1 = 0xd2,
+ 1, // conv_i = 0xd3,
+ 1, // conv_ovf_i = 0xd4,
+ 1, // conv_ovf_u = 0xd5,
+ 1, // add_ovf = 0xd6,
+ 1, // add_ovf_un = 0xd7,
+ 1, // mul_ovf = 0xd8,
+ 1, // mul_ovf_un = 0xd9,
+ 1, // sub_ovf = 0xda,
+ 1, // sub_ovf_un = 0xdb,
+ 1, // endfinally = 0xdc,
+ 5, // leave = 0xdd,
+ 2, // leave_s = 0xde,
+ 1, // stind_i = 0xdf,
+ 1, // conv_u = 0xe0,
+ Invalid, // = 0xe1,
+ Invalid, // = 0xe2,
+ Invalid, // = 0xe3,
+ Invalid, // = 0xe4,
+ Invalid, // = 0xe5,
+ Invalid, // = 0xe6,
+ Invalid, // = 0xe7,
+ Invalid, // = 0xe8,
+ Invalid, // = 0xe9,
+ Invalid, // = 0xea,
+ Invalid, // = 0xeb,
+ Invalid, // = 0xec,
+ Invalid, // = 0xed,
+ Invalid, // = 0xee,
+ Invalid, // = 0xef,
+ Invalid, // = 0xf0,
+ Invalid, // = 0xf1,
+ Invalid, // = 0xf2,
+ Invalid, // = 0xf3,
+ Invalid, // = 0xf4,
+ Invalid, // = 0xf5,
+ Invalid, // = 0xf6,
+ Invalid, // = 0xf7,
+ Invalid, // = 0xf8,
+ Invalid, // = 0xf9,
+ Invalid, // = 0xfa,
+ Invalid, // = 0xfb,
+ Invalid, // = 0xfc,
+ Invalid, // = 0xfd,
+ 1, // prefix1 = 0xfe,
+ Invalid, // = 0xff,
+ 2, // arglist = 0x100,
+ 2, // ceq = 0x101,
+ 2, // cgt = 0x102,
+ 2, // cgt_un = 0x103,
+ 2, // clt = 0x104,
+ 2, // clt_un = 0x105,
+ 6, // ldftn = 0x106,
+ 6, // ldvirtftn = 0x107,
+ Invalid, // = 0x108,
+ 4, // ldarg = 0x109,
+ 4, // ldarga = 0x10a,
+ 4, // starg = 0x10b,
+ 4, // ldloc = 0x10c,
+ 4, // ldloca = 0x10d,
+ 4, // stloc = 0x10e,
+ 2, // localloc = 0x10f,
+ Invalid, // = 0x110,
+ 2, // endfilter = 0x111,
+ 3, // unaligned = 0x112,
+ 2, // volatile_ = 0x113,
+ 2, // tail = 0x114,
+ 6, // initobj = 0x115,
+ 6, // constrained = 0x116,
+ 2, // cpblk = 0x117,
+ 2, // initblk = 0x118,
+ 3, // no = 0x119,
+ 2, // rethrow = 0x11a,
+ Invalid, // = 0x11b,
+ 6, // sizeof_ = 0x11c,
+ 2, // refanytype = 0x11d,
+ 2, // readonly_ = 0x11e,
+ };
+ }
+}
diff --git a/src/Common/src/TypeSystem/IL/ILStackHelper.cs b/src/Common/src/TypeSystem/IL/ILStackHelper.cs
new file mode 100644
index 000000000..d30532e13
--- /dev/null
+++ b/src/Common/src/TypeSystem/IL/ILStackHelper.cs
@@ -0,0 +1,474 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using System;
+using System.Diagnostics;
+
+using Internal.TypeSystem;
+
+namespace Internal.IL
+{
+ public static class ILStackHelper
+ {
+ /// <summary>
+ /// Validates that the CIL evaluation stack is properly balanced.
+ /// </summary>
+ [Conditional("DEBUG")]
+ public static void CheckStackBalance(this MethodIL methodIL)
+ {
+ methodIL.ComputeMaxStack();
+ }
+
+ /// <summary>
+ /// Computes the maximum number of items that can be pushed onto the CIL evaluation stack.
+ /// </summary>
+ public static int ComputeMaxStack(this MethodIL methodIL)
+ {
+ const int StackHeightNotSet = Int32.MinValue;
+
+ byte[] ilbytes = methodIL.GetILBytes();
+ int currentOffset = 0;
+ int stackHeight = 0;
+ int maxStack = 0;
+
+ // TODO: Use Span<T> for this and stackalloc the array if reasonably sized
+ int[] stackHeights = new int[ilbytes.Length];
+ for (int i = 0; i < stackHeights.Length; i++)
+ stackHeights[i] = StackHeightNotSet;
+
+ // Catch and filter clauses have a known non-zero stack height.
+ foreach (ILExceptionRegion region in methodIL.GetExceptionRegions())
+ {
+ if (region.Kind == ILExceptionRegionKind.Catch)
+ {
+ stackHeights[region.HandlerOffset] = 1;
+ }
+ else if (region.Kind == ILExceptionRegionKind.Filter)
+ {
+ stackHeights[region.FilterOffset] = 1;
+ stackHeights[region.HandlerOffset] = 1;
+ }
+ }
+
+ while (currentOffset < ilbytes.Length)
+ {
+ ILOpcode opcode = (ILOpcode)ilbytes[currentOffset];
+ if (opcode == ILOpcode.prefix1)
+ opcode = 0x100 + (ILOpcode)ilbytes[currentOffset + 1];
+
+ // The stack height could be unknown if the previous instruction
+ // was an unconditional control transfer.
+ // In that case we check if we have a known stack height due to
+ // this instruction being a target of a previous branch or an EH block.
+ if (stackHeight == StackHeightNotSet)
+ stackHeight = stackHeights[currentOffset];
+
+ // If we still don't know the stack height, ECMA-335 III.1.7.5
+ // "Backward branch constraint" demands the evaluation stack be empty.
+ if (stackHeight == StackHeightNotSet)
+ stackHeight = 0;
+
+ // Remeber the stack height at this offset.
+ Debug.Assert(stackHeights[currentOffset] == StackHeightNotSet
+ || stackHeights[currentOffset] == stackHeight);
+ stackHeights[currentOffset] = stackHeight;
+
+ bool isVariableSize = false;
+ switch (opcode)
+ {
+ case ILOpcode.arglist:
+ case ILOpcode.dup:
+ case ILOpcode.ldc_i4:
+ case ILOpcode.ldc_i4_0:
+ case ILOpcode.ldc_i4_1:
+ case ILOpcode.ldc_i4_2:
+ case ILOpcode.ldc_i4_3:
+ case ILOpcode.ldc_i4_4:
+ case ILOpcode.ldc_i4_5:
+ case ILOpcode.ldc_i4_6:
+ case ILOpcode.ldc_i4_7:
+ case ILOpcode.ldc_i4_8:
+ case ILOpcode.ldc_i4_m1:
+ case ILOpcode.ldc_i4_s:
+ case ILOpcode.ldc_i8:
+ case ILOpcode.ldc_r4:
+ case ILOpcode.ldc_r8:
+ case ILOpcode.ldftn:
+ case ILOpcode.ldnull:
+ case ILOpcode.ldsfld:
+ case ILOpcode.ldsflda:
+ case ILOpcode.ldstr:
+ case ILOpcode.ldtoken:
+ case ILOpcode.ldarg:
+ case ILOpcode.ldarg_0:
+ case ILOpcode.ldarg_1:
+ case ILOpcode.ldarg_2:
+ case ILOpcode.ldarg_3:
+ case ILOpcode.ldarg_s:
+ case ILOpcode.ldarga:
+ case ILOpcode.ldarga_s:
+ case ILOpcode.ldloc:
+ case ILOpcode.ldloc_0:
+ case ILOpcode.ldloc_1:
+ case ILOpcode.ldloc_2:
+ case ILOpcode.ldloc_3:
+ case ILOpcode.ldloc_s:
+ case ILOpcode.ldloca:
+ case ILOpcode.ldloca_s:
+ case ILOpcode.sizeof_:
+ stackHeight += 1;
+ break;
+
+ case ILOpcode.add:
+ case ILOpcode.add_ovf:
+ case ILOpcode.add_ovf_un:
+ case ILOpcode.and:
+ case ILOpcode.ceq:
+ case ILOpcode.cgt:
+ case ILOpcode.cgt_un:
+ case ILOpcode.clt:
+ case ILOpcode.clt_un:
+ case ILOpcode.div:
+ case ILOpcode.div_un:
+ case ILOpcode.initobj:
+ case ILOpcode.ldelem:
+ case ILOpcode.ldelem_i:
+ case ILOpcode.ldelem_i1:
+ case ILOpcode.ldelem_i2:
+ case ILOpcode.ldelem_i4:
+ case ILOpcode.ldelem_i8:
+ case ILOpcode.ldelem_r4:
+ case ILOpcode.ldelem_r8:
+ case ILOpcode.ldelem_ref:
+ case ILOpcode.ldelem_u1:
+ case ILOpcode.ldelem_u2:
+ case ILOpcode.ldelem_u4:
+ case ILOpcode.ldelema:
+ case ILOpcode.mkrefany:
+ case ILOpcode.mul:
+ case ILOpcode.mul_ovf:
+ case ILOpcode.mul_ovf_un:
+ case ILOpcode.or:
+ case ILOpcode.pop:
+ case ILOpcode.rem:
+ case ILOpcode.rem_un:
+ case ILOpcode.shl:
+ case ILOpcode.shr:
+ case ILOpcode.shr_un:
+ case ILOpcode.stsfld:
+ case ILOpcode.sub:
+ case ILOpcode.sub_ovf:
+ case ILOpcode.sub_ovf_un:
+ case ILOpcode.xor:
+ case ILOpcode.starg:
+ case ILOpcode.starg_s:
+ case ILOpcode.stloc:
+ case ILOpcode.stloc_0:
+ case ILOpcode.stloc_1:
+ case ILOpcode.stloc_2:
+ case ILOpcode.stloc_3:
+ case ILOpcode.stloc_s:
+ Debug.Assert(stackHeight > 0);
+ stackHeight -= 1;
+ break;
+
+ case ILOpcode.throw_:
+ Debug.Assert(stackHeight > 0);
+ stackHeight = StackHeightNotSet;
+ break;
+
+ case ILOpcode.br:
+ case ILOpcode.leave:
+ case ILOpcode.brfalse:
+ case ILOpcode.brtrue:
+ case ILOpcode.beq:
+ case ILOpcode.bge:
+ case ILOpcode.bge_un:
+ case ILOpcode.bgt:
+ case ILOpcode.bgt_un:
+ case ILOpcode.ble:
+ case ILOpcode.ble_un:
+ case ILOpcode.blt:
+ case ILOpcode.blt_un:
+ case ILOpcode.bne_un:
+ {
+ int target = currentOffset + ReadInt32(ilbytes, currentOffset + 1) + 5;
+
+ int adjustment;
+ bool isConditional;
+ if (opcode == ILOpcode.br || opcode == ILOpcode.leave)
+ {
+ isConditional = false;
+ adjustment = 0;
+ }
+ else if (opcode == ILOpcode.brfalse || opcode == ILOpcode.brtrue)
+ {
+ isConditional = true;
+ adjustment = 1;
+ }
+ else
+ {
+ isConditional = true;
+ adjustment = 2;
+ }
+
+ Debug.Assert(stackHeight >= adjustment);
+ stackHeight -= adjustment;
+
+ Debug.Assert(stackHeights[target] == StackHeightNotSet
+ || stackHeights[target] == stackHeight);
+
+ // Forward branch carries information about stack height at a future
+ // offset. We need to remember it.
+ if (target > currentOffset)
+ stackHeights[target] = stackHeight;
+
+ if (!isConditional)
+ stackHeight = StackHeightNotSet;
+ }
+ break;
+
+ case ILOpcode.br_s:
+ case ILOpcode.leave_s:
+ case ILOpcode.brfalse_s:
+ case ILOpcode.brtrue_s:
+ case ILOpcode.beq_s:
+ case ILOpcode.bge_s:
+ case ILOpcode.bge_un_s:
+ case ILOpcode.bgt_s:
+ case ILOpcode.bgt_un_s:
+ case ILOpcode.ble_s:
+ case ILOpcode.ble_un_s:
+ case ILOpcode.blt_s:
+ case ILOpcode.blt_un_s:
+ case ILOpcode.bne_un_s:
+ {
+ int target = currentOffset + (sbyte)ilbytes[currentOffset + 1] + 2;
+
+ int adjustment;
+ bool isConditional;
+ if (opcode == ILOpcode.br_s || opcode == ILOpcode.leave_s)
+ {
+ isConditional = false;
+ adjustment = 0;
+ }
+ else if (opcode == ILOpcode.brfalse_s || opcode == ILOpcode.brtrue_s)
+ {
+ isConditional = true;
+ adjustment = 1;
+ }
+ else
+ {
+ isConditional = true;
+ adjustment = 2;
+ }
+
+ Debug.Assert(stackHeight >= adjustment);
+ stackHeight -= adjustment;
+
+ Debug.Assert(stackHeights[target] == StackHeightNotSet
+ || stackHeights[target] == stackHeight);
+
+ // Forward branch carries information about stack height at a future
+ // offset. We need to remember it.
+ if (target > currentOffset)
+ stackHeights[target] = stackHeight;
+
+ if (!isConditional)
+ stackHeight = StackHeightNotSet;
+ }
+ break;
+
+ case ILOpcode.call:
+ case ILOpcode.calli:
+ case ILOpcode.callvirt:
+ case ILOpcode.newobj:
+ {
+ int token = ReadILToken(ilbytes, currentOffset + 1);
+ object obj = methodIL.GetObject(token);
+ MethodSignature sig = obj is MethodSignature ?
+ (MethodSignature)obj :
+ ((MethodDesc)obj).Signature;
+ int adjustment = sig.Length;
+ if (opcode == ILOpcode.newobj)
+ {
+ adjustment--;
+ }
+ else
+ {
+ if (opcode == ILOpcode.calli)
+ adjustment++;
+ if (!sig.IsStatic)
+ adjustment++;
+ if (!sig.ReturnType.IsVoid)
+ adjustment--;
+ }
+
+ Debug.Assert(stackHeight >= adjustment);
+ stackHeight -= adjustment;
+ }
+ break;
+
+ case ILOpcode.ret:
+ {
+ bool hasReturnValue = !methodIL.OwningMethod.Signature.ReturnType.IsVoid;
+ if (hasReturnValue)
+ stackHeight -= 1;
+
+ Debug.Assert(stackHeight == 0);
+
+ stackHeight = StackHeightNotSet;
+ }
+ break;
+
+ case ILOpcode.cpobj:
+ case ILOpcode.stfld:
+ case ILOpcode.stind_i:
+ case ILOpcode.stind_i1:
+ case ILOpcode.stind_i2:
+ case ILOpcode.stind_i4:
+ case ILOpcode.stind_i8:
+ case ILOpcode.stind_r4:
+ case ILOpcode.stind_r8:
+ case ILOpcode.stind_ref:
+ case ILOpcode.stobj:
+ Debug.Assert(stackHeight > 1);
+ stackHeight -= 2;
+ break;
+
+ case ILOpcode.cpblk:
+ case ILOpcode.initblk:
+ case ILOpcode.stelem:
+ case ILOpcode.stelem_i:
+ case ILOpcode.stelem_i1:
+ case ILOpcode.stelem_i2:
+ case ILOpcode.stelem_i4:
+ case ILOpcode.stelem_i8:
+ case ILOpcode.stelem_r4:
+ case ILOpcode.stelem_r8:
+ case ILOpcode.stelem_ref:
+ Debug.Assert(stackHeight > 2);
+ stackHeight -= 3;
+ break;
+
+ case ILOpcode.break_:
+ case ILOpcode.constrained:
+ case ILOpcode.no:
+ case ILOpcode.nop:
+ case ILOpcode.readonly_:
+ case ILOpcode.tail:
+ case ILOpcode.unaligned:
+ case ILOpcode.volatile_:
+ break;
+
+ case ILOpcode.endfilter:
+ Debug.Assert(stackHeight > 0);
+ stackHeight = StackHeightNotSet;
+ break;
+
+ case ILOpcode.jmp:
+ case ILOpcode.rethrow:
+ case ILOpcode.endfinally:
+ stackHeight = StackHeightNotSet;
+ break;
+
+ case ILOpcode.box:
+ case ILOpcode.castclass:
+ case ILOpcode.ckfinite:
+ case ILOpcode.conv_i:
+ case ILOpcode.conv_i1:
+ case ILOpcode.conv_i2:
+ case ILOpcode.conv_i4:
+ case ILOpcode.conv_i8:
+ case ILOpcode.conv_ovf_i:
+ case ILOpcode.conv_ovf_i_un:
+ case ILOpcode.conv_ovf_i1:
+ case ILOpcode.conv_ovf_i1_un:
+ case ILOpcode.conv_ovf_i2:
+ case ILOpcode.conv_ovf_i2_un:
+ case ILOpcode.conv_ovf_i4:
+ case ILOpcode.conv_ovf_i4_un:
+ case ILOpcode.conv_ovf_i8:
+ case ILOpcode.conv_ovf_i8_un:
+ case ILOpcode.conv_ovf_u:
+ case ILOpcode.conv_ovf_u_un:
+ case ILOpcode.conv_ovf_u1:
+ case ILOpcode.conv_ovf_u1_un:
+ case ILOpcode.conv_ovf_u2:
+ case ILOpcode.conv_ovf_u2_un:
+ case ILOpcode.conv_ovf_u4:
+ case ILOpcode.conv_ovf_u4_un:
+ case ILOpcode.conv_ovf_u8:
+ case ILOpcode.conv_ovf_u8_un:
+ case ILOpcode.conv_r_un:
+ case ILOpcode.conv_r4:
+ case ILOpcode.conv_r8:
+ case ILOpcode.conv_u:
+ case ILOpcode.conv_u1:
+ case ILOpcode.conv_u2:
+ case ILOpcode.conv_u4:
+ case ILOpcode.conv_u8:
+ case ILOpcode.isinst:
+ case ILOpcode.ldfld:
+ case ILOpcode.ldflda:
+ case ILOpcode.ldind_i:
+ case ILOpcode.ldind_i1:
+ case ILOpcode.ldind_i2:
+ case ILOpcode.ldind_i4:
+ case ILOpcode.ldind_i8:
+ case ILOpcode.ldind_r4:
+ case ILOpcode.ldind_r8:
+ case ILOpcode.ldind_ref:
+ case ILOpcode.ldind_u1:
+ case ILOpcode.ldind_u2:
+ case ILOpcode.ldind_u4:
+ case ILOpcode.ldlen:
+ case ILOpcode.ldobj:
+ case ILOpcode.ldvirtftn:
+ case ILOpcode.localloc:
+ case ILOpcode.neg:
+ case ILOpcode.newarr:
+ case ILOpcode.not:
+ case ILOpcode.refanytype:
+ case ILOpcode.refanyval:
+ case ILOpcode.unbox:
+ case ILOpcode.unbox_any:
+ Debug.Assert(stackHeight > 0);
+ break;
+
+ case ILOpcode.switch_:
+ Debug.Assert(stackHeight > 0);
+ isVariableSize = true;
+ stackHeight -= 1;
+ currentOffset += 1 + (ReadInt32(ilbytes, currentOffset + 1) * 4) + 4;
+ break;
+
+ default:
+ Debug.Fail("Unknown instruction");
+ break;
+ }
+
+ if (!isVariableSize)
+ currentOffset += opcode.GetSize();
+
+ maxStack = Math.Max(maxStack, stackHeight);
+ }
+
+ return maxStack;
+ }
+
+ private static int ReadInt32(byte[] ilBytes, int offset)
+ {
+ return ilBytes[offset]
+ + (ilBytes[offset + 1] << 8)
+ + (ilBytes[offset + 2] << 16)
+ + (ilBytes[offset + 3] << 24);
+ }
+
+ private static int ReadILToken(byte[] ilBytes, int offset)
+ {
+ return ReadInt32(ilBytes, offset);
+ }
+ }
+}
diff --git a/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs b/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs
index 318174dc0..596a9b4bf 100644
--- a/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs
+++ b/src/Common/src/TypeSystem/IL/Stubs/ILEmitter.cs
@@ -473,12 +473,16 @@ namespace Internal.IL.Stubs
private readonly MethodDesc _method;
private readonly MethodDebugInformation _debugInformation;
+ private const int MaxStackNotSet = -1;
+ private int _maxStack;
+
public ILStubMethodIL(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, MethodDebugInformation debugInfo = null)
{
_ilBytes = ilBytes;
_locals = locals;
_tokens = tokens;
_method = owningMethod;
+ _maxStack = MaxStackNotSet;
if (debugInfo == null)
debugInfo = MethodDebugInformation.None;
@@ -492,6 +496,7 @@ namespace Internal.IL.Stubs
_tokens = methodIL._tokens;
_method = methodIL._method;
_debugInformation = methodIL._debugInformation;
+ _maxStack = methodIL._maxStack;
}
public override MethodDesc OwningMethod
@@ -516,8 +521,9 @@ namespace Internal.IL.Stubs
{
get
{
- // Conservative estimate...
- return _ilBytes.Length;
+ if (_maxStack == MaxStackNotSet)
+ _maxStack = this.ComputeMaxStack();
+ return _maxStack;
}
}
@@ -543,6 +549,25 @@ namespace Internal.IL.Stubs
}
}
+ // Workaround for places that emit IL that doesn't conform to the ECMA-335 CIL stack requirements.
+ // https://github.com/dotnet/corert/issues/5152
+ // This class and all references to it should be deleted when the issue is fixed.
+ // Do not add new references to this.
+ public class ILStubMethodILWithNonConformingStack : ILStubMethodIL
+ {
+ public ILStubMethodILWithNonConformingStack(MethodDesc owningMethod, byte[] ilBytes, LocalVariableDefinition[] locals, Object[] tokens, MethodDebugInformation debugInfo)
+ : base(owningMethod, ilBytes, locals, tokens, debugInfo)
+ {
+ }
+
+ public ILStubMethodILWithNonConformingStack(ILStubMethodIL methodIL)
+ : base(methodIL)
+ {
+ }
+
+ public override int MaxStack => GetILBytes().Length;
+ }
+
public class ILCodeLabel
{
private ILCodeStream _codeStream;
@@ -640,7 +665,7 @@ namespace Internal.IL.Stubs
return newLabel;
}
- public MethodIL Link(MethodDesc owningMethod)
+ public MethodIL Link(MethodDesc owningMethod, bool nonConformingStackWorkaround = false)
{
int totalLength = 0;
int numSequencePoints = 0;
@@ -686,7 +711,18 @@ namespace Internal.IL.Stubs
debugInfo = new EmittedMethodDebugInformation(sequencePoints);
}
- return new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
+ ILStubMethodIL result;
+ if (nonConformingStackWorkaround)
+ {
+ // nonConformingStackWorkaround is a workaround for https://github.com/dotnet/corert/issues/5152
+ result = new ILStubMethodILWithNonConformingStack(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
+ }
+ else
+ {
+ result = new ILStubMethodIL(owningMethod, ilInstructions, _locals.ToArray(), _tokens.ToArray(), debugInfo);
+ result.CheckStackBalance();
+ }
+ return result;
}
private class EmittedMethodDebugInformation : MethodDebugInformation
diff --git a/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs b/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
index 318bdb002..6a0669b8c 100644
--- a/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
+++ b/src/Common/src/TypeSystem/IL/Stubs/PInvokeILEmitter.cs
@@ -306,7 +306,7 @@ namespace Internal.IL.Stubs
unmarshallingCodestream.Emit(ILOpcode.ret);
- return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod),
+ return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(_targetMethod, nonConformingStackWorkaround: true),
IsStubRequired());
}
@@ -395,7 +395,7 @@ namespace Internal.IL.Stubs
}
}
- public sealed class PInvokeILStubMethodIL : ILStubMethodIL
+ public sealed class PInvokeILStubMethodIL : ILStubMethodILWithNonConformingStack
{
public bool IsStubRequired { get; }
public PInvokeILStubMethodIL(ILStubMethodIL methodIL, bool isStubRequired) : base(methodIL)
diff --git a/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs b/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs
index 00954908a..980fd8921 100644
--- a/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs
+++ b/src/Common/src/TypeSystem/Interop/IL/MarshalHelpers.cs
@@ -898,8 +898,8 @@ namespace Internal.TypeSystem.Interop
codeStream.Emit(ILOpcode.throw_);
codeStream.Emit(ILOpcode.ret);
- return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method), true);
+ return new PInvokeILStubMethodIL((ILStubMethodIL)emitter.Link(method, nonConformingStackWorkaround: true), true);
}
}
-} \ No newline at end of file
+}
diff --git a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
index af067a0ad..d9de5b3c9 100644
--- a/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
+++ b/src/ILCompiler.TypeSystem/src/ILCompiler.TypeSystem.csproj
@@ -406,9 +406,15 @@
<Compile Include="..\..\Common\src\TypeSystem\IL\InstantiatedMethodIL.cs">
<Link>IL\InstantiatedMethodIL.cs</Link>
</Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILStackHelper.cs">
+ <Link>IL\ILStackHelper.cs</Link>
+ </Compile>
<Compile Include="..\..\Common\src\TypeSystem\IL\ILOpcode.cs">
<Link>IL\ILOpcode.cs</Link>
</Compile>
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILOpcodeHelper.cs">
+ <Link>IL\ILOpcodeHelper.cs</Link>
+ </Compile>
<Compile Include="..\..\Common\src\TypeSystem\IL\TypeSystemContext.EnumMethods.cs">
<Link>IL\TypeSystemContext.EnumMethods.cs</Link>
</Compile>
diff --git a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
index 5a966e7db..c2f190cc0 100644
--- a/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
+++ b/src/System.Private.TypeLoader/src/System.Private.TypeLoader.csproj
@@ -80,6 +80,8 @@
<Compile Include="..\..\Common\src\TypeSystem\IL\HelperExtensions.cs" />
<Compile Include="..\..\Common\src\TypeSystem\IL\ILDisassembler.cs" />
<Compile Include="..\..\Common\src\TypeSystem\IL\ILOpcode.cs" />
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILOpcodeHelper.cs" />
+ <Compile Include="..\..\Common\src\TypeSystem\IL\ILStackHelper.cs" />
<Compile Include="..\..\Common\src\TypeSystem\IL\InstantiatedMethodIL.cs" />
<Compile Include="..\..\Common\src\TypeSystem\IL\MethodIL.cs" />
<Compile Include="..\..\Common\src\TypeSystem\IL\MethodIL.Symbols.cs" />