汇编语言编程基础 基于LoongArch

978-7-115-59542-3
作者: 孙国云敖琪王锐
译者:
编辑: 赵祥妮

图书目录:

详情

汇编语言是人和计算机沟通的最直接的方式,它描述了机器最终所要执行的指令序列。汇编语言和机器语言一样都是和计算机体系架构强绑定的低级语言。本书是龙芯自主指令集的首本汇编语言教程,系统讲解龙芯处理器全新的自主指令系统架构 LoongArch。 本书循序渐进地介绍基于 LoongArch 的汇编语言知识,包括计算机语言、使用汇编语言的场景等基础知识,寄存器、指令集、函数调用等汇编语言的核心内容,以及编写程序示例和常用的调试手段。本书并不是逐条地讲解每一条指令的功能,而是通过关键指令的拆解来介绍计算机工作的基本原理,同时恰当地指出 LoongArch 的特殊之处,便于读者理解、实践、应用 LoongArch。 本书非常适合基于龙芯架构的工程技术人员学习参考,也可作为从事计算机体系结构或计算机系统设计的工程技术人员的参考书,还可用作大学计算机专业的延伸阅读资料。本书的读者应具备以下基础:具有计算机的使用经验,具有计算机基础知识,具有一门高级语言(C、Java、Python 等)的基本编程基础知识。

图书摘要

版权信息

书名:汇编语言编程基础:基于LoongArch

ISBN:978-7-115-59542-3

本书由人民邮电出版社发行数字版。版权所有,侵权必究。

您购买的人民邮电出版社电子书仅供您个人使用,未经授权,不得以任何方式复制和传播本书内容。

我们愿意相信读者具有这样的良知和觉悟,与我们共同保护知识产权。

如果购买者有侵权行为,我们可能对该用户实施包括但不限于关闭该帐号等维权措施,并可能追究法律责任。

版  权

著    孙国云 敖 琪 王 锐

责任编辑 赵祥妮

人民邮电出版社出版发行  北京市丰台区成寿寺路11号

邮编 100164  电子邮件 315@ptpress.com.cn

网址 http://www.ptpress.com.cn

读者服务热线:(010)81055410

反盗版热线:(010)81055315

微信扫码关注【异步社区】微信公众号,回复“e59542”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

内 容 提 要

汇编语言是人和计算机沟通的最直接的方式,它描述了机器最终所要执行的指令序列。汇编语言和机器语言一样都是和计算机体系架构强绑定的低级语言。本书是龙芯自主指令集的首本汇编语言教程,系统讲解龙芯处理器全新的自主指令系统架构LoongArch。

本书循序渐进地介绍基于LoongArch的汇编语言知识,包括计算机语言、使用汇编语言的场景等基础知识,寄存器、指令集、函数调用等汇编语言的核心内容,以及编写程序示例和常用的调试手段。本书并不是逐条地讲解每一条指令的功能,而是通过关键指令的拆解来介绍计算机工作的基本原理,同时恰当地指出LoongArch的特殊之处,便于读者理解、实践、应用LoongArch。

本书非常适合基于龙芯架构的工程技术人员学习参考,也可作为从事计算机体系架构或计算机系统设计的工程技术人员的参考书,还可用作大学计算机专业的延伸阅读资料。本书的读者应具备以下基础:具有计算机的使用经验,具有计算机基础知识,具有一门高级语言(C、Java、Python等)的基本编程基础知识。

前   言

掌握好一类汇编语言的使用需要涉及多方面的知识,例如基本的信息表示和处理、处理器的体系架构、存储器的层次结构、程序的生命周期和编译过程,而不仅限于汇编语言本身。对于如此多的知识点,一本书很难做到面面俱到。本书的重点是介绍龙芯汇编语言的使用,围绕如何使用龙芯汇编语言编写程序展开讲解,并扩展介绍部分处理器体系架构、程序编译过程、程序调试工具等相关知识。对于汇编语言涉及的信息表示和处理,例如最基本的二进制和十六进制表示、进制间的转换、基本数学运算、逻辑运算等,本书没有做专门的讲解。因此,本书适合对计算机基础理论有一定了解的读者。对于缺少相关知识的读者,我建议通过其他课程或教材进行学习。

本书第01~04章重点介绍汇编语言的概念和龙芯基础指令集LoongArch,具体包括汇编语言的概念及其使用场景、LoongArch指令特性、C语言到LoongArch指令的编译过程、LoongArch基础整数指令集和LoongArch基础浮点数指令集。龙芯官方发布的龙芯架构参考手册已经对每一条指令的使用都做了单独的功能解释。本书在此基础上,聚焦指令集中整数指令集和浮点数指令集的使用,而且在每一章都穿插了很多示例,以此希望读者可以快速入门龙芯汇编语言。

本书第05~08章重点介绍LoongArch ABI和汇编程序的编写,具体包括整型寄存器和浮点寄存器的使用约定、函数调用约定和栈布局、目标文件的格式、汇编源程序和内嵌汇编的基本语法和编写示例。如果读者希望能够无障碍地阅读汇编程序,甚至能独立编写正确且健壮的汇编程序,那么这部分的知识是必须理解和掌握的。因为这部分内容的工程性很强,所以希望读者在学习此部分的过程中多动手实践,从而更深刻地理解相关内容。

本书第09~10章重点介绍汇编程序的调试手段和程序的性能优化,具体包括程序功能调试工具GDB、程序性能分析工具perf的使用、常见汇编性能优化手段。这部分对上述调试工具的使用仅是概要性的介绍,因为每一类工具的具体使用命令参数非常多,在平时工作中我们也很难全部用到。读者在实际工作如有需要,我建议多多使用工具的帮助文档进行了解。汇编程序的性能优化可以说是涉及的知识面最广、难度最大的一部分。本书尽量对这部分涉及的相关体系架构知识进行简单直白的介绍,并对常见的优化手段、向量指令、指令融合、指令调度、循环展开等进行讲解。

最后,建议读者在阅读本书或实际工作中,常备龙芯官方发布的龙芯架构参考手册,以对使用的每条汇编指令有清晰的认识。如果读者希望对LoongArch指令集有更深入的了解,我推荐阅读《深入理解计算机系统》以及龙芯团队编写的《计算机体系结构 第2版》和《计算机体系结构基础 第3版》。

孙国云

2022年10月

第01章 汇编语言和龙芯架构简介

初次接触计算机编程的人可能都会有一个困惑:怎么会有那么多种程序设计语言?例如,C、C++、Java、Go、Python、C#、Ruby、Objective-C、x86汇编语言、ARM汇编语言,以及本书将要介绍的龙芯汇编语言。它们是什么关系?为什么要学习汇编语言?希望看过本章内容以后,你会对此有所理解。

1.1 计算机语言

前文所罗列的语言统称为计算机语言。计算机语言就是用于人和计算机之间交流的语言。计算机是一组电子器件,要让它完成特定的工作,就需要向它输入一组它能识别和执行的语言(或者叫指令)。和人类语言一样,计算机语言也有一套标准的语法规范,有了规范才得以让计算机理解我们的意图并遵照执行。

计算机语言的种类很多,从使用层次的角度常被分成机器语言、汇编语言和高级语言三大类。简单来说,离计算机处理器最远(需要更多的编译流程后才能被计算机识别和执行)、层次最高(更接近自然语言和数学公式)的是高级语言,例如C、C++、Java等都属于高级语言;中间层次的是汇编语言;离计算机最近、处于最低层次的是机器语言,机器语言是唯一可以被计算机直接识别和执行的语言。绝大多数的计算机软件开发人员(或称程序员)通常使用更接近人类语言语法规则、更易于编写的高级语言来编写程序,然后利用编译器、汇编器把高级语言一步步转换成计算机可以识别的机器语言,最终交由计算机处理器执行并得出结果。这个流程如图1-1所示。

图1-1 计算机语言转换过程

图1-1简单描绘了高级语言到机器语言的转换过程,涉及的工具是编译器和汇编器。其中编译器负责把高级语言(比如C++语言)翻译成汇编语言,汇编器又把汇编语言翻译成机器语言。而机器语言就是最终可以被计算机识别和执行的语言。

下面按照与计算机处理器的距离,以由近及远的顺序分别介绍机器语言、汇编语言和高级语言。

1.1.1 机器语言

机器语言是计算机能直接识别和执行的程序语言,它的表示形式是二进制。进制就是记数的一种方法,我们最为熟悉的十进制,就是用0~9共10个数来表示的,遵循“逢十进一”的进制规则。而二进制只有“0”和“1”两个数,遵循“逢二进一”的规则。计算机的硬件作为一种电路元件,最容易用有电和没电状态来与外界进行交互,这两种状态,也称高电平和低电平,分别对应到二进制数的“1”和“0”,每个数字被称为一位(bit)。

机器语言由多条机器指令(简称指令)组成。一条指令由固定长度的二进制数组成,用于指导计算机执行一个动作,例如加法运算、减法运算、与运算、从内存读取数据等。因此,指令是计算机执行的基本单位。计算机呈现给程序员的全部指令的集合就称为指令集或指令系统。可以说指令系统是软件和硬件的接口层,我们就是通过这个接口层指导计算机处理器为我们工作。指令系统有很多,常见的有x86、ARM等。中央处理器(Central Processing Unit,CPU)是计算机中的核心部件,其功能主要是解释指令以及处理计算机软件中的数据。特定的CPU只能识别特定指令,比如x86指令只能被采用x86指令集的处理器识别,而不能被采用ARM或MIPS指令集的处理器识别。一般而言指令集和体系架构是两个同义词,都包含一组指令集和一些寄存器。

龙芯指令系统中一条指令占用32位。比如我们要让龙芯处理器完成一个加法操作,它的机器指令可能如下:

   0000 0010 1100 0001 0000 0000 0110 0011

这是让人很头疼的一串数字,因为我们很难直观读出这32个0和1的组合的语义。但又不是没有规律可循,因为机器指令同自然语言和高级计算机语言一样,是有语法规范的。一条机器指令长度固定(例如龙芯指令长度为32位),由操作码和操作数两部分组成,操作数又分为源操作数和目的操作数。这里拿C语言做个简单的比喻。

      long c = a + 2;

这里可以认为符号“+”是操作码,表示这是个加法操作。变量a和常数2为源操作数,变量c即目的操作数,用于存放加法运算的结果。机器指令的语法规范语义也很类似,不过机器指令中的操作码不仅要表示运算类别(比如加法、减法、乘法等),还要表示是哪种数据类型(比如int、double、long等)的运算。同时机器指令中的操作数为寄存器(寄存器是计算机中临时存储数据的器件)或者常数。例如龙芯指令集中加法运算指令的语法规范如图1-2所示。

图1-2 龙芯指令集中加法运算指令的语法规范

从图1-2可以看出,在龙芯指令集中,一条指令的长度为32位。对于加法指令,32位中的高10位代表操作码。0b0000001010代表32位的加法指令(ADDI.W),0b0000001011代表64位数的加法指令(ADDI.D)。接下来的12位表示一个常数;再分别用两个5位表示源寄存器操作数ri和目的寄存器操作数rd,ri和rd可以是龙芯指令集提供的32个通用寄存器中的任意一个。

对照图1-2,我们可以解读出这条机器指令的语义。其中高10位(0000 0010 11)为操作码,语义为带立即数的64位数(对应C语言的long类型)加法运算;接下来的12位(00 0001 0000 00)为第一个源操作数,且该源操作数是常数,换算成十进制数值为64;再接下来的5位(00 011)为第二个源操作数,且该源操作数是寄存器,换算成十进制数值为3,即代表第3个寄存器;最后的5位(0 0011)为目的操作数,看来也是第3个寄存器。故这条机器指令功能是实现第3个寄存器值和常数64的加法运算,将结果存入第3个寄存器。龙芯汇编指令的写法就是addi.d r3, r3, 64。此条机器指令按语法规范表示为

0000 0010 11  |  00 0001 0000 00    |    00 011       |        0 0011
    操作码         第一个源操作数          第二个源操作数           目的操作数

要解读程序中每一条机器指令所代表的意思,要不断地对照指令手册来翻译。推想开来,我们要让计算机完成一个功能可能需要成千上万个这样的指令,如果使用机器指令编写,难度可想而知。故有了后来更易读、易编写的汇编语言和高级语言。

1.1.2 汇编语言

汇编语言可以看作机器语言的升级版,用一些容易理解和记忆的字母、单词来代替特定的机器指令。通过这种方法让我们更容易阅读和理解程序正在执行的功能。比如1.1.1小节中龙芯指令架构下实现两个数的加法操作,其对应的机器指令和汇编指令分别如下:

    机器指令: 0000 0010 1100 0001 0000 0000 0110 0011
    汇编指令: addi.d   r3, r3, 64

从对应的汇编指令的书写上更容易解读出这条指令的语义:实现寄存器r3和常数64的加法操作,并将结果写回寄存器r3。这样我们就省去了对照指令手册逐个翻译操作码和操作数来解读指令语义的过程。一条汇编指令通常由助记符和操作数两部分组成。助记符对应机器指令中的操作码,例如这里的addi.d就是助记符,代表这是一个64位加法操作;操作数代表指令的计算对象,例如这里的两个r3寄存器和常数64。

通过这个例子可以看到,使用汇编语言,程序员不用关心这条指令对应的二进制数是多少,汇编器会帮助我们把它翻译成二进制的机器语言,编程效率得到很大提高。

汇编语言和机器语言一样都是和计算机体系架构强绑定的低级语言。也就是说,用龙芯汇编指令集编写的程序在不加以转换的情况下,不可能运行在基于x86指令集或ARM指令集的处理器上,反之亦然。

1.1.3 高级语言

高级语言是一个相对概念,通常可解读为越是易于程序员高效编写的语言越高级。例如刚开始出现C语言时,人们认为C语言比汇编语言高级,故称C语言为高级语言,而汇编语言为低级语言,当Java、Python语言出现后,人们又认为C语言不够高级。本书提到的高级语言是相对于汇编语言而言的,即不再强依赖计算机处理器的硬件体系架构、表达方式更接近自然语言和数学公式的程序设计语言,比如C、C++、Java、Go等。这些语言本身都是独立于处理器架构的,都有自己的语法规则且不能直接被计算机识别和执行,需要编译器和汇编器的翻译过程,相关程序被转变为机器指令后才被处理器识别和执行。例如要实现一个数的累加运算,使用C语言的编写如下:

   ++a;  

一条语句就完成了变量a的累加功能,如果a初始值为1,那么++a执行后,a的值为2。这条语句对应的汇编语言指令至少需要3条,即首先从内存地址加载a的初始值到一个寄存器,然后进行这个寄存器和常数1的加法操作,最后把结果写回内存地址。这个过程可用如下龙芯汇编指令表示:

   load  r3,  [addr]     //从内存地址addr加载值到寄存器r3
   add   r4,  r3, 1      //加法计算r3+1,将结果写到寄存器r4
   store r4,  [addr]     //把寄存器r4的值写回内存地址addr

对比后不难发现,C语言比汇编语言更直观,更方便程序员编程。而且同样功能的高级语言程序只需要编写一次,对应不同体系架构平台的机器语言可由对应的编译器生成。

高级语言设计思想发展的主旨是更便于程序员快速编程。编程思想经历了面向过程(将一个功能块定义为一个函数/方法,以C语言为代表)、面向对象(把相关的数据、函数/方法组织为一个整体来管理,以Java语言为代表)、面向函数(即高阶函数的出现,很多语言都在“拥抱”高阶函数,如Java、Groovy、Scala、JavaScript等)等。未来还可能有面向应用的设计思想转变,也就是说:只需要告诉程序你要干什么,程序就能自动生成算法,自动进行处理。高级语言设计思想的不断进化,让计算机语言越来越接近人类语言,也更智能,编程效率也越来越高,使得程序员可把更多时间花费在解决复杂业务场景上。

1.2 汇编语言的使用场景

很多使用高级语言编程的人员从业之初甚至从业多年都会有这样的疑问:现在大多数应用程序都是使用高级语言进行编程,学习汇编语言有什么用处?下面列举几个汇编语言的使用场景。

1.2.1 场景1——快速定位问题和分析问题

举一个不太“烧脑”的小例子:浮点数例外。多数程序开发者在工作中都会遇到异常信号SIGFPE,当程序执行除法运算语句时,如果被除数为0,那么系统就会毫不留情地发送给你一个信号SIGFPE(中文或许显示“浮点数例外”)。这样一个异常的C语言代码如下:

   int test(int a, int b) {                                                         
       return a/b;                                                             
   }

当我们调用函数test时 ,故意给参数b传入0,那么就会收到“浮点数例外”。使用调试工具GDB(GNU Debugger ,Linux系统下的调试工具)在龙芯平台上调试这段代码时,可以获取如下信息:

Program received signal SIGFPE, Arithmetic exception.
0x00000001200006ec in test ()
(gdb) bt
#0  0x00000001200006ec in test ()
#1  0x0000000120000734 in main ()

这里GDB已经列出了函数调用栈,即函数main调用了函数test,在执行函数test中地址为0x00000001200006ec 处的指令时,触发异常SIGFPE。那么0x00000001200006ec处的指令是什么呢?我们可以使用GDB进一步确认。

(gdb) x/5i $pc-12 
  0x1200006e0 <test+40>: ld.w    $r12,$r22,-24(0xfe8)
  0x1200006e4 <test+44>: div.w   $r14,$r13,$r12
  0x1200006e8 <test+48>: bne     $r12,$r0,8(0x8) # 0x1200006f0 <test+56>
=> 0x1200006ec <test+52>: break  0x7
  0x1200006f0 <test+56>:  move   $r12,$r14

其中 => 标识了当前PC(Program Counter,PC用来存放当前欲执行指令的地址)位置,即当前程序停在的位置。上面的汇编指令div.w $r14,$r13,$r12 为除法指令,实现用寄存器 $r13除以$r12,将结果写入$r14 。汇编指令bne $r12,$r0,8(0x8) # 0x1200006f0 是条件跳转指令,判断被除数$r12是否等于0(寄存器$r0 为特殊寄存器,其值永远为0),如果不相等则跳转到地址0x1200006f0 处继续执行,否则就不跳转,执行接下来的汇编指令break 0x7。break 指令将无条件触发断点例外,其参数0x7对应SIGFPE。至此,我们就知道了当前程序异常是由除法指令中的被除数为0引起的,对应的C语言代码就是return a/b;,语句中的b为0。

其实对大型软件的异常问题定位,基本都是这个思路。不过好在很多大型软件都会内置一套完整的异常处理机制,在异常发生时,可自动收集异常原因、异常进程、异常位置、栈回溯等信息,比如Java虚拟机中提供的捕获异常、Android系统中的tombstone。尽管如此,我们还是有可能遇到异常处理机制无法捕获的异常(漏网之鱼),这时掌握一些调试工具的使用方法和汇编语言的知识是很有必要的。

对于GDB工具的使用,在后面章节还会有更详细的介绍。

1.2.2 场景2——性能分析和优化

了解计算机体系架构和汇编语言有助于我们深入分析软件性能瓶颈。虽然编译器已经做了大部分的性能优化工作,比如C/C++语言的编译器GCC(GNU Compiler Collection,GNU编译器组件)编译时使用-O3比-O1可以带来更进一步的性能优化;支持Java虚拟机根据函数大小及函数被使用的次数来动态调整优化策略。但是在特定场景中,这些还是不够用,比如游戏引擎、音视频的编解码等领域,会经常遇到和算法相关的大数据量数学运算。这时如果我们会使用汇编语言,就可以更进一步做针对特定处理器的优化工作。比如多数处理器中都实现了单指令流多数据流(Single-Instruction stream Multiple-Data stream, SIMD)功能的汇编指令,亦称为向量指令,其可实现一条指令操作多组数据。龙芯架构LoongArch中也实现了SIMD,包括向量扩展(Loongson SIMD Extension,LSX)和高级向量扩展(Loongson Advanced SIMD Extension,LASX),其中LSX为128位向量位宽,LASX为256位向量位宽。

举一个使用龙芯LASX实现程序优化的小例子。下面的代码(使用C语言实现)实现a数组与b数组中的各项数据相加,将结果写入c数组的加法运算。这里假设数组类型为整型int(32位),循环长度为10000。

for(int i = 0; i < 10000; i++)
  c[i] = a[i] + b[i];

使用GCC编译后,生成的最终可供CPU执行的指令如下:

//LoongArch汇编指令
L:
ld.w  t1, a1, 0         # 加载数组a[i]值到寄存器t1
add.w t3,  t1, t2       # 实现a[i]+b[i],将结果存入寄存器t3
st.w  t3,  t4, 0        # t3数据写回c[i]
addi.d a1, a1, 4        # 数组a[]累加4,即指向a[i+1]
addi.d a1, a2, 4        # 数组b[]累加 4
addi.d t4, t4, 4        # 数组c[]累加 4
 
bne  a5, a6, L          # 判断若for()没有结束,跳转到L,继续执行

上面这段指令实现了循环操作c[i] = a[i] + b[i],相关指令格式在后面章节还会有详细介绍。在这里可以看出要实现两个长度为10000的整型数组加法运算,CPU要循环执行10000次,每次循环要执行8条指令,那么完成整个功能要执行80000条指令。

同样的功能,用龙芯LASX指令实现如下:

//LoongArch 汇编指令
L:
xvld    x1, a1, 0             # 加载数组a[]中的8组整型值到向量寄存器x1
xvld    x2, a2, 0             # 加载数组b[]中的8组整型值到向量寄存器x2
xvadd.w x3, x1, x2            # a[i…i+8]+b[i…i+8],将结果存入向量寄存器x3
xvst    x3, t4, 0             # 把x3数据写回数组c[i…i+8]
addi.d  a1, a1, 32            # 数组a[]地址累加32,即指向a[i+9]
addi.d  a1, a2, 32            # 数组b[]地址累加32
addi.d  t4, t4, 32            # 数组c[]地址累加32
bne     a5, a6, L             # 判断若for()没有结束,跳转到L,继续执行

龙芯LASX指令是256位宽(即向量寄存器的长度),故循环一次可以完成8组整型值(8×32位)的加法运算。循环一次也是执行8条指令,但总的循环次数仅为1250次(10000/8),那么完成整个功能执行10000(1250×8)条指令即可,在理论上是GCC编译器生成的普通指令执行性能的8倍。在本书的第10章将专门介绍和指令架构相关的性能优化基本思路和方法。

1.2.3 场景3——完成高级语言无法实现的功能

在一些基础软件的源代码中,比如数据库、GCC编译器、OpenJDK等,我们能频繁看到汇编语言的身影。因为它们作为应用软件的支撑或工具,相对于应用软件在运行逻辑上更靠近CPU,也就更可能出现和计算机体系架构相关的功能要求。例如,GCC编译器负责将C/C++语言翻译成和计算机体系架构相关的汇编语言;Java语言开发者熟知的OpenJDK负责Java语言到机器指令的动态翻译和执行。这方面的软件从业者就不仅要熟知某种高级语言,还要熟知特定处理器支持的汇编语言。

例如有这样一个问题:在C语言中如何获取程序运行的当前PC值?不同架构有不同的方式,在龙芯平台上可以通过如下内嵌汇编来实现。

 static long * get_PC(void)  {                                                                               
     unsigned long *val;                                                          
     __asm__ volatile ("move %0, $r1" : "=r"(val));                               
     return val;                                                                  
  } 

这里__asm__是内嵌汇编指令,用来实现汇编语言和C语言的混合编程(后面会有专门章节来详细介绍其语法规范)。这里只需关注核心汇编指令move %0, $r1 。在龙芯架构寄存器使用约定里,寄存器$r1存放了函数的返回地址,%0代表val,所以move %0, $r1就完成了把当前函数的返回地址存到变量val中。而当前函数的返回地址就是调用该函数时的PC,所以你就可以通过调用这个函数来获取当前位置的PC。

汇编语言也是编写嵌入式设备上程序的理想工具。和通用计算机处理器相比,嵌入式设备(比如电话、打印机、门禁设备等)的典型特征是没有大容量内存,这就要求其上的程序尽量短小。如果使用高级语言编写,经过编译器翻译后的机器指令可能会有一些冗余,例如大量的函数调用开销、动态库加载(尽管程序中仅用了某个动态库的几个函数)等。如果直接使用汇编语言进行针对性的编写,那么内存占用肯定最少,因此汇编语言特别适合编写嵌入式程序。后面章节会专门介绍如何编写一个脱离libc库的程序示例。

1.3 龙芯系列处理器和龙芯架构介绍

1.3.1 龙芯系列处理器

龙芯中科技术股份有限公司(简称龙芯中科)从成立至今共开发了龙芯1号系列、龙芯2号系列、龙芯3号系列处理器及桥片等配套芯片。龙芯1号系列为低功耗、低成本专用嵌入式SoC或MCU处理器,通常集成1个32位低功耗处理器核,应用场景面向嵌入式专用应用领域,如物联终端、仪器设备、数据采集等;龙芯2号系列为低功耗通用处理器,采用单芯片SoC设计,通常集成1~4个64位低功耗处理器核,应用场景面向工业控制与终端等领域,如网络设备、行业终端、智能制造等;龙芯3号系列为高性能通用处理器,通常集成4个及以上64位高性能处理器核,与桥片配套使用,应用场景面向桌面和服务器等信息化领域。

龙芯3A5000/3B5000是面向个人计算机、服务器等信息化领域的通用处理器,基于龙芯自主指令系统(LoongArch)的LA464微结构,并进一步提升频率,降低功耗,优化性能。在与龙芯3A4000处理器保持引脚兼容的基础上,频率提升至2.5GHz,功耗降低30%以上,性能提升50%以上。龙芯3B5000在龙芯3A5000的基础上支持多路互连。龙芯3C5000是龙芯中科面向服务器领域的通用处理器,片上集成16个高性能LA464处理器核。表1-1中列举了龙芯3A5000/3B5000的关键产品参数。

表1-1 龙芯3A5000/3B5000的关键产品参数

产品参数

简要说明

主频

2.3GHz~2.5GHz

核心个数

4

峰值运算速度

160GFlops

处理器核

支持LoongArch指令系统;支持128/256位向量指令;四发射乱序执行;4个定点单元、2个向量单元和2个访存单元

高速缓存

每个处理器核包含64KB私有一级指令缓存和64KB私有一级数据缓存;每个处理器核包含256KB私有二级缓存;所有处理器核共享16MB三级缓存

内存控制器

2个72位DDR4-3200控制器;支持ECC校验

高速I/O

2个HyperTransport3.0控制器;支持多处理器数据一致性互连(CC-NUMA)

其他I/O

1个SPI、1个UART、2个I2C、16个GPIO接口

功耗管理

支持主要模块时钟动态关闭;支持主要时钟域动态变频;支持主电压域动态调压

典型功耗

35W@2.5GHz

1.3.2 龙芯自主指令系统

2021年,龙芯中科基于20年的CPU研制和生态建设积累,正式对外发布了龙芯自主指令系统(LoongArch)。指令系统由龙芯基础指令系统(Loongson Base)、二进制翻译扩展(Loongson Binary Translation, LBT)、虚拟化扩展(Loongson Virtualization, LVZ)、向量扩展和高级向量扩展共5部分组成,近2000条指令,它们之间的关系如图1-3所示。

龙芯基础指令系统定义了常用的整数和浮点数指令,用于支持原有各主流编译系统生成高效的目标代码。LVZ部分的指令集用于为操作系统虚拟化提供硬件加速以提升性能。LBT部分的指令集用于提升跨指令系统二进制翻译在龙芯架构平台上的执行效率。LSX和LASX两部分均用于加速计算密集型应用,区别在于LSX操作的向量位宽为128位,而LASX操作的向量位宽为256位。

龙芯架构分为32位(称为LA32架构)和64位(称为LA64架构)两个版本,分别用于龙芯寄存器位宽是32位和64位的处理器上。LA64架构应用级向下二进制兼容LA32架构。即采用LA32架构的应用软件的二进制可以直接运行在LA64架构的机器上并能获得同样的运行结果。但

图1-3 龙芯自主指令系统

在兼容LA32架构的机器上运行的系统软件(例如操作系统内核)的二进制直接在兼容LA64架构的机器上运行时,并不能保证总能获得相同的运行结果。

龙芯指令系统从整个架构的顶层规划,到各部分的功能定义,再到细节上每条指令的编码、名称、含义,在架构上进行自主重新设计,具有充分的自主性。同时,龙芯指令系统摒弃了传统指令系统中部分不适应当前软硬件设计技术发展趋势的陈旧内容,吸纳了近年来指令系统设计领域诸多先进的技术发展成果。同原有兼容指令系统相比,不仅在硬件方面更易于高性能低功耗设计,而且在软件方面更易于编译优化和操作系统、虚拟机的开发。

本书介绍了龙芯基础指令集,包括指令类型、指令使用方法、指令使用场景等。书中常出现的LoongArch也特指龙芯基础指令集。

1.4 龙芯汇编语言程序编写示例

和C语言类似,汇编程序也是以函数(也称为方法)为单位编写,函数可以有输入参数或者输出参数。汇编程序所在文件称为汇编源文件(扩展名为.S)。编写后的汇编源文件使用GCC编译器进行编译链接,和其他C语言文件形成最终可执行的二进制文件(即内部已是机器指令)。龙芯汇编源文件的编写、编译、执行全过程的示例如下:

#文件add.S
#接口定义 int add_f(int a, int b, int c, int d)
#功能定义:return (a+b+c+d)
 
    .text
    .align   2
    .globl   add_f
    .type    add_f,@function
add_f:
    add.w    $a0,$a0,$a1
    add.w    $a0,$a0,$a2
    add.w    $a0,$a0,$a3
    jr       $ra
    .size    add_f, .-add_f

add.S源文件里实现了一个add_f函数,其功能为实现4个32位整型数据(分别为寄存器a0、a1、a2和a3)的加法操作,并将结果返回(使用寄存器a0作为返回值)。汇编指令“jr $r1”意为函数返回。

接下来C语言文件test.c对这个汇编源文件中的汇编程序进行调用。

#include <stdio.h>
extern int add_f(int a, int b, int c, int d); //外部函数引用
int main(){
  int ret = add_f(1, 2, 3, 4); //调用add.S中的汇编函数add_f
  printf("ret = %d\n", ret); //输出结果
  return 0;
}

C语言文件test.c对汇编程序的调用与对其他C语言外部函数的调用方式是一致的,使用前通过关键字extern声明即可。

下面通过GCC编译器将汇编源文件add.S和C语言文件test.c编译成最终可执行文件test_add。

$ gcc test.c add.S -o test_add

最后我们可以运行这个可执行文件test_add并查看结果,结果显示为10(1+2+3+4),说明汇编源程序编写和执行正确。

$ ./a.out
ret = 10

可见,编写汇编语言程序还是还挺简单的。龙芯汇编源程序更详细的语法和编写方式会在后续章节中详细介绍。

1.5 本章小结

本章首先介绍了什么是机器语言、汇编语言、高级语言,以及三者之间的关系。概括来说处理器能识别的只有机器语言,汇编语言是对机器语言的等价封装,而高级语言是从工程角度出发设计的语言,目的是让开发者更高效地编程。接下来介绍了对非系统基础软件研发人员而言,汇编语言的可能使用场景。最后简单介绍了龙芯系列处理器和龙芯自主指令系统LoongArch的组成。

1.6 习题

1.请分别列举Java、C、C++、GO、C#等语言的特点及使用领域。

2.简述C语言、汇编语言和机器语言的关系。

3.汇编语言可移植吗?请简述理由。

4.龙芯自主指令系统由几部分组成?请一一列举。

5.查看龙芯架构参考手册,解释如下机器指令的含义。

0000 0000 0001 0001 1101 1111 0011 1001

读者服务:

微信扫码关注【异步社区】微信公众号,回复“e59542”获取本书配套资源以及异步社区15天VIP会员卡,近千本电子书免费畅读。

相关图书

Rust游戏开发实战
Rust游戏开发实战
JUnit实战(第3版)
JUnit实战(第3版)
Kafka实战
Kafka实战
Rust实战
Rust实战
PyQt编程快速上手
PyQt编程快速上手
Elasticsearch数据搜索与分析实战
Elasticsearch数据搜索与分析实战

相关文章

相关课程