1

我正在读一本书 Computer Systems: A Programmer's Perspective (2nd Edition) 和 Practice Problem 3.23 让我有点困惑:

函数 fun_b 具有以下整体结构:

int fun_b(unsigned x) {
   int val = 0;
   int i;
   for ( ____;_____;_____) {
   }
   return val;
}

gcc C 编译器生成以下汇编代码:

x at %ebp+8
1 movl 8(%ebp), %ebx
2 movl $0, %eax
3 movl $0, %ecx
.L13:
5 leal (%eax,%eax), %edx
6 movl %ebx, %eax
7 andl $1, %eax
8 orl  %edx, %eax
9 shrl %ebx   Shift right by 1
10 addl $1, %ecx
11 cmpl $32, %ecx
12 jne .L13

对该代码的操作进行逆向工程,然后执行以下操作:

A. 使用汇编代码版本来填写 C 代码的缺失部分。

我的解决方案。

int fun_b(unsigned x) {
   int val = 0;
   int i;
   for ( i = 0 ;i < 32;i++) {
      val  += val; //because leal (%eax,%eax), edx  --> %edx = %eax + %eax
      val = val | x & 0x1;
      x >>= 1;
   }
   return val;
}

书的解决方案。

int fun_b(unsigned x) {
  int val = 0;
  int i;
  for (i = 0; i < 32; i++) {
    val = (val << 1) | (x & 0x1);
    x >>= 1;
  }
 return val;
}

请向我解释为什么 leal 函数在此函数中具有非典型行为。而且我不明白这个汇编代码是如何产生这个语句的val = (val << 1) | (x & 0x1)

4

2 回答 2

0

在您的代码中:

val  += val;
val = val | x & 0x1;

在这里,val += valwhich 等价于(val*2)which 实际上等于val左移1

但我认为只有当汇编代码类似于以下内容时,您的解决方案才是正确的:

x at %ebp+8
1 movl 8(%ebp), %ebx
2 movl $0, %eax
3 movl $0, %ecx
.L13:
5 addl  %eax, %eax
6 movl %ebx, %edx
7 andl $1, %edx
8 orl  %edx, %eax
9 shrl %ebx   # shift right by 1
10 addl $1, %ecx
11 cmpl $32, %ecx
12 jne .L13

因为 ifval + val是一个单独的语句,编译器通常将它放在 eax 寄存器中而不是 edx 中(我不确定是否总是如此)。因此,对于您给出的代码,可能的解决方案是:

val = (val << 1) | (x & 0x1);

或者

val = (val + val) | (x & 0x1);

或者

val = (val * 2) | (x & 0x1);

于 2013-05-21T04:27:20.097 回答
0

x >>= 1; 表示将 x 乘以 2,二进制是向左移动或在右侧添加 0

x >>= 1; == x * 2; == x +=x;
于 2016-01-18T16:45:00.747 回答