Emit学习(2) - IL - 常用指令介绍
学习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 - 常用指令介绍的更多相关文章
- corosync+pacemaker的crmsh的常用指令介绍
配置crmsh的yum仓库,此仓库的RPM包有openSUSE提供,将这个network:ha-clustering:Stable.repo文件直接下载到本地并且命名为crmsh.repo wget ...
- Nginx学习系列四默认负载均衡轮询及Ip_hash等常用指令介绍
一.简介 Upstream模块是Nginx中一个核心模块,当客户端访问Nginx服务器的时候,Nginx会从服务器列表中选取压力小的服务器,然后分配给客户端进行访问.这个过程,Nginx通过轮询算法轮 ...
- 新人成长之入门Vue.js常用指令介绍(一)
写在前面 作为一个刚步入职场工作的新人,对于公司中所用的技术和框架基本上不懂,只能从最基础的开始做起,进入公司接触的第一个框架就是前端框架Vue.js,几个功能做下来,觉得Vue.js首先学习起来真的 ...
- docker创建image方法以及常用指令介绍
docker -help # 显示帮助 docker COMMAND -help # 帮助信息更详细 docker start “容器名称” # 启动一个或多个容器 docker s ...
- linux学习(四)-----linux常用指令
touch 指令 touch 指令创建空文件 基本语法 touch 文件名称 应用实例 案例 1: 创建一个空文件 hello.txt cp 指令 cp 指令拷贝文件到指定目录 基本语法 cp [选项 ...
- Docker学习笔记_Dockerfile常用指令
Dockerfile常用指令
- Emit学习(2) - IL - 值类型和引用类型(补)
上周末回家去享受生活了, 工作是为了更好的生活嘛, 所以我把生活, 工作分的比较开. 这几天不是很忙, 在学习工作技能的同时, 发点博文, 也算是做一个学习笔记 上篇中, 贴出的地址里面那位哥, 也有 ...
- Emit学习(2) - IL - 对象的创建过程
上一篇的介绍中, 并没有介绍到对象的创建过程, 这一篇主要就介绍一下, 对象的创建过程. 其实熟悉了IL语法之后, 完全可以用Reflector反编译代码去查看. 而且正因为有这个工具, 可以对照着R ...
- 学习笔记_ADB常用指令
ADB 查看连接到计算机的Android设备或模拟器 adb devices 说明: 正常显示状态应该是IP:Port State. State=device说明设备已经连接到计算机, State=o ...
随机推荐
- 一个App完成入门篇(五)- 完成新闻页面
本节教程将介绍如何用DeviceOne简单而高效的完成一个新闻页面. 导入项目 数据模板分离MVVM模型 自定义事件 展示新闻 九宫格展示 将要学习的demo效果图如下所示 1. 导入完整项目 本节示 ...
- Homework 1 -- The beginning
我是在北京在读的一位大学生.如果问我学的什么专业,我会用一个冷笑话回答你:我精通多种语言,在老家我说家乡话:跟北京我讲普通话:跟老外就玩English:我跟机器得敲代码.现在你知道我学的就是计算机了. ...
- 在MVVM模式中,按钮Click事件的绑定方法
在MVVM模式中,我们将Button的方法写到ViewModel中,然后绑定到前端界面.通常的做法是写一个类,继承ICommand接口,然而如果按钮比较多的话,就需要写很多的类,对于后期维护造成很大的 ...
- 根据BOM和已存在的文件生成文件列表
在BOM中记录中有物料编码,物料名称,物料规格等,而且依据BOM已经生成了相应的文件,如采购规格书,检验规格书等,这个时候需要获得这些文件的标题,并且生成一个列表,可以使用下面的VBA代码,具体代码如 ...
- [异常解决] 安卓6.0权限问题导致老蓝牙程序出现异常解决办法:Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission...
一.问题: 之前写的一款安卓4.4的应用程序,用来连接蓝牙BLE,而现在拿出来用新的AS编译(此时SDK为6.0,手机也是6.0)应用程序并不能搜索到蓝牙,查看log总是报权限错误: Need ACC ...
- 使用后缀数组寻找最长公共子字符串JavaScript版
后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论. 本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用. 首先需要说明,小菜实现的这个后缀数组算法,并非标 ...
- mac命令
mac下卸载nodesudo rm -rf /usr/local/{bin/{node,npm},lib/node_modules/npm,lib/node,share/man/*/node.*}xc ...
- IOS 推送-客户端处理推送消息
IOS 推送-客户端处理推送消息 1.推送调用顺序 APN push的消息到达后,UIApplicationDelegate有两个方法和处理消息有关: 1)application:didReceive ...
- EF架构~扩展一个分页处理大数据的方法
回到目录 最近总遇到大数据的问题,一次性处理几千万数据不实际,所以,我们需要对大数据进行分块处理,或者叫分页处理,我在EF架构里曾经写过类似的,那是在进行BulkInsert时,对大数据批量插入时候用 ...
- 《轻量级Java Web整合开发入门SSH》 - 快速理解Java框架的又一积木
学习JAVA不难,难的是没有多余的时间给你仔细学习. 伴随着项目的不断跟进,责任重于泰山,必须快速提升. 我不能期望把一本书或者一个项目完全吃透,只希望能用数量去 ...