Введение

В компьютерном программировании, тханк — это подпрограмма, используемая для внедрения вычисления в другую подпрограмму. Тханки в основном используются для отсрочки вычисления до тех пор, пока не потребуется его результат, или для вставки операций в начало или конец другой подпрограммы. Они также широко применяются при генерации кода компилятором и в модульном программировании. Термин возник как шутливая, неправильная форма глагола "думать". Он отсылает к первоначальному использованию тханков в компиляторах ALGOL 60, которым требовался специальный анализ (обдумывание) для определения, какую рутину следует сгенерировать.

Предыстория

В первые годы исследований компиляторов проводились широкие эксперименты с различными стратегиями вычисления. Ключевым вопросом было то, как компилировать вызов подпрограммы, если аргументы могут быть произвольными математическими выражениями, а не константами. Один из подходов, известный как "вызов по значению", вычисляет все аргументы перед вызовом и затем передает полученные значения в подпрограмму. В конкурирующем подходе "вызов по имени" подпрограмма получает невычисленное выражение аргумента и должна самостоятельно его вычислить. Простая реализация "вызова по имени" может подставлять код выражения аргумента вместо каждого вхождения соответствующего параметра в подпрограмму, но это может привести к созданию нескольких версий подпрограммы и множеству копий кода выражения. Для улучшения компилятор может генерировать вспомогательную подпрограмму, называемую thunk, которая вычисляет значение аргумента. Адрес и окружение этой вспомогательной подпрограммы затем передаются исходной подпрограмме вместо исходного аргумента, где она может быть вызвана столько раз, сколько необходимо. Питер Ингерман впервые описал thunk в контексте языка программирования ALGOL 60, который поддерживает вычисление по имени.

Числовые расчеты, требующие оценки в нескольких точках

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

Совместимость

Thunks широко используются для обеспечения взаимодействия между программными модулями, чьи процедуры не могут вызывать друг друга напрямую. Это может происходить из-за различных соглашений о вызовах, выполнения в разных режимах процессора или адресных пространствах, или, по крайней мере, одна из них выполняется в виртуальной машине. Компилятор (или другой инструмент) может решить эту проблему, генерируя thunk, который автоматизирует дополнительные шаги, необходимые для вызова целевой процедуры, будь то преобразование аргументов, их копирование в другое место или переключение режима процессора. Успешный thunk минимизирует дополнительную работу, которую должен выполнить вызывающий код по сравнению с обычным вызовом. Большая часть литературы, посвященная thunks для взаимодействия, относится к различным платформам Wintel, включая MS DOS, OS/2, Windows и .NET, а также к переходу от 16-битной к 32-битной адресации памяти. По мере перехода клиентов с одной платформы на другую, thunks были необходимы для поддержки устаревшего программного обеспечения, написанного для старых платформ. Переход от 32-битного кода к 64-битному на x86 также использует форму thunking (WoW64). Однако, поскольку адресное пространство x86-64 больше, чем доступное для 32-битного кода, старый механизм "универсального thunk" нельзя использовать для вызова 64-битного кода из 32-битного кода. Единственным случаем вызова 64-битного кода из 32-битного является thunking Windows API в WoW64 для 32-битной версии.

Наложения и динамические связи

На системах, не имеющих аппаратной поддержки автоматической виртуальной памяти, thunks могут реализовать ограниченную форму виртуальной памяти, известную как наложения. При использовании наложений разработчик разделяет код программы на сегменты, которые можно загружать и выгружать независимо, и определяет точки входа в каждый сегмент. Сегмент, вызывающий другой сегмент, должен делать это косвенно, через таблицу переходов. Когда сегмент находится в памяти, записи в его таблице переходов осуществляют переход внутрь этого сегмента. Когда сегмент выгружается, его записи заменяются "thunks перезагрузки", которые могут загрузить его по требованию. Аналогично, системы, динамически связывающие модули программы во время выполнения, могут использовать thunks для соединения этих модулей. Каждый модуль может вызывать другие через таблицу thunks, которую компоновщик заполняет при загрузке модуля. Таким образом, модули могут взаимодействовать, не имея предварительных знаний об их расположении в памяти.