将MSIL代码编译到某些特定计算机时,有哪些简化方法?我以前认为机器代码没有基于堆栈的操作,并且MSIL中所有基于堆栈的操作都转换为具有所需推入/弹出堆栈结果的大量数据移动操作,因此机器代码通常比MSIL长得多码。但这似乎并非如此,所以让我感到奇怪的是-机器代码与MSIL代码有何不同?在哪些方面?
我希望从不同的角度对两者进行比较,例如: 操作/指令的数量有何不同?机器代码通常会有更多的行吗? 除了平台独立性(至少在cpu架构独立性和基于Windows的平台独立性方面),元数据样式的代码以及众多高级编程语言的某种“通用基础”语言之外,中间/ MSIL还可以做其他事情代码允许吗?如果将一些MSIL代码和相应的机器代码进行比较,最明显的区别是什么?
我真的很希望能进行高水平的比较,但也许会举一些简单而具体的例子。
答案 0 :(得分:4)
首先,让我们假设“机器代码”是指x86-64
指令集。对于ARM
之类的其他体系结构,某些方面可能会稍有不同。
将MSIL代码编译到某些特定计算机时,有哪些简化方法?
这些并不是真正的简化。 MSIL和典型的机器指令集(例如x86-64`)主要不同。
我以前认为机器代码没有基于堆栈的操作,并且MSIL中的所有基于堆栈的操作都被转换为具有所需的推入/弹出堆栈结果的大量数据移动操作,因此机器代码通常是远远不够的比MSIL代码长。
堆栈是每个CPU架构实际上都必需的核心概念(存在/曾经有一些CPU架构without a stack,但我认为这种情况很少见)。如果没有可用的堆栈,很多操作将变得不切实际。
但是:硬件CPU中的主要概念是寄存器。大多数计算和内存操作可以完全在寄存器中进行,而不是在计算机的主存储器中进行。将它们视为临时变量。此外,与使用主内存相比,它们的使用速度要快得多(即使介于两者之间的所有缓存级别也是如此)。
话虽这么说,虽然MSIL指令必须遵循纯粹的基于堆栈的方法来处理数据(MSIL中没有寄存器),但对于硬件CPU来说,则是必需的使用寄存器。因此,这导致两种 different 方法将相同的表达式转换为相应的机器代码。
但是事实并非如此,所以让我感到奇怪的是-机器代码与MSIL代码有何不同?在哪些方面?
让我们使用C#表达式:a = b + c * d;
,其中每个变量都是一个整数。
在MSIL中:
ldloc.1 // b — load from local variable slot 1
ldloc.2 // c — load from local variable slot 2
ldloc.3 // d — load from local variable slot 3
mul // multiple two top-most values, storing the result on the stack
add // add two top-most values, storing the result on the stack
stloc.0 // a — store top-most value to local variable slot 0
此概念的一大优势在于,为纯基于堆栈的机器代码编写代码生成器非常容易。
在x86-64
程序集中:
mov eax, dword ptr [c] // load c into register eax
mul dword ptr [d] // multiply eax (default argument) with d
add eax, dword ptr [b] // add b to eax
mov dword ptr [a], eax // store eax to a
如您所见,在这种简单情况下,x86-64
中没有堆栈。该代码看起来也更短,也许更具可读性。但是,生成真实的x86-64
机器代码是一项非常困难的任务。
免责声明:我是用辛苦编写的汇编代码片段;原谅我可能包含的错误。这些天来,我不是每天写汇编的日常工作:)
操作/指令的数量有何不同?
答案是:这取决于。一些简单的运算(例如算术运算)有时为1:1,例如MSIL中的add
可以在add
中产生单个x86-64
。另一方面,MSIL可以利用定义更多高级操作的优势。例如,调用虚拟方法的MSIL指令callvirt
在x86-64
中没有简单的对应物:您需要多条指令来执行该调用。
机器代码通常有很多行吗?
我必须比较硬数据;但是,按照上述有关指令复杂性的规定,我会说是的。
中间/ MSIL代码还允许平台独立性和元数据样式代码之外的其他功能吗?
我认为问题应该是:机器代码还允许什么? MSIL具有相当的限制性。 CLR定义了许多规则,有助于维护MSIL代码的一致性和正确性。在机器代码中,您拥有完全的自由,而且您也可以完全弄乱一切。
如果将一些MSIL代码和相应的机器代码进行比较,最明显的区别是什么?
在我看来,它是基于寄存器的CPU体系结构,例如x86-64
。
除了这些功能之外,MSIL还有哪些其他功能? MSIL语言有哪些自然结构/特征可以使某些事情变得更容易?
事实上,有很多。首先,作为基于堆栈的体系结构,将.NET编程语言编译成MSIL更加容易,正如我之前解释的那样。然后还有许多其他较小的东西,例如:
newobj
),包括虚拟方法调用的调用方法(非常重要)