sicp.io
5.5.5 · Пример скомпилированного кода

Скомпилированная рекурсия и передачи вызова

Полный контроллер factorial с входной меткой, меткой базового случая, сохраненными продолжениями, общей точкой возврата и возвратом через continue.

Вопрос для размышления

Какое состояние машины заменяет неявный стек вызовов рекурсивной процедуры исходного кода?

  • Чтение полного ассемблированного списка инструкций для одной рекурсивной процедуры
  • Определение меток entry, after-call, base-case и done
  • Сохранение продолжения вызывающей стороны и живого аргумента перед рекурсивным вызовом
  • Восстановление состояния перед умножением возвращенного значения
  • Косвенный возврат через регистр continue
  • Измерение числа входов, количества инструкций, максимальной глубины стека и финального баланса стека

Скомпилированный листинг начинается с factorial-entry. Каждый вызов, кроме базового случая, сохраняет continue и n, уменьшает n, устанавливает after-factorial в качестве нового адреса возврата и переходит обратно к той же метке entry. Базовый случай помещает 1 в val и выполняет возврат через continue. Метка after-factorial восстанавливает состояние вызывающей стороны, умножает n на возвращенное значение val и снова выполняет возврат.

Измерительный запуск начинается с n = 5 и регистром continue, указывающим на done. Наблюдается шесть входов, включая n = 0. Пять приостановленных вызовов хранят по два значения в стеке, поэтому максимальная глубина стека равна 10. Все десять значений восстанавливаются до инструкции halt. 66 выбранных инструкций и точный листинг контроллера измеряют эту конечную скомпилированную процедуру.

Код SICP6,732 из 1,048,576 байт UTF-8
Примеры
Результат
Вывод
Значение
Диагностика
Трасса выполнения0 / 0 событий
    Запуски происходят внутри браузера с отображением результата программы и трассы выполнения.
    Ожидаемый результат

    Листинг возвращает (((factorial-entry . 0) (after-factorial . 8) (base-case . 12) (done . 14)) 15 (perform test branch save save assign assign goto restore restore assign goto assign goto halt)). Запуск возвращает (15 complete 120 66 10 #t (5 4 3 2 1 0)).

    На что обратить внимание в трассе

    Проследите, как continue меняется с done на after-factorial перед каждым рекурсивным переходом. В базовом случае проследите пять косвенных возвратов через один и тот же код after-call. Сопоставьте каждую пару инструкций save с последующими restore и убедитесь, что глубина стека возвращается с 10 до 0 перед тем, как финальный goto достигнет done.

    Попробуйте сами

    Измените программу и сравните результат.

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

    Показать подсказку

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

    Завершить этот урок

    В этой главе пройдено 0 из 23 урока0%