【问题标题】:Converting c code into assembly for arrays将 c 代码转换为数组的程序集
【发布时间】:2021-04-18 16:49:55
【问题描述】:

我目前正在学习如何将 C 代码转换为汇编 x86 64 位代码。我的代码是:

long add_arrays(long *arr1, long *arr2, unsigned long num, long *result)
{
    unsigned long sum = 0, i = 0; 
    while(i < num) {
        result[i] = arr1[i] + arr2[i]; 
        sum = sum + result[i]; 
        i++;
    }
    return sum; 
}

当我将它转换为程序集时,我得到一个 0 或地址号的输出。我的代码是:

        .global add_arrays_asm

add_arrays_asm:
        xor %r8, %r8

while_start:
        cmp %r8, %rdx
        jge while_break

        movq (%rdi, %r8, 8), %r9 
        addq (%rsi, %r8, 8), %r9 
        movq (%rcx, %r8, 8), %r10 
        movq (%r9, %r8, 8), %rcx 
        addq (%r10, %r8, 8), %rax 
        addq (%rcx,%r8, 8),  %rax
        inc %r8
        jmp while_start

while_break:
        ret

我用来测试的代码是:

printf("Testing add_arrays_asm\n");
    long l3[] = {3, 23, 32, 121, 0, 43, 95, 4};
    long l4[] = {-823,12,-1222,84834, -328, 0, 9, -1387};
    long res1[] = {0, 0, 0, 0, 0, 0, 0, 0}; 
    long sum1 = add_arrays_asm(l3, l4, 8, res1); 
    int j = 0; 
    for(j = 0; j < 8; j++) {
        printf("%8ld + %8ld = %8ld\n", l3[j], l4[j], res1[j]); 
    }
    printf("        Sum = %ld\n\n", sum1); 

我是汇编编码的新手,所以我找不到代码出错的地方。任何帮助,将不胜感激。谢谢。

【问题讨论】:

  • movq (%rcx, %r8, 8), %r10: 将result[i] 的值加载到寄存器有什么意义,因为它只是您打算覆盖的垃圾(但您实际上从未这样做,另一个错误)? movq (%r9, %r8, 8), %rcx:对此的评论是不准确的:它实际上类似于rcx = r9[i]。由于r9 包含了添加arr1[i]+arr2[i] 的结果,而且这不是指针,所以这一行没有意义,很可能会崩溃。

标签: c assembly x86-64


【解决方案1】:

你的条件跳转不正确。

    cmp %r8, %rdx
    jge while_break

这意味着

if (rdx >= r8)
    break;

所以你直接中断循环而不计算任何东西。此外,您应该使用jae 而不是jge 进行无符号比较,因为您将其声明为unsigned long num。虽然如果该值没有溢出最大有符号整数以使其为负数,则可能不是问题。

您的加载和存储的另一件事似乎不正确。除此之外,您最好尽量减少内存访问,如果您真的不需要它,则无需从内存中重新加载。

另外,您忘记将%rax 归零,您应该在计算开始时使用sum = 0

解决这个问题

我修改了你的汇编代码。

func.S


.section .text
.global add_arrays_asm

add_arrays_asm:
    #rdi -> arr1
    #rsi -> arr2
    #rdx -> num
    #rcx -> result
    #r8 -> i

    xorl    %r8d, %r8d          # i = 0
    xorl    %eax, %eax          # sum = 0
while_start:
    cmpq    %rdx, %r8           # if (i >= num)
    jae     while_break         #   goto while_break

    movq    (%rdi, %r8, 8), %r9 # r9  = arr1[i]
    addq    (%rsi, %r8, 8), %r9 # r9 += arr2[i]
    movq    %r9, (%rcx, %r8, 8) # result[i] = r9
    addq    %r9, %rax           # sum += r9
    incq    %r8                 # i++
    jmp     while_start         # goto while_start

while_break:
    ret

main.c


#include <stdio.h>

long add_arrays_asm(long *arr1, long *arr2, unsigned long num, long *result);

int main()
{
    printf("Testing add_arrays_asm\n");
    long l3[] = {3, 23, 32, 121, 0, 43, 95, 4};
    long l4[] = {-823,12,-1222,84834, -328, 0, 9, -1387};
    long res1[] = {0, 0, 0, 0, 0, 0, 0, 0}; 
    long sum1 = add_arrays_asm(l3, l4, 8, res1); 
    int j = 0; 
    for(j = 0; j < 8; j++) {
        printf("%8ld + %8ld = %8ld\n", l3[j], l4[j], res1[j]); 
    }
    printf("        Sum = %ld\n\n", sum1); 
}

执行

ammarfaizi2@integral:/tmp/testz$ gcc -Wall -Wextra func.S main.c -o main
ammarfaizi2@integral:/tmp/testz$ valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./main
==748530== Memcheck, a memory error detector
==748530== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==748530== Using Valgrind-3.17.0 and LibVEX; rerun with -h for copyright info
==748530== Command: ./main
==748530== 
Testing add_arrays_asm
       3 +     -823 =     -820
      23 +       12 =       35
      32 +    -1222 =    -1190
     121 +    84834 =    84955
       0 +     -328 =     -328
      43 +        0 =       43
      95 +        9 =      104
       4 +    -1387 =    -1383
        Sum = 81416

==748530== 
==748530== HEAP SUMMARY:
==748530==     in use at exit: 0 bytes in 0 blocks
==748530==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==748530== 
==748530== All heap blocks were freed -- no leaks are possible
==748530== 
==748530== For lists of detected and suppressed errors, rerun with: -s
==748530== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
ammarfaizi2@integral:/tmp/testz$ 

【讨论】:

  • 感谢您的帮助
  • @Bob:另见Why are loops always compiled into "do...while" style (tail jump)? - 编译器会稍微改变你的循环,以便它可以在底部有一个条件分支,而不是jmpgodbolt.org/z/T4jdMEzaM 显示 GCC 和 clang -O3(禁用自动矢量化和循环展开)以这种方式编译 - clang 显然比 GCC 更好(不浪费指令复制指针 args),尽管我之前有 xor-zeroed EAX n==0 测试以避免单独的循环退出路径。
  • @PeterCordes wew,这令人惊讶。我认为 GCC 没有任何充分的理由来复制这些寄存器。乍一看,我认为它试图尽量减少r8r9 的使用。
  • 谈论异或零 EAX,在 while 循环之前使用 if 语句和 goto 有助于 GCC 生成单个 xor %eax, %eax 和仅单个 ret(这很好)。但不适用于 Clang。 godbolt.org/z/x3To3jbE5
  • @AmmarFaizi:不太可能是故意的,更可能是由其他优化过程引入的回归,这有助于其他事情,但没有人注意到它伤害了这个案例。你想在gcc.gnu.org/bugzilla 上报告它(使用关键字“missed-optimization”),还是我应该?哦,没关系,它已经固定在(主干)上。 godbolt.org/z/7vxMPxfcq。所以 GCC11,或者可能是 10.4,应该有修复。有趣的是,您能够让 GCC 避免不必要的尾部重复,方法是使源代码更像您想要的 asm,而不是让优化通过来完成
猜你喜欢
  • 2017-06-22
  • 1970-01-01
  • 1970-01-01
  • 2018-03-30
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-03-15
相关资源
最近更新 更多