MSIL代码和机器代码比较(.NET)

时间:2020-02-28 16:38:28

标签: .net cil

将MSIL代码编译到某些特定计算机时,有哪些简化方法?我以前认为机器代码没有基于堆栈的操作,并且MSIL中所有基于堆栈的操作都转换为具有所需推入/弹出堆栈结果的大量数据移动操作,因此机器代码通常比MSIL长得多码。但这似乎并非如此,所以让我感到奇怪的是-机器代码与MSIL代码有何不同?在哪些方面?

我希望从不同的角度对两者进行比较,例如: 操作/指令的数量有何不同?机器代码通常会有更多的行吗? 除了平台独立性(至少在cpu架构独立性和基于Windows的平台独立性方面),元数据样式的代码以及众多高级编程语言的某种“通用基础”语言之外,中间/ MSIL还可以做其他事情代码允许吗?如果将一些MSIL代码和相应的机器代码进行比较,最明显的区别是什么?

我真的很希望能进行高水平的比较,但也许会举一些简单而具体的例子。

1 个答案:

答案 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指令callvirtx86-64中没有简单的对应物:您需要多条指令来执行该调用。

机器代码通常有很多行吗?

我必须比较硬数据;但是,按照上述有关指令复杂性的规定,我会说是的。

中间/ MSIL代码还允许平台独立性和元数据样式代码之外的其他功能吗?

我认为问题应该是:机器代码还允许什么? MSIL具有相当的限制性。 CLR定义了许多规则,有助于维护MSIL代码的一致性和正确性。在机器代码中,您拥有完全的自由,而且您也可以完全弄乱一切。

如果将一些MSIL代码和相应的机器代码进行比较,最明显的区别是什么?

在我看来,它是基于寄存器的CPU体系结构,例如x86-64

除了这些功能之外,MSIL还有哪些其他功能? MSIL语言有哪些自然结构/特征可以使某些事情变得更容易?

事实上,有很多。首先,作为基于堆栈的体系结构,将.NET编程语言编译成MSIL更加容易,正如我之前解释的那样。然后还有许多其他较小的东西,例如:

  • MSIL自然可以理解所有原始CLR(.NET)数据类型
  • MSIL可以表达类型转换
  • MSIL能够理解对象(类型的实例),可以分配实例(newobj),包括虚拟方法调用的调用方法(非常重要)
  • 手工编写MSIL的语法支持面向对象的代码结构,即MSIL支持表达高级OO概念
  • MSIL提供对装箱/拆箱的支持
  • MSIL支持引发和捕获异常(这也很重要)
  • MSIL具有基于互斥锁的同步(锁定)的说明