常见方法
Call,Calli,Callvirt
-
Call:常用于调用编译时就确定的方法,可以直接去元数据里找方法,如静态函数,实例方法,也可以call虚方法,不过只是call这个类型本身的虚方法,和实例的方法性质一样。另外,call不做null检测。
-
Calli: MSDN上讲是间接调用指针指向的函数。
-
Callvirt: 可以调用实例方法和虚方法,调用虚方法时以多态方式调用,不能调用静态方法。Callvirt调用时会做null检测,如果实例是null,会抛出NullReferenceException,所以速度上比call慢点。
加载(Id)和存储(st)
Idloc.0:
把索引为0的局部变量加载到计算栈中。
Id:load;loc:Local variable(局部变量);.0:表示索引。
- ldstr = load string,
- ldnull = load null,
- ldobj = load object,
- ldfld = load field,
- ldflda = load field address,
- ldsfld = load static field,
- ldsflda = load static field address,
- ldelem = load element in array,
- ldarg = load argument,
ldc 则表示加载数值,如ldc.i4.0; .i[n]表示字节数,一个字节8位;i4四个字节就是int32类型;.0表示索引为0。
关于后缀
-
.i[n]:[n]表示字节数,1个字节是8位,所以是8*n的int,比如i1, i2, i4, i8,i1就是int8(byte), i2是int16(short),i4是int32(int),i8是int64(long)。相似的还有.u1 .u2 .u4 .u8 分别表示unsigned int8(byte), unsigned int16(short), unsigned int32(int), unsigned int64(long);
-
.R4,.R8 表示的是float和double。
-
.ovf (overflow)则表示会进行溢出检查,溢出时会抛出异常;
-
.un (unsigned)表示无符号数;
-
.ref (reference)表示引用;
-
.s (short)表示短格式,比如说正常的是用int32,加了.s的话就是用int8;
-
.[n] 比如 .1,.2 等,如果跟在i[n]后面则表示数值,其他都表示索引。如 ldc.i4.1就是加载数值1到计算栈上,再如ldarg.0就是加载第一个参数到计算栈上。
ldarg要特别注意一个问题:如果是实例方法的话ldarg.0加载的是本身,也就是this,ldarg.1加载的才是函数的第一个参数;如果是静态函数,ldarg.0就是第一个参数。
与Id对应的就是st,可以理解为store(储藏),意思是把值从计算栈存到变量中去, ld相关的指令很多都有st对应的,比如stloc, starg, stelem(存储数组中的元素)等,
比较跳转指令,判断bool值
以b开头:beq, bge, bgt, ble, blt, bne
先把b去掉看看:
- eq: equivalent with, == 等于
- ge: greater than or equivalent with , >= 大于等于
- gt: greater than , > 大于
- le: less than or equivalent with, <= 小于等于
- lt: less than, < 小于
- ne: not equivalent with, != 不等于
这样是不是很好理解了,beq IL_0005就是计算栈上两个值相等的话就跳转到IL_0005, ble IL_0023是第一个值小于或等于第二个值就跳转到IL_0023。
以br(break)开头:br, brfalse, brtrue,
- br是无条件跳转;
- brfalse表示计算栈上的值为 false/null/0 时发生跳转;
- brtrue表示计算栈上的值为 true/非空/非0 时发生跳转
还有一部分是c开头,算bool值的,和前面b开头的有点像:
- ceq 比较两个值,相等则将 1 (true) 推到栈上,否则就把 0 (false)推到栈上
- cgt 比较两个值,第一个大于第二个则将 1 (true) 推到栈上,否则就把 0 (false)推到栈上
- clt 比较两个值,第一个小于第二个则将 1 (true) 推到栈上,否则就把 0 (false)推到栈上