summaryrefslogtreecommitdiff
path: root/src/wasm2js.h
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2020-11-11 15:50:33 -0800
committerGitHub <noreply@github.com>2020-11-11 15:50:33 -0800
commitf0d6b089960e089bcc5c1794003585ebbd91d33a (patch)
tree25e44d01fb2343f3152abeceed593986d1a0998b /src/wasm2js.h
parentd0d96a815fb2e6c397dce76fb1b74b803fd431f4 (diff)
downloadbinaryen-f0d6b089960e089bcc5c1794003585ebbd91d33a.tar.gz
binaryen-f0d6b089960e089bcc5c1794003585ebbd91d33a.tar.bz2
binaryen-f0d6b089960e089bcc5c1794003585ebbd91d33a.zip
wasm2js: Declare data segments before calling asmFunc (#3337)
This is because we maybe need to reference the segments during the start function. For example in the case of pthreads we conditionally load passive segments during start. Tested in emscripten with: tests/runner.py wasm2js1
Diffstat (limited to 'src/wasm2js.h')
-rw-r--r--src/wasm2js.h166
1 files changed, 97 insertions, 69 deletions
diff --git a/src/wasm2js.h b/src/wasm2js.h
index 2f0761497..7a0f4692d 100644
--- a/src/wasm2js.h
+++ b/src/wasm2js.h
@@ -92,6 +92,37 @@ bool isTableExported(Module& wasm) {
return false;
}
+bool hasActiveSegments(Module& wasm) {
+ for (Index i = 0; i < wasm.memory.segments.size(); i++) {
+ if (!wasm.memory.segments[i].isPassive) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool needsBufferView(Module& wasm) {
+ if (!wasm.memory.exists) {
+ return false;
+ }
+
+ // If there are any active segments, initActiveSegments needs access
+ // to bufferView.
+ if (hasActiveSegments(wasm)) {
+ return true;
+ }
+
+ // The special support functions are emitted as part of the JS glue, if we
+ // need them.
+ bool need = false;
+ ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
+ if (ABI::wasm2js::isHelper(import->base)) {
+ need = true;
+ }
+ });
+ return need;
+}
+
IString stringToIString(std::string str) { return IString(str.c_str(), false); }
// Used when taking a wasm name and generating a JS identifier. Each scope here
@@ -487,6 +518,18 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) {
asmFunc[3]->push_back(ValueBuilder::makeName("// EMSCRIPTEN_END_FUNCS\n"));
}
+ if (needsBufferView(*wasm)) {
+ asmFunc[3]->push_back(
+ ValueBuilder::makeBinary(ValueBuilder::makeName("bufferView"),
+ SET,
+ ValueBuilder::makeName(HEAPU8)));
+ }
+ if (hasActiveSegments(*wasm)) {
+ asmFunc[3]->push_back(
+ ValueBuilder::makeCall(ValueBuilder::makeName("initActiveSegments"),
+ ValueBuilder::makeName(ENV)));
+ }
+
addTable(asmFunc[3], wasm);
addStart(asmFunc[3], wasm);
addExports(asmFunc[3], wasm);
@@ -512,12 +555,6 @@ void Wasm2JSBuilder::addBasics(Ref ast, Module* wasm) {
addHeap(HEAPU32, UINT32ARRAY);
addHeap(HEAPF32, FLOAT32ARRAY);
addHeap(HEAPF64, FLOAT64ARRAY);
- if ((!wasm->memory.segments.empty()) || wasm->features.hasBulkMemory()) {
- ast->push_back(
- ValueBuilder::makeBinary(ValueBuilder::makeName("bufferView"),
- SET,
- ValueBuilder::makeName(HEAPU8)));
- }
}
// core asm.js imports
auto addMath = [&](IString name, IString base) {
@@ -2351,7 +2388,7 @@ void Wasm2JSBuilder::addMemoryGrowFunc(Ref ast, Module* wasm) {
ValueBuilder::makeName(BUFFER)));
}
- if ((!wasm->memory.segments.empty()) || wasm->features.hasBulkMemory()) {
+ if (needsBufferView(*wasm)) {
ValueBuilder::appendToBlock(
block,
ValueBuilder::makeBinary(ValueBuilder::makeName("bufferView"),
@@ -2390,7 +2427,7 @@ private:
void emitPostEmscripten();
void emitPostES6();
- void emitMemory(std::function<std::string(std::string)> accessGlobal);
+ void emitMemory();
void emitSpecialSupport();
};
@@ -2422,6 +2459,7 @@ void Wasm2JSGlue::emitPre() {
<< "}\n\n";
}
+ emitMemory();
emitSpecialSupport();
}
@@ -2465,18 +2503,6 @@ void Wasm2JSGlue::emitPreES6() {
}
void Wasm2JSGlue::emitPost() {
- // Create a helper bufferView to access the buffer if we need one. We use it
- // for creating memory segments if we have any (we may not if the segments are
- // shipped in a side .mem file, for example), and also in bulk memory
- // operations.
- // This will get assigned during `asmFunc` (and potentially re-assigned
- // during __wasm_memory_grow).
- // TODO: We should probably just share a single HEAPU8 var.
- if (wasm.memory.exists &&
- ((!wasm.memory.segments.empty()) || wasm.features.hasBulkMemory())) {
- out << "var bufferView;\n";
- }
-
if (flags.emscripten) {
emitPostEmscripten();
} else {
@@ -2485,13 +2511,7 @@ void Wasm2JSGlue::emitPost() {
}
void Wasm2JSGlue::emitPostEmscripten() {
- out << "var exports = asmFunc(asmLibraryArg);\n";
-
- emitMemory([](std::string globalName) {
- return std::string("asmLibraryArg['") + asmangle(globalName) + "']";
- });
-
- out << "return exports;\n}";
+ out << " return asmFunc(asmLibraryArg);\n}\n";
}
void Wasm2JSGlue::emitPostES6() {
@@ -2541,10 +2561,6 @@ void Wasm2JSGlue::emitPostES6() {
out << "\n });\n";
- if (wasm.memory.exists) {
- emitMemory([](std::string globalName) { return globalName; });
- }
-
if (flags.allowAsserts) {
return;
}
@@ -2574,16 +2590,35 @@ void Wasm2JSGlue::emitPostES6() {
}
}
-void Wasm2JSGlue::emitMemory(
- std::function<std::string(std::string)> accessGlobal) {
+void Wasm2JSGlue::emitMemory() {
+ if (needsBufferView(wasm)) {
+ // Create a helper bufferView to access the buffer if we need one. We use it
+ // for creating memory segments if we have any (we may not if the segments
+ // are shipped in a side .mem file, for example), and also in bulk memory
+ // operations.
+ // This will get assigned during `asmFunc` (and potentially re-assigned
+ // during __wasm_memory_grow).
+ // TODO: We should probably just share a single HEAPU8 var.
+ out << " var bufferView;\n";
+ }
+
// If there are no memory segments, we don't need to emit any support code for
// segment creation.
if ((!wasm.memory.exists) || wasm.memory.segments.empty()) {
return;
}
+ // If we have passive memory segments, we need to store those.
+ for (auto& seg : wasm.memory.segments) {
+ if (seg.isPassive) {
+ out << " var memorySegments = {};\n";
+ break;
+ }
+ }
+
out <<
- R"(for (var base64ReverseLookup = new Uint8Array(123/*'z'+1*/), i = 25; i >= 0; --i) {
+ R"( var base64ReverseLookup = new Uint8Array(123/*'z'+1*/);
+ for (var i = 25; i >= 0; --i) {
base64ReverseLookup[48+i] = 52+i; // '0-9'
base64ReverseLookup[65+i] = i; // 'A-Z'
base64ReverseLookup[97+i] = 26+i; // 'a-z'
@@ -2608,27 +2643,11 @@ void Wasm2JSGlue::emitMemory(
}
out << R"(
}
- )";
-
- auto globalOffset = [&](const Memory::Segment& segment) {
- if (auto* c = segment.offset->dynCast<Const>()) {
- return std::to_string(c->value.getInteger());
- }
- if (auto* get = segment.offset->dynCast<GlobalGet>()) {
- auto internalName = get->name;
- auto importedName = wasm.getGlobal(internalName)->base;
- return accessGlobal(asmangle(importedName.str));
- }
- Fatal() << "non-constant offsets aren't supported yet\n";
- };
+)";
for (Index i = 0; i < wasm.memory.segments.size(); i++) {
auto& seg = wasm.memory.segments[i];
- if (!seg.isPassive) {
- // Plain active segments are decoded directly into the main memory.
- out << "base64DecodeToExistingUint8Array(bufferView, "
- << globalOffset(seg) << ", \"" << base64Encode(seg.data) << "\");\n";
- } else {
+ if (seg.isPassive) {
// Fancy passive segments are decoded into typed arrays on the side, for
// later copying.
out << "memorySegments[" << i
@@ -2637,6 +2656,32 @@ void Wasm2JSGlue::emitMemory(
<< ", 0, \"" << base64Encode(seg.data) << "\");\n";
}
}
+
+ if (hasActiveSegments(wasm)) {
+ auto globalOffset = [&](const Memory::Segment& segment) {
+ if (auto* c = segment.offset->dynCast<Const>()) {
+ return std::to_string(c->value.getInteger());
+ }
+ if (auto* get = segment.offset->dynCast<GlobalGet>()) {
+ auto internalName = get->name;
+ auto importedName = wasm.getGlobal(internalName)->base;
+ return std::string("imports[") + asmangle(importedName.str) + "]";
+ }
+ Fatal() << "non-constant offsets aren't supported yet\n";
+ };
+
+ out << "function initActiveSegments(imports) {\n";
+ for (Index i = 0; i < wasm.memory.segments.size(); i++) {
+ auto& seg = wasm.memory.segments[i];
+ if (!seg.isPassive) {
+ // Plain active segments are decoded directly into the main memory.
+ out << " base64DecodeToExistingUint8Array(bufferView, "
+ << globalOffset(seg) << ", \"" << base64Encode(seg.data)
+ << "\");\n";
+ }
+ }
+ out << "}\n";
+ }
}
void Wasm2JSGlue::emitSpecialSupport() {
@@ -2711,15 +2756,6 @@ void Wasm2JSGlue::emitSpecialSupport() {
var f64ScratchView = new Float64Array(scratchBuffer);
)";
- // If we have passive memory segments, or bulk memory operations that operate
- // on segment indexes, we need to store those.
- bool needMemorySegmentsList = false;
- for (auto& seg : wasm.memory.segments) {
- if (seg.isPassive) {
- needMemorySegmentsList = true;
- }
- }
-
ModuleUtils::iterImportedFunctions(wasm, [&](Function* import) {
if (import->base == ABI::wasm2js::SCRATCH_STORE_I32) {
out << R"(
@@ -2758,7 +2794,6 @@ void Wasm2JSGlue::emitSpecialSupport() {
}
)";
} else if (import->base == ABI::wasm2js::MEMORY_INIT) {
- needMemorySegmentsList = true;
out << R"(
function wasm2js_memory_init(segment, dest, offset, size) {
// TODO: traps on invalid things
@@ -2782,7 +2817,6 @@ void Wasm2JSGlue::emitSpecialSupport() {
}
)";
} else if (import->base == ABI::wasm2js::DATA_DROP) {
- needMemorySegmentsList = true;
out << R"(
function wasm2js_data_drop(segment) {
// TODO: traps on invalid things
@@ -2853,12 +2887,6 @@ void Wasm2JSGlue::emitSpecialSupport() {
}
});
- if (needMemorySegmentsList) {
- out << R"(
- var memorySegments = {};
- )";
- }
-
out << '\n';
}