本次笔记内容:
09.控制流-2

文章目录

  • 练习题:条件转移指令局限性
  • 如何实现循环(Loops)
    • “Do-While”循环实例
    • “While”循环版本
      • “While”循环版本1
      • “While”循环版本2
    • “For” -> “While” -> “Do-While”
  • 为什么gcc历史上经历了多种转换模式?
    • 以“While”转换成“jump-to-middle”为例
    • 从微体系结构背景解释
  • Switch语句
    • Swith语句示例(x86-32)
    • x86-64下的Switch语句
    • Switch语句实例(case很稀疏)

练习题:条件转移指令局限性

int cread(int *xp) {return (xp ? *xp : 0);
}

空指针返回0,否则返回指针所指。

是否可以用如下汇编代码段完成?

# xp in register %edx
movl $0, %eax       # Set 0 as return value
testl %edx, %edx    # Test xp
cmovne (%edx), %eax # if !0, dereference xp to get return value

答:不可以,因为cmovne:

  • cmove先进行了地址计算;
  • 再决定最后选择谁。
  • 在这道题中,如果xp不等于0的话,就把(%edx)取出来,挪到%eax里面去,替换掉预先填在%eax中的0值;
  • 如果xp是空指针的话,虽然没有将(%edx)移动到%eax中,但是xp已经访问了一个0的地址,这是不对的。

下面是一种正确的操作:

先从c的思考方式入手:

int cread_alt(int *xp) {int t = 0;return *(xp ? xp : &t);
}

则对应的汇编代码如下:

movl $0 -4(%ebp)     # t = 0
movl 8(%ebp) %eax       # %eax = xp
leal -4(%ebp) %edx
testl %eax %eax
cmove %edx %eax
movl (%eax) %eax

如此,xp等于0时,则无需访问它。

如何实现循环(Loops)

  • 所有的循环模式(while, do-while, for)都转换为“do-while”形式,再转换为汇编形式;
  • 历史上gcc采用过多种转换模式,经历了“否定之否定”的过程:do-while -> Jump-to-middle -> do-while。

“Do-While”循环实例

int fact_do(int x) {int result = ;do {result *= x;x = x - 1;} while (x > 1);return result;
}

编译器首先将代码转换为Goto Version,方便转换成机器语言:

int fact_goto(int x) {int result = 1;loop:result *= x;x = x - 1;if (x > 1)goto loop;return result;
}

与汇编相对照:
Registers:
%edx x;
%eax result

fact_goto:pushl %ebp         # Setupmovl %esp, %ebp      # Setupmovl $1, %eax        # eax = 1movl 8(%ebp), %edx    # edx = xL11:imull %edx, %eax  # result *= xdecl %edx         # x--cmpl $1, %edx      # Compare X : 1jg L11               #if > goto loopmovl %ebp, %esp       # Finishpopl %ebp           # Finishret                 # Finish

“While”循环版本

“While”循环版本1
int fact_while(int x) {int result = 1;while (x > 1) {result *= x;x = x - 1;};return result;
}

Goto Version - 1

int fact_while_goto(int x) {int result = 1;loop:if (! (x > 1))goto done;result *= x;x = x - 1;goto loop;done:return result;
}
“While”循环版本2

目前gcc模式(4.0以后(不确定),以及早期gcc模式)如下:

Goto Version - 2

int fact_while_goto2(int x) {int result = 1;if (! (x > 1))goto done;loop:result *= x;x = x - 1;if (x > 1)goto loop;done:return result;
}

编译器转换成了do-while模式。

“For” -> “While” -> “Do-While”

对于for循环,也要转换为Goto Version,历程如下:

For Version -> While Version -> Do-While Version -> Goto Version

为什么gcc历史上经历了多种转换模式?

历史上gcc采用过多种转换模式,经历了“否定之否定”的过程:do-while -> Jump-to-middle -> do-while。

以“While”转换成“jump-to-middle”为例

int fact_while(int x) {int result = 1;while (x > 1) {result *= x;x = x - 1;};return result;
}

转换成Goto Version:

int fact_while_goto3(int x) {int result = 1;goto middle;loop:result *= x;x = x - 1;middle:if (x > 1)goto loop;return result;
}

Jump-to-Middle转换成汇编效率更高。

使用gcc 3.4.4 -O2对c代码进行编译:

# x in %edx, result in $eaxjmp L34               # goto Middle
L35:                    # Loop:imull %edx, %eax #   result *= xdecl %edx           #   x--
L34:                    # Middle:cmp $1, %edx       #   x : 1jg L35             #   if >, goto Loop

这么做的好处:

  • 避免了双重测试;
  • 无条件跳转指令处理器运行开销非常低(可以忽略)。

但是问题是:

  • 跳转指令的执行次数是无法改变的(由程序本身决定);
  • 因此从指令次数角度并没有节省什么。

从微体系结构背景解释

调转指令往往会引起一定的性能损失,Branch Prediction技术被引入进行优化。

在硬件中做了一张表,如果发现有跳转指令,使用PC记录其调转结果,之后可以根据表、PC来预测跳转指令是否为跳转。

Branch Prediction的表项数有限,且其依据跳转与否的历史信息来做预测。因此条件跳转指令越多(一般以指令地址来识别),跳转历史信息越碎片化,就越不利于提升预测精确度。

Branch Prediction继续发展,采用了循环预测器技术(US Patent 5909573),能够对loop进行专门的预测,即对于“循环入口”的预测基本为真。

Switch语句

  • 多个case对应同一段处理语句;
  • “Fall through”;
  • case值并不连续。

在内存中设置Jump Table跳转表:

Jump Table Jump Targets
Targ0 Code Block 0
…1 …1

Swith语句示例(x86-32)

long switch_eg(long x, long y, long z) {long w = 1;switch(x) {case 1:w = y * z;break;case 2:w = y / z;/* Fall Through */case 3:w += z;break;case 5:case 6:w -= z;break;default:w = 2;     }return w;
}

Setup:

switch_eg:pushl %ebp             # Setupmovl %esp, %ebp          # Setuppushl %ebx               # Setupmovl $1, %ebx            # w = 1movl 8(%ebp), %edx      # edx = xmovl 16(%ebp), %ecx       # ecx = zcmpl $6, %edx         # x : 6ja .L61                  # if > goto defaultjmp *.L62(, %edx, 4)  # goto Jtab[x]

表结构:

  • 每个表项(及跳转地址)占4字节;
  • 基地址是 .L62。

无条件跳转指令:

  • jmp .L61
    • Jump target is denoted by label .L61
  • jmp *.L62(, %edx, 4)
    • Start of jump table denoted by label .L62
    • Register %edx holds x
    • Must scale by factor of 4 to get offset into table
    • Fetch target from effective Address .L61 + x * 4, Only for 0≤x≤60 \le x \le 60≤x≤6
    • 这表明是一个间接跳转,即目标地址存于内存地址中

本例中表项内容如下图:

对于switch结构的汇编如下:

switch(x) {...case 2: // .L57w = y/z;/* Fall Through */case 3: // .L58w += z;break;default: // .L61w = 2;
}

其汇编为:

.L61:    // Default casemovl $2, %ebx            # w = 2movl %ebx, %eax         # Return wpopl %ebxleaveret
.L57:   // Case 2:movl 12(%ebp), %eax       # ycltd                 # Div prepidivl %ecx                # y/zmovl %eax, %ebx            # w = y/z# Fall through
.L58:   // Case 3:addl %ecx, %ebx           # w += zmovl %ebx, %eax           # Return wpopl %ebxleaveret
switch(x) {...case 1: // .L56w = y*z;break;...case 5:case 6: // .L60w -= z;break;...
}

对于汇编指令:

.L60:    // Case 5&6:subl %ecx, %ebx         # w -= zmovl %ebx, %eax            # Return wpopl %ebxleaveret
.L56:   // Case 1:movl 12(%ebp), %ebx           # w = yimull %ecx, %ebx            # w *= zmovl %ebx, %eax                # Return wleaveret

x86-64下的Switch语句

基本与32位版本一样,地址长度64位。

Switch语句实例(case很稀疏)

使用跳转表性能差,不现实,编译器编译成二叉树形式。

【汇编语言与计算机系统结构笔记08】如何实现循环(Loops),gcc历史上经历了多种转换模式(微体系结构角度解释),Switch语句,跳转表相关推荐

  1. 【汇编语言与计算机系统结构笔记01】x86/MIPS/ARM指令集概述与特性,一篇HPCA引发的思考(商业生态的决定性作用)

    资源Bilibili AV46914471 + AV57921488 汇编语言与计算机系统结构 清华大学 张悠慧 本次笔记内容: 01.汇编语言与计算机系统结构 02.汇编基础知识--指令集综述 文章 ...

  2. 【汇编语言与计算机系统结构笔记07】条件码,比较、测试、条件跳转与条件转移指令,结合微体系结构与流水的说明

    本次笔记内容: 08.控制流-1 文章目录 条件码 基于add的CF, ZF, SF, OF 比较(Compare)指令 测试(Test)指令 读取条件码(SetX)指令 例子 拓展:流水设计与微体系 ...

  3. 【汇编语言与计算机系统结构笔记14】循环和分支程序设计

    本次笔记内容: 18.循环程序设计-1 19.分支程序设计 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录老师上课强调的内容与对应ppt页码. 本节课对应 ...

  4. 【汇编语言与计算机系统结构笔记02】整数的计算机表示与运算,C中的无符号字符(unsigned)和带符号字符(signed),补码,一些例题

    本次笔记内容: 03.整数的计算机表示与运算 文章目录 预备知识 数制 数的机器表示 机器字在内存中的组织 字节序(Byte Ordering) 整数表示 计算机中整数的二进制编码方式 无符号数与带符 ...

  5. 【汇编语言与计算机系统结构笔记05】汇编的系统结构,从C代码生产汇编代码,一个具体的、经典的数据传送指令(mov)实例与分析

    本次笔记内容: 06.寻址模式与数据传输指令等 文章目录 汇编程序员眼中的系统结构 如何从C代码生产汇编代码 如何装gcc? 汇编语言数据格式 第一条汇编指令实例 数据传送指令(mov) 语法与操作数 ...

  6. 【汇编语言与计算机系统结构笔记17】MIPS 汇编初步

    本次笔记内容: 25.MIPS汇编初步-1 26.MIPS汇编初步-2 27.MIPS指令集与汇编程序设计 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录 ...

  7. 【汇编语言与计算机系统结构笔记12】序格式与伪操作:简化段的定义、操作符等

    本次笔记内容: 15.程序格式与伪操作-2 16.上机过程-1(前15分钟) 注:我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录老师上课强调的内容与对应ppt ...

  8. 【汇编语言与计算机系统结构笔记11】程序格式与伪操作:段定义、堆栈 #简洁笔记形式

    本次笔记内容: 14.程序格式与伪操作-1 注:本节课更换为一名女老师.我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录老师上课强调的内容与对应ppt页码. 注 ...

  9. 【汇编语言与计算机系统结构笔记10】C语言数组的汇编访问:连续存储、代码优化、无边界检查;结构对齐要求 #简洁笔记形式

    本次笔记内容: 13.数据的机器表示 注:本次笔记开始,我找到了对应内容的课件,请见我于GitHub的CS笔记仓库.因此,为了节省时间,我只记录老师上课强调的内容与对应ppt页码. 本节课对应幻灯片: ...

最新文章

  1. 新型内存攻击,专治制程提高的芯片
  2. 机器学习7—AdaBoost学习笔记
  3. 2016年第二季度全球以太网交换机销量破60亿美元
  4. flutter布局-8-animated_icons动画图片
  5. LOL手游诺手对线技巧,上分率提高60%,战神玩家推荐玩法
  6. [leetcode] 198.打家劫舍
  7. java中接口回调_Java中的接口回调实例
  8. MyBatis 阶段总结
  9. 后勤管理系统_还在用人工进行宿舍后勤管理?超级适用的宿舍管理系统推荐
  10. android自动计步_自动计步器app下载
  11. linux分配oracle内存,【学习笔记】Linux系统 ORACLE用户进程占用私有内存分析
  12. 大数据导论2之大数据与云计算、物联网、人工智能
  13. 理解HTC Vive更新——控制相机旋转和位移
  14. 中创软件哪个部分是外包_什么是外包| 第2部分
  15. 問題の解決策 [USACO18JAN]Lifeguards P(题解)
  16. Blackbox_exporter黑盒监测
  17. 关于wav amr音频分析
  18. JavaScript名字的由来
  19. Java File类创建目录文件
  20. ACM-ICPC 2018 南京赛区网络预赛 I. Skr (马拉车+字符串hash/回文自动机)

热门文章

  1. 三次握手的本质_动画讲解TCP的3次握手,4次挥手,让你一次看明白
  2. 【PL/SQL】 控制结构
  3. 文本视图(UITextView)占位符Swift
  4. 打开模式时防止BODY滚动
  5. 您如何轻松地水平居中 div 使用CSS? [重复]
  6. 从GitHub存储库下载单个文件夹或目录
  7. 如何决定何时使用Node.js?
  8. 循环报数java代码_循环报数 Java实现
  9. VC6.0的工程设置解读Project--Settings
  10. android dao设计模式,DAO设计模式