C# IL&;net中的堆栈实现?
我编写了一个简单的程序来检查IL是如何工作的:C# IL&;net中的堆栈实现?,c#,.net,compiler-construction,il,C#,.net,Compiler Construction,Il,我编写了一个简单的程序来检查IL是如何工作的: void Main() { int a=5; int b=6; if (a<b) Console.Write("333"); Console.ReadLine(); } 我试图了解实施的效率: 在第1行(IL代码),它将值5推送到堆栈上(4个字节,即int32) 在第2行(IL代码),它从堆栈中弹出到一个局部变量中 接下来的两行也是如此 然后,它将这些局部变量加载到堆栈中,然后计算bge.s 问题1 他为什么要将局部变量加载到
void Main()
{
int a=5;
int b=6;
if (a<b) Console.Write("333");
Console.ReadLine();
}
我试图了解实施的效率:
- 在第1行(IL代码),它将值5推送到堆栈上(4个字节,即int32)
- 在第2行(IL代码),它从堆栈中弹出到一个局部变量中
bge.s
问题1
他为什么要将局部变量加载到堆栈中?这些值已在堆栈中。但是他把它们放在一个局部变量中。这不是浪费吗
我是说,为什么代码不能是这样的:
IL_0000: ldc.i4.5
IL_0001: ldc.i4.6
IL_0002: bge.s IL_0004
IL_0003: ldstr "333"
IL_0004: call System.Console.Write
IL_0005: call System.Console.ReadLine
我的代码示例只有5行代码。5000万行代码怎么样?IL将发出大量额外的代码
问题2
查看代码地址:
- IL_0009地址在哪里?它不应该是连续的吗
p、 我可以轻松回答第二个问题。指令长度可变。例如,
ldstr“333”
由ldstr
(地址8
)的操作码和表示字符串的数据(用户字符串表中的字符串引用)组成
与之后的call
语句类似,您需要call
操作码本身以及有关要调用的函数的信息
将小值(如4或6)推送到堆栈上的指令没有额外数据的原因是,这些值被编码到操作码本身中
有关说明和编码,请参阅
关于第一个问题,您可能想看看,其中说明:
/optimize标志不会改变大量的发射和生成逻辑。我们总是试图生成直接的、可验证的代码,然后在抖动生成真正的机器代码时依靠抖动来完成繁重的优化工作
在这个层次上,关于IL效率的推理是没有意义的 JIT将完全消除堆栈,将所有堆栈操作转换为中间三地址代码(并进一步转换为SSA)。由于IL从不被解释,堆栈操作不应该是有效的和优化的 例如,请参见开源Mono实现 他为什么要将局部变量加载到堆栈中?这些值已在堆栈中。但是他把它们放在一个局部变量中。这不是浪费吗 浪费什么?您必须记住,IL(通常)不是按原样执行的,它是由执行大多数优化的JIT编译器再次编译的。使用“中间语言”的要点之一是可以在一个地方实现优化:JIT编译器和每种语言(C#,VB.NET,F#,…)都不必重新实现它们。Eric Lippert在他的文章中对此进行了解释 IL_0009地址在哪里?它不应该是连续的吗 让我们看一下
ldstr
指令的规范(来自):
三、 4.16ldstr
–加载文本字符串
格式:72[…]
ldstr
指令推送一个新的string对象,该对象将元数据中存储的文本表示为string(字符串文本)
上面对元数据的引用,意味着指令的字节72
后跟一个元数据标记,该标记指向一个包含字符串的表。这种代币有多大?同一文件第III.1.9节:
许多CIL指令后面都有一个“元数据令牌”。这是一个4字节的值,用于指定元数据表中的一行[…]
因此,在您的例子中,指令的字节
72
位于地址0008,而令牌(在本例中为0x7000001,其中0x70字节表示用户字符串表)位于地址0009到000C。给出关于“额外代码”的所有讨论的最终答案
C#编译器读取inta=5代码>并将其转换为:
ldc.i4.5
stloc.0
然后它转到下一行并读取intb=6代码>并转换为:
ldc.i4.6
stloc.1
然后用if语句读取下一行,依此类推
当从C#编译到IL时,它逐行读取并将该行转换为IL,而不是在查看其他行时将该行转换为IL
在这个阶段,为了优化IL并删除“额外代码”(您称之为“额外代码”),C#编译器必须检查所有IL代码,构建它的树表示,删除所有不需要的节点,然后再次将其作为IL写入。这不是C#编译器应该做的事情,因为从IL到机器语言时,这将由JIT编译器完成
因此,您看到的额外代码不是额外代码,它是C编译器从您的C代码中读取的语句的一部分,当JIT编译器将代码编译为本机可执行文件时,它将被删除
这是一个关于C代码如何翻译的高级解释,因为我认为您没有在编译器构造或类似的东西中使用过任何类。
如果你想知道更多,互联网上有很多书和网页可供阅读。这是调试还是优化编译器?@spender release mode+optimize on对于问题1,它完全是在编译你的代码,但我认为真正的问题是,为什么优化器不去掉你的局部变量“a”和“b”,而只是将其编译成If(5<6).
或者,既然它总是真的,为什么不一起去掉条件呢?
?也许优化器就没有那么好。你说推理没有意义是什么意思。我想看看和学习事情是如何运作的。我想
ldc.i4.6
stloc.1