it-swarm.dev

Разница между JUMP и CALL

Чем отличаются инструкции JUMP и CALL? Как это относится к понятиям более высокого уровня, таким как GOTO или вызов процедуры? (Я прав в сравнении?)

Вот что я думаю:

JUMP или GOTO - это перенос элемента управления в другое место, и элемент управления не возвращается автоматически в точку, откуда он вызывается.

С другой стороны, вызов CALL или процедуры/функции возвращается к точке, откуда он вызывается. Из-за этого различия в их природе языки обычно используют стек, а кадр стека выдвигается, чтобы "запомнить" место для возврата для каждой вызванной процедуры. Это поведение относится и к рекурсивным процедурам. В случае хвостовой рекурсии, однако, нет необходимости "выдвигать" кадр стека для вызова каждого.

Ваши ответы и комментарии будут высоко оценены.

20
user59634

Вы в основном правы, если говорите о CALL/JMP в сборке x86 или о чем-то подобном. Основным отличием является:

  • JMP выполняет переход к локации, ничего не делая
  • CALL помещает в стек текущий указатель инструкций (точнее: один после текущей инструкции), а затем JMP в местоположение. С RET вы можете вернуться туда, где вы были.

Обычно CALL - это просто удобная функция, реализованная с использованием JMP. Вы могли бы сделать что-то вроде

          movl $afterJmp, -(%esp)
          jmp location
afterJmp:

вместо звонка.

23
Anteru

Вы совершенно правы относительно разницы между прыжком и коллом.

В примере с одной функцией с хвостовой рекурсией компилятор может повторно использовать существующий кадр стека. Однако, это может стать более сложным с взаимно рекурсивными функциями:

void ping() { printf("ping\n"); pong(); }
void pong() { printf("pong\n"); ping(); }

Рассмотрим случай, когда ping () и pong () являются более сложными функциями, которые принимают разное количество параметров. статья Марка Пробста подробно описывает реализацию хвостовой рекурсии для GCC.

1
Greg Hewgill

Я думаю, у вас есть общая идея.

Это зависит от архитектуры, но в целом от аппаратного уровня:

  • Инструкция перехода изменит счетчик программы , чтобы продолжить выполнение в другой части программы.

  • Инструкция вызова переведет текущее местоположение программы (или текущее местоположение + 1) в стек вызовов и перейдет к другой части программы. Инструкция возврата затем вытолкнет местоположение из стека вызовов и вернется к исходному положению (или исходному положению + 1).

Таким образом, инструкция перехода близка к GOTO, а инструкция вызова близка к процедурному/функциональному вызову.

Кроме того, по причине того, что стек вызовов используется при выполнении вызовов функций, добавление слишком большого количества адресов возврата в стек вызовов посредством рекурсии вызовет переполнение стека .

Когда я изучаю ассемблер, мне легче работать с процессорами RISC , чем с процессорами x86, так как в нем меньше инструкций и более простых операций.

0
coobird

Одна поправка к вашим мыслям: не только с tail recursion, но, как правило, с tail Calls, нам не нужен стековый фрейм, и, следовательно, мы можем просто использовать JMP там ( при условии, что аргументы были установлены правильно).

0
Ingo