Reputation: 13
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?
Seems like there was a mistake in my 3rd example. I didnt peverify or test and there was an extra push after the load argument. After I fixed that maxstack was 9. For the first two examples the maximum stack size remains over 4K with reflection.emit. With the C# compiler the switch method is 9, instead of 4502 with reflection emit.
Per the answer it seems like they add the maximum depth for each basic block as sum them up, where as the C# compiler calculates it more correctly. I am still curious about the implications though of such a high value.
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);
// mistake, this caused stack size to be very high. // 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");
}
}
Upvotes: 1
Views: 467
Reputation: 21
There is an existing connect bug on this. maxstack is used by the jit to determine whether a method can be inlined.
Upvotes: 2
Reputation: 239764
Looking in the Reference Source, we can find the method that tracks the required stack size, called UpdateStackSize. The comments within seem to be telling:
// 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.
That is, there's no attempt to perform any control flow analysis. Each basic block's maximum required stack size is added together.
I'm not aware of any material implications of this size being unusually high.
Upvotes: 2