.maxstack 是如何通过反射发射计算出来的?

How is .maxstack calculated by reflection emit?

我正在生成一个包含大量 switch 语句的方法。我注意到 ildasm 中的 .maxstack 值非常高。我的理解是 .maxstack 是给定方法的最大堆栈深度?我在网上找不到太多信息。

在前两个示例中,最大堆栈大小超过 4KB。在最后一个例子中,最大堆栈大小是 509,但实际最大深度似乎是 10。为什么这个值这么高?这只是对 jit 的提示?拥有如此高的 .maxstack 有什么影响吗?我在互联网上读到的关于最大深度的所有内容都不正确吗?

更新

我的第三个例子好像有错误。我没有进行验证或测试,并且在加载参数之后有一个额外的推动。在我修复 maxstack 为 9 之后。对于前两个示例,最大堆栈大小仍然超过 4K reflection.emit。使用 C# 编译器时,switch 方法是 9,而不是带有反射发射的 4502。

根据答案,他们似乎为每个基本块添加了最大深度作为总和,而 C# 编译器计算得更正确。这么高的价值,我还是很好奇它的含义。

class Program
{
    static void Main(string[] args)
    {
        Foo();
        Bar();
        FooBar();
    }

    static void Foo()
    {
        // Increasing this value will increase the stack depth by the number of labels.
        int caseStackDepth = 8;

        string name = Path.ChangeExtension("foo", ".dll");
        AssemblyName assemblyName = new AssemblyName("foo");
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assemblyName.Name, name);


        TypeBuilder type = module.DefineType("boo", System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
        MethodBuilder method = type.DefineMethod(
            "bar", 
            System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static,
            typeof(int),
            new [] { typeof(int) });

        ILGenerator generator = method.GetILGenerator();
        LocalBuilder result = generator.DeclareLocal(typeof(int));

        Label[] labels = new Label[500];
        for (int index = 0; index < labels.Length; index++)
        {
            labels[index] = generator.DefineLabel();
        }

        Label end = generator.DefineLabel();

        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Switch, labels);
        generator.Emit(OpCodes.Br, end);

        for (int index = 0; index < labels.Length; index++)
        {
            generator.MarkLabel(labels[index]);
            generator.Emit(OpCodes.Ldc_I4, index);

            // Simulate stack depth.
            for (int depth = 0; depth < caseStackDepth; depth++)
            {
                generator.Emit(OpCodes.Dup);
            }
            for (int depth = 0; depth < caseStackDepth; depth++)
            {
                generator.Emit(OpCodes.Add);
            }

            generator.Emit(OpCodes.Stloc, result);
            generator.Emit(OpCodes.Br, end);
        }

        generator.MarkLabel(end);


        generator.Emit(OpCodes.Ldloc, result);

        generator.Emit(OpCodes.Ret);

        type.CreateType();

        assemblyBuilder.Save("foo.dll");
    }

    static void Bar()
    {
        // Increasing this value will increase the stack depth by the number of labels.
        int caseStackDepth = 8;

        string name = Path.ChangeExtension("bar", ".dll");
        AssemblyName assemblyName = new AssemblyName("bar");
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assemblyName.Name, name);


        TypeBuilder type = module.DefineType("boo", System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
        MethodBuilder method = type.DefineMethod(
            "bar",
            System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static,
            typeof(int),
            new[] { typeof(int) });

        ILGenerator generator = method.GetILGenerator();
        LocalBuilder result = generator.DeclareLocal(typeof(int));

        Label end = generator.DefineLabel();

        for (int index = 0; index < 500; index++)
        {
            Label equal = generator.DefineLabel();
            Label notEqual = generator.DefineLabel();

            generator.Emit(OpCodes.Ldarg_0);
            generator.Emit(OpCodes.Ldc_I4, index);
            generator.Emit(OpCodes.Beq, equal);
            generator.Emit(OpCodes.Br, notEqual);

            generator.MarkLabel(equal);
            generator.Emit(OpCodes.Ldc_I4, index);

            // Simulate stack depth.
            for (int depth = 0; depth < caseStackDepth; depth++)
            {
                generator.Emit(OpCodes.Dup);
            }
            for (int depth = 0; depth < caseStackDepth; depth++)
            {
                generator.Emit(OpCodes.Add);
            }

            generator.Emit(OpCodes.Stloc, result);
            generator.Emit(OpCodes.Br, end);

            generator.MarkLabel(notEqual);
        }

        generator.MarkLabel(end);


        generator.Emit(OpCodes.Ldloc, result);

        generator.Emit(OpCodes.Ret);

        type.CreateType();

        assemblyBuilder.Save("bar.dll");

    }

    static void FooBar()
    {
        // Increasing this value will increase the stack depth by the number of labels.
        int caseStackDepth = 8;

        string name = Path.ChangeExtension("foobar", ".dll");
        AssemblyName assemblyName = new AssemblyName("foobar");
        AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.RunAndSave);
        ModuleBuilder module = assemblyBuilder.DefineDynamicModule(assemblyName.Name, name);


        TypeBuilder type = module.DefineType("boo", System.Reflection.TypeAttributes.Class | System.Reflection.TypeAttributes.Public);
        MethodBuilder method = type.DefineMethod(
            "bar",
            System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static,
            typeof(int),
            new[] { typeof(int) });

        ILGenerator generator = method.GetILGenerator();
        LocalBuilder result = generator.DeclareLocal(typeof(int));

        for (int index = 0; index < 500; index++)
        {
            generator.Emit(OpCodes.Ldarg_0);

// 错误,这导致堆栈大小非常高。 // generator.Emit(OpCodes.Ldc_I4, 索引);

            // Simulate stack depth.
            for (int depth = 0; depth < caseStackDepth; depth++)
            {
                generator.Emit(OpCodes.Dup);
            }
            for (int depth = 0; depth < caseStackDepth; depth++)
            {
                generator.Emit(OpCodes.Add);
            }

            generator.Emit(OpCodes.Stloc, result);
        }

        generator.Emit(OpCodes.Ldloc, result);

        generator.Emit(OpCodes.Ret);

        type.CreateType();

        assemblyBuilder.Save("foobar.dll");

    }
}

正在查看 Reference Source, we can find the method that tracks the required stack size, called UpdateStackSize。里面的评论好像在说:

// If the current instruction signifies end of a basic, which basically
// means an unconditional branch, add m_maxMidStack to m_maxStackSize.
// m_maxStackSize will eventually be the sum of the stack requirements for
// each basic block.

也就是说,没有尝试执行任何控制流分析。每个基本块的最大所需堆栈大小相加。

我不知道这个大小异常高有任何 material 影响。

这方面存在一个现有的连接错误。 jit 使用 maxstack 来确定一个方法是否可以内联。

https://connect.microsoft.com/VisualStudio/feedback/details/698581/ilgenerator-incorrectly-updates-maxstack-for-unconditional-branch-instructions