• 【06】指令跳转:原来if...else就是goto


    【计算机组成原理】学习笔记——总目录

    引言

    带着问题学习

    if…else 这样的条件判断语句、while 和 for 这样的循环语句在计算机中是如何执行的?

    一、CPU 是如何执行指令的?

    1、CPU由很多寄存器组成

    我们先不管几百亿的晶体管的背后是怎么通过电路运转起来的,逻辑上,我们可以认为,CPU 其实就是由一堆寄存器组成的。而寄存器就是 CPU 内部,由多个触发器(Flip-Flop)或者锁存器(Latches)组成的简单电路

    触发器和锁存器,其实就是两种不同原理的数字电路组成的逻辑门。【暂先不讲,知道即可(数字电路课程)】

    N 个触发器或者锁存器,就可以组成一个 N 位(Bit)的寄存器,能够保存 N 位的数据。比方说,我们用的 64 位 Intel 服务器,寄存器就是 64 位的

    2、CPU中不同功能的寄存器
    在这里插入图片描述

    三种比较特殊的寄存器

    1. PC 寄存器(Program Counter Register),我们也叫指令地址寄存器(Instruction Address Register)。顾名思义,它就是用来存放下一条需要执行的计算机指令的内存地址。

    2. 指令寄存器(Instruction Register),用来存放当前正在执行的指令。

    3. 条件码寄存器(Status Register),用里面的一个一个标记位(Flag),存放 CPU 进行算术或者逻辑计算的结果。

    【以下略看】
    除了这些特殊的寄存器,CPU 里面还有更多用来存储数据和内存地址的寄存器。这样的寄存器通常一类里面不止一个。我们通常根据存放的数据内容来给它们取名字,比如整数寄存器、浮点数寄存器、向量寄存器和地址寄存器等等。有些寄存器既可以存放数据,又能存放地址,我们就叫它通用寄存器

    实际上,一个程序执行的时候,CPU 会根据 PC 寄存器里的地址,从内存里面把需要执行的指令读取到指令寄存器里面执行,然后根据指令长度自增,开始顺序读取下一条指令。可以看到,一个程序的一条条指令,在内存里面是连续保存的,也会一条条顺序加载。

    而有些特殊指令,比如上一讲我们讲到 J 类指令,也就是跳转指令,会修改 PC 寄存器里面的地址值。这样,下一条要执行的指令就不是从内存里面顺序加载的了。事实上,这些跳转指令的存在,也是我们可以在写程序的时候,使用 if…else 条件语句和 while/for 循环语句的原因**。

    二、从 if…else 来看程序的执行和跳转

    // test.c
    #include 
    #include 
    
    int main()
    {
      srand(time(NULL));
      int r = rand() % 2;
      int a = 10;
      if (r == 0)
      {
        a = 1;
      } else {
        a = 2;
      } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    对应的汇编代码【只关注 if…else 条件判断语句】:

        if (r == 0)
      3b:   83 7d fc 00             cmp    DWORD PTR [rbp-0x4],0x0
      3f:   75 09                   jne    4a <main+0x4a>
        {
            a = 1;
      41:   c7 45 f8 01 00 00 00    mov    DWORD PTR [rbp-0x8],0x1
      48:   eb 07                   jmp    51 <main+0x51>
        }
        else
        {
            a = 2;
      4a:   c7 45 f8 02 00 00 00    mov    DWORD PTR [rbp-0x8],0x2
      51:   b8 00 00 00 00          mov    eax,0x0
        } 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    【补充】
    cmp 指令的比较结果,会存入到条件码寄存器当中去。在这里,如果比较的结果是 True,也就是 r == 0,就把零标志条件码(对应的条件码是 ZF,Zero Flag)设置为 1。除了零标志之外,Intel 的 CPU 下还有进位标志(CF,Carry Flag)、符号标志(SF,Sign Flag)以及溢出标志(OF,Overflow Flag),用在不同的判断条件下。

    图解:
    在这里插入图片描述

    上一讲我们讲打孔卡的时候说到,读取打孔卡的机器会顺序地一段一段地读取指令,然后执行。执行完一条指令,它会自动地顺序读取下一条指令。如果执行的当前指令带有跳转的地址,比如往后跳 10 个指令,那么机器会自动将卡片带往后移动 10 个指令的位置,再来执行指令。同样的,机器也能向前移动,去读取之前已经执行过的指令。这也就是我们的 while/for 循环实现的原理。

    三、如何通过 if…else 和 goto 来实现循环?

    int main()
    {
        int a = 0;
        for (int i = 0; i < 3; i++)
        {
            a += i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    对应的汇编代码:

        for (int i = 0; i <= 2; i++)
       b:   c7 45 f8 00 00 00 00    mov    DWORD PTR [rbp-0x4],0x0
      12:   eb 0a                   jmp    1e 
        {
            a += i;
      14:   8b 45 f8                mov    eax,DWORD PTR [rbp-0x4]
      17:   01 45 fc                add    DWORD PTR [rbp-0x8],eax
    
      1a:   83 45 f8 01             add    DWORD PTR [rbp-0x4],0x1
      1e:   83 7d f8 02             cmp    DWORD PTR [rbp-0x4],0x2
      22:   7e f0                   jle    14 
      24:   b8 00 00 00 00          mov    eax,0x0
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以看到,对应的循环也是用 1e 这个地址上的 cmp 比较指令,和紧接着的 jle 条件跳转指令来实现的。

    图解:
    在这里插入图片描述如果你看一长条打孔卡的话,就会看到卡片往后移动一段,执行了之后,又反向移动,去重新执行前面的指令。

    其实,你有没有觉得,jle 和 jmp 指令,有点像程序语言里面的 goto 命令,直接指定了一个特定条件下的跳转位置。虽然我们在用高级语言开发程序的时候反对使用 goto,但是

    【重要!!!】
    实际在机器指令层面,无论是 if…else…也好,还是 for/while 也好,都是用和 goto 相同的跳转到特定指令位置的方式来实现的。

    想要在硬件层面实现这个 goto 语句,除了本身需要用来保存下一条指令地址,以及当前正要执行指令的 PC 寄存器指令寄存器外,我们只需要再增加一个条件码寄存器,来保留条件判断的状态。这样简简单单的三个寄存器,就可以实现条件判断和循环重复执行代码的功能

    三、总结【个人总结的重点】

    • if…else、while、for在机器指令层面,都相当于执行goto进行跳转

    • 三类寄存器的作用

      1. PC寄存器(指令地址寄存器):存放下一条需要执行的计算机指令的内存地址【存放地址】
      2. 指令寄存器:用来存放当前正在执行的指令【存放指令】
      3. 条件码寄存器:用里面的一个一个标记位(Flag),存放 CPU 进行算术或者逻辑计算的结果。【存放结果,用作是否跳转到相应地址的判断条件】
    • 简简单单的三个寄存器(PC寄存器、指令寄存器、条件码寄存器),就可以实现条件判断和循环重复执行代码的功能。

    • 文中的图解,可以学习条件码寄存器的作用

    • 清楚程序在机器指令层面的执行过程,及过程中PC寄存器、指令寄存器、条件码寄存器是何时、何逻辑顺序发挥作用的。

    【计算机组成原理】学习笔记——总目录

  • 相关阅读:
    力扣第300题 最长递增子序列 c++ 动态规划题 附Java代码
    【Qt控件之微调框、进度条】QSpinBox、QDoubleSpinBox、QDial、QProgressBar介绍及使用
    目录启示:PHP 与命名空间的声明
    计算机网络-概述
    Shell编程之第二讲——shell 的变量
    nn.Sequential()实例化模型的三种方法
    ElasticSearch 04 -- 集群
    每天40min,我们一起用70天稳扎稳打学完《JavaEE初阶》——33/70 第三十三天【JavaScript(webapi)】
    jQuery append和prepend和appendTo的区别和用法
    STM32F103xx TFT液晶显示ASCII字符串、中文、图片并且显示带有镜像和旋转功能
  • 原文地址:https://blog.csdn.net/sinat_40003796/article/details/125908182