From 9cd6c7dcf888355bf0b7667159a54d4723483860 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Fri, 25 Apr 2025 14:13:48 +0500 Subject: [PATCH 01/11] Initial commit: update CMakeLists and add some basic embind functionality --- CMakeLists.txt | 22 ++++---- src/binaryen-embind.cpp | 121 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 src/binaryen-embind.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 16e2add800f..5991c8683d5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -528,9 +528,11 @@ endif() # Note that we can't emit binaryen.js directly, as there is libbinaryen already # declared earlier, so we create binaryen_wasm/js.js, which must then be copied. if(EMSCRIPTEN) + add_compile_flag("-DEMSCRIPTEN_HAS_UNBOUND_TYPE_NAMES=0") + # binaryen.js WebAssembly variant - add_executable(binaryen_wasm ${binaryen_SOURCES}) - target_link_libraries(binaryen_wasm PRIVATE binaryen) + add_executable(binaryen_wasm src/binaryen-embind.cpp) + target_link_libraries(binaryen_wasm PRIVATE "-Wl,--whole-archive" binaryen) target_link_libraries(binaryen_wasm PRIVATE "-sFILESYSTEM") target_link_libraries(binaryen_wasm PRIVATE "-sEXPORT_NAME=Binaryen") target_link_libraries(binaryen_wasm PRIVATE "-sNODERAWFS=0") @@ -542,12 +544,10 @@ if(EMSCRIPTEN) target_link_libraries(binaryen_wasm PRIVATE "-sSINGLE_FILE") endif() target_link_libraries(binaryen_wasm PRIVATE "-sEXPORT_ES6") - target_link_libraries(binaryen_wasm PRIVATE "-sEXPORTED_RUNTIME_METHODS=stringToUTF8OnStack,stringToAscii") - target_link_libraries(binaryen_wasm PRIVATE "-sEXPORTED_FUNCTIONS=_malloc,_free") - target_link_libraries(binaryen_wasm PRIVATE "--post-js=${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.js-post.js") + target_link_libraries(binaryen_wasm PRIVATE "-lembind") + target_link_libraries(binaryen_wasm PRIVATE "--emit-tsd binaryen.d.ts") target_link_libraries(binaryen_wasm PRIVATE "-msign-ext") target_link_libraries(binaryen_wasm PRIVATE "-mbulk-memory") - target_link_libraries(binaryen_wasm PRIVATE optimized "--closure=1") # TODO: Fix closure warnings! (#5062) target_link_libraries(binaryen_wasm PRIVATE optimized "-Wno-error=closure") if (BYN_ENABLE_LTO) @@ -560,8 +560,8 @@ if(EMSCRIPTEN) install(TARGETS binaryen_wasm DESTINATION ${CMAKE_INSTALL_BINDIR}) # binaryen.js JavaScript variant - add_executable(binaryen_js ${binaryen_SOURCES}) - target_link_libraries(binaryen_js PRIVATE binaryen) + add_executable(binaryen_js src/binaryen-embind.cpp) + target_link_libraries(binaryen_js PRIVATE "-Wl,--whole-archive" binaryen) target_link_libraries(binaryen_js PRIVATE "-sWASM=0") target_link_libraries(binaryen_js PRIVATE "-sWASM_ASYNC_COMPILATION=0") @@ -590,14 +590,12 @@ if(EMSCRIPTEN) else() target_link_libraries(binaryen_js PRIVATE "-sEXPORT_ES6=1") endif() - target_link_libraries(binaryen_js PRIVATE "-sEXPORTED_RUNTIME_METHODS=stringToUTF8OnStack,stringToAscii") - target_link_libraries(binaryen_js PRIVATE "-sEXPORTED_FUNCTIONS=_malloc,_free") - target_link_libraries(binaryen_js PRIVATE "--post-js=${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.js-post.js") + target_link_libraries(binaryen_js PRIVATE "-lembind") + target_link_libraries(binaryen_js PRIVATE "--emit-tsd binaryen.d.ts") # js_of_ocaml needs a specified variable with special comment to provide the library to consumers if(JS_OF_OCAML) target_link_libraries(binaryen_js PRIVATE "--extern-pre-js=${CMAKE_CURRENT_SOURCE_DIR}/src/js/binaryen.jsoo-extern-pre.js") endif() - target_link_libraries(binaryen_js PRIVATE optimized "--closure=1") # Currently, js_of_ocaml can only process ES5 code if(JS_OF_OCAML) target_link_libraries(binaryen_js PRIVATE optimized "--closure-args=\"--language_out=ECMASCRIPT5\"") diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp new file mode 100644 index 00000000000..09b61de7960 --- /dev/null +++ b/src/binaryen-embind.cpp @@ -0,0 +1,121 @@ +#include "binaryen-c.h" +#include + +using namespace emscripten; + +namespace { + +EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); + +EMSCRIPTEN_DECLARE_VAL_TYPE(TypeList); + +namespace { +class ExpressionFactory { +protected: + uintptr_t module; + +public: + ExpressionFactory(uintptr_t module) : module(module) {} +}; +} // namespace + +class Module { +public: + uintptr_t ptr; + + Module() : Module(reinterpret_cast(BinaryenModuleCreate())) {} + + Module(uintptr_t ptr) : ptr(ptr) {} + + uintptr_t + block(const std::string& name, ExpressionList children, uintptr_t type) { + std::vector childrenVec = + convertJSArrayToNumberVector(children); + return reinterpret_cast(BinaryenBlock( + reinterpret_cast(ptr), + name.c_str(), + reinterpret_cast(childrenVec.begin().base()), + childrenVec.size(), + type)); + } + + uintptr_t if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse) { + return reinterpret_cast( + BinaryenIf(reinterpret_cast(ptr), + reinterpret_cast(condition), + reinterpret_cast(ifTrue), + reinterpret_cast(ifFalse))); + } + + uintptr_t loop(const std::string& label, uintptr_t body) { + return reinterpret_cast( + BinaryenLoop(reinterpret_cast(ptr), + label.c_str(), + reinterpret_cast(body))); + } + + uintptr_t br(const std::string& label, uintptr_t condition, uintptr_t value) { + return reinterpret_cast( + BinaryenBreak(reinterpret_cast(ptr), + label.c_str(), + reinterpret_cast(condition), + reinterpret_cast(value))); + } + + uintptr_t addFunction(const std::string& name, + BinaryenType params, + BinaryenType results, + TypeList varTypes, + uintptr_t body) { + std::vector varTypesVec = + convertJSArrayToNumberVector(varTypes); + return reinterpret_cast( + BinaryenAddFunction(reinterpret_cast(ptr), + name.c_str(), + params, + results, + varTypesVec.begin().base(), + varTypesVec.size(), + reinterpret_cast(body))); + } + + std::string emitText() { + char* text = BinaryenModuleAllocateAndWriteText( + reinterpret_cast(ptr)); + std::string str = text; + delete text; + return str; + } +}; + +Module* parseText(const std::string& text) { + return new Module( + reinterpret_cast(BinaryenModuleParse(text.c_str()))); +} +}; // namespace + +EMSCRIPTEN_BINDINGS(Binaryen) { + constant("none", BinaryenTypeNone()); + constant("i32", BinaryenTypeInt32()); + constant("i64", BinaryenTypeInt64()); + + class_("Module") + .constructor() + + .function("block", &Module::block) + .function("if", &Module::if_) + .function("loop", &Module::loop) + .function("br", &Module::br) + .function("break", &Module::br) + .function("br_if", &Module::br) + + .function("addFunction", &Module::addFunction) + + .function("emitText", &Module::emitText); + + function("parseText", parseText, allow_raw_pointer()); + + register_type("number[]"); + + register_type("number[]"); +} \ No newline at end of file From a8f3995c4aea543d91622aef0a0e5c052152e2f1 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Fri, 25 Apr 2025 17:08:01 +0500 Subject: [PATCH 02/11] Extend embind functionality --- src/binaryen-embind.cpp | 190 +++++++++++++++++++++++----------------- src/binaryen-embind.h | 67 ++++++++++++++ 2 files changed, 176 insertions(+), 81 deletions(-) create mode 100644 src/binaryen-embind.h diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 09b61de7960..d9a3bd791ea 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -1,104 +1,126 @@ -#include "binaryen-c.h" -#include +#include "binaryen-embind.h" using namespace emscripten; namespace { -EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); - -EMSCRIPTEN_DECLARE_VAL_TYPE(TypeList); - namespace { -class ExpressionFactory { -protected: - uintptr_t module; +ExpressionFactory::ExpressionFactory(Module* module) : module(module) {} + +uintptr_t LocalExpressionFactory::get(BinaryenIndex index, BinaryenType type) { + return reinterpret_cast(BinaryenLocalGet( + reinterpret_cast(module->ptr), index, type)); +} -public: - ExpressionFactory(uintptr_t module) : module(module) {} -}; +uintptr_t I32ExpressionFactory::add(uintptr_t left, uintptr_t right) { + return reinterpret_cast( + BinaryenBinary(reinterpret_cast(module->ptr), + BinaryenAddInt32(), + reinterpret_cast(left), + reinterpret_cast(right))); +} } // namespace -class Module { -public: - uintptr_t ptr; - - Module() : Module(reinterpret_cast(BinaryenModuleCreate())) {} - - Module(uintptr_t ptr) : ptr(ptr) {} - - uintptr_t - block(const std::string& name, ExpressionList children, uintptr_t type) { - std::vector childrenVec = - convertJSArrayToNumberVector(children); - return reinterpret_cast(BinaryenBlock( - reinterpret_cast(ptr), - name.c_str(), - reinterpret_cast(childrenVec.begin().base()), - childrenVec.size(), - type)); - } - - uintptr_t if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse) { - return reinterpret_cast( - BinaryenIf(reinterpret_cast(ptr), - reinterpret_cast(condition), - reinterpret_cast(ifTrue), - reinterpret_cast(ifFalse))); - } - - uintptr_t loop(const std::string& label, uintptr_t body) { - return reinterpret_cast( - BinaryenLoop(reinterpret_cast(ptr), - label.c_str(), - reinterpret_cast(body))); - } - - uintptr_t br(const std::string& label, uintptr_t condition, uintptr_t value) { - return reinterpret_cast( - BinaryenBreak(reinterpret_cast(ptr), - label.c_str(), - reinterpret_cast(condition), - reinterpret_cast(value))); - } - - uintptr_t addFunction(const std::string& name, - BinaryenType params, - BinaryenType results, - TypeList varTypes, - uintptr_t body) { - std::vector varTypesVec = - convertJSArrayToNumberVector(varTypes); - return reinterpret_cast( - BinaryenAddFunction(reinterpret_cast(ptr), - name.c_str(), - params, - results, - varTypesVec.begin().base(), - varTypesVec.size(), - reinterpret_cast(body))); - } - - std::string emitText() { - char* text = BinaryenModuleAllocateAndWriteText( - reinterpret_cast(ptr)); - std::string str = text; - delete text; - return str; - } -}; +Module::Module() + : Module(reinterpret_cast(BinaryenModuleCreate())) {} + +Module::Module(uintptr_t ptr) : ptr(ptr) {} + +uintptr_t Module::block(const std::string& name, + ExpressionList children, + uintptr_t type) { + std::vector childrenVec = + convertJSArrayToNumberVector(children); + return reinterpret_cast(BinaryenBlock( + reinterpret_cast(ptr), + name.c_str(), + reinterpret_cast(childrenVec.begin().base()), + childrenVec.size(), + type)); +} +uintptr_t +Module::if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse) { + return reinterpret_cast( + BinaryenIf(reinterpret_cast(ptr), + reinterpret_cast(condition), + reinterpret_cast(ifTrue), + reinterpret_cast(ifFalse))); +} +uintptr_t Module::loop(const std::string& label, uintptr_t body) { + return reinterpret_cast( + BinaryenLoop(reinterpret_cast(ptr), + label.c_str(), + reinterpret_cast(body))); +} +uintptr_t +Module::br(const std::string& label, uintptr_t condition, uintptr_t value) { + return reinterpret_cast( + BinaryenBreak(reinterpret_cast(ptr), + label.c_str(), + reinterpret_cast(condition), + reinterpret_cast(value))); +} +uintptr_t Module::return_(uintptr_t value) { + return reinterpret_cast( + BinaryenReturn(reinterpret_cast(ptr), + reinterpret_cast(value))); +} + +uintptr_t Module::addFunction(const std::string& name, + BinaryenType params, + BinaryenType results, + TypeList varTypes, + uintptr_t body) { + std::vector varTypesVec = + convertJSArrayToNumberVector(varTypes); + return reinterpret_cast( + BinaryenAddFunction(reinterpret_cast(ptr), + name.c_str(), + params, + results, + varTypesVec.begin().base(), + varTypesVec.size(), + reinterpret_cast(body))); +} +uintptr_t Module::addFunctionExport(const std::string& internalName, + const std::string& externalName) { + return reinterpret_cast( + BinaryenAddFunctionExport(reinterpret_cast(ptr), + internalName.c_str(), + externalName.c_str())); +} + +std::string Module::emitText() { + char* text = BinaryenModuleAllocateAndWriteText( + reinterpret_cast(ptr)); + std::string str = text; + delete text; + return str; +} Module* parseText(const std::string& text) { return new Module( reinterpret_cast(BinaryenModuleParse(text.c_str()))); } -}; // namespace + +BinaryenType createType(TypeList types) { + std::vector typesVec = + convertJSArrayToNumberVector(types); + return BinaryenTypeCreate(typesVec.begin().base(), typesVec.size()); +} +} // namespace EMSCRIPTEN_BINDINGS(Binaryen) { constant("none", BinaryenTypeNone()); constant("i32", BinaryenTypeInt32()); constant("i64", BinaryenTypeInt64()); + class_("LocalExpressionFactory") + .function("get", &LocalExpressionFactory::get); + + class_("I32ExpressionFactory") + .function("add", &I32ExpressionFactory::add); + class_("Module") .constructor() @@ -108,13 +130,19 @@ EMSCRIPTEN_BINDINGS(Binaryen) { .function("br", &Module::br) .function("break", &Module::br) .function("br_if", &Module::br) + .property("local", &Module::local) + .property("i32", &Module::i32) + .function("return", &Module::return_) .function("addFunction", &Module::addFunction) + .function("addFunctionExport", &Module::addFunctionExport) .function("emitText", &Module::emitText); function("parseText", parseText, allow_raw_pointer()); + function("createType", createType); + register_type("number[]"); register_type("number[]"); diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h new file mode 100644 index 00000000000..a22ae0de0f9 --- /dev/null +++ b/src/binaryen-embind.h @@ -0,0 +1,67 @@ +#include "binaryen-c.h" +#include + +namespace { + +EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); + +EMSCRIPTEN_DECLARE_VAL_TYPE(TypeList); + +class Module; + +namespace { +class ExpressionFactory { +protected: + Module* module; + +public: + ExpressionFactory(Module* module); +}; + +class LocalExpressionFactory : public ExpressionFactory { +public: + using ExpressionFactory::ExpressionFactory; + + uintptr_t get(BinaryenIndex index, BinaryenType type); +}; + +class I32ExpressionFactory : public ExpressionFactory { +public: + using ExpressionFactory::ExpressionFactory; + + uintptr_t add(uintptr_t left, uintptr_t right); +}; +} // namespace + +class Module { +public: + uintptr_t ptr; + + Module(); + + Module(uintptr_t ptr); + + uintptr_t + block(const std::string& name, ExpressionList children, uintptr_t type); + uintptr_t if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse); + uintptr_t loop(const std::string& label, uintptr_t body); + uintptr_t br(const std::string& label, uintptr_t condition, uintptr_t value); + const LocalExpressionFactory local = LocalExpressionFactory(this); + const I32ExpressionFactory i32 = I32ExpressionFactory(this); + uintptr_t return_(uintptr_t value); + + uintptr_t addFunction(const std::string& name, + BinaryenType params, + BinaryenType results, + TypeList varTypes, + uintptr_t body); + uintptr_t addFunctionExport(const std::string& internalName, + const std::string& externalName); + + std::string emitText(); +}; + +Module* parseText(const std::string& text); + +BinaryenType createType(TypeList types); +}; // namespace \ No newline at end of file From 515e6a1f16064a5e19c4ffa9a1d6cb728412ce56 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Fri, 25 Apr 2025 23:52:02 +0500 Subject: [PATCH 03/11] Add automatic expression generation and move away from C API --- src/binaryen-embind.cpp | 180 ++++++++++++++++++++++++++++------------ src/binaryen-embind.h | 83 ++++++++++++++---- 2 files changed, 192 insertions(+), 71 deletions(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index d9a3bd791ea..ab16bb16860 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -1,30 +1,34 @@ #include "binaryen-embind.h" +#include "binaryen-c.h" using namespace emscripten; -namespace { +namespace binaryen { namespace { -ExpressionFactory::ExpressionFactory(Module* module) : module(module) {} +ExpressionFactory::ExpressionFactory(wasm::Module* module) : module(module) {} -uintptr_t LocalExpressionFactory::get(BinaryenIndex index, BinaryenType type) { - return reinterpret_cast(BinaryenLocalGet( - reinterpret_cast(module->ptr), index, type)); +uintptr_t LocalExpressionFactory::get(Index index, Type type) { + return reinterpret_cast( + BinaryenLocalGet(reinterpret_cast(module), index, type)); } uintptr_t I32ExpressionFactory::add(uintptr_t left, uintptr_t right) { return reinterpret_cast( - BinaryenBinary(reinterpret_cast(module->ptr), + BinaryenBinary(reinterpret_cast(module), BinaryenAddInt32(), reinterpret_cast(left), reinterpret_cast(right))); } } // namespace -Module::Module() - : Module(reinterpret_cast(BinaryenModuleCreate())) {} +Module::Module() : Module(BinaryenModuleCreate()) {} -Module::Module(uintptr_t ptr) : ptr(ptr) {} +Module::Module(wasm::Module* module) : module(module) {} + +const uintptr_t& Module::ptr() const { + return reinterpret_cast(module); +} uintptr_t Module::block(const std::string& name, ExpressionList children, @@ -32,7 +36,7 @@ uintptr_t Module::block(const std::string& name, std::vector childrenVec = convertJSArrayToNumberVector(children); return reinterpret_cast(BinaryenBlock( - reinterpret_cast(ptr), + module, name.c_str(), reinterpret_cast(childrenVec.begin().base()), childrenVec.size(), @@ -41,29 +45,26 @@ uintptr_t Module::block(const std::string& name, uintptr_t Module::if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse) { return reinterpret_cast( - BinaryenIf(reinterpret_cast(ptr), + BinaryenIf(module, reinterpret_cast(condition), reinterpret_cast(ifTrue), reinterpret_cast(ifFalse))); } uintptr_t Module::loop(const std::string& label, uintptr_t body) { - return reinterpret_cast( - BinaryenLoop(reinterpret_cast(ptr), - label.c_str(), - reinterpret_cast(body))); + return reinterpret_cast(BinaryenLoop( + module, label.c_str(), reinterpret_cast(body))); } uintptr_t Module::br(const std::string& label, uintptr_t condition, uintptr_t value) { return reinterpret_cast( - BinaryenBreak(reinterpret_cast(ptr), + BinaryenBreak(module, label.c_str(), reinterpret_cast(condition), reinterpret_cast(value))); } uintptr_t Module::return_(uintptr_t value) { return reinterpret_cast( - BinaryenReturn(reinterpret_cast(ptr), - reinterpret_cast(value))); + BinaryenReturn(module, reinterpret_cast(value))); } uintptr_t Module::addFunction(const std::string& name, @@ -74,7 +75,7 @@ uintptr_t Module::addFunction(const std::string& name, std::vector varTypesVec = convertJSArrayToNumberVector(varTypes); return reinterpret_cast( - BinaryenAddFunction(reinterpret_cast(ptr), + BinaryenAddFunction(module, name.c_str(), params, results, @@ -84,66 +85,135 @@ uintptr_t Module::addFunction(const std::string& name, } uintptr_t Module::addFunctionExport(const std::string& internalName, const std::string& externalName) { - return reinterpret_cast( - BinaryenAddFunctionExport(reinterpret_cast(ptr), - internalName.c_str(), - externalName.c_str())); + return reinterpret_cast(BinaryenAddFunctionExport( + module, internalName.c_str(), externalName.c_str())); } std::string Module::emitText() { - char* text = BinaryenModuleAllocateAndWriteText( - reinterpret_cast(ptr)); + char* text = BinaryenModuleAllocateAndWriteText(module); std::string str = text; delete text; return str; } Module* parseText(const std::string& text) { - return new Module( - reinterpret_cast(BinaryenModuleParse(text.c_str()))); + return new Module(BinaryenModuleParse(text.c_str())); } BinaryenType createType(TypeList types) { - std::vector typesVec = + std::vector typeIdsVec = convertJSArrayToNumberVector(types); - return BinaryenTypeCreate(typesVec.begin().base(), typesVec.size()); + std::vector typesVec(typeIdsVec.begin(), typeIdsVec.end()); + return wasm::Type(typesVec).getID(); } -} // namespace +} // namespace binaryen EMSCRIPTEN_BINDINGS(Binaryen) { - constant("none", BinaryenTypeNone()); - constant("i32", BinaryenTypeInt32()); - constant("i64", BinaryenTypeInt64()); + constant("none", wasm::Type::none); + constant("i32", wasm::Type::i32); + constant("i64", wasm::Type::i64); + constant("f32", wasm::Type::f32); + constant("f64", wasm::Type::f64); + constant("v128", wasm::Type::v128); + constant("funcref", + wasm::Type(wasm::HeapType::func, wasm::Nullable).getID()); + constant("externref", + wasm::Type(wasm::HeapType::ext, wasm::Nullable).getID()); + constant("anyref", + wasm::Type(wasm::HeapType::any, wasm::Nullable).getID()); + constant("eqref", + wasm::Type(wasm::HeapType::eq, wasm::Nullable).getID()); + constant("i31ref", + wasm::Type(wasm::HeapType::i31, wasm::Nullable).getID()); + constant( + "structref", wasm::Type(wasm::HeapType::struct_, wasm::Nullable).getID()); + constant( + "stringref", wasm::Type(wasm::HeapType::string, wasm::Nullable).getID()); + constant("nullref", + wasm::Type(wasm::HeapType::none, wasm::Nullable).getID()); + constant( + "nullexternref", wasm::Type(wasm::HeapType::noext, wasm::Nullable).getID()); + constant( + "nullfuncref", wasm::Type(wasm::HeapType::nofunc, wasm::Nullable).getID()); + constant("unreachable", wasm::Type::unreachable); + constant("auto", uintptr_t(-1)); + + constant("notPacked", wasm::Field::PackedType::not_packed); + constant("i8", wasm::Field::PackedType::i8); + constant("i16", wasm::Field::PackedType::i16); + + auto expressionIds = enum_("ExpressionIds"); + expressionIds.value("Invalid", wasm::Expression::Id::InvalidId); +#define DELEGATE(CLASS_TO_VISIT) \ + expressionIds.value(#CLASS_TO_VISIT, wasm::Expression::Id::CLASS_TO_VISIT##Id) +#include "wasm-delegations.def" + + constant("InvalidId", wasm::Expression::Id::InvalidId); +#define DELEGATE(CLASS_TO_VISIT) \ + constant(#CLASS_TO_VISIT "Id", \ + wasm::Expression::Id::CLASS_TO_VISIT##Id); +#include "wasm-delegations.def" + + class_("LocalExpressionFactory") + .function("get", &binaryen::LocalExpressionFactory::get); + + class_("I32ExpressionFactory") + .function("add", &binaryen::I32ExpressionFactory::add); + + class_("Module") + .constructor() + .property("ptr", &binaryen::Module::ptr) - class_("LocalExpressionFactory") - .function("get", &LocalExpressionFactory::get); + .function("block", &binaryen::Module::block) + .function("if", &binaryen::Module::if_) + .function("loop", &binaryen::Module::loop) + .function("br", &binaryen::Module::br) + .function("break", &binaryen::Module::br) + .function("br_if", &binaryen::Module::br) + .property("local", &binaryen::Module::local) + .property("i32", &binaryen::Module::i32) + .function("return", &binaryen::Module::return_) - class_("I32ExpressionFactory") - .function("add", &I32ExpressionFactory::add); + .function("addFunction", &binaryen::Module::addFunction) + .function("addFunctionExport", &binaryen::Module::addFunctionExport) - class_("Module") - .constructor() + .function("emitText", &binaryen::Module::emitText); + + function( + "parseText", binaryen::parseText, allow_raw_pointer()); + + function("createType", binaryen::createType); + +#define DELEGATE_FIELD_MAIN_START + +#define DELEGATE_FIELD_CASE_START(id) class_(#id) + +#define DELEGATE_FIELD_CASE_END(id) ; - .function("block", &Module::block) - .function("if", &Module::if_) - .function("loop", &Module::loop) - .function("br", &Module::br) - .function("break", &Module::br) - .function("br_if", &Module::br) - .property("local", &Module::local) - .property("i32", &Module::i32) - .function("return", &Module::return_) +#define DELEGATE_FIELD_MAIN_END - .function("addFunction", &Module::addFunction) - .function("addFunctionExport", &Module::addFunctionExport) +#define DELEGATE_FIELD_CHILD(id, field) .property(#field, &binaryen::id::field) - .function("emitText", &Module::emitText); +#define DELEGATE_FIELD_CHILD_VECTOR(id, field) - function("parseText", parseText, allow_raw_pointer()); +#define DELEGATE_FIELD_TYPE(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) +#define DELEGATE_FIELD_INT(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) +#define DELEGATE_FIELD_INT_ARRAY(id, field) +#define DELEGATE_FIELD_INT_VECTOR(id, field) +#define DELEGATE_FIELD_NAME_VECTOR(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) +#define DELEGATE_FIELD_TYPE_VECTOR(id, field) - function("createType", createType); +#include "wasm-delegations-fields.def" - register_type("number[]"); + register_type("number[]"); - register_type("number[]"); + register_type("number[]"); } \ No newline at end of file diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index a22ae0de0f9..18b0ef8552a 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -1,28 +1,30 @@ -#include "binaryen-c.h" +#include "wasm.h" #include -namespace { +namespace binaryen { + +typedef uint32_t Index; + +typedef uintptr_t Type; EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); EMSCRIPTEN_DECLARE_VAL_TYPE(TypeList); -class Module; - namespace { class ExpressionFactory { protected: - Module* module; + wasm::Module* module; public: - ExpressionFactory(Module* module); + ExpressionFactory(wasm::Module* module); }; class LocalExpressionFactory : public ExpressionFactory { public: using ExpressionFactory::ExpressionFactory; - uintptr_t get(BinaryenIndex index, BinaryenType type); + uintptr_t get(Index index, Type type); }; class I32ExpressionFactory : public ExpressionFactory { @@ -34,25 +36,28 @@ class I32ExpressionFactory : public ExpressionFactory { } // namespace class Module { -public: - uintptr_t ptr; +private: + wasm::Module* module; +public: Module(); - Module(uintptr_t ptr); + Module(wasm::Module* module); + + const uintptr_t& ptr() const; uintptr_t block(const std::string& name, ExpressionList children, uintptr_t type); uintptr_t if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse); uintptr_t loop(const std::string& label, uintptr_t body); uintptr_t br(const std::string& label, uintptr_t condition, uintptr_t value); - const LocalExpressionFactory local = LocalExpressionFactory(this); - const I32ExpressionFactory i32 = I32ExpressionFactory(this); + const LocalExpressionFactory local = LocalExpressionFactory(this->module); + const I32ExpressionFactory i32 = I32ExpressionFactory(this->module); uintptr_t return_(uintptr_t value); uintptr_t addFunction(const std::string& name, - BinaryenType params, - BinaryenType results, + Type params, + Type results, TypeList varTypes, uintptr_t body); uintptr_t addFunctionExport(const std::string& internalName, @@ -63,5 +68,51 @@ class Module { Module* parseText(const std::string& text); -BinaryenType createType(TypeList types); -}; // namespace \ No newline at end of file +Type createType(TypeList types); + +namespace { +template class Expression { +protected: + Type* expression; + +public: + Expression(Type* expression) : expression(expression) {} +}; + +#define DELEGATE_FIELD_MAIN_START + +#define DELEGATE_FIELD_CASE_START(id) \ + class id : public Expression { \ + public: + +#define DELEGATE_FIELD_CASE_END(id) \ + } \ + ; + +#define DELEGATE_FIELD_MAIN_END + +#define DELEGATE_FIELD_CHILD(id, field) \ + auto& field() const { \ + return expression->field; \ + } // TODO: cast to primitive types & split getter and setter + +#define DELEGATE_FIELD_CHILD_VECTOR(id, field) + +#define DELEGATE_FIELD_TYPE(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) +#define DELEGATE_FIELD_INT(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) +#define DELEGATE_FIELD_INT_ARRAY(id, field) +#define DELEGATE_FIELD_INT_VECTOR(id, field) +#define DELEGATE_FIELD_NAME_VECTOR(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) +#define DELEGATE_FIELD_TYPE_VECTOR(id, field) + +#include "wasm-delegations-fields.def" +} // namespace +}; // namespace binaryen \ No newline at end of file From f23396ff209ff2be7c4ec36b3fc42c266dd7c41b Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sat, 26 Apr 2025 17:58:53 +0500 Subject: [PATCH 04/11] Switch to direct bindings --- src/binaryen-embind.cpp | 78 ++++++++++++++++++++++++++++++----------- src/binaryen-embind.h | 46 ------------------------ 2 files changed, 57 insertions(+), 67 deletions(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index ab16bb16860..46f13973b8c 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -108,6 +108,14 @@ BinaryenType createType(TypeList types) { } } // namespace binaryen +namespace { +static std::string capitalize(std::string str) { + assert(!str.empty()); + str[0] = std::toupper(str[0]); + return str; +} +} // namespace + EMSCRIPTEN_BINDINGS(Binaryen) { constant("none", wasm::Type::none); constant("i32", wasm::Type::i32); @@ -164,7 +172,7 @@ EMSCRIPTEN_BINDINGS(Binaryen) { .constructor() .property("ptr", &binaryen::Module::ptr) - .function("block", &binaryen::Module::block) + /*.function("block", &binaryen::Module::block) .function("if", &binaryen::Module::if_) .function("loop", &binaryen::Module::loop) .function("br", &binaryen::Module::br) @@ -175,45 +183,73 @@ EMSCRIPTEN_BINDINGS(Binaryen) { .function("return", &binaryen::Module::return_) .function("addFunction", &binaryen::Module::addFunction) - .function("addFunctionExport", &binaryen::Module::addFunctionExport) + .function("addFunctionExport", &binaryen::Module::addFunctionExport)*/ .function("emitText", &binaryen::Module::emitText); function( "parseText", binaryen::parseText, allow_raw_pointer()); - function("createType", binaryen::createType); + // function("createType", binaryen::createType); + + class_("Expression") + /*.property("id", &wasm::Expression::_id) + .property("type", &wasm::Expression::type)*/ + ; #define DELEGATE_FIELD_MAIN_START -#define DELEGATE_FIELD_CASE_START(id) class_(#id) +#define DELEGATE_FIELD_CASE_START(id) class_(#id) #define DELEGATE_FIELD_CASE_END(id) ; #define DELEGATE_FIELD_MAIN_END -#define DELEGATE_FIELD_CHILD(id, field) .property(#field, &binaryen::id::field) - -#define DELEGATE_FIELD_CHILD_VECTOR(id, field) - -#define DELEGATE_FIELD_TYPE(id, field) -#define DELEGATE_FIELD_HEAPTYPE(id, field) -#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) +#define DELEGATE_FIELD_CHILD(id, field) \ + .function(("get" + capitalize(#field)).c_str(), \ + +[](const wasm::id& expr) { return expr.field; }, \ + allow_raw_pointer(), \ + nonnull()) \ + .function( \ + ("set" + capitalize(#field)).c_str(), \ + +[](wasm::id& expr, wasm::Expression* value) { expr.field = value; }, \ + allow_raw_pointer() /* nonnull() */) \ + .property( \ + #field, \ + &wasm::id::field, \ + allow_raw_pointer< \ + wasm::Expression>() /* nonnull() */) // Embind doesn't support + // non-null properties yet +#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) \ + .function(("get" + capitalize(#field)).c_str(), \ + +[](const wasm::id& expr) { return expr.field; }, \ + allow_raw_pointer()) \ + .function( \ + ("set" + capitalize(#field)).c_str(), \ + +[](wasm::id& expr, wasm::Expression* value) { expr.field = value; }, \ + allow_raw_pointer()) \ + .property(#field, &wasm::id::field, allow_raw_pointer()) +#define DELEGATE_FIELD_CHILD_VECTOR(id, field) \ + //.property(#field, &wasm::id::field, + // allow_raw_pointer()) #define DELEGATE_FIELD_INT(id, field) -#define DELEGATE_FIELD_LITERAL(id, field) -#define DELEGATE_FIELD_NAME(id, field) -#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) -#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) -#define DELEGATE_FIELD_ADDRESS(id, field) #define DELEGATE_FIELD_INT_ARRAY(id, field) #define DELEGATE_FIELD_INT_VECTOR(id, field) +#define DELEGATE_FIELD_LITERAL(id, field) +#define DELEGATE_FIELD_NAME(id, field) \ + .property( \ + #field, \ + +[](const wasm::id& expr) { return expr.field.toString(); }, \ + +[](wasm::id& expr, std::string value) { expr.field = value; }) #define DELEGATE_FIELD_NAME_VECTOR(id, field) -#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) DELEGATE_FIELD_NAME(id, field) +#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) \ + DELEGATE_FIELD_NAME_VECTOR(id, field) +#define DELEGATE_FIELD_TYPE(id, field) #define DELEGATE_FIELD_TYPE_VECTOR(id, field) +#define DELEGATE_FIELD_HEAPTYPE(id, field) +#define DELEGATE_FIELD_ADDRESS(id, field) #include "wasm-delegations-fields.def" - - register_type("number[]"); - - register_type("number[]"); } \ No newline at end of file diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index 18b0ef8552a..9f98b9e0d43 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -69,50 +69,4 @@ class Module { Module* parseText(const std::string& text); Type createType(TypeList types); - -namespace { -template class Expression { -protected: - Type* expression; - -public: - Expression(Type* expression) : expression(expression) {} -}; - -#define DELEGATE_FIELD_MAIN_START - -#define DELEGATE_FIELD_CASE_START(id) \ - class id : public Expression { \ - public: - -#define DELEGATE_FIELD_CASE_END(id) \ - } \ - ; - -#define DELEGATE_FIELD_MAIN_END - -#define DELEGATE_FIELD_CHILD(id, field) \ - auto& field() const { \ - return expression->field; \ - } // TODO: cast to primitive types & split getter and setter - -#define DELEGATE_FIELD_CHILD_VECTOR(id, field) - -#define DELEGATE_FIELD_TYPE(id, field) -#define DELEGATE_FIELD_HEAPTYPE(id, field) -#define DELEGATE_FIELD_OPTIONAL_CHILD(id, field) -#define DELEGATE_FIELD_INT(id, field) -#define DELEGATE_FIELD_LITERAL(id, field) -#define DELEGATE_FIELD_NAME(id, field) -#define DELEGATE_FIELD_SCOPE_NAME_DEF(id, field) -#define DELEGATE_FIELD_SCOPE_NAME_USE(id, field) -#define DELEGATE_FIELD_ADDRESS(id, field) -#define DELEGATE_FIELD_INT_ARRAY(id, field) -#define DELEGATE_FIELD_INT_VECTOR(id, field) -#define DELEGATE_FIELD_NAME_VECTOR(id, field) -#define DELEGATE_FIELD_SCOPE_NAME_USE_VECTOR(id, field) -#define DELEGATE_FIELD_TYPE_VECTOR(id, field) - -#include "wasm-delegations-fields.def" -} // namespace }; // namespace binaryen \ No newline at end of file From 38354b1ccb58633426180d6dcccdb0e524a38b04 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sat, 26 Apr 2025 20:25:00 +0500 Subject: [PATCH 05/11] Transform expression factories into inline structs --- src/binaryen-embind.cpp | 127 +++++++++++++++++++--------------------- src/binaryen-embind.h | 44 +++++--------- 2 files changed, 77 insertions(+), 94 deletions(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 46f13973b8c..6843021d33b 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -4,25 +4,7 @@ using namespace emscripten; namespace binaryen { - -namespace { -ExpressionFactory::ExpressionFactory(wasm::Module* module) : module(module) {} - -uintptr_t LocalExpressionFactory::get(Index index, Type type) { - return reinterpret_cast( - BinaryenLocalGet(reinterpret_cast(module), index, type)); -} - -uintptr_t I32ExpressionFactory::add(uintptr_t left, uintptr_t right) { - return reinterpret_cast( - BinaryenBinary(reinterpret_cast(module), - BinaryenAddInt32(), - reinterpret_cast(left), - reinterpret_cast(right))); -} -} // namespace - -Module::Module() : Module(BinaryenModuleCreate()) {} +Module::Module() : Module(new wasm::Module()) {} Module::Module(wasm::Module* module) : module(module) {} @@ -30,41 +12,38 @@ const uintptr_t& Module::ptr() const { return reinterpret_cast(module); } -uintptr_t Module::block(const std::string& name, - ExpressionList children, - uintptr_t type) { - std::vector childrenVec = - convertJSArrayToNumberVector(children); - return reinterpret_cast(BinaryenBlock( - module, - name.c_str(), - reinterpret_cast(childrenVec.begin().base()), - childrenVec.size(), - type)); +wasm::Expression* Module::block(const std::string& name, + ExpressionList children, + uintptr_t type) { + auto childrenVec = vecFromJSArray( + children, allow_raw_pointer()); + return BinaryenBlock( + module, name.c_str(), childrenVec.begin().base(), childrenVec.size(), type); } -uintptr_t -Module::if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse) { - return reinterpret_cast( - BinaryenIf(module, - reinterpret_cast(condition), - reinterpret_cast(ifTrue), - reinterpret_cast(ifFalse))); +wasm::Expression* Module::if_(wasm::Expression* condition, + wasm::Expression* ifTrue, + wasm::Expression* ifFalse) { + return BinaryenIf(module, condition, ifTrue, ifFalse); } -uintptr_t Module::loop(const std::string& label, uintptr_t body) { - return reinterpret_cast(BinaryenLoop( - module, label.c_str(), reinterpret_cast(body))); +wasm::Expression* Module::loop(const std::string& label, + wasm::Expression* body) { + return BinaryenLoop(module, label.c_str(), body); } -uintptr_t -Module::br(const std::string& label, uintptr_t condition, uintptr_t value) { - return reinterpret_cast( - BinaryenBreak(module, - label.c_str(), - reinterpret_cast(condition), - reinterpret_cast(value))); +wasm::Expression* Module::br(const std::string& label, + wasm::Expression* condition, + wasm::Expression* value) { + return BinaryenBreak(module, label.c_str(), condition, value); } -uintptr_t Module::return_(uintptr_t value) { - return reinterpret_cast( - BinaryenReturn(module, reinterpret_cast(value))); +wasm::Expression* Module::Local::get(Index index, Type type) { + return BinaryenLocalGet(module, index, type); +} + +wasm::Expression* Module::I32::add(wasm::Expression* left, + wasm::Expression* right) { + return BinaryenBinary(module, BinaryenAddInt32(), left, right); +} +wasm::Expression* Module::return_(wasm::Expression* value) { + return BinaryenReturn(module, value); } uintptr_t Module::addFunction(const std::string& name, @@ -162,27 +141,40 @@ EMSCRIPTEN_BINDINGS(Binaryen) { wasm::Expression::Id::CLASS_TO_VISIT##Id); #include "wasm-delegations.def" - class_("LocalExpressionFactory") - .function("get", &binaryen::LocalExpressionFactory::get); + class_("Module_Local") + .function("get", + &binaryen::Module::Local::get, + allow_raw_pointer()); - class_("I32ExpressionFactory") - .function("add", &binaryen::I32ExpressionFactory::add); + class_("Module_I32") + .function("add", + &binaryen::Module::I32::add, + allow_raw_pointer()); class_("Module") .constructor() .property("ptr", &binaryen::Module::ptr) - /*.function("block", &binaryen::Module::block) - .function("if", &binaryen::Module::if_) - .function("loop", &binaryen::Module::loop) - .function("br", &binaryen::Module::br) - .function("break", &binaryen::Module::br) - .function("br_if", &binaryen::Module::br) - .property("local", &binaryen::Module::local) - .property("i32", &binaryen::Module::i32) - .function("return", &binaryen::Module::return_) - - .function("addFunction", &binaryen::Module::addFunction) + .function( + "block", &binaryen::Module::block, allow_raw_pointer()) + .function( + "if", &binaryen::Module::if_, allow_raw_pointer()) + .function( + "loop", &binaryen::Module::loop, allow_raw_pointer()) + .function( + "br", &binaryen::Module::br, allow_raw_pointer()) + .function( + "break", &binaryen::Module::br, allow_raw_pointer()) + .function( + "br_if", &binaryen::Module::br, allow_raw_pointer()) + .property( + "local", &binaryen::Module::local, return_value_policy::reference()) + .property("i32", &binaryen::Module::i32, return_value_policy::reference()) + .function("return", + &binaryen::Module::return_, + allow_raw_pointer()) + + /*.function("addFunction", &binaryen::Module::addFunction) .function("addFunctionExport", &binaryen::Module::addFunctionExport)*/ .function("emitText", &binaryen::Module::emitText); @@ -197,9 +189,12 @@ EMSCRIPTEN_BINDINGS(Binaryen) { .property("type", &wasm::Expression::type)*/ ; + register_type("Expression[]"); + #define DELEGATE_FIELD_MAIN_START -#define DELEGATE_FIELD_CASE_START(id) class_(#id) +#define DELEGATE_FIELD_CASE_START(id) \ + class_>(#id) #define DELEGATE_FIELD_CASE_END(id) ; diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index 9f98b9e0d43..b1b223e30f3 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -11,29 +11,9 @@ EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); EMSCRIPTEN_DECLARE_VAL_TYPE(TypeList); -namespace { -class ExpressionFactory { -protected: +struct ExpressionFactory { wasm::Module* module; - -public: - ExpressionFactory(wasm::Module* module); -}; - -class LocalExpressionFactory : public ExpressionFactory { -public: - using ExpressionFactory::ExpressionFactory; - - uintptr_t get(Index index, Type type); -}; - -class I32ExpressionFactory : public ExpressionFactory { -public: - using ExpressionFactory::ExpressionFactory; - - uintptr_t add(uintptr_t left, uintptr_t right); }; -} // namespace class Module { private: @@ -46,14 +26,22 @@ class Module { const uintptr_t& ptr() const; - uintptr_t + wasm::Expression* block(const std::string& name, ExpressionList children, uintptr_t type); - uintptr_t if_(uintptr_t condition, uintptr_t ifTrue, uintptr_t ifFalse); - uintptr_t loop(const std::string& label, uintptr_t body); - uintptr_t br(const std::string& label, uintptr_t condition, uintptr_t value); - const LocalExpressionFactory local = LocalExpressionFactory(this->module); - const I32ExpressionFactory i32 = I32ExpressionFactory(this->module); - uintptr_t return_(uintptr_t value); + wasm::Expression* if_(wasm::Expression* condition, + wasm::Expression* ifTrue, + wasm::Expression* ifFalse); + wasm::Expression* loop(const std::string& label, wasm::Expression* body); + wasm::Expression* br(const std::string& label, + wasm::Expression* condition, + wasm::Expression* value); + const struct Local : ExpressionFactory { + wasm::Expression* get(Index index, Type type); + } local{module}; + const struct I32 : ExpressionFactory { + wasm::Expression* add(wasm::Expression* left, wasm::Expression* right); + } i32{module}; + wasm::Expression* return_(wasm::Expression* value); uintptr_t addFunction(const std::string& name, Type params, From f87350c87e365f308adf559e1dac1d31d57c1818 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sun, 27 Apr 2025 01:57:27 +0500 Subject: [PATCH 06/11] Switch to C++ API --- src/binaryen-embind.cpp | 133 ++++++++++++++++++++++------------------ src/binaryen-embind.h | 23 ++++--- 2 files changed, 87 insertions(+), 69 deletions(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 6843021d33b..c56432f9e69 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -1,5 +1,6 @@ #include "binaryen-embind.h" -#include "binaryen-c.h" +#include "parser/wat-parser.h" +#include "wasm-builder.h" using namespace emscripten; @@ -14,39 +15,50 @@ const uintptr_t& Module::ptr() const { wasm::Expression* Module::block(const std::string& name, ExpressionList children, - uintptr_t type) { - auto childrenVec = vecFromJSArray( - children, allow_raw_pointer()); - return BinaryenBlock( - module, name.c_str(), childrenVec.begin().base(), childrenVec.size(), type); + std::optional type) { + return wasm::Builder(*module).makeBlock( + name, + vecFromJSArray(children, + allow_raw_pointer()), + type); } wasm::Expression* Module::if_(wasm::Expression* condition, wasm::Expression* ifTrue, wasm::Expression* ifFalse) { - return BinaryenIf(module, condition, ifTrue, ifFalse); + return wasm::Builder(*module).makeIf(condition, ifTrue, ifFalse); } wasm::Expression* Module::loop(const std::string& label, wasm::Expression* body) { - return BinaryenLoop(module, label.c_str(), body); + return wasm::Builder(*module).makeLoop(label, body); } wasm::Expression* Module::br(const std::string& label, wasm::Expression* condition, wasm::Expression* value) { - return BinaryenBreak(module, label.c_str(), condition, value); + return wasm::Builder(*module).makeBreak(label, condition, value); } -wasm::Expression* Module::Local::get(Index index, Type type) { - return BinaryenLocalGet(module, index, type); +wasm::Expression* Module::switch_(NameList names, + const std::string& defaultName, + wasm::Expression* condition, + wasm::Expression* value) { + auto strVec = vecFromJSArray(names); + std::vector namesVec(strVec.begin(), strVec.end()); + return wasm::Builder(*module).makeSwitch( + namesVec, defaultName, condition, value); +} +wasm::Expression* Module::Local::get(Index index, wasm::Type type) { + return wasm::Builder(*module).makeLocalGet(index, type); } wasm::Expression* Module::I32::add(wasm::Expression* left, wasm::Expression* right) { - return BinaryenBinary(module, BinaryenAddInt32(), left, right); + return wasm::Builder(*module).makeBinary( + wasm::BinaryOp::AddInt32, left, right); } wasm::Expression* Module::return_(wasm::Expression* value) { - return BinaryenReturn(module, value); + return wasm::Builder(*module).makeReturn(value); } -uintptr_t Module::addFunction(const std::string& name, +/*uintptr_t Module::addFunction(const std::string& name, BinaryenType params, BinaryenType results, TypeList varTypes, @@ -66,24 +78,29 @@ uintptr_t Module::addFunctionExport(const std::string& internalName, const std::string& externalName) { return reinterpret_cast(BinaryenAddFunctionExport( module, internalName.c_str(), externalName.c_str())); -} +}*/ std::string Module::emitText() { - char* text = BinaryenModuleAllocateAndWriteText(module); - std::string str = text; - delete text; - return str; + std::ostringstream os; + bool colors = Colors::isEnabled(); + Colors::setEnabled(false); // do not use colors for writing + os << *module; + Colors::setEnabled(colors); // restore colors state + return os.str(); } Module* parseText(const std::string& text) { - return new Module(BinaryenModuleParse(text.c_str())); + auto* wasm = new wasm::Module; + auto parsed = wasm::WATParser::parseModule(*wasm, text); + if (auto* err = parsed.getErr()) { + wasm::Fatal() << err->msg << "\n"; + } + return new Module(wasm); } -BinaryenType createType(TypeList types) { - std::vector typeIdsVec = - convertJSArrayToNumberVector(types); - std::vector typesVec(typeIdsVec.begin(), typeIdsVec.end()); - return wasm::Type(typesVec).getID(); +wasm::Type createType(TypeList types) { + auto typesVec = vecFromJSArray(types); + return wasm::Type(typesVec); } } // namespace binaryen @@ -96,34 +113,30 @@ static std::string capitalize(std::string str) { } // namespace EMSCRIPTEN_BINDINGS(Binaryen) { - constant("none", wasm::Type::none); - constant("i32", wasm::Type::i32); - constant("i64", wasm::Type::i64); - constant("f32", wasm::Type::f32); - constant("f64", wasm::Type::f64); - constant("v128", wasm::Type::v128); - constant("funcref", - wasm::Type(wasm::HeapType::func, wasm::Nullable).getID()); - constant("externref", - wasm::Type(wasm::HeapType::ext, wasm::Nullable).getID()); - constant("anyref", - wasm::Type(wasm::HeapType::any, wasm::Nullable).getID()); - constant("eqref", - wasm::Type(wasm::HeapType::eq, wasm::Nullable).getID()); - constant("i31ref", - wasm::Type(wasm::HeapType::i31, wasm::Nullable).getID()); - constant( - "structref", wasm::Type(wasm::HeapType::struct_, wasm::Nullable).getID()); - constant( - "stringref", wasm::Type(wasm::HeapType::string, wasm::Nullable).getID()); - constant("nullref", - wasm::Type(wasm::HeapType::none, wasm::Nullable).getID()); - constant( - "nullexternref", wasm::Type(wasm::HeapType::noext, wasm::Nullable).getID()); - constant( - "nullfuncref", wasm::Type(wasm::HeapType::nofunc, wasm::Nullable).getID()); - constant("unreachable", wasm::Type::unreachable); - constant("auto", uintptr_t(-1)); + class_("Type"); + + register_optional(); + + constant("none", wasm::Type(wasm::Type::none)); + constant("i32", wasm::Type(wasm::Type::i32)); + constant("i64", wasm::Type(wasm::Type::i64)); + constant("f32", wasm::Type(wasm::Type::f32)); + constant("f64", wasm::Type(wasm::Type::f64)); + constant("v128", wasm::Type(wasm::Type::v128)); + constant("funcref", wasm::Type(wasm::HeapType::func, wasm::Nullable)); + constant("externref", wasm::Type(wasm::HeapType::ext, wasm::Nullable)); + constant("anyref", wasm::Type(wasm::HeapType::any, wasm::Nullable)); + constant("eqref", wasm::Type(wasm::HeapType::eq, wasm::Nullable)); + constant("i31ref", wasm::Type(wasm::HeapType::i31, wasm::Nullable)); + constant("structref", wasm::Type(wasm::HeapType::struct_, wasm::Nullable)); + constant("stringref", wasm::Type(wasm::HeapType::string, wasm::Nullable)); + constant("nullref", wasm::Type(wasm::HeapType::none, wasm::Nullable)); + constant("nullexternref", wasm::Type(wasm::HeapType::noext, wasm::Nullable)); + constant("nullfuncref", wasm::Type(wasm::HeapType::nofunc, wasm::Nullable)); + constant("unreachable", wasm::Type(wasm::Type::unreachable)); + constant("auto", val::undefined()); + + register_type("Type[]"); constant("notPacked", wasm::Field::PackedType::not_packed); constant("i8", wasm::Field::PackedType::i8); @@ -135,10 +148,9 @@ EMSCRIPTEN_BINDINGS(Binaryen) { expressionIds.value(#CLASS_TO_VISIT, wasm::Expression::Id::CLASS_TO_VISIT##Id) #include "wasm-delegations.def" - constant("InvalidId", wasm::Expression::Id::InvalidId); + constant("InvalidId", wasm::Expression::Id::InvalidId); #define DELEGATE(CLASS_TO_VISIT) \ - constant(#CLASS_TO_VISIT "Id", \ - wasm::Expression::Id::CLASS_TO_VISIT##Id); + constant(#CLASS_TO_VISIT "Id", wasm::Expression::Id::CLASS_TO_VISIT##Id); #include "wasm-delegations.def" class_("Module_Local") @@ -182,15 +194,16 @@ EMSCRIPTEN_BINDINGS(Binaryen) { function( "parseText", binaryen::parseText, allow_raw_pointer()); - // function("createType", binaryen::createType); + function("createType", binaryen::createType); class_("Expression") - /*.property("id", &wasm::Expression::_id) - .property("type", &wasm::Expression::type)*/ - ; + .property("id", &wasm::Expression::_id) + .property("type", &wasm::Expression::type); register_type("Expression[]"); + register_type("string[]"); + #define DELEGATE_FIELD_MAIN_START #define DELEGATE_FIELD_CASE_START(id) \ diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index b1b223e30f3..79971016e65 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -5,10 +5,10 @@ namespace binaryen { typedef uint32_t Index; -typedef uintptr_t Type; - EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); +EMSCRIPTEN_DECLARE_VAL_TYPE(NameList); + EMSCRIPTEN_DECLARE_VAL_TYPE(TypeList); struct ExpressionFactory { @@ -26,8 +26,9 @@ class Module { const uintptr_t& ptr() const; - wasm::Expression* - block(const std::string& name, ExpressionList children, uintptr_t type); + wasm::Expression* block(const std::string& name, + ExpressionList children, + std::optional type); wasm::Expression* if_(wasm::Expression* condition, wasm::Expression* ifTrue, wasm::Expression* ifFalse); @@ -35,8 +36,12 @@ class Module { wasm::Expression* br(const std::string& label, wasm::Expression* condition, wasm::Expression* value); + wasm::Expression* switch_(NameList names, + const std::string& defaultName, + wasm::Expression* condition, + wasm::Expression* value); const struct Local : ExpressionFactory { - wasm::Expression* get(Index index, Type type); + wasm::Expression* get(Index index, wasm::Type type); } local{module}; const struct I32 : ExpressionFactory { wasm::Expression* add(wasm::Expression* left, wasm::Expression* right); @@ -44,10 +49,10 @@ class Module { wasm::Expression* return_(wasm::Expression* value); uintptr_t addFunction(const std::string& name, - Type params, - Type results, + wasm::Type params, + wasm::Type results, TypeList varTypes, - uintptr_t body); + wasm::Expression body); uintptr_t addFunctionExport(const std::string& internalName, const std::string& externalName); @@ -56,5 +61,5 @@ class Module { Module* parseText(const std::string& text); -Type createType(TypeList types); +wasm::Type createType(TypeList types); }; // namespace binaryen \ No newline at end of file From 9da6d8bd37f346345918c66d548a8db984236be6 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sun, 27 Apr 2025 12:36:45 +0500 Subject: [PATCH 07/11] Extend API --- src/binaryen-embind.cpp | 147 +++++++++++++++++++++++++++++++++++----- src/binaryen-embind.h | 21 ++++++ 2 files changed, 151 insertions(+), 17 deletions(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index c56432f9e69..74ba2a72ee6 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -45,10 +45,68 @@ wasm::Expression* Module::switch_(NameList names, return wasm::Builder(*module).makeSwitch( namesVec, defaultName, condition, value); } +wasm::Expression* Module::call(const std::string& name, + ExpressionList operands, + wasm::Type type) { + return wasm::Builder(*module).makeCall( + name, + vecFromJSArray(operands, + allow_raw_pointer()), + type); +} +wasm::Expression* Module::call_indirect(const std::string& table, + wasm::Expression* target, + ExpressionList operands, + wasm::Type params, + wasm::Type results) { + return wasm::Builder(*module).makeCallIndirect( + table, + target, + vecFromJSArray(operands, + allow_raw_pointer()), + wasm::Signature(params, results)); +} +wasm::Expression* Module::return_call(const std::string& name, + ExpressionList operands, + wasm::Type type) { + return wasm::Builder(*module).makeCall( + name, + vecFromJSArray(operands, + allow_raw_pointer()), + type, + true); +} +wasm::Expression* Module::return_call_indirect(const std::string& table, + wasm::Expression* target, + ExpressionList operands, + wasm::Type params, + wasm::Type results) { + return wasm::Builder(*module).makeCallIndirect( + table, + target, + vecFromJSArray(operands, + allow_raw_pointer()), + wasm::Signature(params, results), + true); +} wasm::Expression* Module::Local::get(Index index, wasm::Type type) { return wasm::Builder(*module).makeLocalGet(index, type); } - +wasm::Expression* Module::Local::set(Index index, wasm::Expression* value) { + return wasm::Builder(*module).makeLocalSet(index, value); +} +wasm::Expression* +Module::Local::tee(Index index, wasm::Expression* value, wasm::Type type) { + return wasm::Builder(*module).makeLocalTee(index, value, type); +} +wasm::Expression* Module::Global::get(const std::string& name, + wasm::Type type) { + return wasm::Builder(*module).makeGlobalGet(name, type); +} +wasm::Expression* Module::Global::set(const std::string& name, + wasm::Expression* value) { + return wasm::Builder(*module).makeGlobalSet(name, value); +} wasm::Expression* Module::I32::add(wasm::Expression* left, wasm::Expression* right) { return wasm::Builder(*module).makeBinary( @@ -134,7 +192,7 @@ EMSCRIPTEN_BINDINGS(Binaryen) { constant("nullexternref", wasm::Type(wasm::HeapType::noext, wasm::Nullable)); constant("nullfuncref", wasm::Type(wasm::HeapType::nofunc, wasm::Nullable)); constant("unreachable", wasm::Type(wasm::Type::unreachable)); - constant("auto", val::undefined()); + constant("auto", val::null()); register_type("Type[]"); @@ -156,35 +214,90 @@ EMSCRIPTEN_BINDINGS(Binaryen) { class_("Module_Local") .function("get", &binaryen::Module::Local::get, - allow_raw_pointer()); + allow_raw_pointer(), + nonnull()) + .function("set", + &binaryen::Module::Local::set, + allow_raw_pointer(), + nonnull()) + .function("get", + &binaryen::Module::Local::tee, + allow_raw_pointer(), + nonnull()); + + class_("Module_Global") + .function("get", + &binaryen::Module::Global::get, + allow_raw_pointer(), + nonnull()) + .function("set", + &binaryen::Module::Global::set, + allow_raw_pointer(), + nonnull()); class_("Module_I32") .function("add", &binaryen::Module::I32::add, - allow_raw_pointer()); + allow_raw_pointer(), + nonnull()); class_("Module") .constructor() .property("ptr", &binaryen::Module::ptr) - .function( - "block", &binaryen::Module::block, allow_raw_pointer()) - .function( - "if", &binaryen::Module::if_, allow_raw_pointer()) - .function( - "loop", &binaryen::Module::loop, allow_raw_pointer()) - .function( - "br", &binaryen::Module::br, allow_raw_pointer()) - .function( - "break", &binaryen::Module::br, allow_raw_pointer()) - .function( - "br_if", &binaryen::Module::br, allow_raw_pointer()) + .function("block", + &binaryen::Module::block, + allow_raw_pointer(), + nonnull()) + .function("if", + &binaryen::Module::if_, + allow_raw_pointer(), + nonnull()) + .function("loop", + &binaryen::Module::loop, + allow_raw_pointer(), + nonnull()) + .function("br", + &binaryen::Module::br, + allow_raw_pointer(), + nonnull()) + .function("break", + &binaryen::Module::br, + allow_raw_pointer(), + nonnull()) + .function("br_if", + &binaryen::Module::br, + allow_raw_pointer(), + nonnull()) + .function("switch", + &binaryen::Module::switch_, + allow_raw_pointer(), + nonnull()) + .function("call", + &binaryen::Module::call, + allow_raw_pointer(), + nonnull()) + .function("call_indirect", + &binaryen::Module::call_indirect, + allow_raw_pointer(), + nonnull()) + .function("return_call", + &binaryen::Module::return_call, + allow_raw_pointer(), + nonnull()) + .function("return_call_indirect", + &binaryen::Module::return_call_indirect, + allow_raw_pointer(), + nonnull()) .property( "local", &binaryen::Module::local, return_value_policy::reference()) + .property( + "global", &binaryen::Module::global, return_value_policy::reference()) .property("i32", &binaryen::Module::i32, return_value_policy::reference()) .function("return", &binaryen::Module::return_, - allow_raw_pointer()) + allow_raw_pointer(), + nonnull()) /*.function("addFunction", &binaryen::Module::addFunction) .function("addFunctionExport", &binaryen::Module::addFunctionExport)*/ diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index 79971016e65..106af2f6b68 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -40,9 +40,30 @@ class Module { const std::string& defaultName, wasm::Expression* condition, wasm::Expression* value); + wasm::Expression* + call(const std::string&, ExpressionList operands, wasm::Type type); + wasm::Expression* call_indirect(const std::string& table, + wasm::Expression* target, + ExpressionList operands, + wasm::Type params, + wasm::Type results); + wasm::Expression* + return_call(const std::string&, ExpressionList operands, wasm::Type type); + wasm::Expression* return_call_indirect(const std::string& table, + wasm::Expression* target, + ExpressionList operands, + wasm::Type params, + wasm::Type results); const struct Local : ExpressionFactory { wasm::Expression* get(Index index, wasm::Type type); + wasm::Expression* set(Index index, wasm::Expression* value); + wasm::Expression* + tee(Index index, wasm::Expression* value, wasm::Type type); } local{module}; + const struct Global : ExpressionFactory { + wasm::Expression* get(const std::string& name, wasm::Type type); + wasm::Expression* set(const std::string& name, wasm::Expression* value); + } global{module}; const struct I32 : ExpressionFactory { wasm::Expression* add(wasm::Expression* left, wasm::Expression* right); } i32{module}; From 89e7f4faba27aaec69e0f11ce655e81e84631493 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sun, 27 Apr 2025 15:43:47 +0500 Subject: [PATCH 08/11] Clean up --- CMakeLists.txt | 4 +-- src/binaryen-embind.cpp | 69 +++++++++++++++++++++++++++-------------- src/binaryen-embind.h | 14 ++++----- 3 files changed, 54 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5991c8683d5..6ac28935a4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -532,7 +532,7 @@ if(EMSCRIPTEN) # binaryen.js WebAssembly variant add_executable(binaryen_wasm src/binaryen-embind.cpp) - target_link_libraries(binaryen_wasm PRIVATE "-Wl,--whole-archive" binaryen) + target_link_libraries(binaryen_wasm PRIVATE binaryen) target_link_libraries(binaryen_wasm PRIVATE "-sFILESYSTEM") target_link_libraries(binaryen_wasm PRIVATE "-sEXPORT_NAME=Binaryen") target_link_libraries(binaryen_wasm PRIVATE "-sNODERAWFS=0") @@ -561,7 +561,7 @@ if(EMSCRIPTEN) # binaryen.js JavaScript variant add_executable(binaryen_js src/binaryen-embind.cpp) - target_link_libraries(binaryen_js PRIVATE "-Wl,--whole-archive" binaryen) + target_link_libraries(binaryen_js PRIVATE binaryen) target_link_libraries(binaryen_js PRIVATE "-sWASM=0") target_link_libraries(binaryen_js PRIVATE "-sWASM_ASYNC_COMPILATION=0") diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 74ba2a72ee6..8c7ea86d4be 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -1,6 +1,7 @@ #include "binaryen-embind.h" #include "parser/wat-parser.h" #include "wasm-builder.h" +#include using namespace emscripten; @@ -116,27 +117,35 @@ wasm::Expression* Module::return_(wasm::Expression* value) { return wasm::Builder(*module).makeReturn(value); } -/*uintptr_t Module::addFunction(const std::string& name, - BinaryenType params, - BinaryenType results, - TypeList varTypes, - uintptr_t body) { - std::vector varTypesVec = - convertJSArrayToNumberVector(varTypes); - return reinterpret_cast( - BinaryenAddFunction(module, - name.c_str(), - params, - results, - varTypesVec.begin().base(), - varTypesVec.size(), - reinterpret_cast(body))); +static std::mutex ModuleAddFunctionMutex; + +wasm::Function* Module::addFunction(const std::string& name, + wasm::Type params, + wasm::Type results, + TypeList varTypes, + wasm::Expression* body) { + auto* ret = new wasm::Function; + ret->setExplicitName(name); + ret->type = wasm::Signature(params, results); + ret->vars = vecFromJSArray(varTypes); + ret->body = body; + + // Lock. This can be called from multiple threads at once, and is a + // point where they all access and modify the module. + { + std::lock_guard lock(ModuleAddFunctionMutex); + module->addFunction(ret); + } + + return ret; +} +wasm::Export* Module::addFunctionExport(const std::string& internalName, + const std::string& externalName) { + auto* ret = + new wasm::Export(externalName, wasm::ExternalKind::Function, internalName); + module->addExport(ret); + return ret; } -uintptr_t Module::addFunctionExport(const std::string& internalName, - const std::string& externalName) { - return reinterpret_cast(BinaryenAddFunctionExport( - module, internalName.c_str(), externalName.c_str())); -}*/ std::string Module::emitText() { std::ostringstream os; @@ -211,6 +220,10 @@ EMSCRIPTEN_BINDINGS(Binaryen) { constant(#CLASS_TO_VISIT "Id", wasm::Expression::Id::CLASS_TO_VISIT##Id); #include "wasm-delegations.def" + class_("Function"); + + class_("Export"); + class_("Module_Local") .function("get", &binaryen::Module::Local::get, @@ -299,13 +312,21 @@ EMSCRIPTEN_BINDINGS(Binaryen) { allow_raw_pointer(), nonnull()) - /*.function("addFunction", &binaryen::Module::addFunction) - .function("addFunctionExport", &binaryen::Module::addFunctionExport)*/ + .function("addFunction", + &binaryen::Module::addFunction, + allow_raw_pointer(), + nonnull()) + .function("addFunctionExport", + &binaryen::Module::addFunctionExport, + allow_raw_pointer(), + nonnull()) .function("emitText", &binaryen::Module::emitText); - function( - "parseText", binaryen::parseText, allow_raw_pointer()); + function("parseText", + binaryen::parseText, + allow_raw_pointer(), + nonnull()); function("createType", binaryen::createType); diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index 106af2f6b68..afc34344d61 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -69,13 +69,13 @@ class Module { } i32{module}; wasm::Expression* return_(wasm::Expression* value); - uintptr_t addFunction(const std::string& name, - wasm::Type params, - wasm::Type results, - TypeList varTypes, - wasm::Expression body); - uintptr_t addFunctionExport(const std::string& internalName, - const std::string& externalName); + wasm::Function* addFunction(const std::string& name, + wasm::Type params, + wasm::Type results, + TypeList varTypes, + wasm::Expression* body); + wasm::Export* addFunctionExport(const std::string& internalName, + const std::string& externalName); std::string emitText(); }; From 8440c8f578ef2f9f254c1ca10c05f6f0bff055ed Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sun, 27 Apr 2025 18:19:10 +0500 Subject: [PATCH 09/11] Add more pass functions --- src/binaryen-embind.cpp | 74 ++++++++++++++++++++++++++++++++++++++++- src/binaryen-embind.h | 11 ++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 8c7ea86d4be..4146a1c4fd9 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -1,11 +1,17 @@ #include "binaryen-embind.h" #include "parser/wat-parser.h" +#include "wasm-binary.h" #include "wasm-builder.h" +#include "wasm-stack.h" +#include "wasm2js.h" #include using namespace emscripten; namespace binaryen { +static wasm::PassOptions passOptions = + wasm::PassOptions::getWithDefaultOptimizationOptions(); + Module::Module() : Module(new wasm::Module()) {} Module::Module(wasm::Module* module) : module(module) {} @@ -147,6 +153,16 @@ wasm::Export* Module::addFunctionExport(const std::string& internalName, return ret; } +Binary Module::emitBinary() { + wasm::BufferWithRandomAccess buffer; + wasm::WasmBinaryWriter writer(module, buffer, passOptions); + writer.setNamesSection(passOptions.debugInfo); + std::ostringstream os; + // TODO: Source map + writer.write(); + return static_cast( + val(typed_memory_view(buffer.size(), buffer.data()))); +} std::string Module::emitText() { std::ostringstream os; bool colors = Colors::isEnabled(); @@ -155,6 +171,47 @@ std::string Module::emitText() { Colors::setEnabled(colors); // restore colors state return os.str(); } +std::string Module::emitStackIR() { + std::ostringstream os; + bool colors = Colors::isEnabled(); + Colors::setEnabled(false); // do not use colors for writing + wasm::printStackIR(os, module, passOptions); + Colors::setEnabled(colors); // restore colors state + auto str = os.str(); + const size_t len = str.length() + 1; + char* output = (char*)malloc(len); + std::copy_n(str.c_str(), len, output); + return output; +} +std::string Module::emitAsmjs() { + wasm::Wasm2JSBuilder::Flags flags; + wasm::Wasm2JSBuilder wasm2js(flags, passOptions); + auto asmjs = wasm2js.processWasm(module); + wasm::JSPrinter jser(true, true, asmjs); + wasm::Output out("", wasm::Flags::Text); // stdout + wasm::Wasm2JSGlue glue(*module, out, flags, "asmFunc"); + glue.emitPre(); + jser.printAst(); + std::string text(jser.buffer); + glue.emitPost(); + return text; +} + +bool Module::validate() { return wasm::WasmValidator().validate(*module); } +void Module::optimize() { + wasm::PassRunner passRunner(module); + passRunner.options = passOptions; + passRunner.addDefaultOptimizationPasses(); + passRunner.run(); +} +void Module::optimizeFunction(wasm::Function* func) { + wasm::PassRunner passRunner(module); + passRunner.options = passOptions; + passRunner.addDefaultFunctionOptimizationPasses(); + passRunner.runOnFunction(func); +} + +void Module::dispose() { delete this; } Module* parseText(const std::string& text) { auto* wasm = new wasm::Module; @@ -180,6 +237,8 @@ static std::string capitalize(std::string str) { } // namespace EMSCRIPTEN_BINDINGS(Binaryen) { + register_type("Uint8Array"); + class_("Type"); register_optional(); @@ -321,7 +380,20 @@ EMSCRIPTEN_BINDINGS(Binaryen) { allow_raw_pointer(), nonnull()) - .function("emitText", &binaryen::Module::emitText); + .function("emitBinary", &binaryen::Module::emitBinary) + .function("emitText", &binaryen::Module::emitText) + .function("emitStackIR", &binaryen::Module::emitStackIR) + .function("emitAsmjs", &binaryen::Module::emitAsmjs) + + .function("validate", &binaryen::Module::validate) + .function("optimize", &binaryen::Module::optimize) + .function("optimizeFunction", &binaryen::Module::optimizeFunction) + + .function( + "dispose", + &binaryen::Module::dispose) // for compatibility, should be removed later + // in favor of Module.delete() + ; function("parseText", binaryen::parseText, diff --git a/src/binaryen-embind.h b/src/binaryen-embind.h index afc34344d61..182e82a28ac 100644 --- a/src/binaryen-embind.h +++ b/src/binaryen-embind.h @@ -5,6 +5,8 @@ namespace binaryen { typedef uint32_t Index; +EMSCRIPTEN_DECLARE_VAL_TYPE(Binary); + EMSCRIPTEN_DECLARE_VAL_TYPE(ExpressionList); EMSCRIPTEN_DECLARE_VAL_TYPE(NameList); @@ -77,7 +79,16 @@ class Module { wasm::Export* addFunctionExport(const std::string& internalName, const std::string& externalName); + Binary emitBinary(); std::string emitText(); + std::string emitStackIR(); + std::string emitAsmjs(); + + bool validate(); + void optimize(); + void optimizeFunction(wasm::Function* func); + + void dispose(); }; Module* parseText(const std::string& text); From 91cf1ee51315235a615da1b4f652e03bc0ce1a74 Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sun, 27 Apr 2025 18:21:25 +0500 Subject: [PATCH 10/11] Fix bugs --- src/binaryen-embind.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 4146a1c4fd9..52da9fedf39 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -387,7 +387,9 @@ EMSCRIPTEN_BINDINGS(Binaryen) { .function("validate", &binaryen::Module::validate) .function("optimize", &binaryen::Module::optimize) - .function("optimizeFunction", &binaryen::Module::optimizeFunction) + .function("optimizeFunction", + &binaryen::Module::optimizeFunction, + allow_raw_pointer()) .function( "dispose", From d1b425b695efa36dee4f2a17e3c332b00febf03c Mon Sep 17 00:00:00 2001 From: GulgDev Date: Sun, 27 Apr 2025 18:57:42 +0500 Subject: [PATCH 11/11] Fix memory corruption --- src/binaryen-embind.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/binaryen-embind.cpp b/src/binaryen-embind.cpp index 52da9fedf39..b804b7cd0e4 100644 --- a/src/binaryen-embind.cpp +++ b/src/binaryen-embind.cpp @@ -4,6 +4,7 @@ #include "wasm-builder.h" #include "wasm-stack.h" #include "wasm2js.h" +#include #include using namespace emscripten; @@ -160,8 +161,8 @@ Binary Module::emitBinary() { std::ostringstream os; // TODO: Source map writer.write(); - return static_cast( - val(typed_memory_view(buffer.size(), buffer.data()))); + return val::global("Uint8Array") + .call("from", val(typed_memory_view(buffer.size(), buffer.data()))); } std::string Module::emitText() { std::ostringstream os;