summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/emscripten-optimizer/simple_ast.h20
-rw-r--r--src/passes/I64ToI32Lowering.cpp17
-rw-r--r--src/passes/RemoveNonJSOps.cpp44
-rw-r--r--src/tools/wasm2asm.cpp2
-rw-r--r--src/wasm2asm.h562
5 files changed, 439 insertions, 206 deletions
diff --git a/src/emscripten-optimizer/simple_ast.h b/src/emscripten-optimizer/simple_ast.h
index 64aab7708..237d9c1b5 100644
--- a/src/emscripten-optimizer/simple_ast.h
+++ b/src/emscripten-optimizer/simple_ast.h
@@ -836,17 +836,25 @@ struct JSPrinter {
}
static char* numToString(double d, bool finalize=true) {
+ // If this number is NaN or infinite then things are a bit tricky. In JS we
+ // want to eventually use `NaN` and/or `Infinity`, but neither of those
+ // identifiers are valid in asm.js. Instead we have to explicitly import
+ // `NaN` and `Infinity` from the global environment, and those names are
+ // bound locally in an asm function as `nan` and `infinity`.
+ //
+ // TODO: the JS names of `NaN` and `Infinity` should be used once literal
+ // asm.js code isn't generated any more
if (std::isnan(d)) {
if (std::signbit(d)) {
- return (char*) "-NaN";
+ return (char*) "-nan";
} else {
- return (char*) "NaN";
+ return (char*) "nan";
}
} else if (!std::isfinite(d)) {
if (std::signbit(d)) {
- return (char*) "-Infinity";
+ return (char*) "-infinity";
} else {
- return (char*) "Infinity";
+ return (char*) "infinity";
}
}
bool neg = d < 0;
@@ -1059,8 +1067,8 @@ struct JSPrinter {
ensure(1); // we temporarily append a 0
char *curr = buffer + last; // ensure might invalidate
buffer[used] = 0;
- if (strstr(curr, "Infinity")) return;
- if (strstr(curr, "NaN")) return;
+ if (strstr(curr, "infinity")) return;
+ if (strstr(curr, "nan")) return;
if (strchr(curr, '.')) return; // already a decimal point, all good
char *e = strchr(curr, 'e');
if (!e) {
diff --git a/src/passes/I64ToI32Lowering.cpp b/src/passes/I64ToI32Lowering.cpp
index f14f33027..e501107bd 100644
--- a/src/passes/I64ToI32Lowering.cpp
+++ b/src/passes/I64ToI32Lowering.cpp
@@ -449,6 +449,7 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
void visitLoad(Load* curr) {
if (curr->type != i64) return;
assert(!curr->isAtomic && "atomic load not implemented");
+ TempVar lowBits = getTemp();
TempVar highBits = getTemp();
TempVar ptrTemp = getTemp();
SetLocal* setPtr = builder->makeSetLocal(ptrTemp, curr->ptr);
@@ -465,6 +466,15 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
i32
)
);
+ } else if (curr->signed_) {
+ loadHigh = builder->makeSetLocal(
+ highBits,
+ builder->makeBinary(
+ ShrSInt32,
+ builder->makeGetLocal(lowBits, i32),
+ builder->makeConst(Literal(int32_t(31)))
+ )
+ );
} else {
loadHigh = builder->makeSetLocal(
highBits,
@@ -475,7 +485,12 @@ struct I64ToI32Lowering : public WalkerPass<PostWalker<I64ToI32Lowering>> {
curr->bytes = std::min(curr->bytes, uint8_t(4));
curr->align = std::min(uint32_t(curr->align), uint32_t(4));
curr->ptr = builder->makeGetLocal(ptrTemp, i32);
- Block* result = builder->blockify(setPtr, loadHigh, curr);
+ Block* result = builder->blockify(
+ setPtr,
+ builder->makeSetLocal(lowBits, curr),
+ loadHigh,
+ builder->makeGetLocal(lowBits, i32)
+ );
replaceCurrent(result);
setOutParam(result, std::move(highBits));
}
diff --git a/src/passes/RemoveNonJSOps.cpp b/src/passes/RemoveNonJSOps.cpp
index 4bd40a6c6..76c9528cb 100644
--- a/src/passes/RemoveNonJSOps.cpp
+++ b/src/passes/RemoveNonJSOps.cpp
@@ -113,6 +113,50 @@ struct RemoveNonJSOpsPass : public WalkerPass<PostWalker<RemoveNonJSOpsPass>> {
PostWalker<RemoveNonJSOpsPass>::doWalkFunction(func);
}
+ void visitLoad(Load *curr) {
+ if (curr->align == 0 || curr->align >= curr->bytes) {
+ return;
+ }
+
+ // Switch unaligned loads of floats to unaligned loads of integers (which we
+ // can actually implement) and then use reinterpretation to get the float
+ // back out.
+ switch (curr->type) {
+ case f32:
+ curr->type = i32;
+ replaceCurrent(builder->makeUnary(ReinterpretInt32, curr));
+ break;
+ case f64:
+ curr->type = i64;
+ replaceCurrent(builder->makeUnary(ReinterpretInt64, curr));
+ break;
+ default:
+ break;
+ }
+ }
+
+ void visitStore(Store *curr) {
+ if (curr->align == 0 || curr->align >= curr->bytes) {
+ return;
+ }
+
+ // Switch unaligned stores of floats to unaligned stores of integers (which
+ // we can actually implement) and then use reinterpretation to store the
+ // right value.
+ switch (curr->valueType) {
+ case f32:
+ curr->valueType = i32;
+ curr->value = builder->makeUnary(ReinterpretFloat32, curr->value);
+ break;
+ case f64:
+ curr->valueType = i64;
+ curr->value = builder->makeUnary(ReinterpretFloat64, curr->value);
+ break;
+ default:
+ break;
+ }
+ }
+
void visitBinary(Binary *curr) {
Name name;
switch (curr->op) {
diff --git a/src/tools/wasm2asm.cpp b/src/tools/wasm2asm.cpp
index 2251a332c..fd65ca5e6 100644
--- a/src/tools/wasm2asm.cpp
+++ b/src/tools/wasm2asm.cpp
@@ -76,7 +76,7 @@ int main(int argc, const char *argv[]) {
if (options.extra["asserts"] == "1") {
if (options.debug) std::cerr << "asserting..." << std::endl;
- flattenAppend(asmjs, wasm2asm.processAsserts(*root, builder));
+ flattenAppend(asmjs, wasm2asm.processAsserts(&wasm, *root, builder));
}
} catch (ParseException& p) {
p.dump(std::cerr);
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index 956ded5e8..e89352432 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -63,6 +63,25 @@ void flattenAppend(Ref ast, Ref extra) {
}
}
+// Used when taking a wasm name and generating a JS identifier. Each scope here
+// is used to ensure that all names have a unique name but the same wasm name
+// within a scope always resolves to the same symbol.
+enum class NameScope {
+ Top,
+ Local,
+ Label,
+ Max,
+};
+
+static uint64_t constOffset(Table::Segment &segment) {
+ auto* c = segment.offset->dynCast<Const>();
+ if (!c) {
+ Fatal() << "non-constant offsets aren't supported yet\n";
+ abort();
+ }
+ return c->value.getInteger();
+}
+
//
// Wasm2AsmBuilder - converts a WebAssembly module into asm.js
//
@@ -122,8 +141,8 @@ public:
Wasm2AsmBuilder(Flags f) : flags(f) {}
- Ref processWasm(Module* wasm);
- Ref processFunction(Function* func);
+ Ref processWasm(Module* wasm, Name funcName = ASM_FUNC);
+ Ref processFunction(Module* wasm, Function* func);
// The first pass on an expression: scan it to see whether it will
// need to be statementized, and note spooky returns of values at
@@ -135,9 +154,9 @@ public:
// @param result Whether the context we are in receives a value,
// and its type, or if not, then we can drop our return,
// if we have one.
- Ref processFunctionBody(Function* func, IString result);
+ Ref processFunctionBody(Module* m, Function* func, IString result);
- Ref processAsserts(Element& e, SExpressionWasmBuilder& sexpBuilder);
+ Ref processAsserts(Module* wasm, Element& e, SExpressionWasmBuilder& sexpBuilder);
// Get a temp var.
IString getTemp(Type type, Function* func) {
@@ -161,15 +180,58 @@ public:
frees[type].push_back(temp);
}
- IString fromName(Name name) {
+ // Generates a mangled name from `name` within the specified scope.
+ //
+ // The goal of this function is to ensure that all identifiers in JS ar
+ // unique. Otherwise there can be clashes with locals and functions and cause
+ // unwanted name shadowing.
+ //
+ // The returned string from this function is constant for a particular `name`
+ // within a `scope`. Or in other words, the same `name` and `scope` pair will
+ // always return the same result. If `scope` changes, however, the return
+ // value may differ even if the same `name` is passed in.
+ IString fromName(Name name, NameScope scope) {
// TODO: checking names do not collide after mangling
- auto it = mangledNames.find(name.c_str());
- if (it != mangledNames.end()) {
+
+ // First up check our cached of mangled names to avoid doing extra work
+ // below
+ auto &mangledScope = mangledNames[(int) scope];
+ auto it = mangledScope.find(name.c_str());
+ if (it != mangledScope.end()) {
return it->second;
}
- auto mangled = asmangle(std::string(name.c_str()));
- IString ret(mangled.c_str(), false);
- mangledNames[name.c_str()] = ret;
+
+ // This is the first time we've seen the `name` and `scope` pair. Generate a
+ // globally unique name based on `name` and then register that in our cache
+ // and return it.
+ //
+ // Identifiers here generated are of the form `${name}_${n}` where `_${n}`
+ // is omitted if `n==0` and otherwise `n` is just looped over to find the
+ // next unused identifier.
+ IString ret;
+ for (int i = 0;; i++) {
+ std::ostringstream out;
+ out << name.c_str();
+ if (i > 0) {
+ out << "_" << i;
+ }
+ auto mangled = asmangle(out.str());
+ ret = IString(mangled.c_str(), false);
+ if (!allMangledNames.count(ret)) {
+ break;
+ }
+
+ // In the global scope that's how you refer to actual function exports, so
+ // it's a bug currently if they're not globally unique. This should
+ // probably be fixed via a different namespace for exports or something
+ // like that.
+ if (scope == NameScope::Top) {
+ Fatal() << "global scope is colliding with other scope: " << mangled << '\n';
+ abort();
+ }
+ }
+ allMangledNames.insert(ret);
+ mangledScope[name.c_str()] = ret;
return ret;
}
@@ -198,7 +260,8 @@ private:
// Mangled names cache by interned names.
// Utilizes the usually reused underlying cstring's pointer as the key.
- std::unordered_map<const char*, IString> mangledNames;
+ std::unordered_map<const char*, IString> mangledNames[(int) NameScope::Max];
+ std::unordered_set<IString> allMangledNames;
// All our function tables have the same size TODO: optimize?
size_t tableSize;
@@ -214,20 +277,29 @@ private:
void addMemoryGrowthFuncs(Ref ast);
bool isAssertHandled(Element& e);
Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
+ Module* wasm,
Builder& wasmBuilder,
- Element& e, Name testFuncName);
+ Element& e,
+ Name testFuncName,
+ Name asmModule);
Ref makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder,
+ Module* wasm,
Builder& wasmBuilder,
- Element& e, Name testFuncName);
+ Element& e,
+ Name testFuncName,
+ Name asmModule);
Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
+ Module* wasm,
Builder& wasmBuilder,
- Element& e, Name testFuncName);
+ Element& e,
+ Name testFuncName,
+ Name asmModule);
Wasm2AsmBuilder() = delete;
Wasm2AsmBuilder(const Wasm2AsmBuilder &) = delete;
Wasm2AsmBuilder &operator=(const Wasm2AsmBuilder&) = delete;
};
-Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
+Ref Wasm2AsmBuilder::processWasm(Module* wasm, Name funcName) {
PassRunner runner(wasm);
runner.add<AutoDrop>();
// First up remove as many non-JS operations we can, including things like
@@ -256,7 +328,7 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
#endif
Ref ret = ValueBuilder::makeToplevel();
- Ref asmFunc = ValueBuilder::makeFunction(ASM_FUNC);
+ Ref asmFunc = ValueBuilder::makeFunction(funcName);
ret[1]->push_back(asmFunc);
ValueBuilder::appendArgumentToFunction(asmFunc, GLOBAL);
ValueBuilder::appendArgumentToFunction(asmFunc, ENV);
@@ -271,13 +343,24 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
tableSize = std::accumulate(wasm->table.segments.begin(),
wasm->table.segments.end(),
0, [&](size_t size, Table::Segment seg) -> size_t {
- return size + seg.data.size();
+ return size + seg.data.size() + constOffset(seg);
});
size_t pow2ed = 1;
while (pow2ed < tableSize) {
pow2ed <<= 1;
}
tableSize = pow2ed;
+
+ // make sure exports get their expected names
+ for (auto& e : wasm->exports) {
+ if (e->kind == ExternalKind::Function) {
+ fromName(e->name, NameScope::Top);
+ }
+ }
+ for (auto& f : wasm->functions) {
+ fromName(f->name, NameScope::Top);
+ }
+ fromName(WASM_FETCH_HIGH_BITS, NameScope::Top);
// globals
bool generateFetchHighBits = false;
for (auto& global : wasm->globals) {
@@ -288,13 +371,13 @@ Ref Wasm2AsmBuilder::processWasm(Module* wasm) {
}
// functions
for (auto& func : wasm->functions) {
- asmFunc[3]->push_back(processFunction(func.get()));
+ asmFunc[3]->push_back(processFunction(wasm, func.get()));
}
if (generateFetchHighBits) {
Builder builder(allocator);
std::vector<Type> params;
std::vector<Type> vars;
- asmFunc[3]->push_back(processFunction(builder.makeFunction(
+ asmFunc[3]->push_back(processFunction(wasm, builder.makeFunction(
WASM_FETCH_HIGH_BITS,
std::move(params),
i32,
@@ -364,6 +447,30 @@ void Wasm2AsmBuilder::addBasics(Ref ast) {
addMath(MATH_FLOOR, FLOOR);
addMath(MATH_CEIL, CEIL);
addMath(MATH_SQRT, SQRT);
+ // abort function
+ Ref abortVar = ValueBuilder::makeVar();
+ ast->push_back(abortVar);
+ ValueBuilder::appendToVar(abortVar,
+ "abort",
+ ValueBuilder::makeDot(
+ ValueBuilder::makeName(ENV),
+ ABORT_FUNC
+ )
+ );
+ // TODO: this shouldn't be needed once we stop generating literal asm.js code
+ // NaN and Infinity variables
+ Ref nanVar = ValueBuilder::makeVar();
+ ast->push_back(nanVar);
+ ValueBuilder::appendToVar(nanVar,
+ "nan",
+ ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), "NaN")
+ );
+ Ref infinityVar = ValueBuilder::makeVar();
+ ast->push_back(infinityVar);
+ ValueBuilder::appendToVar(infinityVar,
+ "infinity",
+ ValueBuilder::makeDot(ValueBuilder::makeName(GLOBAL), "Infinity")
+ );
}
void Wasm2AsmBuilder::addImport(Ref ast, Import* import) {
@@ -371,10 +478,10 @@ void Wasm2AsmBuilder::addImport(Ref ast, Import* import) {
ast->push_back(theVar);
Ref module = ValueBuilder::makeName(ENV); // TODO: handle nested module imports
ValueBuilder::appendToVar(theVar,
- fromName(import->name),
+ fromName(import->name, NameScope::Top),
ValueBuilder::makeDot(
module,
- fromName(import->base)
+ fromName(import->base, NameScope::Top)
)
);
}
@@ -391,10 +498,10 @@ void Wasm2AsmBuilder::addTables(Ref ast, Module* wasm) {
// fill it with the first of its type seen. we have to fill with something; and for asm2wasm output, the first is the null anyhow
table.resize(tableSize);
for (size_t j = 0; j < tableSize; j++) {
- table[j] = fromName(name);
+ table[j] = fromName(name, NameScope::Top);
}
} else {
- table[i] = fromName(name);
+ table[i + constOffset(seg)] = fromName(name, NameScope::Top);
}
}
}
@@ -420,8 +527,8 @@ void Wasm2AsmBuilder::addExports(Ref ast, Module* wasm) {
if (export_->kind == ExternalKind::Function) {
ValueBuilder::appendToObject(
exports,
- fromName(export_->name),
- ValueBuilder::makeName(fromName(export_->value))
+ fromName(export_->name, NameScope::Top),
+ ValueBuilder::makeName(fromName(export_->value, NameScope::Top))
);
}
if (export_->kind == ExternalKind::Memory) {
@@ -457,7 +564,7 @@ void Wasm2AsmBuilder::addExports(Ref ast, Module* wasm) {
descs);
ValueBuilder::appendToObject(
exports,
- fromName(export_->name),
+ fromName(export_->name, NameScope::Top),
memory);
}
}
@@ -488,17 +595,17 @@ void Wasm2AsmBuilder::addGlobal(Ref ast, Global* global) {
break;
}
default: {
- assert(false && "Global const type not supported");
+ assert(false && "Top const type not supported");
}
}
Ref theVar = ValueBuilder::makeVar();
ast->push_back(theVar);
ValueBuilder::appendToVar(theVar,
- fromName(global->name),
+ fromName(global->name, NameScope::Top),
theValue
);
} else {
- assert(false && "Global init type not supported");
+ assert(false && "Top init type not supported");
}
}
@@ -513,7 +620,7 @@ static bool expressionEndsInReturn(Expression *e) {
return expressionEndsInReturn((*stats)[stats->size()-1]);
}
-Ref Wasm2AsmBuilder::processFunction(Function* func) {
+Ref Wasm2AsmBuilder::processFunction(Module* m, Function* func) {
if (flags.debug) {
static int fns = 0;
std::cerr << "processFunction " << (fns++) << " " << func->name
@@ -522,7 +629,7 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) {
// We will be symbolically referring to all variables in the function, so make
// sure that everything has a name and it's unique.
Names::ensureNames(func);
- Ref ret = ValueBuilder::makeFunction(fromName(func->name));
+ Ref ret = ValueBuilder::makeFunction(fromName(func->name, NameScope::Top));
frees.clear();
frees.resize(std::max(i32, std::max(f32, f64)) + 1);
temps.clear();
@@ -530,7 +637,7 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) {
temps[i32] = temps[f32] = temps[f64] = 0;
// arguments
for (Index i = 0; i < func->getNumParams(); i++) {
- IString name = fromName(func->getLocalNameOrGeneric(i));
+ IString name = fromName(func->getLocalNameOrGeneric(i), NameScope::Local);
ValueBuilder::appendArgumentToFunction(ret, name);
ret[3]->push_back(
ValueBuilder::makeStatement(
@@ -560,28 +667,28 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) {
bool endsInReturn = expressionEndsInReturn(func->body);
if (endsInReturn) {
// return already taken care of
- flattenAppend(ret, processFunctionBody(func, NO_RESULT));
+ flattenAppend(ret, processFunctionBody(m, func, NO_RESULT));
} else if (isStatement(func->body)) {
// store result in variable then return it
IString result =
func->result != none ? getTemp(func->result, func) : NO_RESULT;
- flattenAppend(ret, processFunctionBody(func, result));
+ flattenAppend(ret, processFunctionBody(m, func, result));
if (func->result != none) {
appendFinalReturn(ValueBuilder::makeName(result));
freeTemp(func->result, result);
}
} else if (func->result != none) {
// whole thing is an expression, just return body
- appendFinalReturn(processFunctionBody(func, EXPRESSION_RESULT));
+ appendFinalReturn(processFunctionBody(m, func, EXPRESSION_RESULT));
} else {
// func has no return
- flattenAppend(ret, processFunctionBody(func, NO_RESULT));
+ flattenAppend(ret, processFunctionBody(m, func, NO_RESULT));
}
// vars, including new temp vars
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
ValueBuilder::appendToVar(
theVar,
- fromName(func->getLocalNameOrGeneric(i)),
+ fromName(func->getLocalNameOrGeneric(i), NameScope::Local),
makeAsmCoercedZero(wasmToAsmType(func->getLocalType(i)))
);
}
@@ -637,16 +744,10 @@ void Wasm2AsmBuilder::scanFunctionBody(Expression* curr) {
}
}
void visitCallIndirect(CallIndirect* curr) {
- if (parent->isStatement(curr->target)) {
- parent->setStatement(curr);
- return;
- }
- for (auto item : curr->operands) {
- if (parent->isStatement(item)) {
- parent->setStatement(curr);
- break;
- }
- }
+ // TODO: this is a pessimization that probably wants to get tweaked in
+ // the future. If none of the arguments have any side effects then we
+ // should be able to safely have tighter codegen.
+ parent->setStatement(curr);
}
void visitSetLocal(SetLocal* curr) {
if (parent->isStatement(curr->value)) {
@@ -659,9 +760,7 @@ void Wasm2AsmBuilder::scanFunctionBody(Expression* curr) {
}
}
void visitStore(Store* curr) {
- if (parent->isStatement(curr->ptr) || parent->isStatement(curr->value)) {
- parent->setStatement(curr);
- }
+ parent->setStatement(curr);
}
void visitUnary(Unary* curr) {
if (parent->isStatement(curr->value)) {
@@ -693,13 +792,15 @@ void Wasm2AsmBuilder::scanFunctionBody(Expression* curr) {
ExpressionScanner(this).walk(curr);
}
-Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
+Ref Wasm2AsmBuilder::processFunctionBody(Module* m, Function* func, IString result) {
struct ExpressionProcessor : public Visitor<ExpressionProcessor, Ref> {
Wasm2AsmBuilder* parent;
IString result;
Function* func;
+ Module* module;
MixedArena allocator;
- ExpressionProcessor(Wasm2AsmBuilder* parent, Function* func) : parent(parent), func(func) {}
+ ExpressionProcessor(Wasm2AsmBuilder* parent, Module* m, Function* func)
+ : parent(parent), func(func), module(m) {}
// A scoped temporary variable.
struct ScopedTemp {
@@ -797,8 +898,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
// Breaks to the top of a loop should be emitted as continues, to that loop's main label
std::unordered_set<Name> continueLabels;
- IString fromName(Name name) {
- return parent->fromName(name);
+ IString fromName(Name name, NameScope scope) {
+ return parent->fromName(name, scope);
}
// Visitors
@@ -815,7 +916,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
flattenAppend(ret, visitAndAssign(curr->list[size-1], result));
}
if (curr->name.is()) {
- ret = ValueBuilder::makeLabel(fromName(curr->name), ret);
+ ret = ValueBuilder::makeLabel(fromName(curr->name, NameScope::Label), ret);
}
return ret;
}
@@ -841,9 +942,9 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Name asmLabel = curr->name;
continueLabels.insert(asmLabel);
Ref body = blockify(visit(curr->body, result));
- flattenAppend(body, ValueBuilder::makeBreak(fromName(asmLabel)));
+ flattenAppend(body, ValueBuilder::makeBreak(fromName(asmLabel, NameScope::Label)));
Ref ret = ValueBuilder::makeDo(body, ValueBuilder::makeInt(1));
- return ValueBuilder::makeLabel(fromName(asmLabel), ret);
+ return ValueBuilder::makeLabel(fromName(asmLabel, NameScope::Label), ret);
}
Ref visitBreak(Break* curr) {
@@ -859,9 +960,9 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref theBreak;
auto iter = continueLabels.find(curr->name);
if (iter == continueLabels.end()) {
- theBreak = ValueBuilder::makeBreak(fromName(curr->name));
+ theBreak = ValueBuilder::makeBreak(fromName(curr->name, NameScope::Label));
} else {
- theBreak = ValueBuilder::makeContinue(fromName(curr->name));
+ theBreak = ValueBuilder::makeContinue(fromName(curr->name, NameScope::Label));
}
if (!curr->value) return theBreak;
// generate the value, including assigning to the result, and then do the break
@@ -889,19 +990,28 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ret[1]->push_back(theSwitch);
for (size_t i = 0; i < curr->targets.size(); i++) {
ValueBuilder::appendCaseToSwitch(theSwitch, ValueBuilder::makeNum(i));
- ValueBuilder::appendCodeToSwitch(theSwitch, blockify(ValueBuilder::makeBreak(fromName(curr->targets[i]))), false);
+ ValueBuilder::appendCodeToSwitch(theSwitch, blockify(ValueBuilder::makeBreak(fromName(curr->targets[i], NameScope::Label))), false);
}
ValueBuilder::appendDefaultToSwitch(theSwitch);
- ValueBuilder::appendCodeToSwitch(theSwitch, blockify(ValueBuilder::makeBreak(fromName(curr->default_))), false);
+ ValueBuilder::appendCodeToSwitch(theSwitch, blockify(ValueBuilder::makeBreak(fromName(curr->default_, NameScope::Label))), false);
return ret;
}
- Ref makeStatementizedCall(ExpressionList& operands, Ref ret, Ref theCall, IString result, Type type) {
+ Ref makeStatementizedCall(ExpressionList& operands,
+ Ref ret,
+ std::function<Ref()> genTheCall,
+ IString result,
+ Type type) {
std::vector<ScopedTemp*> temps; // TODO: utility class, with destructor?
for (auto& operand : operands) {
temps.push_back(new ScopedTemp(operand->type, parent, func));
IString temp = temps.back()->temp;
flattenAppend(ret, visitAndAssign(operand, temp));
+ }
+ Ref theCall = genTheCall();
+ for (size_t i = 0; i < temps.size(); i++) {
+ IString temp = temps[i]->temp;
+ auto &operand = operands[i];
theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type)));
}
theCall = makeAsmCoercion(theCall, wasmToAsmType(type));
@@ -919,7 +1029,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref visitGenericCall(Expression* curr, Name target,
ExpressionList& operands) {
- Ref theCall = ValueBuilder::makeCall(fromName(target));
+ Ref theCall = ValueBuilder::makeCall(fromName(target, NameScope::Top));
if (!isStatement(curr)) {
// none of our operands is a statement; go right ahead and create a
// simple expression
@@ -931,7 +1041,8 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
return makeAsmCoercion(theCall, wasmToAsmType(curr->type));
}
// we must statementize them all
- return makeStatementizedCall(operands, ValueBuilder::makeBlock(), theCall,
+ return makeStatementizedCall(operands, ValueBuilder::makeBlock(),
+ [&]() { return theCall; },
result, curr->type);
}
@@ -944,34 +1055,34 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
Ref visitCallIndirect(CallIndirect* curr) {
- std::string stable = std::string("FUNCTION_TABLE_") + curr->fullType.c_str();
+ // TODO: the codegen here is a pessimization of what the ideal codegen
+ // looks like. Eventually if necessary this should be tightened up in the
+ // case that the argument expression don't have any side effects.
+ assert(isStatement(curr));
+ std::string stable = std::string("FUNCTION_TABLE_") +
+ getSig(module->getFunctionType(curr->fullType));
IString table = IString(stable.c_str(), false);
- auto makeTableCall = [&](Ref target) {
- return ValueBuilder::makeCall(ValueBuilder::makeSub(
- ValueBuilder::makeName(table),
- ValueBuilder::makeBinary(target, AND, ValueBuilder::makeInt(parent->getTableSize()-1))
- ));
- };
- if (!isStatement(curr)) {
- // none of our operands is a statement; go right ahead and create a simple expression
- Ref theCall = makeTableCall(visit(curr->target, EXPRESSION_RESULT));
- for (auto operand : curr->operands) {
- theCall[2]->push_back(makeAsmCoercion(visit(operand, EXPRESSION_RESULT), wasmToAsmType(operand->type)));
- }
- return makeAsmCoercion(theCall, wasmToAsmType(curr->type));
- }
- // we must statementize them all
Ref ret = ValueBuilder::makeBlock();
- ScopedTemp temp(i32, parent, func);
- flattenAppend(ret, visit(curr->target, temp));
- Ref theCall = makeTableCall(temp.getAstName());
- return makeStatementizedCall(curr->operands, ret, theCall, result, curr->type);
+ ScopedTemp idx(i32, parent, func);
+ return makeStatementizedCall(
+ curr->operands,
+ ret,
+ [&]() {
+ flattenAppend(ret, visitAndAssign(curr->target, idx));
+ return ValueBuilder::makeCall(ValueBuilder::makeSub(
+ ValueBuilder::makeName(table),
+ ValueBuilder::makeBinary(idx.getAstName(), AND, ValueBuilder::makeInt(parent->getTableSize()-1))
+ ));
+ },
+ result,
+ curr->type
+ );
}
- Ref makeSetVar(Expression* curr, Expression* value, Name name) {
+ Ref makeSetVar(Expression* curr, Expression* value, Name name, NameScope scope) {
if (!isStatement(curr)) {
return ValueBuilder::makeBinary(
- ValueBuilder::makeName(fromName(name)), SET,
+ ValueBuilder::makeName(fromName(name, scope)), SET,
visit(value, EXPRESSION_RESULT)
);
}
@@ -983,7 +1094,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ret[1]->push_back(
ValueBuilder::makeStatement(
ValueBuilder::makeBinary(
- ValueBuilder::makeName(fromName(name)), SET,
+ ValueBuilder::makeName(fromName(name, scope)), SET,
temp.getAstName()
)
)
@@ -993,20 +1104,25 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref visitGetLocal(GetLocal* curr) {
return ValueBuilder::makeName(
- fromName(func->getLocalNameOrGeneric(curr->index))
+ fromName(func->getLocalNameOrGeneric(curr->index), NameScope::Local)
);
}
Ref visitSetLocal(SetLocal* curr) {
- return makeSetVar(curr, curr->value, func->getLocalNameOrGeneric(curr->index));
+ return makeSetVar(
+ curr,
+ curr->value,
+ func->getLocalNameOrGeneric(curr->index),
+ NameScope::Local
+ );
}
Ref visitGetGlobal(GetGlobal* curr) {
- return ValueBuilder::makeName(fromName(curr->name));
+ return ValueBuilder::makeName(fromName(curr->name, NameScope::Top));
}
Ref visitSetGlobal(SetGlobal* curr) {
- return makeSetVar(curr, curr->value, curr->name);
+ return makeSetVar(curr, curr->value, curr->name, NameScope::Top);
}
Ref visitLoad(Load* curr) {
@@ -1108,8 +1224,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ScopedTemp tempValue(curr->valueType, parent, func);
GetLocal fakeLocalPtr(allocator);
fakeLocalPtr.index = func->getLocalIndex(tempPtr.getName());
+ fakeLocalPtr.type = curr->ptr->type;
GetLocal fakeLocalValue(allocator);
fakeLocalValue.index = func->getLocalIndex(tempValue.getName());
+ fakeLocalValue.type = curr->value->type;
Store fakeStore = *curr;
fakeStore.ptr = &fakeLocalPtr;
fakeStore.value = &fakeLocalValue;
@@ -1285,7 +1403,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref store = ValueBuilder::makeBinary(ret, SET, value);
return ValueBuilder::makeSeq(
store,
- ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), zero)
+ makeAsmCoercion(
+ ValueBuilder::makeSub(ValueBuilder::makeName(HEAP32), zero),
+ ASM_INT
+ )
);
}
// generate (~~expr), what Emscripten does
@@ -1648,11 +1769,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
tempCondition(i32, parent, func);
return
ValueBuilder::makeSeq(
- ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition),
+ ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue),
ValueBuilder::makeSeq(
- ValueBuilder::makeBinary(tempIfTrue.getAstName(), SET, ifTrue),
+ ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse),
ValueBuilder::makeSeq(
- ValueBuilder::makeBinary(tempIfFalse.getAstName(), SET, ifFalse),
+ ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition),
ValueBuilder::makeConditional(
tempCondition.getAstName(),
tempIfTrue.getAstName(),
@@ -1664,28 +1785,47 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
Ref visitReturn(Return* curr) {
- Ref val = (curr->value == nullptr) ?
- Ref() :
- makeAsmCoercion(
- visit(curr->value, NO_RESULT),
- wasmToAsmType(curr->value->type)
- );
- return ValueBuilder::makeReturn(val);
+ if (curr->value == nullptr) {
+ return ValueBuilder::makeReturn(Ref());
+ }
+ if (isStatement(curr->value)) {
+ ScopedTemp temp(curr->value->type, parent, func);
+ Ref ret = ValueBuilder::makeBlock();
+ flattenAppend(ret, visitAndAssign(curr->value, temp));
+ Ref coerced = makeAsmCoercion(
+ temp.getAstName(),
+ wasmToAsmType(curr->value->type)
+ );
+ flattenAppend(ret, ValueBuilder::makeReturn(coerced));
+ return ret;
+ } else {
+ Ref val = makeAsmCoercion(
+ visit(curr->value, NO_RESULT),
+ wasmToAsmType(curr->value->type)
+ );
+ return ValueBuilder::makeReturn(val);
+ }
}
Ref visitHost(Host* curr) {
+ Ref call;
if (curr->op == HostOp::GrowMemory) {
parent->setNeedsAlmostASM("grow_memory op");
- return ValueBuilder::makeCall(WASM_GROW_MEMORY,
+ call = ValueBuilder::makeCall(WASM_GROW_MEMORY,
makeAsmCoercion(
visit(curr->operands[0], EXPRESSION_RESULT),
wasmToAsmType(curr->operands[0]->type)));
- }
- if (curr->op == HostOp::CurrentMemory) {
+ } else if (curr->op == HostOp::CurrentMemory) {
parent->setNeedsAlmostASM("current_memory op");
- return ValueBuilder::makeCall(WASM_CURRENT_MEMORY);
+ call = ValueBuilder::makeCall(WASM_CURRENT_MEMORY);
+ } else {
+ return ValueBuilder::makeCall(ABORT_FUNC);
+ }
+ if (isStatement(curr)) {
+ return ValueBuilder::makeBinary(ValueBuilder::makeName(result), SET, call);
+ } else {
+ return call;
}
- return ValueBuilder::makeCall(ABORT_FUNC);
}
Ref visitNop(Nop* curr) {
@@ -1696,38 +1836,57 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
return ValueBuilder::makeCall(ABORT_FUNC);
}
};
- return ExpressionProcessor(this, func).visit(func->body, result);
+ return ExpressionProcessor(this, m, func).visit(func->body, result);
}
-static void makeInstantiation(Ref ret) {
- // var __array_buffer = new ArrayBuffer(..)
- Ref mem = ValueBuilder::makeNew(
- ValueBuilder::makeCall(ARRAY_BUFFER, ValueBuilder::makeInt(0x10000)));
- Ref arrayBuffer = ValueBuilder::makeVar();
+static void makeInstantiation(Ref ret, Name funcName, Name moduleName, bool first) {
Name buffer("__array_buffer");
- ValueBuilder::appendToVar(arrayBuffer, buffer, mem);
- flattenAppend(ret, arrayBuffer);
-
- // var HEAP32 = new Int32Array(__array_buffer);
- Ref heap32Array = ValueBuilder::makeNew(
- ValueBuilder::makeCall(INT32ARRAY, ValueBuilder::makeName(buffer)));
- Ref heap32 = ValueBuilder::makeVar();
- ValueBuilder::appendToVar(heap32, HEAP32, heap32Array);
- flattenAppend(ret, heap32);
-
- // var HEAPF32 = new Float32Array(__array_buffer);
- Ref heapf32Array = ValueBuilder::makeNew(
- ValueBuilder::makeCall(FLOAT32ARRAY, ValueBuilder::makeName(buffer)));
- Ref heapf32 = ValueBuilder::makeVar();
- ValueBuilder::appendToVar(heapf32, HEAPF32, heapf32Array);
- flattenAppend(ret, heapf32);
-
- // var HEAPF64 = new Float64Array(__array_buffer);
- Ref heapf64Array = ValueBuilder::makeNew(
- ValueBuilder::makeCall(FLOAT64ARRAY, ValueBuilder::makeName(buffer)));
- Ref heapf64 = ValueBuilder::makeVar();
- ValueBuilder::appendToVar(heapf64, HEAPF64, heapf64Array);
- flattenAppend(ret, heapf64);
+ if (first) {
+ // TODO: nan and infinity shouldn't be needed once literal asm.js code isn't
+ // generated
+ flattenAppend(ret, ValueBuilder::makeName(R"(
+ var __array_buffer = new ArrayBuffer(65536)
+ var HEAP32 = new Int32Array(__array_buffer);
+ var HEAPF32 = new Float32Array(__array_buffer);
+ var HEAPF64 = new Float64Array(__array_buffer);
+ var nan = NaN;
+ var infinity = Infinity;
+ )"));
+
+ // When equating floating point values in spec tests we want to use bitwise
+ // equality like wasm does. Unfortunately though NaN makes this tricky. JS
+ // implementations like Spidermonkey and JSC will canonicalize NaN loads from
+ // `Float32Array`, but V8 will not. This means that NaN representations are
+ // kind of all over the place and difficult to bitwise equate.
+ //
+ // To work around this problem we just use a small shim which considers all
+ // NaN representations equivalent and otherwise tests for bitwise equality.
+ flattenAppend(ret, ValueBuilder::makeName(R"(
+ function f32Equal(a, b) {
+ var i = new Int32Array(1);
+ var f = new Float32Array(i.buffer);
+ f[0] = a;
+ var ai = f[0];
+ f[0] = b;
+ var bi = f[0];
+
+ return (isNaN(a) && isNaN(b)) || a == b;
+ }
+
+ function f64Equal(a, b) {
+ var i = new Int32Array(2);
+ var f = new Float64Array(i.buffer);
+ f[0] = a;
+ var ai1 = i[0];
+ var ai2 = i[1];
+ f[0] = b;
+ var bi1 = i[0];
+ var bi2 = i[1];
+
+ return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2);
+ }
+ )"));
+ }
Ref lib = ValueBuilder::makeObject();
auto insertItem = [&](IString item) {
@@ -1742,64 +1901,39 @@ static void makeInstantiation(Ref ret) {
insertItem(UINT32ARRAY);
insertItem(FLOAT32ARRAY);
insertItem(FLOAT64ARRAY);
+ // TODO: these shouldn't be necessary once we don't generate literal asm.js
+ // code
+ insertItem("Infinity");
+ insertItem("NaN");
Ref env = ValueBuilder::makeObject();
- Ref call = ValueBuilder::makeCall(IString(ASM_FUNC), lib, env,
+ Ref abortFunc = ValueBuilder::makeFunction("abort");
+ abortFunc[3]->push_back(ValueBuilder::makeCall("unreachable"));
+ ValueBuilder::appendToObject(env, "abort", abortFunc);
+
+ Ref printFunc = ValueBuilder::makeFunction("print");
+ abortFunc[3]->push_back(ValueBuilder::makeCall("console_log"));
+ ValueBuilder::appendToObject(env, "print", printFunc);
+ Ref call = ValueBuilder::makeCall(IString(funcName), lib, env,
ValueBuilder::makeName(buffer));
Ref module = ValueBuilder::makeVar();
- ValueBuilder::appendToVar(module, ASM_MODULE, call);
+ ValueBuilder::appendToVar(module, moduleName, call);
flattenAppend(ret, module);
- // When equating floating point values in spec tests we want to use bitwise
- // equality like wasm does. Unfortunately though NaN makes this tricky. JS
- // implementations like Spidermonkey and JSC will canonicalize NaN loads from
- // `Float32Array`, but V8 will not. This means that NaN representations are
- // kind of all over the place and difficult to bitwise equate.
- //
- // To work around this problem we just use a small shim which considers all
- // NaN representations equivalent and otherwise tests for bitwise equality.
- flattenAppend(ret, ValueBuilder::makeName(R"(
- function f32Equal(a, b) {
- var i = new Int32Array(1);
- var f = new Float32Array(i.buffer);
- f[0] = a;
- var ai = f[0];
- f[0] = b;
- var bi = f[0];
-
- return (isNaN(a) && isNaN(b)) || a == b;
- }
- )"));
- flattenAppend(ret, ValueBuilder::makeName(R"(
- function f64Equal(a, b) {
- var i = new Int32Array(2);
- var f = new Float64Array(i.buffer);
- f[0] = a;
- var ai1 = i[0];
- var ai2 = i[1];
- f[0] = b;
- var bi1 = i[0];
- var bi2 = i[1];
-
- return (isNaN(a) && isNaN(b)) || (ai1 == bi1 && ai2 == bi2);
- }
- )"));
-
// 64-bit numbers get a different ABI w/ wasm2asm, and in general you can't
// actually export them from wasm at the boundary. We hack around this though
// to get the spec tests working.
flattenAppend(ret, ValueBuilder::makeName(R"(
- function i64Equal(actual_lo, expected_lo, expected_hi) {
- return actual_lo == (expected_lo | 0) &&
- asmModule.__wasm_fetch_high_bits() == (expected_hi | 0);
+ function i64Equal(actual_lo, actual_hi, expected_lo, expected_hi) {
+ return actual_lo == (expected_lo | 0) && actual_hi == (expected_hi | 0);
}
)"));
}
-static void prefixCalls(Ref asmjs) {
+static void prefixCalls(Ref asmjs, Name asmModule) {
if (asmjs->isArray()) {
ArrayStorage& arr = asmjs->getArray();
for (Ref& r : arr) {
- prefixCalls(r);
+ prefixCalls(r, asmModule);
}
if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) {
assert(arr.size() >= 2);
@@ -1811,7 +1945,7 @@ static void prefixCalls(Ref asmjs) {
} else if (arr[1]->getIString() == "Math_fround") {
arr[1]->setString("Math.fround");
} else {
- Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(ASM_MODULE),
+ Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(asmModule),
arr[1]->getIString());
arr[1]->setArray(prefixed->getArray());
}
@@ -1819,14 +1953,20 @@ static void prefixCalls(Ref asmjs) {
}
if (asmjs->isAssign()) {
- prefixCalls(asmjs->asAssign()->target());
- prefixCalls(asmjs->asAssign()->value());
+ prefixCalls(asmjs->asAssign()->target(), asmModule);
+ prefixCalls(asmjs->asAssign()->value(), asmModule);
+ }
+ if (asmjs->isAssignName()) {
+ prefixCalls(asmjs->asAssignName()->value(), asmModule);
}
}
Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
+ Module* wasm,
Builder& wasmBuilder,
- Element& e, Name testFuncName) {
+ Element& e,
+ Name testFuncName,
+ Name asmModule) {
Expression* actual = sexpBuilder.parseExpression(e[1]);
Expression* body = nullptr;
if (e.size() == 2) {
@@ -1848,7 +1988,11 @@ Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
break;
case i64:
- body = wasmBuilder.makeCall("i64Equal", {actual, expected}, i32);
+ body = wasmBuilder.makeCall(
+ "i64Equal",
+ {actual, wasmBuilder.makeCall(WASM_FETCH_HIGH_BITS, {}, i32), expected},
+ i32
+ );
break;
case f32: {
@@ -1877,14 +2021,17 @@ Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
body
)
);
- Ref jsFunc = processFunction(testFunc.get());
- prefixCalls(jsFunc);
+ Ref jsFunc = processFunction(wasm, testFunc.get());
+ prefixCalls(jsFunc, asmModule);
return jsFunc;
}
Ref Wasm2AsmBuilder::makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder,
+ Module* wasm,
Builder& wasmBuilder,
- Element& e, Name testFuncName) {
+ Element& e,
+ Name testFuncName,
+ Name asmModule) {
Expression* actual = sexpBuilder.parseExpression(e[1]);
Expression* body = wasmBuilder.makeCallImport("isNaN", {actual}, i32);
std::unique_ptr<Function> testFunc(
@@ -1896,14 +2043,17 @@ Ref Wasm2AsmBuilder::makeAssertReturnNanFunc(SExpressionWasmBuilder& sexpBuilder
body
)
);
- Ref jsFunc = processFunction(testFunc.get());
- prefixCalls(jsFunc);
+ Ref jsFunc = processFunction(wasm, testFunc.get());
+ prefixCalls(jsFunc, asmModule);
return jsFunc;
}
Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
+ Module* wasm,
Builder& wasmBuilder,
- Element& e, Name testFuncName) {
+ Element& e,
+ Name testFuncName,
+ Name asmModule) {
Name innerFuncName("f");
Expression* expr = sexpBuilder.parseExpression(e[1]);
std::unique_ptr<Function> exprFunc(
@@ -1914,8 +2064,8 @@ Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
expr)
);
IString expectedErr = e[2]->str();
- Ref innerFunc = processFunction(exprFunc.get());
- prefixCalls(innerFunc);
+ Ref innerFunc = processFunction(wasm, exprFunc.get());
+ prefixCalls(innerFunc, asmModule);
Ref outerFunc = ValueBuilder::makeFunction(testFuncName);
outerFunc[3]->push_back(innerFunc);
Ref tryBlock = ValueBuilder::makeBlock();
@@ -2109,13 +2259,28 @@ bool Wasm2AsmBuilder::isAssertHandled(Element& e) {
&& (*e[1])[0]->str() == Name("invoke");
}
-Ref Wasm2AsmBuilder::processAsserts(Element& root,
- SExpressionWasmBuilder& sexpBuilder) {
+Ref Wasm2AsmBuilder::processAsserts(Module* wasm,
+ Element& root,
+ SExpressionWasmBuilder& sexpBuilder) {
Builder wasmBuilder(sexpBuilder.getAllocator());
Ref ret = ValueBuilder::makeBlock();
- makeInstantiation(ret);
+ Name asmModule = ASM_MODULE;
+ makeInstantiation(ret, ASM_FUNC, asmModule, true);
for (size_t i = 1; i < root.size(); ++i) {
Element& e = *root[i];
+ if (e.isList() && e.size() >= 1 && e[0]->isStr() && e[0]->str() == Name("module")) {
+ std::stringstream funcNameS;
+ funcNameS << ASM_FUNC.c_str() << i;
+ std::stringstream moduleNameS;
+ moduleNameS << ASM_MODULE.c_str() << i;
+ Name funcName(funcNameS.str().c_str());
+ asmModule = Name(moduleNameS.str().c_str());
+ Module wasm;
+ SExpressionWasmBuilder builder(wasm, e);
+ flattenAppend(ret, processWasm(&wasm, funcName));
+ makeInstantiation(ret, funcName, asmModule, false);
+ continue;
+ }
if (!isAssertHandled(e)) {
std::cerr << "skipping " << e << std::endl;
continue;
@@ -2130,18 +2295,19 @@ Ref Wasm2AsmBuilder::processAsserts(Element& root,
testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false);
Ref testFunc = isReturn ?
- makeAssertReturnFunc(sexpBuilder, wasmBuilder, e, testFuncName) :
+ makeAssertReturnFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule) :
(isReturnNan ?
- makeAssertReturnNanFunc(sexpBuilder, wasmBuilder, e, testFuncName) :
- makeAssertTrapFunc(sexpBuilder, wasmBuilder, e, testFuncName));
+ makeAssertReturnNanFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule) :
+ makeAssertTrapFunc(sexpBuilder, wasm, wasmBuilder, e, testFuncName, asmModule));
flattenAppend(ret, testFunc);
std::stringstream failFuncName;
failFuncName << "fail" << std::to_string(i);
+ IString testName = fromName(testFuncName, NameScope::Top);
flattenAppend(
ret,
ValueBuilder::makeIf(
- ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testFuncName)),
+ ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testName)),
ValueBuilder::makeCall(IString(failFuncName.str().c_str(), false)),
Ref()
)