2015-12-15 7 views
4

Это в AT & T синтаксисПереход от Ассамблеи на C код

.global bar 
.type bar, @function 

bar: 

pushl %ebp 
movl %esp, %ebp 
pushl %ebx 
subl $20, %esp 
movl 8($ebp), %ebx 
movl $1, %eax 
cmpl $1, %ebx 
jle .L3 
leal -1(%ebx), %eax //subtracts 1 from ebx and stores into eax 
movl %eax, (%esp) //putting on stack frame 
call bar    //recursive call 
addl %ebx, %eax  // adds %ebx and %eax 


.L3     //returns %eax 
addl $20, %esp 
popl %ebx 
popl %ebp 
ret     //end of bar 

Так что я думаю, что здесь происходит в основном он проверяет,% EBX является < = 1, и если он есть, он возвращается один. в противном случае он вызывает bar с x--;

Так что мой C код:

int bar (int x) 
{ 
if (x<= 1) 
return 1; 
else 
return x + bar(x-1); 
} 

Рекурсивный вызов действительно обманывая меня здесь. Я понимаю, что он вызывает бар с новым регистром% eax (который в основном становится x-1). Так оно просто возвращает сумму чисел?

+1

Я не вижу, где вы получите 'x + (x-1)' для аргумента рекурсивного вызова. Там есть 'add' * after *, который вы, кажется, смешиваете с тем, что происходит * перед * вызовом. 'leal -1 (% ebx),% eax' является * инструкцией по эффективному адресу * загрузки. Он задокументирован в руководстве x86 (много он-лайн). Он используется для хранения адреса со смещением, но здесь, как это иногда бывает, он используется для сохранения одной инструкции, делая шаг и постоянное добавление за один раз. Я не вижу, где происходит * добавление 'eax' в стек *. Вы должны означать * переместить * его в стек, который необходимо передать ему как arg для 'bar'. – lurker

+0

@ lurker Я понял, что испортил рекурсивный вызов, я работаю над его повторной обработкой. команда leal вычитает 1 из ebx и сохраняет ее в eax, хотя правильно? – bkennedy

+1

Да, это правильно. – lurker

ответ

4

Я аннотировать это так:

bar:      // bar() { 
    pushl %ebp   // function prologue 
    movl %esp, %ebp  // 
    pushl %ebx   // 
    subl $20, %esp  // 
    movl 8($ebp), %ebx // %ebx = x 
    movl $1, %eax  // %eax = 1 
    cmpl $1, %ebx  // if (x > 1) 
    jle .L3    // { 
    leal -1(%ebx), %eax //  %eax = x - 1 
    movl %eax, (%esp) //  put (x - 1) on stack 
    call bar    //  %eax = bar(x - 1) 
    addl %ebx, %eax  //  %eax += x 
.L3      // } 
    addl $20, %esp  // function epilogue 
    popl %ebx   // 
    popl %ebp   // 
    ret     // return %eax 
         // } 

Таким образом, C выглядит вполне соответствует тому, что вы писали:

int bar (int x) 
{ 
    if (x > 1) 
    return bar(x - 1) + x; 

    return 1; 
} 

Для исторического интереса: Я собрал ваш o строгий (неправильный) код C с использованием clang -m32 -S и после «оптимизации» вручную вручную, чтобы устранить пару store/load, я получил что-то похожее на ваш код сборки, но было довольно ясно, что вы ошибались в этот момент. Вы исправили это с тех пор.

+0

Я бы упростил комментарии к 'push% ebp/mov% esp,% ebp' к' // шаблону фреймов кадров. «Stack as called» даже не совсем точен, так как это делается после толчка. Кроме того, «разматывание» подразумевает (ко мне все равно), следуя связанным списком кадров стека, создавая цепочку вызовов в кадр стека некоторых родительских функций. Я бы сказал, «верните указатель стека туда, где мы сохранили наши регистры», или что-то в этом роде. –

+1

@PeterCordes: Достаточно честно - я заменил эти комментарии «прологом» и «эпилогом», который, как я думаю, довольно стандартный. –

+0

Да, мне это нравится. Это не часть функциональной логики, а лишь часть ее реализации для этого ABI (с некоторыми регистрами, сохраняющими вызовы и обычным соглашением стека). Не то, на что вам нужно сосредоточиться, когда отслеживать фактическую логику, чтобы увидеть, что делает функция. –

1
int bar(int x) 
{ 
if (x<= 1) 
return 1; 
else 
return x+bar(x-1); 
} 

суммирует х к 1 в порядке возрастания

Смежные вопросы