【问题标题】:How this assembly code will be translated into c?这个汇编代码将如何翻译成c?
【发布时间】:2021-07-25 05:08:28
【问题描述】:

在 C 中我有以下结构:

typedef struct _Node{
    int data;
    struct _Node *left;
    struct _Node *right;
} Node;

以及以下汇编代码:

.section .text
.global _start

_start:
    mov $8, %esi
    mov $A, %rdi
    call func
    movq $60, %rax
    movq $0, %rdi
    syscall
    
func:
    pushq %rbp
    movq %rsp, %rbp
    cmp (%rdi), %esi
    jne continue
    mov $1, %eax
    jmp finish
    
continue:
    cmpq $0, 4(%rdi)
    je next
    pushq %rdi
    mov 4(%rdi), %rdi
    call func
    pop %rdi
    cmp $1, %eax
    je finish

next:
    cmpq $0, 12(%rdi)
    je fail
    pushq %rid
    mov 12(%rdi), %rdi
    call func
    pop %rdi
    cmp $1, %eax
    je finish
   
fail:
    mov $0, %rax

finish:
    leave
    ret

现在尝试用 C 编写它我有一个问题:

__long__ func ( Node *root, __int__ x){
            if (root->data == __x__ )
                return 1;

            if (root->left != null)
               if (_____??_____)
                   return ___ func(root->left, x)____;

            if (root->right != null)
                return ____func(root->right, x)____;
}

为什么我们有两个 if-if?如果 left 不为 null,则汇编代码使用 left 儿子调用该函数,并且不进行其他条件检查(即 cmp 调用)。

【问题讨论】:

  • 为什么要删除之前的问题?这似乎是相同的内容?
  • 它包含的图像非常糟糕,导致它失去了注意力@ecm plus 改变了一点
  • @stacker 编辑您的old question 是正确的解决方案。请勿删除和转发。
  • 公平地说,这个问题在目前的状态下是有些合理的。它可能不应该被认为是对前一个问题做错事的惩罚/报应。我们真正失去的只是结构缺少__attribute__((packed)) 的cmets,以及返回类型。虽然现在我们可以看到它检查子调用的返回值,但我们可以看到它是 32 位类型,所以 intunsigned,而不是 boollong。 (或者如果它是long,那么它正在做(int)func(root->left, x) == 1 什么的!)

标签: c assembly x86 x86-64 att


【解决方案1】:

第二个iffunc 成功时返回的那个。

bool func(Node *nd, int x)
{
    if (nd->data == x)
        return true;

    if (nd->left)
    {
        if (func(nd->left, x) == true)
            return true;
    }

    if (nd->right)
        return func(nd->right, x);
    
    return false;
}

我选择bool 是因为它在这个函数的上下文中是有意义的,即使intlong 或其他可能也有效。

【讨论】:

  • 我们可以从cmp $1, %eax看到返回类型是一个32位整数。 (或long,它正在做if ((int)func(...) == 1)。)在x86-64 System V(和Windows x64)中,允许返回寄存器在实际返回值之外的寄存器位中有高垃圾。 (arg-passing 也一样,except unofficially narrow integers are sign- or zero- extended to 32-bit;clang 在被调用者中依赖于此,GCC 和 clang 在调用者中都这样做。ICC 不...)
  • 我还是不明白为什么我们需要那个额外的条件......
  • 使用了额外的条件,当左路径找到值时,它不应该也在右路径中查找。可能是二叉树实现。
  • @stacker:请注意,next: 块,执行 nd->right 调用,仅在逻辑上等价于尾调用。它实际上是检查返回值是否为== 1,如果是则返回,否则返回0。所以它在func(nd->right, x) 返回值上毫无意义地分支。如果你想用 C 语言表达逻辑,你需要在 if (nd->right) 块内,比如 { int rv = func(); / if (rv == 1) return rv; } (否则到 return false;
  • 这当然是毫无意义的,因为函数只能返回 0 或 1。所以 next: 块的 asm 可能更有效:mov 12(%rdi), %rdi / leave(清理堆栈)/test %rdi, %rdi/jnz func(优化的尾调用),否则落入xor %eax, %eax/ret。当然,具有完全优化的实际编译器会将尾递归转换为循环并进行检查,仅在左侧递归。如果您只启用部分优化 (gcc -O1),那么您也不会获得优化的尾调用。 godbolt.org/z/jPYYrn4d3