.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 来确定一个方法是否可以内联。
我正在生成一个包含大量 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 来确定一个方法是否可以内联。