问题描述
我一直在尝试将此功能转换为程序集:
void foo (int a[],int n) {
int i;
int s = 0;
for (i=0; i<n; i++) {
s += a[i];
if (a[i] == 0) {
a[i] = s;
s = 0;
}
}
}
但是出了点问题。
这是我到目前为止所做的:
.section .text
.globl foo
foo:
.L1:
pushq %rbp
movq %rsp,%rbp
subq $16,%rsp
movl $0,-16(%rbp) /*s*/
movl $0,-8(%rbp) /*i*/
jmp .L2
.L2:
cmpl -8(%rbp),%esi
jle .L4
leave
ret
.L3:
addl $1,-8(%rbp)
jmp .L2
.L4:
movl -8(%rbp),%eax
imull $4,%eax
movslq %eax,%rax
addq %rdi,%rax
movl (%rax),%eax
addl %eax,-16(%rbp)
cmpl $0,%eax
jne .L3
/* if */
leaq (%rax),%rdx
movl -16(%rbp),%eax
movl %eax,(%rdx)
movl $0,-16(%rbp)
jmp .L3
我正在使用.c模块(例如,使用int nums [5] = {65,23,11,34}
来编译.s模块,并且我将返回相同的数组而不是{65,99,34}
。
有人可以帮我吗?
解决方法
大概您有一个可以生成AT&T语法的编译器。查看编译器生成的程序集输出可能更具指导性。这是我对演示的重新制定:
#include <stdio.h>
void foo (int a[],int n)
{
for (int s = 0,i = 0; i < n; i++)
{
if (a[i] != 0)
s += a[i];
else
a[i] = s,s = 0;
}
}
int main (void)
{
int nums[] = {65,23,11,34};
int size = sizeof(nums) / sizeof(int);
foo(nums,size);
for (int i = 0; i < size; i++)
fprintf(stdout,i < (size - 1) ? "%d," : "%d\n",nums[i]);
return (0);
}
在未启用优化的情况下进行编译通常比优化代码要困难得多,因为它会从内存中加载结果并将结果溢出到内存中。如果您花时间在学习如何编写有效的汇编上,那么您将学不到很多东西。
使用Godbolt compiler explorer和-O2优化进行编译可以产生更有效的代码;这对于消除不必要的指令,标签等也很有用,在这种情况下,这些指令会是视觉噪音。
根据我的经验,使用 -O2 优化足够聪明,可以让您重新考虑对寄存器的使用,重构等。 -O3 有时也可以优化 积极进取-展开循环,向量化等,以便轻松跟踪。
最后,对于您介绍的情况,有一个完美的折衷方案:-Os,它可以实现 -O2 的许多优化,但不会以增加代码大小为代价。我将程序集粘贴在这里只是为了进行比较:
foo:
xorl %eax,%eax
xorl %ecx,%ecx
.L2:
cmpl %eax,%esi
jle .L7
movl (%rdi,%rax,4),%edx
testl %edx,%edx
je .L3
addl %ecx,%edx
jmp .L4
.L3:
movl %ecx,(%rdi,4)
.L4:
incq %rax
movl %edx,%ecx
jmp .L2
.L7:
ret
请记住,调用约定将 pointer 传递给(a)
中的%rdi
和(n)
中的'count'%rsi
。这些是正在使用的calling conventions。请注意,您的代码不会通过%rdi
“取消引用”或“索引”任何元素。绝对值得逐步阅读代码(即使有帮助,也可以用笔和纸来理解),以了解分支条件以及如何在元素a[i]
上进行读写。
奇怪的是,使用代码的内部循环:
s += a[i];
if (a[i] == 0)
a[i] = s,s = 0;
似乎用-Os生成的代码比我使用的内部循环更有效:
foo:
xorl %eax,%eax
xorl %edx,%edx
.L2:
cmpl %eax,%esi
jle .L6
movl (%rdi,%ecx
addl %ecx,%edx
testl %ecx,%ecx
jne .L3
movl %edx,4)
xorl %edx,%edx
.L3:
incq %rax
jmp .L2
.L6:
ret
提醒我保持简单!