程序的机器级表示
常数, 变量, 运算
32位常数的装入
int f() { return 0x123; /* 291 */ }
int g() { return -1; }
int h() { return 0x1234; /* 4660 */ }
int i() { return 0xbb8; /* 3000 */ }
rv32gcc -O2 -c a.c
- rvobjdump -M no-alias -d a.o
- 结果:
a.o: file format elf32-littleriscv
Disassembly of section .text:
00000000 <f>:
0: 12300513 addi a0,zero,291
4: 00008067 jalr zero,0(ra)
00000008 <g>:
8: fff00513 addi a0,zero,-1
c: 00008067 jalr zero,0(ra)
00000010 <j>:
10: 00001537 lui a0,0x1
14: 23450513 addi a0,a0,564 # 1234 <i+0x1218>
18: 00008067 jalr zero,0(ra)
0000001c <i>:
1c: 00001537 lui a0,0x1
20: bb850513 addi a0,a0,-1096 # bb8 <i+0xb9c>
24: 00008067 jalr zero,0(ra)
f
: 指令的立即数位数足够, 直接使用addi
指令
- g
: 指令的立即数位数足够, 直接使用addi
指令(立即数为有符号数)
- h
: addi
指令的立即数位数不足以一次性完成操作, 因此先加载高20位(lui
), 再加载低12位(addi
): 0x1 << 12 + 0x234
- i
: bb8
只有三位, 但由于第11为为1
, 经过立即数的符号拓展后, 会出现负数, 并不是我们想要的bb8
, 因此, 同样的, 先加载高20位, 在利用立即数的符号拓展, 减去一个数: 0x1 << 12 + 0xbb8(12位补码, 为-1096)
- 根据addi
的语义, imm[11:0]
需要进行符号拓展
- 相当于, 对lui
的结果进行±2048的范围内进行调整
64位常数在RV64中的装入
- 指令:riscv64-gnu-linux -O2 -c b.c
- 汇编代码:
.file "b.c"
.option pic
.text
.align 1
.globl j
.type j, @function
j:
li a0,38178816
addi a0,a0,-1329
slli a0,a0,35
ret
.size j, .-j
.align 1
.globl k
.type k, @function
k:
ld a0,.LC0
ret
.size k, .-k
.section .rodata.cst8,"aM",@progbits,8
.align 3
.LC0:
.dword 1311768467139281697
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
j
: 38178816 << 35 + (-1329)
- k
: 直接将对应的立即数存储在内存中, 使用ld
从内存中加载
[!note] 为什么从内存中加载 如果一个立即数位数比较长, gcc会选择直接从内存中加载, 用来换取更短的指令序列. 指令太多并不好, 在乱序执行的高性能CPU中, 取指令的代价大于取数据: - 在流水线中, 指令是数据的上游 - 如果从内存中取数据, 只需要让取数逻辑等待, 可以先执行其他无关的指令 - 如果从内存中取指令, 则整条流水线都需要等待指令
64位常数在RV32中的装入
- 指令:
rv32gcc -O2 -S b.c
- 编译结果:
.file "b.c" .option pic .text .align 2 .globl j .type j, @function j: li a1,305418240 li a0,0 addi a1,a1,1656 ret .size j, .-j .align 2 .globl k .type k, @function k: lla a5,.LC0 lw a0,0(a5) lw a1,4(a5) ret .size k, .-k .section .rodata.cst8,"aM",@progbits,8 .align 3 .LC0: .word -2023406815 .word 305419896 .ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0" .section .note.GNU-stack,"",@progbits
- 解析: 使用两个32位的寄存器联合存放
- 使用
clang
进行编译:.text .attribute 4, 16 .attribute 5, "rv32i2p0_m2p0_a2p0_c2p0" .file "b.c" .globl j .p2align 1 .type j,@function j: lui a0, 74565 addi a1, a0, 1656 li a0, 0 ret .Lfunc_end0: .size j, .Lfunc_end0-j .globl k .p2align 1 .type k,@function k: lui a0, 554580 addi a0, a0, 801 lui a1, 74565 addi a1, a1, 1656 ret .Lfunc_end1: .size k, .Lfunc_end1-k .ident "Ubuntu clang version 14.0.0-1ubuntu1.1" .section ".note.GNU-stack","",@progbits .addrsig