以下是第5章中讲到的 CMOV 的指令的例子,原来的源码是这样的:
# cmovtest.s - An example of the CMOV instructions
.section .data
output:
.asciz "The largest value is %d\n"
values:
.int 105, 235, 61, 315, 134, 221, 53, 145, 117, 5
.section .text
.globl _start
_start:
nop
movl values, %ebx
movl $1, %edi
loop:
movl values(, %edi, 4), %eax
cmp %ebx, %eax
cmova %eax, %ebx
inc %edi
cmp $10, %edi
jne loop
pushl %ebx
pushl $output
call printf
addl $8, %esp
pushl $0
call exit
当前使用环境为 Linux cnhz-cmpl42 4.15.0-197-generic #208-Ubuntu SMP Tue Nov 1 17:23:37 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux,这个程序是没法运行的。所以修改成如下:
# cmovtest.s - An example of the CMOV instructions
.section .data
output:
.asciz "The largest value is %d\n"
values:
.int 105, 235, 61, 315, 134, 21, 53, 145, 1117, 50
temp:
.quad 0 #用于存放最大的那个数,因为这个参数是要加载到 rsi 寄存器的,如果用直接用原来的 ebx,则在 movl %ebx, %rsi 无法编译过,即使用 movq 也不行,
#所以用一个临时变量存放
.section .text
.globl main
main:
nop
movl $temp, %ecx #将 temp的地址加载到 ecx 寄存器
movl values, %ebx
movl $1, %edi
loop:
movl values(, %edi, 4), %eax
cmp %ebx, %eax
cmova %eax, %ebx
movl %ebx, (%ecx) #将最大值存放到 ecx 寄存器中地址所指的位置
inc %edi
cmp $10, %edi
jne loop
movq $output, %rdi
movq temp, %rsi
movq $0, %rax
call printf
ret
Makefile:
CC = gcc
CFLAGS = -g -no-pie
SRCS = $(shell ls -t | grep "\.s$$" | head -1)
a.out: $(SRCS)
$(CC) -o $@ $(SRCS) $(CFLAGS)
.PHONY: clean
clean:
rm -rf a.out
修改点:
1,修改入口点为 main,因为我直接用的 gcc 编译,Makefile 如上所示。
2,参数传送方式不一样了,movq $output, %rdi 和 movq temp, %rsi 传递第1、2 个参数,但在传递第2个参数时,如果直接用 movq %ecx, %rsi 会出现编译错误,这是两个不一样长度的寄存器,刚学我也不知道该如何正确使用,所以添加了一个临时变量来存放最大数,然后再从这个临时变量加载到 %rsi 寄存器中以达到目的。
以下还有另外一种写法,用的都是64位寄存器的名称,即全部使用以 r_ 开头的寄存器:
#cmov_test.s -- An example of the CMOV instructions
.section .data
output:
.string "The largest value is %d\n"
value: # 定义成 quad,即元素大小为 8 字节,所以下面的 value(,%rdi,8) 这里为 8
.quad 15, 24, 61, 36, 134, 221, 63, 145, 117, 59
.section .text
.global main
main:
nop
movq value, %rbx # 将 value 的第一个元素加载到 rbx 寄存器
movq $1, %rdi # 将立即数 1 加载到 rdi 寄存器
loop:
movq value(, %rdi, 8), %rax # 将偏移 value (%edi * 8) 个字节位置的值加载到 rax 寄存器,即第2、3、4、5、... 个元素
cmp %rbx, %rax # 比较两个寄存器里值的大小
cmova %rax, %rbx # 如果rax 里的值大于 rbx 里的值,则将 rax 里的值加载到 rbx
inc %rdi # 自增加 1
cmp $10, %rdi # 比较 rdi 里的值和立即数10,类似检查数组下标
jne loop # jump not equal -- 假如不等于则跳转到 loop 标签
movq $output, %rdi
movq %rbx, %rsi
movq $0, %rax
call printf
ret