微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

尝试将C函数转换为x86_64 AT&T程序集

如何解决尝试将C函数转换为x86_64 AT&T程序集

我一直在尝试将此功能转换为程序集:

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

提醒我保持简单!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。