4.1. 运算指令
运算指令包括算术运算指令(加、减、乘、除)、逻辑运算指令(与、或、或非、异或等)、条件赋值指令、移位运算指令(逻辑左移、逻辑右移、算术右移、循环移位等),以及位操作指令(位提取、位替换、半字逆序等)。这类指令在汇编语言中使用非常频繁,下面按类别进行介绍。
4.1.1. 算术运算指令
算术运算指令用于完成加法、减法、乘法、除法、取余、立即数加载、带移位加法等操作。在LA64架构中,运算指令会区分32位操作数和64位操作数。对于长度小于32位的字节(Byte)和半字(Short)类型算术运算,通常可以安全选用32位或64位算术指令。LoongArch算术运算指令如表3-1所示。
表3-1中,rd表示目的寄存器,rj和rk表示两个源寄存器。一般情况下,rd、rj、rk都可以是通用寄存器r0~r31中的任意一个。si12表示12位有符号立即数,si16表示16位有符号立即数,sa2表示长度为2位、用于移位的有符号立即数。
Tip
表3-1中的指令,名称后缀为.d的指令仅LA64架构支持。
表3-1的乘除法指令中,指令名后缀.u表示源操作数按无符号数据类型参与运算。
4.1.2. 逻辑运算和条件赋值指令
逻辑运算指令用于对源操作数逐位执行逻辑运算,并把结果写入目的寄存器,常见功能包括逻辑与、逻辑或、逻辑异或、逻辑或非等。条件赋值指令则根据源操作数的逻辑判断结果,对目的寄存器写入指定值。LoongArch支持的逻辑运算和条件赋值指令如表3-2所示。
表3-2中,条件赋值指令slt、sltu、slti、sltui里的lt是less than的缩写,含义为“小于”。当两个源操作数满足小于关系时,目的寄存器rd写入1;否则写入0。sltu表示把源操作数视为无符号数比较,slti表示参与比较的源操作数中包含立即数。
条件赋值指令maskeqz和masknez分别表示:当源操作数rk等于0(eqz)或不等于0(nez)时,将0写入rd;条件不成立时,则把rj写入rd。
表3-2中的逻辑运算和条件赋值指令不区分源操作数数据类型。在LA32架构下,它们对32位寄存器执行运算;在LA64架构下,则对64位寄存器执行运算。下面通过示例说明具体指令的用法。
4.1.3. 移位运算指令
移位运算指令用于移动源操作数中的二进制位:按照操作符要求移动N位,并把结果写入目标位置。移位类型包括逻辑左移、逻辑右移、算术右移和循环移位(循环右移)。逻辑左移N位时,源操作数整体向高位移动,高N位丢弃,低N位补0;逻辑右移N位时,整体向低位移动,低N位丢弃,高N位补0;算术右移N位时,整体向低位移动,高N位补符号位。循环移位在这里仅包括循环右移,即把移出的低N位放到高N位,高位部分相应右移。N的取值范围由具体指令决定。LoongArch移位运算指令如表3-3所示。
使用表3-3中的指令时,需要注意32位和64位操作数对应的移位量不同。
32位操作数移位指令
sll.w、srl.w、sra.w、rotr.w,以及带立即数的32位移位指令slli.w、srli.w、srai.w、rotri.w,移位量均为5位无符号立即数,范围为0~31。64位操作数移位指令
sll.d、srl.d、sra.d、rotr.d,以及带立即数的64位移位指令slli.d、srli.d、srai.d、rotri.d,移位量均为6位无符号立即数ui6,范围为0~63。
实际编程时,应根据移位量是否为常数、操作数位宽等因素选择合适的移位指令。下面将通过示例说明具体用法。
4.1.4. 位操作指令
位操作指令用于按指定规则处理源操作数中的部分二进制位,功能包括高位符号扩展、按条件计数、指定位数的数据拼接和提取、按条件逆序、指定位置替换等。LoongArch支持的位操作指令如表3-4所示。
Tip
位操作指令仅在LA64架构下支持。
表3-4中,逆序操作包括按位逆序、按字节逆序和按半字逆序,源操作数可以是32位或64位。按位逆序的指令名前缀为bitrev,按字节逆序的前缀为revb,按半字逆序的前缀为revh。执行逆序时,处理器会根据源操作数长度和逆序方式,把源操作数划分为不同操作组;具体分组可通过指令名后缀判断:.w表示源操作数为32位且按字分组,.4b表示源操作数为32位且按4字节分组,.8b表示源操作数为64位且按8字节分组,.2h表示源操作数为32位且按2个半字分组,.2w表示源操作数为64位且按2个字分组,等等。
Tip
位操作指令仅在LA64架构上支持,LA32架构没有相关位操作指令。在LA64架构上编写需要兼容LA32架构的应用程序时,需要注意这一点。