diff options
author | Kris Selden <kris.selden@gmail.com> | 2018-02-06 07:17:17 -0800 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2018-02-06 07:17:17 -0800 |
commit | a85be9781620696b79d0efd694605f608417098d (patch) | |
tree | ce93b6554c6b932a19b2c7cda425da5ac4513bb6 | |
parent | fd03054a5dac582ea1cbe8745c9b4852a7c9c686 (diff) | |
download | binaryen-a85be9781620696b79d0efd694605f608417098d.tar.gz binaryen-a85be9781620696b79d0efd694605f608417098d.tar.bz2 binaryen-a85be9781620696b79d0efd694605f608417098d.zip |
Update wasm2asm to generate "almost asm" if grow_memory is used (#1340)
* Allow wasm2asm to generate "almost asm"
If grow_memory, current_memory or export memory is
used then generate "almost asm" with memory growth
support.
* Log reason for "almost asm" to stderr
-rw-r--r-- | src/asmjs/shared-constants.cpp | 5 | ||||
-rw-r--r-- | src/asmjs/shared-constants.h | 5 | ||||
-rw-r--r-- | src/wasm2asm.h | 228 | ||||
-rw-r--r-- | test/grow_memory.2asm.js | 109 | ||||
-rw-r--r-- | test/grow_memory.wast | 17 | ||||
-rw-r--r-- | test/grow_memory.wast.from-wast | 16 | ||||
-rw-r--r-- | test/grow_memory.wast.fromBinary | 17 | ||||
-rw-r--r-- | test/grow_memory.wast.fromBinary.noDebugInfo | 17 |
8 files changed, 406 insertions, 8 deletions
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp index f62be6168..09452805c 100644 --- a/src/asmjs/shared-constants.cpp +++ b/src/asmjs/shared-constants.cpp @@ -62,6 +62,7 @@ cashew::IString GLOBAL("global"), I32_TEMP("asm2wasm_i32_temp"), DEBUGGER("debugger"), USE_ASM("use asm"), + ALMOST_ASM("almost asm"), BUFFER("buffer"), ENV("env"), INSTRUMENT("instrument"), @@ -84,5 +85,7 @@ cashew::IString GLOBAL("global"), WASM_ROTL32("__wasm_rotl_i32"), WASM_ROTL64("__wasm_rotl_i64"), WASM_ROTR32("__wasm_rotr_i32"), - WASM_ROTR64("__wasm_rotr_i64"); + WASM_ROTR64("__wasm_rotr_i64"), + WASM_GROW_MEMORY("__wasm_grow_memory"), + WASM_CURRENT_MEMORY("__wasm_current_memory"); } diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h index 7e4b27c85..06946c7f5 100644 --- a/src/asmjs/shared-constants.h +++ b/src/asmjs/shared-constants.h @@ -65,6 +65,7 @@ extern cashew::IString GLOBAL, I32_TEMP, DEBUGGER, USE_ASM, + ALMOST_ASM, BUFFER, ENV, INSTRUMENT, @@ -87,7 +88,9 @@ extern cashew::IString GLOBAL, WASM_ROTL32, WASM_ROTL64, WASM_ROTR32, - WASM_ROTR64; + WASM_ROTR64, + WASM_GROW_MEMORY, + WASM_CURRENT_MEMORY; } #endif // wasm_asmjs_shared_constants_h diff --git a/src/wasm2asm.h b/src/wasm2asm.h index 6a355c15c..fd7a03234 100644 --- a/src/wasm2asm.h +++ b/src/wasm2asm.h @@ -205,11 +205,15 @@ private: // All our function tables have the same size TODO: optimize? size_t tableSize; + bool almostASM = false; + void addBasics(Ref ast); void addImport(Ref ast, Import* import); void addTables(Ref ast, Module* wasm); void addExports(Ref ast, Module* wasm); void addWasmCompatibilityFuncs(Module* wasm); + void setNeedsAlmostASM(const char *reason); + void addMemoryGrowthFuncs(Ref ast); bool isAssertHandled(Element& e); Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder, Builder& wasmBuilder, @@ -522,11 +526,54 @@ void Wasm2AsmBuilder::addTables(Ref ast, Module* wasm) { void Wasm2AsmBuilder::addExports(Ref ast, Module* wasm) { Ref exports = ValueBuilder::makeObject(); for (auto& export_ : wasm->exports) { - ValueBuilder::appendToObject( - exports, - fromName(export_->name), - ValueBuilder::makeName(fromName(export_->value)) - ); + if (export_->kind == ExternalKind::Function) { + ValueBuilder::appendToObject( + exports, + fromName(export_->name), + ValueBuilder::makeName(fromName(export_->value)) + ); + } + if (export_->kind == ExternalKind::Memory) { + setNeedsAlmostASM("memory export"); + Ref descs = ValueBuilder::makeObject(); + Ref growDesc = ValueBuilder::makeObject(); + ValueBuilder::appendToObject( + descs, + IString("grow"), + growDesc); + ValueBuilder::appendToObject( + growDesc, + IString("value"), + ValueBuilder::makeName(WASM_GROW_MEMORY)); + Ref bufferDesc = ValueBuilder::makeObject(); + Ref bufferGetter = ValueBuilder::makeFunction(IString("")); + bufferGetter[3]->push_back(ValueBuilder::makeReturn( + ValueBuilder::makeName(BUFFER) + )); + ValueBuilder::appendToObject( + bufferDesc, + IString("get"), + bufferGetter); + ValueBuilder::appendToObject( + descs, + IString("buffer"), + bufferDesc); + Ref memory = ValueBuilder::makeCall( + ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), IString("create")), + ValueBuilder::makeDot(ValueBuilder::makeName(IString("Object")), IString("prototype"))); + ValueBuilder::appendToCall( + memory, + descs); + ValueBuilder::appendToObject( + exports, + fromName(export_->name), + memory); + } + } + if (almostASM) { + // replace "use asm" + ast[0] = ValueBuilder::makeStatement(ValueBuilder::makeString(ALMOST_ASM)); + addMemoryGrowthFuncs(ast); } ast->push_back(ValueBuilder::makeStatement(ValueBuilder::makeReturn(exports))); } @@ -1557,7 +1604,18 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) { } Ref visitHost(Host* curr) { - abort(); + if (curr->op == HostOp::GrowMemory) { + parent->setNeedsAlmostASM("grow_memory op"); + return ValueBuilder::makeCall(WASM_GROW_MEMORY, + makeAsmCoercion( + visit(curr->operands[0], EXPRESSION_RESULT), + wasmToAsmType(curr->operands[0]->type))); + } + if (curr->op == HostOp::CurrentMemory) { + parent->setNeedsAlmostASM("current_memory op"); + return ValueBuilder::makeCall(WASM_CURRENT_MEMORY); + } + return ValueBuilder::makeCall(ABORT_FUNC); } Ref visitNop(Nop* curr) { @@ -1696,6 +1754,164 @@ Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder, return outerFunc; } +void Wasm2AsmBuilder::setNeedsAlmostASM(const char *reason) { + if (!almostASM) { + almostASM = true; + std::cerr << "Switching to \"almost asm\" mode, reason: " << reason << std::endl; + } +} + +void Wasm2AsmBuilder::addMemoryGrowthFuncs(Ref ast) { + Ref growMemoryFunc = ValueBuilder::makeFunction(WASM_GROW_MEMORY); + ValueBuilder::appendArgumentToFunction(growMemoryFunc, IString("pagesToAdd")); + + growMemoryFunc[3]->push_back( + ValueBuilder::makeStatement( + ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("pagesToAdd")), SET, + makeAsmCoercion( + ValueBuilder::makeName(IString("pagesToAdd")), + AsmType::ASM_INT + ) + ) + ) + ); + + Ref oldPages = ValueBuilder::makeVar(); + growMemoryFunc[3]->push_back(oldPages); + ValueBuilder::appendToVar( + oldPages, + IString("oldPages"), + makeAsmCoercion(ValueBuilder::makeCall(WASM_CURRENT_MEMORY), AsmType::ASM_INT)); + + Ref newPages = ValueBuilder::makeVar(); + growMemoryFunc[3]->push_back(newPages); + ValueBuilder::appendToVar( + newPages, + IString("newPages"), + makeAsmCoercion(ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("oldPages")), + PLUS, + ValueBuilder::makeName(IString("pagesToAdd")) + ), AsmType::ASM_INT)); + + Ref block = ValueBuilder::makeBlock(); + growMemoryFunc[3]->push_back(ValueBuilder::makeIf( + ValueBuilder::makeBinary( + ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("oldPages")), + LT, + ValueBuilder::makeName(IString("newPages")) + ), + IString("&&"), + ValueBuilder::makeBinary( + ValueBuilder::makeName(IString("newPages")), + LT, + ValueBuilder::makeInt(Memory::kMaxSize) + ) + ), block, NULL)); + + Ref newBuffer = ValueBuilder::makeVar(); + ValueBuilder::appendToBlock(block, newBuffer); + ValueBuilder::appendToVar( + newBuffer, + IString("newBuffer"), + ValueBuilder::makeNew(ValueBuilder::makeCall( + ARRAY_BUFFER, + ValueBuilder::makeCall( + MATH_IMUL, + ValueBuilder::makeName(IString("newPages")), + ValueBuilder::makeInt(Memory::kPageSize))))); + + Ref newHEAP8 = ValueBuilder::makeVar(); + ValueBuilder::appendToBlock(block, newHEAP8); + ValueBuilder::appendToVar( + newHEAP8, + IString("newHEAP8"), + ValueBuilder::makeNew( + ValueBuilder::makeCall( + ValueBuilder::makeDot( + ValueBuilder::makeName(GLOBAL), + INT8ARRAY + ), + ValueBuilder::makeName(IString("newBuffer")) + ) + )); + + ValueBuilder::appendToBlock(block, + ValueBuilder::makeCall( + ValueBuilder::makeDot( + ValueBuilder::makeName(IString("newHEAP8")), + IString("set") + ), + ValueBuilder::makeName(HEAP8) + ) + ); + + ValueBuilder::appendToBlock(block, + ValueBuilder::makeBinary( + ValueBuilder::makeName(HEAP8), + SET, + ValueBuilder::makeName(IString("newHEAP8")) + ) + ); + + auto setHeap = [&](IString name, IString view) { + ValueBuilder::appendToBlock(block, + ValueBuilder::makeBinary( + ValueBuilder::makeName(name), + SET, + ValueBuilder::makeNew( + ValueBuilder::makeCall( + ValueBuilder::makeDot( + ValueBuilder::makeName(GLOBAL), + view + ), + ValueBuilder::makeName(IString("newBuffer")) + ) + ) + ) + ); + }; + + setHeap(HEAP16, INT16ARRAY); + setHeap(HEAP32, INT32ARRAY); + setHeap(HEAPU8, UINT8ARRAY); + setHeap(HEAPU16, UINT16ARRAY); + setHeap(HEAPU32, UINT32ARRAY); + setHeap(HEAPF32, FLOAT32ARRAY); + setHeap(HEAPF64, FLOAT64ARRAY); + + ValueBuilder::appendToBlock(block, + ValueBuilder::makeBinary( + ValueBuilder::makeName(BUFFER), + SET, + ValueBuilder::makeName(IString("newBuffer")) + ) + ); + + growMemoryFunc[3]->push_back( + ValueBuilder::makeReturn( + ValueBuilder::makeName(IString("oldPages")))); + + Ref currentMemoryFunc = ValueBuilder::makeFunction(WASM_CURRENT_MEMORY); + currentMemoryFunc[3]->push_back(ValueBuilder::makeReturn( + makeAsmCoercion( + ValueBuilder::makeBinary( + ValueBuilder::makeDot( + ValueBuilder::makeName(BUFFER), + IString("byteLength") + ), + DIV, + ValueBuilder::makeInt(Memory::kPageSize) + ), + AsmType::ASM_INT + ) + )); + ast->push_back(growMemoryFunc); + ast->push_back(currentMemoryFunc); +} + bool Wasm2AsmBuilder::isAssertHandled(Element& e) { return e.isList() && e.size() >= 2 && e[0]->isStr() && (e[0]->str() == Name("assert_return") || diff --git a/test/grow_memory.2asm.js b/test/grow_memory.2asm.js new file mode 100644 index 000000000..0f181d94b --- /dev/null +++ b/test/grow_memory.2asm.js @@ -0,0 +1,109 @@ +function asmFunc(global, env, buffer) { + "almost asm"; + var HEAP8 = new global.Int8Array(buffer); + var HEAP16 = new global.Int16Array(buffer); + var HEAP32 = new global.Int32Array(buffer); + var HEAPU8 = new global.Uint8Array(buffer); + var HEAPU16 = new global.Uint16Array(buffer); + var HEAPU32 = new global.Uint32Array(buffer); + var HEAPF32 = new global.Float32Array(buffer); + var HEAPF64 = new global.Float64Array(buffer); + var Math_imul = global.Math.imul; + var Math_fround = global.Math.fround; + var Math_abs = global.Math.abs; + var Math_clz32 = global.Math.clz32; + function $$0(var$0) { + var$0 = var$0 | 0; + var $$1 = 0, $$2 = 0, wasm2asm_i32$0 = 0; + return __wasm_grow_memory(var$0 | 0) | 0; + return wasm2asm_i32$0 | 0; + } + + function $$1() { + var $$0 = 0, wasm2asm_i32$0 = 0; + return __wasm_current_memory() | 0; + return wasm2asm_i32$0 | 0; + } + + function __wasm_ctz_i32(x) { + x = x | 0; + var $$1 = 0, $$2 = 0, $$3 = 0, $$4 = 0, $$5 = 0, $$6 = 0, $$7 = 0, $$8 = 0, $$9 = 0, $$10 = 0; + if ((x | 0) == (0 | 0)) $$9 = 32; else $$9 = 31 - Math_clz32(x ^ (x - 1 | 0) | 0) | 0; + return $$9 | 0; + } + + function __wasm_popcnt_i32(x) { + x = x | 0; + var count = 0, $$2 = 0, $$3 = 0, $$4 = 0, $$5 = 0, $$6 = 0, $$7 = 0, $$8 = 0, $$9 = 0, $$10 = 0, $$11 = 0, $$12 = 0, $$13 = 0, $$14 = 0, $$15 = 0; + count = 0; + b : { + l : do { + $$5 = count; + if ((x | 0) == (0 | 0)) break b; + x = x & (x - 1 | 0) | 0; + count = count + 1 | 0; + continue l; + break l; + } while (1); + }; + return $$5 | 0; + } + + function __wasm_rotl_i32(x, k) { + x = x | 0; + k = k | 0; + var $$2 = 0, $$3 = 0, $$4 = 0, $$5 = 0, $$6 = 0, $$7 = 0, $$8 = 0, $$9 = 0, $$10 = 0, $$11 = 0, $$12 = 0, $$13 = 0, $$14 = 0, $$15 = 0, $$16 = 0, $$17 = 0, $$18 = 0, $$19 = 0, $$20 = 0, wasm2asm_i32$0 = 0; + return ((4294967295 >>> (k & 31 | 0) | 0) & x | 0) << (k & 31 | 0) | 0 | (((4294967295 << (32 - (k & 31 | 0) | 0) | 0) & x | 0) >>> (32 - (k & 31 | 0) | 0) | 0) | 0 | 0; + return wasm2asm_i32$0 | 0; + } + + function __wasm_rotr_i32(x, k) { + x = x | 0; + k = k | 0; + var $$2 = 0, $$3 = 0, $$4 = 0, $$5 = 0, $$6 = 0, $$7 = 0, $$8 = 0, $$9 = 0, $$10 = 0, $$11 = 0, $$12 = 0, $$13 = 0, $$14 = 0, $$15 = 0, $$16 = 0, $$17 = 0, $$18 = 0, $$19 = 0, $$20 = 0, wasm2asm_i32$0 = 0; + return ((4294967295 << (k & 31 | 0) | 0) & x | 0) >>> (k & 31 | 0) | 0 | (((4294967295 >>> (32 - (k & 31 | 0) | 0) | 0) & x | 0) << (32 - (k & 31 | 0) | 0) | 0) | 0 | 0; + return wasm2asm_i32$0 | 0; + } + + function __wasm_grow_memory(pagesToAdd) { + pagesToAdd = pagesToAdd | 0; + var oldPages = __wasm_current_memory() | 0; + var newPages = oldPages + pagesToAdd | 0; + if ((oldPages < newPages) && (newPages < 65535)) { + var newBuffer = new ArrayBuffer(Math_imul(newPages, 65536)); + var newHEAP8 = new global.Int8Array(newBuffer); + newHEAP8.set(HEAP8); + HEAP8 = newHEAP8; + HEAP16 = new global.Int16Array(newBuffer); + HEAP32 = new global.Int32Array(newBuffer); + HEAPU8 = new global.Uint8Array(newBuffer); + HEAPU16 = new global.Uint16Array(newBuffer); + HEAPU32 = new global.Uint32Array(newBuffer); + HEAPF32 = new global.Float32Array(newBuffer); + HEAPF64 = new global.Float64Array(newBuffer); + buffer = newBuffer; + } + return oldPages; + } + + function __wasm_current_memory() { + return buffer.byteLength / 65536 | 0; + } + + return { + memory: Object.create(Object.prototype, { + grow: { + value: __wasm_grow_memory + }, + buffer: { + get: function () { + return buffer; + } + + } + }), + grow: $$0, + current: $$1 + }; +} + diff --git a/test/grow_memory.wast b/test/grow_memory.wast new file mode 100644 index 000000000..9b8b8ee5e --- /dev/null +++ b/test/grow_memory.wast @@ -0,0 +1,17 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func (result i32))) + (memory $0 1) + (export "memory" (memory $0)) + (export "grow" (func $0)) + (export "current" (func $1)) + (func $0 (; 0 ;) (type $0) (param $var$0 i32) (result i32) + (grow_memory + (get_local $var$0) + ) + ) + (func $1 (; 1 ;) (type $1) (result i32) + (current_memory) + ) +) + diff --git a/test/grow_memory.wast.from-wast b/test/grow_memory.wast.from-wast new file mode 100644 index 000000000..49980cd78 --- /dev/null +++ b/test/grow_memory.wast.from-wast @@ -0,0 +1,16 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func (result i32))) + (memory $0 1) + (export "memory" (memory $0)) + (export "grow" (func $0)) + (export "current" (func $1)) + (func $0 (; 0 ;) (type $0) (param $var$0 i32) (result i32) + (grow_memory + (get_local $var$0) + ) + ) + (func $1 (; 1 ;) (type $1) (result i32) + (current_memory) + ) +) diff --git a/test/grow_memory.wast.fromBinary b/test/grow_memory.wast.fromBinary new file mode 100644 index 000000000..9b8b8ee5e --- /dev/null +++ b/test/grow_memory.wast.fromBinary @@ -0,0 +1,17 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func (result i32))) + (memory $0 1) + (export "memory" (memory $0)) + (export "grow" (func $0)) + (export "current" (func $1)) + (func $0 (; 0 ;) (type $0) (param $var$0 i32) (result i32) + (grow_memory + (get_local $var$0) + ) + ) + (func $1 (; 1 ;) (type $1) (result i32) + (current_memory) + ) +) + diff --git a/test/grow_memory.wast.fromBinary.noDebugInfo b/test/grow_memory.wast.fromBinary.noDebugInfo new file mode 100644 index 000000000..9b8b8ee5e --- /dev/null +++ b/test/grow_memory.wast.fromBinary.noDebugInfo @@ -0,0 +1,17 @@ +(module + (type $0 (func (param i32) (result i32))) + (type $1 (func (result i32))) + (memory $0 1) + (export "memory" (memory $0)) + (export "grow" (func $0)) + (export "current" (func $1)) + (func $0 (; 0 ;) (type $0) (param $var$0 i32) (result i32) + (grow_memory + (get_local $var$0) + ) + ) + (func $1 (; 1 ;) (type $1) (result i32) + (current_memory) + ) +) + |