13.4. 汇编伪指令类

【例1】 li 指令示例

# 加载小立即数(单条指令完成)
li.w  $t0, 42            # 将42加载到$t0

# 加载16位范围内的立即数
li.w  $a0, 0x1234        # 加载0x1234到$a0

# 加载大立即数(自动拆分为多条指令)
li.d  $s0, 0x1FFFFFFFF   # 加载64位大立即数(汇编器自动展开为 lu12i.w + ori + lu32i.d + lu52i.d)

# 加载系统调用相关立即数
li.w  $a7, 93            # exit系统调用号:93
li.w  $a0, 100           # 退出码100

【例2】 move 指令示例

move $r4, $r5        # $r4 = $r5
move $r6, $r0        # $r6 = 0 (清零)

【例3】 la 指令示例:PC 相对寻址

当目标符号位于 PC 相对寻址范围内(±1GB)时,la 通常会展开为:

la $rd, symbol
pcaddu12i $rd, %pcrel_hi(symbol)  # 加载高20位
addi.d    $rd, $rd, %pcrel_lo(symbol)  # 添加低12位偏移

【例4】 la 指令示例:绝对地址

需要加载绝对地址时,例如访问全局变量地址,la 可能展开为:

# 假设我们有: la $rd, global_var
lu12i.w $rd, %hi(global_var)      # 加载高20位
addi.d  $rd, $rd, %lo(global_var) # 添加低12位

【例5】 la 指令示例:构造64位地址

对于完整64位地址,la 可能需要展开为多条指令:

# 复杂情况下的展开
lu12i.w $rd, %hi(symbol)        # 高20位
ori     $rd, $rd, %lo(symbol)   # 低12位
slli.d  $rd, $rd, 32            # 左移32位
lu32i.d $t0, %hi2(symbol)       # 高32-51位
ori     $t0, $t0, %lo2(symbol)  # 低32-43位
or      $rd, $rd, $t0           # 合并两部分

【例6】 la 指令示例:通过 GOT(Global Offset Table)加载

在位置无关代码(PIC)中,访问全局符号通常需要经过 GOT:

# 加载GOT中符号的地址
la $t0, %got(symbol)      # 加载symbol在GOT中的偏移
ld.d $t1, $t0, 0($gp)     # 从GOT中加载实际地址

# 简化形式
la $t0, %call16(symbol)   # 用于函数调用的GOT偏移

【例7】 la 指令示例:加载 TLS(Thread-Local Storage)地址

访问线程局部存储变量时,可使用如下写法:

# 加载TLS变量地址
la $t0, %tls_ie(symbol)   # Initial Exec模型
la $t0, %tls_le(symbol)   # Local Exec模型

【例8】 la 指令示例:大小端模式

LoongArch64 支持动态切换大小端模式,符号地址加载时也要考虑当前字节序:

.byte_order little        # 设置小端模式
la $t0, my_data           # 按小端模式加载地址

.byte_order big           # 设置大端模式
la $t0, my_data           # 按大端模式加载地址

【例9】 call 指令示例

# 1. 调用本地函数
call my_function

# 2. 调用外部函数(通过PLT)
call printf

# 3. 调用通过寄存器间接寻址的函数
la $t0, function_pointer
call $t0

# 4. 带偏移的调用
call table_start, 16  # 调用table_start + 16处的函数

【例10】 ret 指令示例

my_function:
    # 函数体
    # ... 计算、处理等
    
    # 返回
    ret

# 带返回值的函数
add_numbers:
    add.d $a0, $a0, $a1  # $a0 = $a0 + $a1
    ret                  # 返回结果在$a0中

# 复杂函数
complex_function:
    # 保存被调用者保存的寄存器
    addi.d $sp, $sp, -16
    st.d $s0, $sp, 0
    st.d $s1, $sp, 8
    
    # 函数逻辑
    li $s0, 42
    li $s1, 84
    add.d $a0, $s0, $s1
    
    # 恢复寄存器
    ld.d $s0, $sp, 0
    ld.d $s1, $sp, 8
    addi.d $sp, $sp, 16
    
    # 返回
    ret

【例11】 jr 指令示例

# 1. 标准返回(等同于ret)
jr $ra

# 2. 从其他寄存器跳转
la $t0, alternative_return
jr $t0

# 3. 间接函数调用
function_pointer:
    .dword my_function
    .dword your_function

# 调用函数指针
ld.d $t0, $zero, function_pointer  # 加载函数地址
jalr.hb $ra, $t0, 0               # 调用函数