我正在为 uni 赋值编写编译器,我目前正在从内部表示发出 asm。我对for (a = 1; a < 10; a++) do print a
有以下表示:
COPY a, 1 // Copy 1 to a
LABEL label0 //
LT tmp0, a, 10 // Store in tmp0 the result of the expression a < 10
JUMP_FALSE label1, tmp0 // Jump to label1 if tmp0 is false
PRINT a //
ADD a, a, 1 // Store in a the result of the expression a + 1
JUMP label0 //
LABEL label1 //
我为此发出以下 asm,其中常量和变量都作为正交性的全局变量发出:
#a := 1
movl var_1(%rip), %eax
movl %eax, var_a(%rip)
.label0:
#EXPR_START tmp0 = a < 10
movl var_a(%rip), %eax
movl var_10(%rip), %edx
cmpl %eax, %edx
setl %al
movzbl %al, %eax
movl %eax, var_tmp0(%rip)
#EXPR_END
#jmpf label1, tmp0
movl var_tmp0(%rip), %eax
testl %eax, %eax
jne .label1
#Print a
movl var_a(%rip), %eax
movl %eax, %esi
leaq printf_int(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
#EXPR_START a = a + 1
movl var_a(%rip), %eax
movl var_1(%rip), %edx
addl %edx, %eax
movl %eax, var_a(%rip)
#EXPR_END
#jmp label0
jmp .label0
.label1:
这似乎是在比较a <= 10
,但我不明白为什么。如果比较结果为"小于",cmpl
和setl
组合不是将%al
的值设置为 1 吗?那么testl %eax, %eax
不应该在a < 10
时1
,jne
发生在a == 10
时吗?如果我的理解是错误的,我如何将比较的结果存储在寄存器中(如果比较成立,则为 1,否则为 0)?
下面是可以使用gcc asm.s
编译的完整asm。预期输出1 2 3 4 5 6 7 8 9
实际输出1 2 3 4 5 6 7 8 9 10
。
main:
pushq %rbp
movq %rsp, %rbp
#a := 1
movl var_1(%rip), %eax
movl %eax, var_a(%rip)
.label0:
#EXPR_START
movl var_a(%rip), %eax
movl var_10(%rip), %edx
cmpl %eax, %edx
setl %al
movzbl %al, %eax
movl %eax, var_tmp0(%rip)
#EXPR_END
#jmpf label1, tmp0
movl var_tmp0(%rip), %eax
movl $1, %edx
cmpl %eax, %edx
je .label1
#Print a
movl var_a(%rip), %eax
movl %eax, %esi
leaq _printf_int(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
#EXPR_START
movl var_a(%rip), %eax
movl var_1(%rip), %edx
addl %edx, %eax
movl %eax, var_a(%rip)
#EXPR_END
#jmp label0
jmp .label0
.label1:
popq %rbp
ret
.text
.globl var_tmp1
.data
.size var_tmp1, 4
var_tmp1:
.long 0
.text
.globl main
.text
.globl var_1
.data
.size var_1, 4
var_1:
.long 1
.text
.globl var_a
.data
.size var_a, 4
var_a:
.long 0
.text
.globl var_10
.data
.size var_10, 4
var_10:
.long 10
.text
.globl var_tmp0
.data
.size var_tmp0, 4
var_tmp0:
.long 0
_printf_int:
.string "%d "
movl var_a(%rip), %eax
movl var_10(%rip), %edx
cmpl %eax, %edx
setl %al
如果%edx
小于%eax
,即如果 10 小于a
,这将设置%al
到 1。这似乎与您想要的相反,因此要么交换cmpl
的操作数,要么反转您的测试(setge %al
)。
部分混淆可能来自这样一个事实,即汇编助记符最初是为英特尔语法发明的,其中双操作数指令的来源和目的地是另一个顺序:mov dest, src
而不是AT&T的movl %src, %dest
。 所以在英特尔语法中,如果rx
小于ry
,cmp rx, ry ; setl rz
会设置rz
,这与代数表示法rx < ry
匹配。 但对于AT&T语法来说,这变得cmpl %ry, %rx ; setl %rz
可能会向后看。 将第二个"目的地"操作数视为谓词"大约"的操作数可能会有所帮助:"如果%rx
小于%ry
则设置"。