Правильная инструкция в цикле for

Я читаю книгу Компьютерные системы: взгляд программиста (2-й издание) и практическая задача 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

Проанализируйте работу этого кода, а затем выполните следующие действия:

А. Используйте версию на ассемблере, чтобы заполнить недостающие части кода 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;
}

Объясните мне, пожалуйста, почему у реальной функции нетипичное поведение в этой функции. И я не понимаю, как этот ассемблерный код дает этот оператор val = (val << 1) | (x & 0x1)


person A.M. Sultanov    schedule 19.05.2013    source источник
comment
Если вы возьмете val = + val и подставите его в строку ниже, вы получите val = (val + val) | (x & 1), что, очевидно, совпадает с val = (val << 1) | (x & 0x1). leal ведет себя не совсем обычно.   -  person harold    schedule 19.05.2013


Ответы (2)


В вашем коде:

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

Здесь val += val эквивалентно (val*2), что фактически равно 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

Потому что, если val + val был отдельным оператором, компилятор обычно помещает его в регистр eax, а не в edx (я не уверен, что это всегда так). Итак, для кода, который вы указали, возможные решения:

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

or

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

or

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

person raj raj    schedule 21.05.2013

х >>= 1; означает умножение x на 2, что в двоичном формате сдвигается влево или добавляет 0 справа

x >>= 1; == x * 2; == x +=x;
person Alazar    schedule 18.01.2016
comment
Разве x>>=1 не является логическим сдвигом вправо (и делением на 2)? - person Michael Petch; 18.01.2016