【问题标题】:Trying to translate a C function to x86_64 AT&T assembly试图将 C 函数转换为 x86_64 AT&T 程序集
【发布时间】:2021-01-23 00:13:02
【问题描述】:

我一直在尝试将此函数转换为程序集:

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 模块编译 .s 模块,例如,使用 int nums [5] = {65, 23, 11, 0, 34} 并返回相同的数组而不是 {65, 23, 11 , 99, 34}

有人可以帮我吗?

【问题讨论】:

  • if,您已经通过将a[i] 加载到eax 中摧毁了rax,如果您到了这一点,它甚至被称为0。我很惊讶你甚至得到任何东西而不是崩溃。编辑:你甚至没有走那么远,因为你在.L2 的条件被逆转了。 PS:学习使用调试器。
  • @Lucy:你知道leaq (%rax), %rdx 只是一种低效的mov %rax, %rdx 方式吗?我有一种感觉,你期待它做其他事情,但我不确定那是什么。
  • lea 移动到movl (%rax), %eax 之前并将jle 反转为jg 可以修复代码。

标签: c assembly x86-64 att


【解决方案1】:

大概你有一个可以生成 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, 0, 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,%rax,4)
.L4:
        incq    %rax
        movl    %edx, %ecx
        jmp     .L2
.L7:
        ret

请记住,调用约定将指针传递给%rdi 中的(a),以及%rsi 中的“计数”(n)。这些是正在使用的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,%rax,4), %ecx
        addl    %ecx, %edx
        testl   %ecx, %ecx
        jne     .L3
        movl    %edx, (%rdi,%rax,4)
        xorl    %edx, %edx
.L3:
        incq    %rax
        jmp     .L2
.L6:
        ret

提醒我保持简单!

【讨论】:

  • -Os 的一个缺点是不幸的循环结构,顶部是 cmp/jcc,底部是 jmp。不幸的是,它不仅仅是 jmp outside 循环(跳转到底部的 cmp/jcc),而是运行一次而不是每次迭代。 Why are loops always compiled into "do...while" style (tail jump)?。这对于代码大小是中性的;似乎是一个愚蠢的选择。
  • 比较 -O3 -fno-tree-vectorize -fno-unroll-loops-O2 会很有趣; GCC 只在-O3 处积极地将 if 转换为 cmov。 gcc optimization flag -O3 makes code slower than -O2How to remove "noise" from GCC/clang assembly output? 默认情况下,GCC 不会在 -O3 处展开(除了作为自动矢量化的一部分),但 clang 会;幸运的是,相同的选项对两者都适用。 (旧的 clang 需要 -fno-vectorize,而不是 ...-tree-vectorize;新的 clang 支持 GCC 兼容选项)
  • @PeterCordes - 是的,我可能在这里超卖了-Os 选项。 -O2 代码逻辑可能更容易理解。 -O3 程序集看起来相同。至于 clang,我讨厌它过度的默认展开和矢量化。在许多情况下,它的启发式方法似乎有点“过时”。
  • 通常-O1 甚至-Og 有利于稍微忠实于源版本,但也会禁用一些窥视孔优化。 -Os 对于初学者来说值得考虑,尽管对于循环结构的当前选择,我很难推荐 gcc -Osclang -Os 在这里循环正常。 godbolt.org/z/eWMq5s。结果 gcc、clang 和 ICC 找不到自动矢量化的方法;对于前缀和(非常元素之间的串行依赖) 具有条件存储,这并不奇怪,因此需要 AVX512 屏蔽存储不吸,以及改组。
猜你喜欢
  • 2019-04-04
  • 1970-01-01
  • 2021-03-05
  • 2016-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-25
相关资源
最近更新 更多