
Binaryen - это библиотека инфраструктуры компилятора и инструментов для Webassembly, написанная в C ++. Он направлен на то, чтобы сделать компиляцию в webassembly , быстрая и эффективная :
Легко : Binaryen имеет простой C API в одном заголовке, а также может использоваться из JavaScript. Он принимает ввод в форму, похожую на Webassembly, но также принимает общий график потока управления для компиляторов, которые предпочитают это.
FAST : Внутренний IR Binaryen использует компактные структуры данных и предназначен для полностью параллельной CodeGen и оптимизации с использованием всех доступных ядер ЦП. IR Binaryen также очень легко и быстро компилируется в Webassembly, потому что он по сути является подмножеством webassembly.
Эффективно : оптимизатор Binaryen имеет много проходов (см. Обзор позже вниз), которые могут улучшить размер и скорость кода. Эти оптимизации направлены на то, чтобы сделать Binaryen достаточно мощным, чтобы их само по себе использовали в качестве бэкэнда компилятора. Одна конкретная область, ориентированная на оптимизацию, специфичные для веб-сайта (то, что компиляторы общего назначения могут не сделать), которые вы можете думать как мини-минимум WASM, аналогично минификации для JavaScript, CSS и т. Д., Каждый из которых являются специфичными для языка.
Набор инструментов с использованием Binaryen в качестве компонента (обычно работает wasm-opt ), включают:
Emscripten (c/c ++)wasm-pack (ржавчина)J2CL (java; J2Wasm )Kotlin (Kotlin/Wasm)Dart (трепетание)wasm_of_ocaml (ocaml)Для получения дополнительной информации о том, как некоторые из них работают, см. Части архитектуры инструментов блог -блога V8 WASMGC.
Компиляторы, использующие Binaryen в качестве библиотеки , включают:
AssemblyScript которая составляет вариант TypeScript для webassemblywasm2js , который собирает webassembly в JSAsterius , который собирает Haskell в WebassemblyGrain , которое компилирует зерно в WebassemblyBinaryen также предоставляет набор утилит инструментов , которые могут
Проконсультируйтесь с инструкциями, если вы заинтересованы в участии.
Внутренний ИК Binaryen разработан, чтобы быть
Есть несколько различий между бинарновым ИК и языком Webassembly:
catch в функции обработки исключений) представлены в виде подпрементов pop .--generate-stack-ir --print-stack-ir , который напечатает стек IR, это гарантированно будет действительным для PARSERS WASM.)ref.func , должна быть либо в таблице, либо объявлена через (elem declare func $..) . Binaryen излучит эти данные при необходимости, но это не представляет их в IR. То есть IR может работать над тем, чтобы не думать о объявлении ссылок на функции.local.get должен быть структурно преобладал local.set Set для проверки (что гарантирует, что мы не читаем значение по умолчанию NULL). Несмотря на то, что вы можете заметить, есть некоторые мелкие детали, которые вы можете заметить:Block в бинарном IR не мешает проверке. Безымянные блоки никогда не излучаются в бинарный формат (мы просто испускаем их содержимое), поэтому мы игнорируем их в целях не нулевых местных жителей. В результате, если вы читаете текст WASM, излучаемый Binaryen, вы можете увидеть, что, кажется, является кодом, который не должен проверять в соответствии с спецификацией (и не может проверять в текстовых анализаторах текста WASM), но эта разница не будет существовать в двоичном формате (двоичные файлы, испускаемые Binary, всегда будут работать везде, в стороне от ошибок, конечно).requiresNonNullableLocalFixups() в pass.h и класс LocalStructuralDominance .funcref ref.func Это также не нулева.try_table отправляет на ветви (если мы развеваемся, нулевое никогда не отправляется), то есть он отправляет (ref exn), а не (ссылка null exn). В обоих случаях, если GC не включен, мы излучаем менее перестроенный тип в двоичном файле. При чтении двоичного файла более изысканные типы будут применяться, когда мы строим ИК.br_if более утонченные в Binaryen IR: они имеют тип значения, когда значение входит. Использование более утонченного типа здесь гарантирует, что мы оптимизируем наилучшим образом, используя всю информацию о типе, но это означает, что некоторые операции по обработке обработки могут выглядеть немного по -другому. В частности, когда мы излучаем br_if , чей тип более утончен в бинарном IR, тогда мы излучаем актерский состав сразу после него, так что вывод имеет правильный тип в спецификации WASM. Это может вызвать несколько байтов дополнительного размера в редких случаях (мы избегаем этого накладных расходов в общем случае, когда значение br_if не используется).stringview_wtf16 и т. Д.) Быть с использованием ref.cast . Это упрощает IR, так как он позволяет ref.cast всегда использоваться во всех местах (и оно опускается до ref.as_non_null , где это возможно в оптимизаторе). Спецификация StringRef, кажется, не допускает этого, и исправить, что двоичный писатель заменит ref.cast , который приводит к тому, что строковое представление не нулевому типу на ref.as_non_null . ref.cast Строковой вид, который не является ооперам, полностью пропускается.В результате вы можете заметить, что конверсии в оба конца (wasm => binaryen ir => wasm) немного изменяют код в некоторых угловых случаях.
src/wasm-stack.h ). Stack IR позволяет кучу оптимизации, которые предназначены для формы бината Webassembly (но Stack IR менее эффективен для общей оптимизации, чем основной бинарный IR). Если у вас есть файл WASM, который был особенно хорошо оптимизирован, простое преобразование в оба конца (просто прочитайте и пишите, без оптимизации) может вызвать более заметные различия, поскольку BinaryN вписывает его в более структурированный формат Binary IR. Если вы также оптимизируете во время преобразования в оба конца, то Stack IR Opts будет запущен, и окончательный WASM будет лучше оптимизирован.Примечания при работе с бинарным IR:
Бинарные внутренние функции выглядят как вызовы импорта, например,
( import " binaryen-intrinsics " " foo " ( func $foo ))Реализация их таким образом позволяет им читать и записанными другими инструментами, и это избегает запутанных ошибок при ошибке двоичного формата, которая может произойти в этих инструментах, если у нас было пользовательское расширение двоичного формата.
Внутренний метод может быть оптимизирован оптимизатором. Если это не так, то это должно быть понижено перед отправкой WASM, так как в противном случае он будет выглядеть как вызов импорта, которого не существует (и виртуальные машины будут показывать ошибку, не имея надлежащего значения для этого импорта). Это последнее понижение не выполняется автоматически. Пользователь внутренней системы должен запустить проход для этого явно, потому что инструменты не знают, когда пользователь намерен закончить оптимизацию, поскольку пользователь может иметь трубопровод с множественными этапами оптимизации или может провести локальные эксперименты, или размывать/уменьшать, и т. Д. Только пользователь знает, когда окончательная оптимизация произойдет до того, как WASM «окончательно» и готова к выводу. Обратите внимание, что в целом некоторые дополнительные оптимизации могут быть возможны после окончательного опускания, и поэтому полезная схема заключается в том, чтобы оптимизировать после того, как обычно с внутренней частью, затем снизите их, затем оптимизируйте после этого, например:
wasm-opt input.wasm -o output.wasm -O --intrinsic-lowering -OКаждое внутреннее определяет свою семантику, которая включает в себя то, что с ним разрешено делать оптимизатор, и к тому, к чему его приведет последнее понижение. См. Intrinsics.h для подробных определений. Здесь появляется быстрое резюме:
call.without.effects : аналогично call_ref в том смысле, что он получает параметры, и ссылка на функцию вызова, и вызывает, что функционирует с этими параметрами, за исключением того, что оптимизатор может предположить, что вызов не имеет побочных эффектов и может быть в состоянии оптимизировать его (если у него нет результата, в целом, в целом). Этот репозиторий содержит код, который строит следующие инструменты в bin/ (см. Инструкции по строительству):
wasm-opt : загружает WebAssembly и запускает бинарные проходы IR.wasm-as : Сборка Webassembly в текстовом формате (в настоящее время S-экспрессия в формате S-Expression) в двоичный формат (проходя через бинарный ИК).wasm-dis : Un-Assembls Webassembly в двоичном формате в текстовый формат (проходя через бинарный IR).wasm2js : компилятор webassembly-js. Это используется Emscripten для генерации JavaScript в качестве альтернативы Webassembly.wasm-reduce : REDUCER TESTCASE для файлов webassembly. Учитывая файл WASM, который по какой-то причине интересен (скажем, он сбоя в конкретной виртуальной машине), WASM-Reduce может найти меньший файл WASM, который имеет одинаковое свойство, которое часто проще отладки. Смотрите документы для более подробной информации.wasm-shell : оболочка, которая может загружать и интерпретировать код Webassembly. Он также может запустить Spec Test Suite.wasm-emscripten-finalize : принимает двоичный файл WASM, произведенный LLVM+LLD, и выполняет эмпипенные проходы над ним.wasm-ctor-eval : инструмент, который может выполнять функции (или части функций) во время компиляции.wasm-merge : объединяет несколько файлов WASM в один файл, подключая соответствующие импорты для экспорта при этом. Как бундлер для JS, но для WASM.wasm-metadce : инструмент для удаления частей файлов WASM гибким способом, который зависит от того, как используется модуль.binaryen.js : автономная библиотека JavaScript, которая раскрывает бинарные методы для создания и оптимизации модулей WASM. Для сборки см. Binaryen.js на NPM (или загружайте его непосредственно с github или unpkg). Минимальные требования: Node.js V15.8 или Chrome V75 или Firefox V78.Все инструменты Binaryen детерминированы, то есть, учитывая те же входы, вы всегда должны получать одинаковые выходы. (Если вы видите случай, который ведет себя иначе, подайте проблему.)
Инструкции по использованию для каждого ниже.
Binaryen содержит много проходов оптимизации, чтобы сделать Webassembly меньше и быстрее. Вы можете запустить оптимизатор Binaryen, используя wasm-opt , но также их можно запустить при использовании других инструментов, таких как wasm2js и wasm-metadce .
addDefaultFunctionOptimizationPasses .wasm-opt --help , как их установить и другие детали.Смотрите каждый проход оптимизации для получения подробной информации о том, что он делает, но вот краткий обзор некоторых из соответствующих:
if у двух рук есть некоторые общие инструкции на своем конце).block до внешнего, где это возможно, уменьшая их номер.local.set Набор значения, которое уже присутствует в локальном. (Перекрывается с коасесколокалами; это достигает конкретной операции, только что упомянутой без всех других рабочих, выполняющих Coalescelocals, и, следовательно, полезно в других местах в трубопроводе оптимизации.)br или br_table (например, поворот block с br в середину в, if возможно, когда это возможно).local.get/set/tee " Пропуск оптимизации, делая такие вещи, как замена набора и Get с перемещением значения набора в Get (и создание футболки), где это возможно. Также создает возвратные значения block/if/loop вместо использования локального для передачи значения.if у которого нет содержимого, капля постоянного значения без побочных эффектов, block с одним ребенком и т. Д.«LTO» в вышеперечисленном означает, что оптимизация-это оптимизация времени, как оптимизация, так как она работает по нескольким функциям, но в некотором смысле Binaryen-это всегда «LTO», поскольку он обычно работает на последнем связанном WASM.
Усовершенствованные методы оптимизации в Binaryen Optimizer включают SSAification, Flat IR и Stack/Poppy IR.
Посмотрите на страницу Wiki Optimizer Cookbook, чтобы узнать больше о том, как эффективно использовать оптимизатор.
Binaryen также содержит различные проходы, которые делают другие вещи, кроме оптимизации, такие как легализация для JavaScript, Asyncify и т. Д.
Binaryen использует подмодули GIT (на момент написания только для GTEST), поэтому, прежде чем вы построете, вам придется инициализировать подмодули:
git submodule init
git submodule updateПосле этого вы можете построить с Cmake:
cmake . && make Требуется компилятор C ++ 17. На MacOS вам необходимо установить cmake , например, через brew install cmake . Обратите внимание, что вы также можете использовать ninja в качестве генератора: cmake -G Ninja . && ninja .
Чтобы избежать зависимости gtest, вы можете передать -DBUILD_TESTS=OFF to cmake.
Binaryen.js может быть построен с использованием Emscripten, который может быть установлен через SDK.
emcmake cmake . && emmake make binaryen_jsemcmake cmake -DBUILD_FOR_BROWSER=ON . && emmake makeИспользуя установщик Microsoft Visual Studio, установите компонент «Инструменты Visual C ++ для Cmake».
Генерировать проекты:
mkdir build
cd build
" %VISUAL_STUDIO_ROOT%Common7IDECommonExtensionsMicrosoftCMakeCMakebincmake.exe " ..Замените visual_studio_root путем к вашей установке Visual Studio. В случае, если вы используете инструменты Visual Studio Build, путь будет «C: Program Files (x86) Microsoft Visual Studio 2017 BuildTools».
Из командной строки разработчика создайте желаемые проекты:
msbuild binaryen.vcxprojCmake генерирует проект с именем "all_build.vcxproj" для удобного создания всех проектов.
Сборки распределяются различными инструментами, которые используют Binaryen, такие как Emscripten, wasm-pack и т. Д. Есть также официальные релизы на GitHub:
https://github.com/webassembly/binaryen/releases
В настоящее время включены сборки следующих платформ:
Linux-x86_64Linux-arm64MacOS-x86_64MacOS-arm64Windows-x86_64Node.js (экспериментальный): порт wasm-opt to javascript+webassembly. Запустите node wasm-opt.js в качестве замены на собственную сборку wasm-opt , на любой платформе, на которой работает Node.js. Требуется node.js 18+ (для потоков wasm eh и wasm). (Обратите внимание, что эта сборка может также работать в средах Deno, Bun или других Javascript+Webassembly, но тестируется только на Node.js.) Бегать
bin/wasm-opt [.wasm or .wat file] [options] [passes, see --help] [--help]Оптимизатор WASM получает WebAssembly в качестве входных данных и может запускать преобразование, а также печатать его (до и/или после преобразования). Например, попробуйте
bin/wasm-opt test/lit/passes/name-types.wast -all -S -o -Это выведет один из тестовых случаев в испытательном наборе. Чтобы запустить трансформацию, попробуйте
bin/wasm-opt test/lit/passes/name-types.wast --name-types -all -S -o - Пропуск name-types гарантирует, что у каждого типа есть имя и переименование исключительно имен длинных типов. Вы можете увидеть изменение, которое вызывает преобразование, сравнивая вывод двух команд.
Легко добавить свои собственные проходы преобразования в оболочку, просто добавьте файлы .cpp в src/passes и восстановить оболочку. Например, посмотрите на проход name-types .
Еще несколько заметок:
bin/wasm-opt --help для полного списка параметров и проходов.--debug излучит некоторую информацию отладки. Индивидуальные каналы отладки (определенные в исходном коде через #define DEBUG_TYPE xxx ) могут быть включены, передавая их в виде списка, разделенных запятыми строками. Например: bin/wasm-opt --debug=binary . Эти каналы отладки также могут быть включены через переменную среды BINARYEN_DEBUG .Бегать
bin/wasm2js [input.wasm file]Это распечатает JavaScript в консоли.
Например, попробуйте
bin/wasm2js test/hello_world.watЭтот выход содержит
function add ( x , y ) {
x = x | 0 ;
y = y | 0 ;
return x + y | 0 | 0 ;
}как перевод
( func $add (; 0 ;) ( type $0 ) ( param $x i32 ) ( param $y i32 ) ( result i32 )
( i32.add
( local.get $x )
( local.get $y )
)
)Выход WASM2JS находится в формате модуля ES6 - в основном, он преобразует модуль WASM в модуль ES6 (для запуска в более старых браузерах и версиях Node.js, которые вы можете использовать Babel и т. Д. Чтобы преобразовать его в ES5). Давайте посмотрим на полный пример называния этого Hello World Wat; Во -первых, создайте основной файл JS:
// main.mjs
import { add } from "./hello_world.mjs" ;
console . log ( 'the sum of 1 and 2 is:' , add ( 1 , 2 ) ) ;Запустите это (обратите внимание, что вам нужен достаточно новый Node.js с поддержкой модуля ES6):
$ bin/wasm2js test/hello_world.wat -o hello_world.mjs
$ node --experimental-modules main.mjs
the sum of 1 and 2 is: 3Вещи помнят с результатом WasM2JS:
-O или другой уровень оптимизации. Это будет оптимизироваться по всему трубопроводу (WASM и JS). Это не сделает все, что минифер JS будет, хотя, например, Minify Whitespace, так что вы все равно должны запустить обычный минифер JS после этого. wasm-ctor-eval выполняет функции или их части, во время компиляции. После этого он сериализует состояние выполнения в WASM, что все равно, что сделать «снимок». Когда WASM позже загружается и запускается в виртуальной машине, с этого момента он будет продолжать выполнять выполнение, не заново выполняя работу, которая уже была выполнена.
Например, рассмотрим эту небольшую программу:
( module
;; A global variable that begins at 0.
( global $global ( mut i32 ) ( i32.const 0 ))
( import " import " " import " ( func $import ))
( func " main "
;; Set the global to 1.
( global.set $global
( i32.const 1 ))
;; Call the imported function. This *cannot* be executed at
;; compile time.
( call $import )
;; We will never get to this point, since we stop at the
;; import.
( global.set $global
( i32.const 2 ))
)
)Мы можем оценить часть этого во время компиляции, как это:
wasm-ctor-eval input.wat --ctors=main -S -o - Это говорит о том, что существует одна функция, которую мы хотим выполнить («CTOR» короткий для «глобального конструктора», имя, которое происходит из кода, который выполняется до точки входа в программу), а затем печатать его как текст в stdout . Результат таково:
trying to eval main
...partial evalling successful, but stopping since could not eval: call import: import.import
...stopping
(module
(type $none_ = > _none (func))
(import " import " " import " (func $import ))
(global $global (mut i32) (i32.const 1))
(export " main " (func $0 _0))
(func $0 _0
(call $import )
(global.set $global
(i32.const 2)
)
)
) Регистрация показывает, что мы управляем оценить часть main() , но не все, как и ожидалось: мы можем оценить первый global.get , но затем мы останавливаемся на вызове импортируемой функции (потому что мы не знаем, какая эта функция будет, когда WASM фактически запускается в VM позже). Обратите внимание, как в результате вывода значение Global была обновлена с 0 до 1, и что первый global.get был удален: WASM теперь находится в состоянии, которое, когда мы запускаем его в виртуальной машине, будет плавно продолжать работать с точки зрения, в котором остановился wasm-ctor-eval .
В этом крошечном примере мы только что сохранили небольшой объем работы. Сколько работы можно сохранить, зависит от вашей программы. (Это может помочь сделать чистые вычисления заранее и оставить звонки на импорт как можно ближе.)
Обратите внимание, что имя wasm-ctor-eval связано с глобальными функциями конструктора, как упоминалось ранее, но нет никаких ограничений на то, что вы можете выполнить здесь. Любой экспорт из WASM может быть выполнен, если его содержимое подходит. Например, в Emscripten wasm-ctor-eval даже работает на main() когда это возможно.
wasm-merge объединяет файлы WASM вместе. Например, представьте, что у вас есть проект, который использует файлы WASM из нескольких инструментов. Тогда может быть полезно объединить их все в один файл WASM перед отправкой, поскольку в одном файле WASM вызовы между модулями становятся просто нормальными вызовами внутри модуля, что позволяет им внедрять, устранена мертвый код и т. Д., Потенциально улучшая скорость и размер.
wasm-merge работает в нормальных файлах WASM. В этом отношении он отличается от wasm-ld , так как wasm-ld работает на объектных файлах WASM. wasm-merge может помочь в многоотражающих ситуациях, когда хотя бы один из инструментов не использует объектные файлы WASM.
Например, представьте, что у нас есть эти два файла WASM:
;; a.wasm
( module
( import " second " " bar " ( func $second.bar ))
( export " main " ( func $func ))
( func $func
( call $second.bar )
)
) ;; b.wasm
( module
( import " outside " " log " ( func $log ( param i32 )))
( export " bar " ( func $func ))
( func $func
( call $log
( i32.const 42 )
)
)
) Имена файлов на вашем локальном драйве - a.wasm и b.wasm , но для целей слияния / объединения скажем, что первый известен как "first" , а второй - "second" . То есть мы хотим, чтобы первый модуль "second.bar" назвал функцию $func во втором модуле. Вот команда Wasm-Merge для этого:
wasm-merge a.wasm first b.wasm second -o output.wasmМы даем ему первый файл WASM, затем его имя, а затем второй файл WASM, а затем его имя. Объединенный вывод таков:
( module
( import " outside " " log " ( func $log ( param i32 )))
( export " main " ( func $func ))
( export " bar " ( func $func_2 ))
( func $func
( call $func_2 )
)
( func $func_2
( call $log
( i32.const 42 )
)
)
) wasm-merge объединил два файла в один, объединив свои функции, импорт и т. Д. При этом исправляя конфликты имен и подключая соответствующие импорты для экспорта. В частности, обратите внимание, как $func называет $func_2 , что именно то, что мы хотели: $func_2 - это функция со второго модуля (переименовано в то, чтобы избежать столкновения имени).
Обратите внимание, что выход WASM в этом примере может выиграть от дополнительной оптимизации. Во -первых, призыв к $func_2 теперь может быть легко подставлен, поэтому мы можем запустить wasm-opt -O3 , чтобы сделать это для нас. Кроме того, нам может не понадобиться весь импорт и экспорт, для которого мы можем запустить wasm-metadce. Хорошим рабочим процессом может быть управление wasm-merge , затем wasm-metadce , затем закончить с wasm-opt .
wasm-merge -это как будлер для файлов WASM, в смысле «Бандлера JS», но для WASM. То есть, с файлами WASM выше, представьте, что у нас есть этот код JS для создания экземпляра и подключения их во время выполнения:
// Compile the first module.
var first = await fetch ( "a.wasm" ) ;
first = new WebAssembly . Module ( first ) ;
// Compile the first module.
var second = await fetch ( "b.wasm" ) ;
second = new WebAssembly . Module ( second ) ;
// Instantiate the second, with a JS import.
second = new WebAssembly . Instance ( second , {
outside : {
log : ( value ) => {
console . log ( 'value:' , value ) ;
}
}
} ) ;
// Instantiate the first, importing from the second.
first = new WebAssembly . Instance ( first , {
second : second . exports
} ) ;
// Call the main function.
first . exports . main ( ) ; То, wasm-merge это в основном то, что делает JS: он подключает импорт для экспорта, разрешает имена, используя имена модулей, которые вы предоставили. То есть, используя wasm-merge мы перемещаем работу по подключению модулей со времени выполнения до сбора времени. В результате, после запуска wasm-merge нам нужно намного меньше JS, чтобы получить тот же результат:
// Compile the single module.
var merged = await fetch ( "merged.wasm" ) ;
merged = new WebAssembly . Module ( merged ) ;
// Instantiate it with a JS import.
merged = new WebAssembly . Instance ( merged , {
outside : {
log : ( value ) => {
console . log ( 'value:' , value ) ;
}
}
} ) ;
// Call the main function.
merged . exports . main ( ) ;Нам все еще нужно принести и составить объединенный WASM и предоставить ему импорт JS, но работа по подключению двух модулей WASM больше не нужна.
По умолчанию ошибки wasm-merge если есть перекрывающиеся имена экспорта. То есть wasm-merge будет автоматически обрабатывать имена функций перекрытия и т. Д., Поскольку они не видны извне (код по-прежнему ведет себя одинаково), но если мы переименован в экспорт, то снаружи необходимо изменить, чтобы ожидать новых имен экспорта, и поэтому мы ошибки в таком именем конфликты.
Если вы хотите переименовать экспорт, запустите wasm-merge с --rename-export-conflicts . Позднее экспорт будет иметь суффикс, добавленный к ним, чтобы убедиться, что они не совпадают с предыдущим экспортом. Суффиксы детерминированные, поэтому, как только вы увидите, что они есть, вы можете назвать их снаружи.
Другой вариант-использовать --skip-export-conflicts , которые просто пропустят позже экспорт, которые имеют противоречивые имена. Например, это может быть полезно в случае, когда первый модуль является единственным, который взаимодействует с внешними, а более поздние модули просто взаимодействуют с первым модулем.
wasm-merge использует функции с несколькими памятниками и мультител. То есть, если у нескольких входных модулей есть память, то у выходной WASM будет несколько воспоминаний и будут зависеть от функции нескольких памяти, что означает, что старые виртуальные машины WASM могут не могут запустить WASM. (В качестве обходного пути для таких старых виртуальных машин вы можете запустить wasm-opt --multi-memory-lowering чтобы снизить многочисленные воспоминания в одну.)
./check.py (или python check.py ) будет работать wasm-shell , wasm-opt и т. Д. На тестах в test/ и проверяет их выходы.
Скрипт check.py поддерживает некоторые варианты:
./check.py [--interpreter = /path/to/interpreter] [TEST1] [TEST2].../check.py --list-suites .emcc или nodejs на пути. Они не будут работать, если инструмент не будет найден, и вы увидите предупреждение.tests/spec , в подмодулях GIT. Запуск ./check.py должен обновить их. Обратите внимание, что мы пытаемся постепенно перенести бы наследие тесты WASM-OPT для использования lit и filecheck , когда мы их модифицируем. Для passes , которые выводят, это можно сделать автоматически с помощью scripts/port_passes_tests_to_lit.py и для не passes , которые выводят, см. #4779 для примера того, как сделать простой ручной порт.
Для освещенных испытаний тестовые ожидания (контрольные строки) часто могут автоматически обновляться, поскольку изменения вносятся в Binaryen. См. scripts/update_lit_checks.py .
Несоответствующие тесты также могут быть автоматически обновлены в большинстве случаев. См scripts/auto_update_tests.py .
./third_party/setup.py [mozjs | v8 | wabt | all] (или python third_party/setup.py ) устанавливает необходимые зависимости, такие как Spidermonkey JS Shell, оболочка V8 JS и WABT в third_party/ . Другие сценарии автоматически поднимают их при установке.
Запустите pip3 install -r requirements-dev.txt чтобы получить требования для тестов lit Обратите внимание, что вам нужно установить местоположение pip в вашем $PATH (на Linux, ~/.local/bin ).
./scripts/fuzz_opt.py [--binaryen-bin = build/bin] (или python scripts/fuzz_opt.py ) будет запускать различные режимы пушины на случайных входах со случайными проходами, пока не найдет возможную ошибку. Смотрите страницу вики для всех деталей.
Binaryen can read and write source maps (see the -ism and -osm flags to wasm-opt ). It can also read and read source map annotations in the text format, that is,
;; @ src.cpp:100:33
( i32.const 42 ) That 42 constant is annotated as appearing in a file called src.cpp at line 100 and column 33 . Source maps and text format annotations are interchangeable, that is, they both lead to the same IR representation, so you can start with an annotated wat and have Binaryen write that to a binary + a source map file, or read a binary + source map file and print text which will contain those annotations.
The IR representation of source map info is simple: in each function we have a map of expressions to their locations. Optimization passes should update the map as relevant. Often this "just works" because the optimizer tries to reuse nodes when possible, so they keep the same debug info.
The text format annotations support a shorthand in which repeated annotations are not necessary. For example, children are tagged with the debug info of the parent, if they have no annotation of their own:
;; @ src.cpp:100:33
( i32.add
( i32.const 41 ) ;; This receives an annotation of src.cpp:100:33
;; @ src.cpp:111:44
( i32.const 1 )
)The first const will have debug info identical to the parent, because it has none specified, and generally such nesting indicates a "bundle" of instructions that all implement the same source code.
Note that text printing will not emit such repeated annotations, which can be confusing. To print out all the annotations, set BINARYEN_PRINT_FULL=1 in the environment. That will print this for the above add :
[i32] ;; @ src.cpp:100:33
( i32.add
[i32] ;; @ src.cpp:100:33
( i32.const 41 )
[i32] ;; @ src.cpp:111:44
( i32.const 1 )
) (full print mode also adds a [type] for each expression, right before the debug location).
The debug information is also propagated from an expression to its next sibling:
;; @ src.cpp:100:33
( local.set $x
( i32.const 0 )
)
( local.set $y ;; This receives an annotation of src.cpp:100:33
( i32.const 0 )
) You can prevent the propagation of debug info by explicitly mentioning that an expression has not debug info using the annotation ;;@ with nothing else:
;; @ src.cpp:100:33
( local.set $x
;; @
( i32.const 0 ) ;; This does not receive any annotation
)
;; @
( local.set $y ;; This does not receive any annotation
( i32.const 7 )
) This stops the propagatation to children and siblings as well. So, expression (i32.const 7) does not have any debug info either.
There is no shorthand in the binary format. That is, roundtripping (writing and reading) through a binary + source map should not change which expressions have debug info on them or the contents of that info.
The source maps format defines a mapping using segments , that is, if a segment starts at binary offset 10 then it applies to all instructions at that offset and until another segment begins (or the end of the input is reached). Binaryen's IR represents a mapping from expressions to locations, as mentioned, so we need to map to and from the segment-based format when writing and reading source maps.
That is mostly straightforward, but one thing we need to do is to handle the lack of debug info in between things that have it. If we have ABC where B lacks debug info, then just emitting a segment for A and C would lead A 's segment to also cover B , since in source maps segments do not have a size - rather they end when a new segment begins. To avoid B getting smeared in this manner, we emit a source maps entry to B of size 1, which just marks the binary offset it has, and without the later 3 fields of the source file, line number, and column. (This appears to be the intent of the source maps spec, and works in browsers and tools.)
Binaryen also has optional support for DWARF. This primarily just tracks the locations of expressions and rewrites the DWARF's locations accordingly; it does not handle things like re-indexing of locals, and so passes that might break DWARF are disabled by default. As a result, this mode is not suitable for a fully optimized release build, but it can be useful for local debugging.
Binaryen's name was inspired by Emscripten 's: Emscripten's name suggests it converts something into a script - specifically JavaScript - and Binaryen's suggests it converts something into a binary - specifically WebAssembly . Binaryen began as Emscripten's WebAssembly generation and optimization tool, so the name fit as it moved Emscripten from something that emitted the text-based format JavaScript (as it did from its early days) to the binary format WebAssembly (which it has done since WebAssembly launched).
"Binaryen" is pronounced in the same manner as "Targaryen".
Да, это так. Here's a step-by-step tutorial on how to compile it under Windows 10 x64 with with CMake and Visual Studio 2015 . However, Visual Studio 2017 may now be required. Help would be appreciated on Windows and OS X as most of the core devs are on Linux.