《Inside Microsoft IL Assembler》学习笔记2:让IL代码简短些
还是学习笔记1中的那个例子,可以改成如下的样子,功能不变,但运行时占用的资源会小一些。我在所有和笔记1中代码有了变化的位置加上了注释,如下:
1
//----------- Program header
2
.assembly extern mscorlib
{ }
3
.assembly OddOrEven
{ }
4
.module OddOrEven.exe
5
//----------- Class declaration
6
.namespace Odd.or
{
7
.class public auto ansi Even extends [mscorlib]System.Object
{
8
//----------- Field declaration
9
.field public static int32 val
10
//----------- Method declaration
11
.method public static void check( ) cil managed
{
12
.entrypoint
13
.locals init (int32 Retval)
14
AskForNumber:
15
ldstr "Enter a number"
16
call void [mscorlib]System.Console::WriteLine(string)
17
call string [mscorlib]System.Console::ReadLine()
18
ldstr "%d" // 所有的字符串常量都不需要特殊的声明,il汇编器看到这样的字符串之后会自动将其加入到元数据中
19
ldsflda int32 Odd.or.Even::val
20
call vararg int32 sscanf(string,string,
,int32*)
21
stloc.0 // java的虚机和.net clr都使用0,1,2,3这样的编号来表示本地变量。这里的"0"就指向唯一的一个本地变量Retval
22
ldloc.0 // 将第一个本地变量的值压栈
23
brfalse.s Error // brfalse.s指令是brfalse的简化指令。当指令被编译成操作码之后,brfalse接受的是一个4字节的参数,
24
//而brfalse.s仅接受一个单字节的参数,这意味着只能在代码段的{-128字节~~+128字节}的范围内(相对于当前位置而言)跳转。
25
ldsfld int32 Odd.or.Even::val
26
ldc.i4.1 // 向栈顶压入一个单字节常量,值为1(ldc.i4 1也是向栈顶压入1,但要用4个字节)
27
and
28
brfalse.s ItsEven
29
ldstr "odd!"
30
br.s PrintAndReturn
31
ItsEven:
32
ldstr "even!"
33
br.s PrintAndReturn
34
Error:
35
ldstr "How rude!"
36
PrintAndReturn:
37
call void [mscorlib]System.Console::WriteLine(string)
38
ldloc.0
39
brtrue.s AskForNumber
40
ret
41
} // End of method
42
} // End of class
43
} // End of namespace
44
//----------- Calling unmanaged code
45
.method public static pinvokeimpl("msvcrt.dll" cdecl)
46
vararg int32 sscanf(string,string) cil managed
{ }
47
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
另外,提起上文中的ldloc.0这样的指令,笔者一开始也是未明其意。其实,在clr执行程序的过程中,涉及到的内存使用主要有三类,除了我们常说的托管堆,每个线程都有自己专属的两个栈:计算栈(Evaluation Stack)和调用栈(Call Stack)。这两个栈都经常被提到,又都专属于clr线程,所以有的时候会混,其实它们的功能是完全不同的。调用栈就是最传统的用来记录方法调用次序的栈,每个线程会得到1MB的调用栈空间,每调用一个新的方法都会压栈,传入参数、临时变量等都会压入栈中,待方法执行完毕就完全释放。ldloc.0中的"0",所指的就是当前调用栈上的第一个临时变量。而计算栈在某种程度上可以视为clr虚拟机的寄存器(这么说只是因为它是和虚机上的计算功能直接交互的存储单位),我们常说的clr虚机是完全基于“栈”的,指的就是计算栈。虚机上的所有操作,都要从计算栈的参数,结果也都会压入到计算栈顶。计算栈的单位既不是字,也不是字节,而是slot(怎么翻译合适呢,咳咳),根据slot内所装数据类型的不同,slot的大小也不同。当clr栈顶的slot里取到数据之后,在运算之前会检验数据的类型,如果无法转换成运算期望的类型,会抛出异常。蔡学庸在msdn上有篇文章,里面的一个贴图看着很明白,就在这里借用一下吧:
转载于:https://www.cnblogs.com/xingyukun/archive/2007/07/10/813102.html