Можно ли добавить новую функцию в экземпляр модуля Webassembly?
Я пишу программу wasm, которая будет динамически генерировать функции wasm в виде байт-кода (включая сигнатуру типа, вектор локальных переменных и последовательность инструкций тела; все, что определяет функцию в соответствии со спецификацией). Мне нужен эффективный способ выполнения этих функций (т. е. иметь возможность получить к ним доступ) из уже созданного работающего модуля.
Кажется, что большинство реализаций делают такие вещи, просто создавая новый модуль из сгенерированного кода, подключая необходимые импорты, а затем вызывая новый модуль из JavaScript.
Мне нужно сделать это без JavaScript, а в идеале и без создания нового модуля. Кажется, что это должно быть выполнимо относительно простым способом:
- Просто добавьте новую функцию к существующему вектору функций текущего модуля с новым расширением . Очевидно, что необходимо позаботиться о том, чтобы сгенерированный код ссылался на другие функции, глобальные переменные, импорты и т. д. по их соответствующим индексам.
- Ссылайтесь на новую функцию по ее новому
, включая звонки чтобы вызвать его косвенно.
Основываясь на моем понимании wasm, шаг 1 невозможен, потому что нет инструкции добавить новую функцию к функции по умолчанию.
Если фактическая инструкция в спецификации не является стартовой, кажется, что это может быть альтернативно выполнено через API времени выполнения, такое как WASI, который может ввести метод API для изменения текущего модуля на месте. AFAICT WASI в настоящее время не проектирует для этого и не планирует. Я ошибаюсь в этом или есть другой интерфейс времени выполнения, который планирует это сделать?
2 ответа
WebAssembly поддерживает добавление функций к запущенным экземплярам через таблицу функций, которую можно импортировать из среды хоста.
Очень нетривиальным примером этого является то, что Emscripten реализует динамическое связывание совершенно отдельных модулей WebAssembly с существующими (https://emscripten.org/docs/compiling/Dynamic-Linking.html). В частности, Emscripten решает проблему добавления функций (с очень высокой производительностью) в работающий экземпляр WebAssembly. Реализация dlopen в Emscripten использует возможность внешнего изменения таблицы функций.
Если вы перейдете на https://pyodide.org/en/stable/console.html и наберете «import numpy», затем «numpy.array (10)», затем (1) весь C-код numpy будет добавлен в запуск экземпляра Python Wasm после его создания, и (2) вы только что вызвали одну из этих функций C из экземпляра Python WASM (REPL является его частью). Это также очень быстро, например, он не проходит через медленный слой Javascript.
Это основано на существующей спецификации WebAssembly. Хотя это нетривиально! Например, ключевым компонентом этой работы является цель wasm32-unknown-emscripten, являющаяся частью LLVM, которая генерирует независимый от фактического положения код (-fPIC). Я не знаю, есть ли какой-либо другой язык WebAssembly, кроме C/C++, с поддержкой -fPIC. См. https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md Кроме того, если вы посмотрите на сам emscripten, например, https://github.com/emscripten-core/emscripten/blob/main/src/library_dylink.js там есть код для разбора динамической библиотеки wasm и т.д.
WebAssembly не поддерживает добавление или удаление функций (или любых других элементов) из запущенных экземпляров, и я не знаю никаких предложений по добавлению таких функций.
Чтобы создать новую функцию (например, при написании JIT-компиляции), вам необходимо создать новый модуль. Есть ли особая причина, по которой вы хотите избежать этого маршрута?