【问题标题】:Calculation of maxstack value in IL codeIL代码中maxstack值的计算
【发布时间】:2016-03-01 07:57:01
【问题描述】:

我有以下程序来添加值。 当我在 main 方法中添加方法调用并查看 ILDASM.EXE 时,Maxstack 大小为 2。取消注释后,maxstack 大小变为 4。

为什么在 Main 方法的情况下,所有变量都没有进入堆栈,因为堆栈大小仅保持 2,而在 Add 方法调用的情况下,每个变量都进入堆栈? 是不是在main方法内部计算是一个一个发生,所以一次只需要两个变量。

请解开我的困惑。

static void Main(string[] args)
{
    int x = 2;
    int y = 3;
    int a = 4;
    int b = 5;
    int c = 6;

    Console.WriteLine(x + y + a + b + c);
    Console.WriteLine(Add(10, 20, 30, 40));
    Console.ReadLine();
}

static int Add(int x, int y, int z, int a)
{
    return x + y + z + a;
}

【问题讨论】:

    标签: c# stack cil il ildasm


    【解决方案1】:

    每个变量初始化:

    int x = 2;
    

    将要求值在堆栈上:(堆栈大小:到目前为止需要 1 个)

    .locals init ([0] int32 x,
           [1] int32 y,
           [2] int32 a,
           [3] int32 b,
           [4] int32 c)
    IL_0000:  ldc.i4.2  // push 2 to the stack
    IL_0001:  stloc.0   // load local variable 0 from stack ( x = 2 )
    

    这些操作是按顺序发生的,因此所需的最大堆栈大小仍为 1,期间:

    int y = 3;
    int a = 4;
    int b = 5;
    int c = 6;
    

    说到这里:

    Console.WriteLine(x + y + a + b + c);
    

    要添加任意两个变量,堆栈大小必须为 2:

    IL_000b:  ldloc.0 // copy to stack x, max stack size required is still 1.
    IL_000c:  ldloc.1 // copy to stack y, max stack size required is 2 now.
    IL_000d:  add     // execute add, will cause the sum x + y to be on stack
    IL_000e:  ldloc.2 // copy to stack a
    IL_000f:  add     // execute add... (adds a to the result of x + y)
    ....
    

    取消注释 Add 方法时的差异 IL 如下。

    调用方法时,需要将实例引用压入堆栈(也就是说,如果 Add 方法是非静态的,则应将指向其声明类型的实例指针压入堆栈)

    然后,每个需要传递给方法的参数也应该被压入堆栈。

    因此,在您的情况下,Add 方法的参数数量定义了最大堆栈大小。给这个 Add 方法添加一个参数,你会看到最大堆栈大小会增加到 5:

    // method is static so no need to push an instance pointer to the stack
    IL_001a:  ldc.i4.s   10 // push to stack
    IL_001c:  ldc.i4.s   20 // push to stack
    IL_001e:  ldc.i4.s   30 // push to stack
    IL_0020:  ldc.i4.s   40 // push to stack
    IL_0022:  call       int32 Program::Add(int32,
                                          int32,
                                          int32,
                                          int32)
    IL_0027:  call       void [mscorlib]System.Console::WriteLine(int32)
    

    【讨论】:

    • 我会稍微调整顶部的措辞 - 它不是使用堆栈空间的 声明,而是 初始化
    • @Damien_The_Unbeliever 感谢您的审查和更正。修复它。
    • 那么是不是像 IL 不是逐行生成的。我的意思是说 x=2 需要一个堆栈,那么为什么不声明其他值呢?是不是编译器知道它只需要加法,所以堆栈中的 2 个空间就足够了。
    • @TBAG - x=2 需要一个堆栈空间 - 它将2 推入堆栈,然后将其从堆栈中弹出到变量x 中。此时,堆栈再次为空。
    • 内联方法体的好处将是大量 IL 指令,主要取决于方法具有的参数数量。如上所示,调用具有 4 个参数的静态方法需要 5 条 IL 指令。编译器足够聪明,可以在必要时内联。不要为此破坏模块化。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多