public static int ComputeMaxStack()

in src/coreclr/tools/Common/TypeSystem/IL/ILStackHelper.cs [25:415]


        public static int ComputeMaxStack(this MethodIL methodIL)
        {
            const int StackHeightNotSet = int.MinValue;
            const int StackAllocThreshold = 256 / sizeof(int);

            var ilReader = new ILReader(methodIL.GetILBytes());
            int stackHeight = 0;
            int maxStack = 0;

            Span<int> stackHeights = ilReader.Size <= StackAllocThreshold ?
                stackalloc int[StackAllocThreshold].Slice(0, ilReader.Size) : new int[ilReader.Size];

            stackHeights.Fill(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 (ilReader.HasNext)
            {
                int currentOffset = ilReader.Offset;
                ILOpcode opcode = ilReader.ReadILOpcode();

                // 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 hasReadOperand = 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:
                    case ILOpcode.switch_:
                        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:
                    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 = ilReader.ReadBranchDestination(opcode);
                            hasReadOperand = true;

                            int adjustment;
                            bool isConditional;
                            if (opcode is ILOpcode.br or ILOpcode.br_s or ILOpcode.leave or ILOpcode.leave_s)
                            {
                                isConditional = false;
                                adjustment = 0;
                            }
                            else if (opcode is ILOpcode.brfalse or ILOpcode.brfalse_s or ILOpcode.brtrue_s or 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.call:
                    case ILOpcode.calli:
                    case ILOpcode.callvirt:
                    case ILOpcode.newobj:
                        {
                            object obj = methodIL.GetObject(ilReader.ReadILToken());
                            hasReadOperand = true;

                            MethodSignature sig = obj is MethodSignature methodSignature ?
                                methodSignature : ((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;

                    default:
                        Debug.Fail("Unknown instruction");
                        break;
                }

                if (!hasReadOperand)
                    ilReader.Skip(opcode);

                maxStack = Math.Max(maxStack, stackHeight);
            }

            return maxStack;
        }