学习Emit必不可少的, 会使用到IL中间代码. 初见IL代码, 让我有一种汇编的感觉, 让我想起了, 大学时, 学习8051的汇编语言. 多的就不扯了, 直接进入正题, OpCodes指令集是不是有一种让人望而却步的感觉, 那么多, 具体我没有数过, 但是肯定是比8051的指令多不少, 应该有200多个吧, 不过在实际使用的过程中, 肯定是用不到这么多的, 所以只要掌握一些常用的就够用了, 其余的, 查资料就可以了(大学老师当时也是这么教的, 实际使用中, 也确实是这样的)

一、示例

上一篇的结束部分, 贴出了一个中文注释版的OpCodes文件, 这部分内容跟那个文件是有很大关联的. 貌似, 贴在这一篇更合适呢...

嗯, 还是应该从示例里去开始讲

来源 : http://www.cnblogs.com/zery/p/3366175.html

static void Sum(int sum, string sumStr)
{
int a, b, c;
a = ;
b = ;
c = ;
sum = a + b + c;
sumStr = sum.ToString();
Console.WriteLine(sumStr); for (int i = ; i < c; i++)
{
if (i > b)
{
Console.WriteLine("满足条件, 跳出循环");
break;
}
}
Console.ReadKey();
}
.method private hidebysig static void Sum(int32 sum, string sumStr) cil managed
{
.maxstack //定义函数代码所用堆栈的最大深度,也指Evaluation Stackk中最多能同时存在2个值
.locals init ( //变量的声明, (此时已经把num,num2,num3,num4,flag存入了Call Stack中的Record Frame中)
[] int32 num,
[] int32 num2,
[] int32 num3,
[] int32 num4,
[] bool flag) L_0000: nop //无任何操作, 可忽略
L_0001: ldc.i4. //加载 常量1 到栈中(压栈)
L_0002: stloc. //从栈中把 常量1 拿出来, 赋值给num(出栈, 此时栈中已经没有东西了)
L_0003: ldc.i4. //加载 常量2 到栈中(压栈)
L_0004: stloc.
L_0005: ldc.i4.
L_0006: stloc. L_0007: ldloc. //将num变量压栈
L_0008: ldloc. //将变量num2压栈 (此时栈中有两个值, num2在上面, num在下面)
L_0009: add //将num,num2求和的结果压栈(求和的时候, 会把两个值都提取出来, 所以结束后, 栈中只有一个结果值)
L_000a: ldloc. //将num3压栈
L_000b: add //将num3,(num+num2)求和, 并压栈, 此时栈中, 只有最后的结果值
L_000c: starg.s sum //将栈顶的值传给传参sum(短格式) L_000e: ldarga.s sum //加载sum的地址到堆栈上(短格式)
L_0010: call instance string [mscorlib]System.Int32::ToString() //调用ToString()方法, 完成格式转换,将结果值放入堆栈中
L_0015: starg.s sumStr //将堆栈顶的值传给传参sumStr(短格式) L_0017: ldarg. //将索引为1的传参(sumStr)加载到堆栈中
L_0018: call void [mscorlib]System.Console::WriteLine(string) //调用Console.WriteLine方法 L_001d: nop
L_001e: ldc.i4.
L_001f: stloc. // i = 0
L_0020: br.s L_0043 //无条件跳转到下面, 去判断 i<c 是否成立 L_0022: nop
L_0023: ldloc. // i
L_0024: ldloc. // b
L_0025: cgt // i > b ? 1 : 0
L_0027: ldc.i4. //压栈0
L_0028: ceq //比较的结果在与0比较, (i > b ? 1 : 0) == 0 ? 1 : 0
L_002a: stloc.s flag //将结果存入本地变量flag
L_002c: ldloc.s flag //加载flag到堆栈中
L_002e: brtrue.s L_003e //为真跳转到 L_003e L_0030: nop
L_0031: ldstr "\u6ee1\u8db3\u6761\u4ef6, \u8df3\u51fa\u5faa\u73af" //"满足条件, 跳出循环"
L_0036: call void [mscorlib]System.Console::WriteLine(string)
L_003b: nop
L_003c: br.s L_004d L_003e: nop
L_003f: ldloc. // i
L_0040: ldc.i4. //
L_0041: add // i + 1
L_0042: stloc. // i = i + 1 L_0043: ldloc. // i
L_0044: ldloc. //c
L_0045: clt // i < c ? 1 : 0
L_0047: stloc.s flag //将结果传给 flag
L_0049: ldloc.s flag //加载flag变量到堆栈中
L_004b: brtrue.s L_0022 //为真跳转 L_0022 L_004d: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
L_0052: pop //移除当前位于计算堆栈顶部的值
L_0053: ret //即为 return 标记 返回值
}

ldc.i4.1:  i4--int32, 1--数值, 合起来就是  加载int32的数值1到堆栈中

stloc.0: 0--前面声明的locals变量组中的第0个  将堆栈顶的值付给locals0变量

ldloc.0: 加载locals0变量到堆栈中

add : 将栈顶的两个值求和, 并将结果压栈

二、常用的指令

维基百科:https://en.wikipedia.org/wiki/List_of_CIL_instructions

来源:http://blog.csdn.net/joyhen/article/details/47276433

1. 常用的加载类指令

ldarg (及多个变化形式)

ld -- load , arg -- argument, 对这个大家都不陌生吧, 就不多解释了

加载方法的参数的值到栈中。除了泛型ldarg(需要一个索引作为参数),还有后其他很多的变化形式。'.'有个数字后缀的ldarg操作码来指定需要加载的参数。

a -- address, s -- short

ldarga/ldarga.s表示的是加载参数的地址, 而不是加载参数的值

ldc (及多个变化形式)

c -- constant, const这个关键字大家肯定都很熟了, constant表示常量

加载一个常数到栈中

Ldc.I4.2   i4表示是int32的值(1个表示8位), 2表示常量

ldfld (及多个变化形式) 加载一个对象实例的成员到栈中
ldloc (及多个变化形式)

loc -- locals

加载一个本地变量到栈中

ldobj 获得一个堆对象的所有数据,并将它们放置到栈中. OpCodes:将地址指向的值类型对象复制到计算堆栈的顶部。
ldstr 加载一个字符串数据到栈中

2. 常用的弹出操作指令

pop  删除当前栈顶的值,但是并不影响存储的值
starg

st -- store

存储栈顶的值到给出方法的参数,根据索引确定这个参数. OpCodes:将位于计算堆栈顶部的值存储到位于指定索引的参数槽中

stloc (及多个变化形式) 弹出当前栈顶的值并存储在一个本地变量列表中,根据所以确定这个参数
stobj 从栈中复制一个特定的类型到指定的内存地址
stfld 用从栈中获得的值替换对象成员的值

3. 常用的其他操作类指令

add, sub, mul, div, rem

用于两个数加减乘除求模, 并将结果推送到计算堆栈上

and, or, not, xor 用于在两个值上进行二进制操作
ceq, cgt, clt

用不同的方法比较两个在栈上的值

c -- compare

ceq:是否相等 -- 如果这两个值相等,则将整数值 1 (int32) 推送到计算堆栈上;否则,将 0 (int32) 推送到计算堆栈上

cgt:是否大于 -- 如果第一个值大于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。

cgt.un -- 比较两个无符号的或不可排序的值, un -- unsigned 无符号

clt:是否小于 -- 如果第一个值小于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。

box, unbox

在引用类型和值类型之间转换

box: 装箱

unbox: 拆箱

ret 退出方法和返回一个值
beq, bgt,bge,ble, blt, switch

控制方法中的条件分支

b -- break, eq,e -- equal

beq:如果两个值相等,则将控制转移到目标指令;

bgt:如果第一个值 > 第二个值,则将控制转移到目标指令

bge:如果第一个值 >= 第二个值,则将控制转移到目标指令

ble:如果第一个值 <= 第二个值,则将控制转移到目标指令

blt:如果第一个值 < 第二个值,则将控制转移到目标指令

switch:实现跳转表

所有的分支控制操作码都需要给出一个CIL代码标签作为条件为真的跳转目的地

brtrue

如果 value 为 true、非空或非零,则将控制转移到目标指令

br/br.s

br:(无条件)中止到代码标签

br.s:无条件地将控制转移到目标指令(短格式)

call 调用一个成员
newarr, newobj

在内存中创建一个新的数组或新的对象类型

newarr:将对新的从零开始的一维数组(其元素属于特定类型)的对象引用推送到计算堆栈上

newobj:创建一个值类型的新对象或新实例,并将对象引用(O 类型)推送到计算堆栈上

未完待续......

Emit学习(2) - IL - 常用指令介绍的更多相关文章

  1. corosync+pacemaker的crmsh的常用指令介绍

    配置crmsh的yum仓库,此仓库的RPM包有openSUSE提供,将这个network:ha-clustering:Stable.repo文件直接下载到本地并且命名为crmsh.repo wget ...

  2. Nginx学习系列四默认负载均衡轮询及Ip_hash等常用指令介绍

    一.简介 Upstream模块是Nginx中一个核心模块,当客户端访问Nginx服务器的时候,Nginx会从服务器列表中选取压力小的服务器,然后分配给客户端进行访问.这个过程,Nginx通过轮询算法轮 ...

  3. 新人成长之入门Vue.js常用指令介绍(一)

    写在前面 作为一个刚步入职场工作的新人,对于公司中所用的技术和框架基本上不懂,只能从最基础的开始做起,进入公司接触的第一个框架就是前端框架Vue.js,几个功能做下来,觉得Vue.js首先学习起来真的 ...

  4. docker创建image方法以及常用指令介绍

    docker -help    # 显示帮助 docker COMMAND -help    # 帮助信息更详细 docker start “容器名称”    # 启动一个或多个容器 docker s ...

  5. linux学习(四)-----linux常用指令

    touch 指令 touch 指令创建空文件 基本语法 touch 文件名称 应用实例 案例 1: 创建一个空文件 hello.txt cp 指令 cp 指令拷贝文件到指定目录 基本语法 cp [选项 ...

  6. Docker学习笔记_Dockerfile常用指令

    Dockerfile常用指令

  7. Emit学习(2) - IL - 值类型和引用类型(补)

    上周末回家去享受生活了, 工作是为了更好的生活嘛, 所以我把生活, 工作分的比较开. 这几天不是很忙, 在学习工作技能的同时, 发点博文, 也算是做一个学习笔记 上篇中, 贴出的地址里面那位哥, 也有 ...

  8. Emit学习(2) - IL - 对象的创建过程

    上一篇的介绍中, 并没有介绍到对象的创建过程, 这一篇主要就介绍一下, 对象的创建过程. 其实熟悉了IL语法之后, 完全可以用Reflector反编译代码去查看. 而且正因为有这个工具, 可以对照着R ...

  9. 学习笔记_ADB常用指令

    ADB 查看连接到计算机的Android设备或模拟器 adb devices 说明: 正常显示状态应该是IP:Port State. State=device说明设备已经连接到计算机, State=o ...

随机推荐

  1. 一个App完成入门篇(五)- 完成新闻页面

    本节教程将介绍如何用DeviceOne简单而高效的完成一个新闻页面. 导入项目 数据模板分离MVVM模型 自定义事件 展示新闻 九宫格展示 将要学习的demo效果图如下所示 1. 导入完整项目 本节示 ...

  2. Homework 1 -- The beginning

    我是在北京在读的一位大学生.如果问我学的什么专业,我会用一个冷笑话回答你:我精通多种语言,在老家我说家乡话:跟北京我讲普通话:跟老外就玩English:我跟机器得敲代码.现在你知道我学的就是计算机了. ...

  3. 在MVVM模式中,按钮Click事件的绑定方法

    在MVVM模式中,我们将Button的方法写到ViewModel中,然后绑定到前端界面.通常的做法是写一个类,继承ICommand接口,然而如果按钮比较多的话,就需要写很多的类,对于后期维护造成很大的 ...

  4. 根据BOM和已存在的文件生成文件列表

    在BOM中记录中有物料编码,物料名称,物料规格等,而且依据BOM已经生成了相应的文件,如采购规格书,检验规格书等,这个时候需要获得这些文件的标题,并且生成一个列表,可以使用下面的VBA代码,具体代码如 ...

  5. [异常解决] 安卓6.0权限问题导致老蓝牙程序出现异常解决办法:Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission...

    一.问题: 之前写的一款安卓4.4的应用程序,用来连接蓝牙BLE,而现在拿出来用新的AS编译(此时SDK为6.0,手机也是6.0)应用程序并不能搜索到蓝牙,查看log总是报权限错误: Need ACC ...

  6. 使用后缀数组寻找最长公共子字符串JavaScript版

    后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论. 本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用. 首先需要说明,小菜实现的这个后缀数组算法,并非标 ...

  7. mac命令

    mac下卸载nodesudo rm -rf /usr/local/{bin/{node,npm},lib/node_modules/npm,lib/node,share/man/*/node.*}xc ...

  8. IOS 推送-客户端处理推送消息

    IOS 推送-客户端处理推送消息 1.推送调用顺序 APN push的消息到达后,UIApplicationDelegate有两个方法和处理消息有关: 1)application:didReceive ...

  9. EF架构~扩展一个分页处理大数据的方法

    回到目录 最近总遇到大数据的问题,一次性处理几千万数据不实际,所以,我们需要对大数据进行分块处理,或者叫分页处理,我在EF架构里曾经写过类似的,那是在进行BulkInsert时,对大数据批量插入时候用 ...

  10. 《轻量级Java Web整合开发入门SSH》 - 快速理解Java框架的又一积木

           学习JAVA不难,难的是没有多余的时间给你仔细学习.       伴随着项目的不断跟进,责任重于泰山,必须快速提升.       我不能期望把一本书或者一个项目完全吃透,只希望能用数量去 ...