diff options
author | Michal Strehovský <MichalStrehovsky@users.noreply.github.com> | 2017-12-24 17:27:47 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-12-24 17:27:47 +0300 |
commit | 4846700d032ca59a17e9ecbbd884f21cf85130be (patch) | |
tree | 6c7eac2e7144ef126cb3ddd6e7788e032b0c421e | |
parent | f0706feddf13d7c9b491b7ea2cb24da85378c3f5 (diff) | |
parent | c04d41a4509535d995d92dec0cb8eb1026445cf7 (diff) |
Merge pull request #5149 from MichalStrehovsky/maxStack
Add algorithm to compute the MaxStack value
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" /> |