摘要
微软#8217;。NET Framework是一个新的平台,旨在简化 分布式应用程序的发展。位于底部的。NET的 CLR中,所谓的公共语言运行库。 CLR提供了一个环境 控制。NET代码,如内存管理,线程执行,代码, 检查,安全服务和垃圾收集。 CLR还实行了严格的 基础设施要求检查代码中旅(普通型子系统) 可以安全地使用和类型,即在一种语言中创建的类型,并提到 在另一种语言。托管执行环境解决某些问题 与传统的本机代码的执行。 CLR会自动处理 安排的对象和它们的引用,释放他们时,他们没有 不再需要,从而避免无效引用和内存泄漏。 CLR会
不依赖于本机x86指令集。相反,微软已经创建
; 支持所有的编译器生成的中间语言(IL)
60;。NET平台。微软中间语言(MSIL)是一个处理器 独立的指令集,包括执行控制指令, 直接内存访问,异常处理,逻辑和算术运算,如 以及装卸,储存和对象初始化的说明。 " MSIL代码不能直接执行,它必须被转换成一个特定处理器 代码由一个JIT编译器(时间)。任何支持的体系结构必须 提供其特定的JIT编译器。
当我在写三角洲代码生成等等。NET编译器() 我发现,IL可以在一定程度上的改善。本文 建议加强为Microsoft中间语言。Microsoft中间语言
MSIL是从我们对传统的汇编语言的认识不同。 虽然赋与低级别的指令,语言也与 高层次的概念,如异常处理和内存管理。 " 白细胞介素也有一个虚拟的参数堆栈,可以容纳不同类型的对象, 堆栈的纪律是如字符串,整数,浮点数,指针等。 &
#160; 严格,这意味着在执行命令时,堆栈必须
持有所需的参数的确切人数。在另一侧,在x86
; 汇编语言允许在堆栈上参数的随机数,甚至
提供和清洗后留下一个程序的堆栈指令(见 RET ñ)。
的指示处理堆栈操作: 推值的LOAD指令(ldarg,ldloc,ldnull等) 入堆栈 关闭弹出值的存储指令(starg,stloc,stind等) 堆栈 复制栈顶元素的DUP指令#8211; POP指令#8211;删除栈顶元素
IL指令集的有关信息,在[2]看看。
虽然足以实现其目标,IL指令集似乎 导致低效的代码生成。在发展过程中 为第四。NET平台编译器,我发现多次 生成的代码是更长的时间比它应该是事实由于缺乏一些简单的 说明。为了使读者在理解的代码序列 遵循,这是适当的去了解一些堆栈处理原语 第四编程语言:
0;
第四原始
160;
堆栈过渡
说明
的DUP &
#160;
(#8211;大)
重复的顶级元素 堆栈
;
0;
(A B#8211; A B A)
复制第二个元素 栈顶
60;
以物易物
;
(A B#8211;乙一)
互换最上面的两个元素 在栈上 &
#160;
腐
0;
(A B C#8211; B C A)
旋转三个最顶层的元素 在栈上
;
1
;
(#8211; 1)
递增1的元素&
#160; 栈顶
0;
2
(#8211; 2)
递增2的元素
; 栈顶
栈过渡列显示前和执行后的堆栈状态 的本原。这两种状态是由破折号签署分隔。
解决在第四的数学表达式是相当复杂的程序员 使用传统的命令式语言,但是有#8217;没有什么特别的
0; 除了使用逆波兰表示法(RPN)。进一步阅读
160; 我建议[3]。
让#8217;例如,表达式:
(二)*(#8211;二):
后缀相当于是:
60;
A B A B -
第四原语序列,解决了表达(假设 A和B都已经在这个顺序栈上的值)是:
(A B#8211; A B A)
结束(A B#8211; A B A B)
(A B A B#8211; A B C),其中C = A B
0; ROT(A B C#8211; B C A)
ROT(C A#8211,C A B)
- (C A B#8211; C D),其中D = C#8211,B
*(C D#8211,E),其中E = C * D
了解上面的代码将帮助你理解IL代码片段 遵循。让#8217;的举动,并期待#8230; 引擎盖下
有趣的是,在编译程序的生成的IL代码 写在一个。NET支持的语言。我选择的语言是C#的这 似乎非常有前途的。为了下面你重复实验需要Visual Studio NET和。NET Framework的IL反汇编。
进行测试的类,我们将使用非常简单,很容易理解 即使是初学者:
class UnderTheHood
{
private static int[] x = new int [] {1, 2, 3};
private static int i = 0, j = 0, t = 0;
static void Main(string[] args)
{
// Here we will insert code snippets
}
}
让#8217;采取的第一个例子:
X [I 1 = I 2;
功能主要由编译器生成的代码是:{C}
第一行和第二行推栈上的静态值 变量x和我分别。我的价值得到增加(推 1,执行加法)。增量操作(这2次) 重复行IL_000c和IL_0012。最终结果是存储 在其目的地行IL_0013。
乍一看,我们看到的代码序列计算两个索引I 1 I 2分别。如果我们还记得从以前的1第四原始 段,我们看到,使用它会导致更大的效益,特别是 因为大多数现代处理器INC和DEC指令。翻译从 本机代码的IL是显而易见的。修改后的顺序可能看起来像 如下: IL_0000: ldsfld int32[] CallTest.UnderTheHood::x
IL_0005: ldsfld int32 CallTest.UnderTheHood::i
IL_000a: ldc.i4.1
IL_000b: add
IL_000c: dup
IL_000d: inc.1 // Non-existent mnemonic, see text
IL_000e: stelem.i4
IL_000f: ret
我选择的助记符inc.1,这意味着堆栈顶部的值递增
;1。同样,我们可以写inc.2,12月1日,12月2日的其他业务。 " &
#160; 增益是7个字节。
让#8217;重做实验,这一次与下列顺序:
X [我1] = 1;
在这种情况下,IL顺序是:.locals ( [0] int32[] CS$00000002$00000000,
[1] int32 CS$00000002$00000001)
IL_0000: ldsfld int32[] CallTest.UnderTheHood::x
IL_0005: dup
IL_0006: stloc.0
IL_0007: ldsfld int32 CallTest.UnderTheHood::i
IL_000c: ldc.i4.1
IL_000d: add
IL_000e: dup
IL_000f: stloc.1
IL_0010: ldloc.0
IL_0011: ldloc.1
IL_0012: ldelem.i4
IL_0013: ldc.i4.1
IL_0014: add
IL_0015: stelem.i4
IL_0016: ret
我们可以看到,编译器自动生成了两个匿名当地 变量的值保持为X(线IL_0006),我1(行IL_000f)。 这个序列,就可以大大优化使用过第四原始, 复制堆栈上的第二个最顶端的元素。重写的IL 代码看起来像这样:IL_0000: ldsfld int32[] CallTest.UnderTheHood::x
IL_0005: dup
IL_0006: stloc.0
IL_0007: ldsfld int32 CallTest.UnderTheHood::i
IL_000c: inc.1 // Non-existent mnemonic, see text
IL_000d: over // Non-existent mnemonic, see text
IL_000e: over
IL_000f: ldelem.i4
IL_0010: ldc.i4.1
IL_0011: add
IL_0012: stelem.i4
IL_0013: ret
的收益是6个字节。
一个,而通常的顺序是交换两个变量的发现的元素。在C# 程序看起来像这样:
等效的IL代码是:t = i;
i = j;
j = t;
IL_0000: ldsfld int32 CallTest.UnderTheHood::i
IL_0005: stsfld int32 CallTest.UnderTheHood::t
IL_000a: ldsfld int32 CallTest.UnderTheHood::j
IL_000f: stsfld int32 CallTest.UnderTheHood::i
IL_0014: ldsfld int32 CallTest.UnderTheHood::t
IL_0019: stsfld int32 CallTest.UnderTheHood::j
IL_001e: ret
注意,IL顺序如下C#程序的紧密合作。这将是有趣 如果高层次的语言本身提供的程序员交换指令, 所以,前面的代码片段可以改写如下:IL_0000: ldsfld int32 CallTest.UnderTheHood::i
IL_0005: ldsfld int32 CallTest.UnderTheHood::j
IL_000a: swap // Non-existent mnemonic, see text
IL_000f: stsfld int32 CallTest.UnderTheHood::j
IL_0014: stsfld int32 CallTest.UnderTheHood::i
IL_0019: ret
获得9个字节,不考虑节省空间 不宣布一个中间的临时变量(T)。该代码可以进一步 完全取代了交换操作的细化:
结论 IL_0000: ldsfld int32 CallTest.UnderTheHood::i
IL_0005: ldsfld int32 CallTest.UnderTheHood::j
IL_000a: stsfld int32 CallTest.UnderTheHood::i
IL_000f: stsfld int32 CallTest.UnderTheHood::j
IL_0014: ret
即使在乍看之下增益是空间小,在现实中的事情 略有不同。这样的代码序列在每一天的比较频繁 应用。如果我们认为,一个中等规模的应用程序可能包含约 这种优化的结构和200个中等增益大约是8个字节
; 每构造,我们节省代码空间,这不是可以忽略不计为嵌入式1.6KB 系统。
空间增益是不是唯一的参数,在使用新的IL指令的青睐。 由JIT生成的本地代码可能会受益于使用这些指令 更高效的转换,因为一些专用处理器指令 可以使用。
160; 传记
[1]的Microsoft Developer Network,
[2]通用语言文字基础设施,三分区(CIL的指令集)
[3]所列的表达式,Valer BOCAN,净 报告显示,2002年1月(罗马尼亚)
[4]三角洲等等。NET开发系统,(三)1997-2002年Valer BOCAN,