jeudi 24 mars 2016

How is .maxstack calculated by reflection emit?

I am generating a method that has a large number of switch statements. I noticed in ildasm the .maxstack value is really high. My understanding is the .maxstack is the maximum stack depth of a given method? I cannot find to much information about online.

In the first two examples the maximum stack size is over 4KB. In the last example the maximum stack size is 509 but it seems like the actual maximum depth is 10. Why is the value so high? This is just a hint for the jit? Is there an implications of having such a high .maxstack? Is everything I read on the internet about it being the maximum depth incorrect?

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, 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.Ldloc, result);

        generator.Emit(OpCodes.Ret);

        type.CreateType();

        assemblyBuilder.Save("foobar.dll");

    }
}





Aucun commentaire:

Enregistrer un commentaire