Рекурсивная сборка фибоначчи

Сегодня я написал рекурсивный фибоначчи в сборке, и он не работает. Я скомпилировал его в объектный файл с помощью NASM и сделал его эльфом с gcc.
Когда я вхожу в 1 или 2, функция работает отлично, но когда я вхожу в 3, 4, 5, 6 и более, функция не работает. Я думаю, что есть проблема, когда функция вызывает себя.

Этот код:

SECTION .data ;init data




str: db "This equal: %d",10,0

SECTION .text   ;asm code


extern printf
global main

main:
push ebp
mov ebp,esp
;--------------------


push 03  ; the index 
call _fibonacci
add esp,4

push DWORD eax
push str
call printf


;---------------------

mov esp,ebp
pop ebp
ret

Это функция:

_fibonacci:

push ebp
mov ebp,esp


mov ebx, [ebp+8] ;; param n 
cmp ebx,0
jne CHECK2

mov eax,0
jmp _endFIbofunc        

CHECK2: 
    cmp ebx,0x1
    jne ELSE3
    mov eax,1
jmp _endFIbofunc

ELSE3:

mov ebx,[ebp+8] 
dec ebx  ;; n-1


;;  FIRST call
push ebx
call _fibonacci
add esp,4
mov edx,eax

;;  SEC CALL
dec ebx
push ebx
call _fibonacci
add esp,4 
add eax,edx


mov eax,[ebp-4]

_endFIbofunc:

mov esp,ebp
pop ebp
ret

После того, как я запустил его на Ubuntu 16.04, он отправил ошибку:

Ошибка сегментации (сброс ядра)

Что может быть проблемой?

linux,recursion,assembly,fibonacci,

0

Ответов: 3


0

Вы должны сохранить (нажать) регистры, которые вы собираетесь изменить в рекурсивном вызове. А затем восстановить их исходные значения (поп). Это должно решить проблему.

Что-то вроде этого:

  • Нажимайте все регистры, которые вы собираетесь использовать в своей функции (кроме mov eax, [ebp-4], где вы получите возвращаемое значение)
  • Нажмите [ebp-4], так как это ваш параметр
  • Вызовите функцию
  • Добавить esp, 4
  • Поп всех регистров, которые вы нажали на первом шаге, теперь в обратном порядке

0
_fibonacci:
    push ebp
    mov  ebp, esp
    sub  esp, 4

Вы используете память, EAXне вкладывая в нее что-то полезное! Вы должны зарезервировать это пространство в своей пролог функции:

EAX

Возвращаясь с первого рекурсивного вызова, вы положили результат из EDXэтого словаря памяти.
Возвращаясь со второго рекурсивного вызова, вы добавляете к EBXсодержимому этого словаря памяти.
Таким образом, EAXрегистр больше не будет сбиваться.


Почему вы используете EBXрегистр вообще? Если вы используете его, вы должны сохранить его, как было объяснено в ответе Аль Кеппа .
Если вы начнете с ввода аргумента EAX, вы знаете, что для обоих значений ниже 2 (так что 0 и 1) результат будет равен аргументу. Просто.

    mov  eax, [ebp+8] ;; param n 
    cmp  eax, 2
    jb   _endFIbofunc        

Если вы не балансируете стек сразу после первого рекурсивного вызова, вы можете просто уменьшить двоеточие, которое уже существует, и сделать второй рекурсивный вызов.

    dec  eax              ; n-1
    push eax              ;(*)
    call _fibonacci
    mov  [ebp-4], eax
    dec  dword ptr [esp]  ; n-2
    call _fibonacci
    add  esp,4            ;(*)
    add  eax, [ebp-4]

Весь процесс:

_fibonacci:
    push ebp
    mov  ebp, esp
    sub  esp, 4           ;(*)
    mov  eax, [ebp+8] ;; param n 
    cmp  eax, 2
    jb   _endFIbofunc        
    dec  eax              ; n-1
    push eax              ;(*)
    call _fibonacci
    mov  [ebp-4], eax
    dec  dword ptr [esp]  ;(*) n-2
    call _fibonacci
    add  esp,4            ;(*)
    add  eax, [ebp-4]
_endFIbofunc:
    mov  esp, ebp
    pop  ebp
    ret

0

В дополнение к другим предоставленным ответам, это альтернативное решение:

_fibonacci:
        mov     eax,[esp+4]             ;eax = n
        cmp     eax,2                   ;br if n < 2
        jb      _endFIbofunc
        dec     eax                     ;push n-1
        push    eax
        call    _fibonacci              ;returns eax = fib(n-1)
        xchg    eax,[esp]               ;eax = n-1, [esp] = fib(n-1)
        dec     eax                     ;push n-2
        push    eax
        call    _fibonacci              ;returns eax = fib(n-2)
        add     eax,[esp+4]             ;eax = fib(n-1)+fib(n-2)
        add     esp,8
_endFIbofunc:
        ret

Общая информация - fib (47) является наибольшей <2 ^ 32. Число рекурсивных вызовов = 2 * fib (n + 1) -1.

 n     fib(n)      # calls

 0          0            1
 1          1            1
 2          1            3
 3          2            5
 4          3            9
 5          5           15
 6          8           25
 7         13           41
 8         21           67
 9         34          109
10         55          177
11         89          287
12        144          465
13        233          753
14        377         1219
15        610         1973
16        987         3193
17       1597         5167
18       2584         8361
19       4181        13529
20       6765        21891
21      10946        35421
22      17711        57313
23      28657        92735
24      46368       150049
25      75025       242785
26     121393       392835
27     196418       635621
28     317811      1028457
29     514229      1664079
30     832040      2692537
31    1346269      4356617
32    2178309      7049155
33    3524578     11405773
34    5702887     18454929
35    9227465     29860703
36   14930352     48315633
37   24157817     78176337
38   39088169    126491971
39   63245986    204668309
40  102334155    331160281
41  165580141    535828591
42  267914296    866988873
43  433494437   1402817465
44  701408733   2269806339
45 1134903170   3672623805
46 1836311903   5942430145
47 2971215073   9615053951
48 4807526976   ...
Linux, рекурсия, сборка, Фибоначи,
Похожие вопросы