summaryrefslogtreecommitdiff
path: root/src/wasm2asm.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/wasm2asm.h')
-rw-r--r--src/wasm2asm.h280
1 files changed, 229 insertions, 51 deletions
diff --git a/src/wasm2asm.h b/src/wasm2asm.h
index e2bb2bca7..7ba9d5175 100644
--- a/src/wasm2asm.h
+++ b/src/wasm2asm.h
@@ -45,8 +45,8 @@ IString ASM_FUNC("asmFunc"),
// Appends extra to block, flattening out if extra is a block as well
void flattenAppend(Ref ast, Ref extra) {
int index;
- if (ast->isArray() && ast[0] == BLOCK) index = 1;
- else if (ast->isArray() && ast[0] == DEFUN) index = 3;
+ if (ast[0] == BLOCK || ast[0] == TOPLEVEL) index = 1;
+ else if (ast[0] == DEFUN) index = 3;
else abort();
if (extra->isArray() && extra[0] == BLOCK) {
for (size_t i = 0; i < extra[1]->size(); i++) {
@@ -108,7 +108,12 @@ class Wasm2AsmBuilder {
MixedArena allocator;
public:
- Wasm2AsmBuilder(bool debug) : debug(debug), tableSize(-1) {}
+ struct Flags {
+ bool debug: 1;
+ bool pedantic: 1;
+ };
+
+ Wasm2AsmBuilder(Flags f) : flags(f) {}
Ref processWasm(Module* wasm);
Ref processFunction(Function* func);
@@ -125,15 +130,21 @@ public:
// if we have one.
Ref processFunctionBody(Function* func, IString result);
+ Ref processAsserts(Element& e, SExpressionWasmBuilder& sexpBuilder);
+
// Get a temp var.
- IString getTemp(WasmType type) {
+ IString getTemp(WasmType type, Function* func) {
IString ret;
if (frees[type].size() > 0) {
ret = frees[type].back();
frees[type].pop_back();
} else {
size_t index = temps[type]++;
- ret = IString((std::string("wasm2asm_") + printWasmType(type) + "$" + std::to_string(index)).c_str(), false);
+ ret = IString((std::string("wasm2asm_") + printWasmType(type) + "$" +
+ std::to_string(index)).c_str(), false);
+ }
+ if (func->localIndices.find(ret) == func->localIndices.end()) {
+ Builder::addVar(func, ret, type);
}
return ret;
}
@@ -175,7 +186,8 @@ public:
}
private:
- bool debug;
+ Flags flags;
+
// How many temp vars we need
std::vector<size_t> temps; // type => num temps
// Which are currently free to use
@@ -192,7 +204,13 @@ private:
void addTables(Ref ast, Module *wasm);
void addExports(Ref ast, Module *wasm);
void addWasmCompatibilityFuncs(Module *wasm);
-
+ bool isAssertHandled(Element& e);
+ Ref makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
+ Builder& wasmBuilder,
+ Element& e, Name testFuncName);
+ Ref makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
+ Builder& wasmBuilder,
+ Element& e, Name testFuncName);
Wasm2AsmBuilder() = delete;
Wasm2AsmBuilder(const Wasm2AsmBuilder &) = delete;
Wasm2AsmBuilder &operator=(const Wasm2AsmBuilder&) = delete;
@@ -496,7 +514,7 @@ void Wasm2AsmBuilder::addExports(Ref ast, Module *wasm) {
}
Ref Wasm2AsmBuilder::processFunction(Function* func) {
- if (debug) std::cerr << " processFunction " << func->name << std::endl;
+ if (flags.debug) std::cerr << " processFunction " << func->name << std::endl;
Ref ret = ValueBuilder::makeFunction(fromName(func->name));
frees.clear();
frees.resize(std::max(i32, std::max(f32, f64)) + 1);
@@ -523,7 +541,8 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) {
// body
scanFunctionBody(func->body);
if (isStatement(func->body)) {
- IString result = func->result != none ? getTemp(func->result) : NO_RESULT;
+ IString result =
+ func->result != none ? getTemp(func->result, func) : NO_RESULT;
flattenAppend(ret, ValueBuilder::makeStatement(processFunctionBody(func, result)));
if (func->result != none) {
// do the actual return
@@ -542,15 +561,6 @@ Ref Wasm2AsmBuilder::processFunction(Function* func) {
for (Index i = func->getVarIndexBase(); i < func->getNumLocals(); i++) {
ValueBuilder::appendToVar(theVar, fromName(func->getLocalName(i)), makeAsmCoercedZero(wasmToAsmType(func->getLocalType(i))));
}
- for (auto f : frees[i32]) {
- ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_INT));
- }
- for (auto f : frees[f32]) {
- ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_FLOAT));
- }
- for (auto f : frees[f64]) {
- ValueBuilder::appendToVar(theVar, f, makeAsmCoercedZero(ASM_DOUBLE));
- }
if (theVar[1]->size() == 0) {
ret[3]->splice(theVarIndex, 1);
}
@@ -676,10 +686,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
// @param possible if provided, this is a variable we can use as our temp. it has already been
// allocated in a higher scope, and we can just assign to it as our result is
// going there anyhow.
- ScopedTemp(WasmType type, Wasm2AsmBuilder* parent, IString possible = NO_RESULT) : parent(parent), type(type) {
+ ScopedTemp(WasmType type, Wasm2AsmBuilder* parent, Function* func,
+ IString possible = NO_RESULT) : parent(parent), type(type) {
assert(possible != EXPRESSION_RESULT);
if (possible == NO_RESULT) {
- temp = parent->getTemp(type);
+ temp = parent->getTemp(type, func);
needFree = true;
} else {
temp = possible;
@@ -715,7 +726,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
// this result is for an asm expression slot, but it might be a statement
Ref visitForExpression(Expression* curr, WasmType type, IString& tempName) {
if (isStatement(curr)) {
- ScopedTemp temp(type, parent);
+ ScopedTemp temp(type, parent, func);
tempName = temp.temp;
return visit(curr, temp);
} else {
@@ -838,7 +849,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref ret = ValueBuilder::makeBlock();
Ref condition;
if (isStatement(curr->condition)) {
- ScopedTemp temp(i32, parent);
+ ScopedTemp temp(i32, parent, func);
flattenAppend(ret[2], visit(curr->condition, temp));
condition = temp.getAstName();
} else {
@@ -858,7 +869,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref makeStatementizedCall(ExpressionList& operands, Ref ret, Ref theCall, IString result, WasmType type) {
std::vector<ScopedTemp*> temps; // TODO: utility class, with destructor?
for (auto& operand : operands) {
- temps.push_back(new ScopedTemp(operand->type, parent));
+ temps.push_back(new ScopedTemp(operand->type, parent, func));
IString temp = temps.back()->temp;
flattenAppend(ret, visitAndAssign(operand, temp));
theCall[2]->push_back(makeAsmCoercion(ValueBuilder::makeName(temp), wasmToAsmType(operand->type)));
@@ -911,7 +922,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
// we must statementize them all
Ref ret = ValueBuilder::makeBlock();
- ScopedTemp temp(i32, parent);
+ 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);
@@ -925,7 +936,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ValueBuilder::makeName(fromName(func->getLocalName(curr->index))),
SET, visit(curr->value, EXPRESSION_RESULT));
}
- ScopedTemp temp(curr->type, parent, result); // if result was provided, our child can just assign there. otherwise, allocate a temp for it to assign to.
+ ScopedTemp temp(curr->type, parent, func, result); // if result was provided, our child can just assign there. otherwise, allocate a temp for it to assign to.
Ref ret = blockify(visit(curr->value, temp));
// the output was assigned to result, so we can just assign it to our target
ret[1]->push_back(ValueBuilder::makeStatement(
@@ -936,7 +947,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
Ref visitLoad(Load *curr) {
if (isStatement(curr)) {
- ScopedTemp temp(i32, parent);
+ ScopedTemp temp(i32, parent, func);
GetLocal fakeLocal(allocator);
fakeLocal.index = func->getLocalIndex(temp.getName());
Load fakeLoad = *curr;
@@ -947,7 +958,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
if (curr->align != 0 && curr->align < curr->bytes) {
// set the pointer to a local
- ScopedTemp temp(i32, parent);
+ ScopedTemp temp(i32, parent, func);
SetLocal set(allocator);
set.index = func->getLocalIndex(temp.getName());
set.value = curr->ptr;
@@ -970,9 +981,10 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
break;
}
- default:
+ default: {
std::cerr << "Unhandled type in load: " << curr->type << std::endl;
abort();
+ }
}
return ValueBuilder::makeSeq(ptrSet, rest);
}
@@ -1002,10 +1014,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ValueBuilder::makeName(curr->signed_ ? HEAP32 : HEAPU32),
ValueBuilder::makePtrShift(ptr, 2));
break;
- default:
+ default: {
std::cerr << "Unhandled number of bytes in i32 load: "
<< curr->bytes << std::endl;
abort();
+ }
}
break;
}
@@ -1017,16 +1030,17 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64),
ValueBuilder::makePtrShift(ptr, 3));
break;
- default:
+ default: {
std::cerr << "Unhandled type in load: " << curr->type << std::endl;
abort();
+ }
}
return makeAsmCoercion(ret, wasmToAsmType(curr->type));
}
Ref visitStore(Store *curr) {
if (isStatement(curr)) {
- ScopedTemp tempPtr(i32, parent);
- ScopedTemp tempValue(curr->type, parent);
+ ScopedTemp tempPtr(i32, parent, func);
+ ScopedTemp tempValue(curr->valueType, parent, func);
GetLocal fakeLocalPtr(allocator);
fakeLocalPtr.index = func->getLocalIndex(tempPtr.getName());
GetLocal fakeLocalValue(allocator);
@@ -1041,7 +1055,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
if (curr->align != 0 && curr->align < curr->bytes) {
// set the pointer to a local
- ScopedTemp temp(i32, parent);
+ ScopedTemp temp(i32, parent, func);
SetLocal set(allocator);
set.index = func->getLocalIndex(temp.getName());
set.value = curr->ptr;
@@ -1049,7 +1063,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
GetLocal get(allocator);
get.index = func->getLocalIndex(temp.getName());
// set the value to a local
- ScopedTemp tempValue(curr->value->type, parent);
+ ScopedTemp tempValue(curr->value->type, parent, func);
SetLocal setValue(allocator);
setValue.index = func->getLocalIndex(tempValue.getName());
setValue.value = curr->value;
@@ -1061,7 +1075,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
store.ptr = &get;
store.bytes = 1; // do the worst
Ref rest;
- switch (curr->type) {
+ switch (curr->valueType) {
case i32: {
Const _255(allocator);
_255.value = Literal(int32_t(255));
@@ -1091,10 +1105,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
break;
}
- default:
- std::cerr << "Unhandled type in store: " << curr->type
+ default: {
+ std::cerr << "Unhandled type in store: " << curr->valueType
<< std::endl;
abort();
+ }
}
return ValueBuilder::makeSeq(ValueBuilder::makeSeq(ptrSet, valueSet), rest);
}
@@ -1105,7 +1120,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
Ref value = visit(curr->value, EXPRESSION_RESULT);
Ref ret;
- switch (curr->type) {
+ switch (curr->valueType) {
case i32: {
switch (curr->bytes) {
case 1: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAP8), ValueBuilder::makePtrShift(ptr, 0)); break;
@@ -1117,7 +1132,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
case f32: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF32), ValueBuilder::makePtrShift(ptr, 2)); break;
case f64: ret = ValueBuilder::makeSub(ValueBuilder::makeName(HEAPF64), ValueBuilder::makePtrShift(ptr, 3)); break;
- default: abort();
+ default: {
+ std::cerr << "Unhandled type in store: " << curr->valueType
+ << std::endl;
+ abort();
+ }
}
return ValueBuilder::makeBinary(ret, SET, value);
}
@@ -1148,7 +1167,7 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
Ref visitUnary(Unary *curr) {
if (isStatement(curr)) {
- ScopedTemp temp(curr->value->type, parent);
+ ScopedTemp temp(curr->value->type, parent, func);
GetLocal fakeLocal(allocator);
fakeLocal.index = func->getLocalIndex(temp.getName());
Unary fakeUnary = *curr;
@@ -1176,10 +1195,11 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
makeAsmCoercion(visit(curr->value,
EXPRESSION_RESULT), ASM_INT), EQ,
makeAsmCoercion(ValueBuilder::makeInt(0), ASM_INT));
- default:
+ default: {
std::cerr << "Unhandled unary i32 operator: " << curr
<< std::endl;
abort();
+ }
}
}
case f32:
@@ -1246,17 +1266,18 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
}
return ret;
}
- default:
+ default: {
std::cerr << "Unhandled type: " << curr << std::endl;
abort();
+ }
}
}
Ref visitBinary(Binary *curr) {
if (isStatement(curr)) {
- ScopedTemp tempLeft(curr->left->type, parent);
+ ScopedTemp tempLeft(curr->left->type, parent, func);
GetLocal fakeLocalLeft(allocator);
fakeLocalLeft.index = func->getLocalIndex(tempLeft.getName());
- ScopedTemp tempRight(curr->right->type, parent);
+ ScopedTemp tempRight(curr->right->type, parent, func);
GetLocal fakeLocalRight(allocator);
fakeLocalRight.index = func->getLocalIndex(tempRight.getName());
Binary fakeBinary = *curr;
@@ -1371,21 +1392,22 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
return ValueBuilder::makeCall(ROTL32, left, right);
case RotRInt32:
return ValueBuilder::makeCall(ROTR32, left, right);
- default:
+ default: {
std::cerr << "Unhandled binary operator: " << curr << std::endl;
abort();
+ }
}
return makeAsmCoercion(ret, wasmToAsmType(curr->type));
}
Ref visitSelect(Select *curr) {
if (isStatement(curr)) {
- ScopedTemp tempIfTrue(curr->ifTrue->type, parent);
+ ScopedTemp tempIfTrue(curr->ifTrue->type, parent, func);
GetLocal fakeLocalIfTrue(allocator);
fakeLocalIfTrue.index = func->getLocalIndex(tempIfTrue.getName());
- ScopedTemp tempIfFalse(curr->ifFalse->type, parent);
+ ScopedTemp tempIfFalse(curr->ifFalse->type, parent, func);
GetLocal fakeLocalIfFalse(allocator);
fakeLocalIfFalse.index = func->getLocalIndex(tempIfFalse.getName());
- ScopedTemp tempCondition(i32, parent);
+ ScopedTemp tempCondition(i32, parent, func);
GetLocal fakeCondition(allocator);
fakeCondition.index = func->getLocalIndex(tempCondition.getName());
Select fakeSelect = *curr;
@@ -1402,9 +1424,9 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
Ref ifTrue = visit(curr->ifTrue, EXPRESSION_RESULT);
Ref ifFalse = visit(curr->ifFalse, EXPRESSION_RESULT);
Ref condition = visit(curr->condition, EXPRESSION_RESULT);
- ScopedTemp tempIfTrue(curr->type, parent),
- tempIfFalse(curr->type, parent),
- tempCondition(i32, parent);
+ ScopedTemp tempIfTrue(curr->type, parent, func),
+ tempIfFalse(curr->type, parent, func),
+ tempCondition(i32, parent, func);
return
ValueBuilder::makeSeq(
ValueBuilder::makeBinary(tempCondition.getAstName(), SET, condition),
@@ -1433,6 +1455,162 @@ Ref Wasm2AsmBuilder::processFunctionBody(Function* func, IString result) {
return ExpressionProcessor(this, func).visit(func->body, result);
}
+static Ref makeInstantiation() {
+ Ref lib = ValueBuilder::makeObject();
+ auto insertItem = [&](IString item) {
+ ValueBuilder::appendToObject(lib, item, ValueBuilder::makeName(item));
+ };
+ insertItem(MATH);
+ insertItem(INT8ARRAY);
+ insertItem(INT16ARRAY);
+ insertItem(INT32ARRAY);
+ insertItem(UINT8ARRAY);
+ insertItem(UINT16ARRAY);
+ insertItem(UINT32ARRAY);
+ insertItem(FLOAT32ARRAY);
+ insertItem(FLOAT64ARRAY);
+ Ref env = ValueBuilder::makeObject();
+ Ref mem = ValueBuilder::makeNew(
+ ValueBuilder::makeCall(ARRAY_BUFFER, ValueBuilder::makeInt(0x10000)));
+ Ref call = ValueBuilder::makeCall(IString(ASM_FUNC), lib, env, mem);
+ Ref ret = ValueBuilder::makeVar();
+ ValueBuilder::appendToVar(ret, ASM_MODULE, call);
+ return ret;
+}
+
+static void prefixCalls(Ref asmjs) {
+ if (asmjs->isArray()) {
+ ArrayStorage& arr = asmjs->getArray();
+ for (Ref& r : arr) {
+ prefixCalls(r);
+ }
+ if (arr.size() > 0 && arr[0]->isString() && arr[0]->getIString() == CALL) {
+ assert(arr.size() >= 2);
+ Ref prefixed = ValueBuilder::makeDot(ValueBuilder::makeName(ASM_MODULE),
+ arr[1]->getIString());
+ arr[1]->setArray(prefixed->getArray());
+ }
+ }
+}
+
+Ref Wasm2AsmBuilder::makeAssertReturnFunc(SExpressionWasmBuilder& sexpBuilder,
+ Builder& wasmBuilder,
+ Element& e, Name testFuncName) {
+ Expression* actual = sexpBuilder.parseExpression(e[1]);
+ Expression* expected = sexpBuilder.parseExpression(e[2]);
+ WasmType resType = expected->type;
+ actual->type = resType;
+ BinaryOp eqOp;
+ switch (resType) {
+ case i32: eqOp = EqInt32; break;
+ case i64: eqOp = EqInt64; break;
+ case f32: eqOp = EqFloat32; break;
+ case f64: eqOp = EqFloat64; break;
+ default: {
+ std::cerr << "Unhandled type in assert: " << resType << std::endl;
+ abort();
+ }
+ }
+ Binary* test = wasmBuilder.makeBinary(eqOp, actual, expected);
+ std::unique_ptr<Function> testFunc(
+ wasmBuilder.makeFunction(testFuncName,
+ std::vector<NameType>{},
+ i32,
+ std::vector<NameType>{},
+ test)
+ );
+ Ref jsFunc = processFunction(testFunc.get());
+ prefixCalls(jsFunc);
+ return jsFunc;
+}
+
+Ref Wasm2AsmBuilder::makeAssertTrapFunc(SExpressionWasmBuilder& sexpBuilder,
+ Builder& wasmBuilder,
+ Element& e, Name testFuncName) {
+ Name innerFuncName("f");
+ Expression* expr = sexpBuilder.parseExpression(e[1]);
+ std::unique_ptr<Function> exprFunc(
+ wasmBuilder.makeFunction(innerFuncName,
+ std::vector<NameType>{},
+ expr->type,
+ std::vector<NameType>{},
+ expr)
+ );
+ IString expectedErr = e[2]->str();
+ Ref innerFunc = processFunction(exprFunc.get());
+ Ref outerFunc = ValueBuilder::makeFunction(testFuncName);
+ outerFunc[3]->push_back(innerFunc);
+ Ref tryBlock = ValueBuilder::makeBlock();
+ ValueBuilder::appendToBlock(tryBlock, ValueBuilder::makeCall(innerFuncName));
+ Ref catchBlock = ValueBuilder::makeBlock();
+ ValueBuilder::appendToBlock(
+ catchBlock,
+ ValueBuilder::makeReturn(
+ ValueBuilder::makeCall(
+ ValueBuilder::makeDot(
+ ValueBuilder::makeName(IString("e")),
+ ValueBuilder::makeName(IString("message")),
+ ValueBuilder::makeName(IString("includes"))
+ ),
+ ValueBuilder::makeString(expectedErr)
+ )
+ )
+ );
+ outerFunc[3]->push_back(ValueBuilder::makeTry(
+ tryBlock,
+ ValueBuilder::makeName((IString("e"))),
+ catchBlock));
+ outerFunc[3]->push_back(ValueBuilder::makeReturn(ValueBuilder::makeInt(0)));
+ return outerFunc;
+}
+
+bool Wasm2AsmBuilder::isAssertHandled(Element& e) {
+ return e.isList() && e.size() >= 2 && e[0]->isStr()
+ && (e[0]->str() == Name("assert_return") ||
+ (flags.pedantic && e[0]->str() == Name("assert_trap")))
+ && e[1]->isList() && e[1]->size() >= 2 && (*e[1])[0]->isStr()
+ && (*e[1])[0]->str() == Name("invoke");
+}
+
+Ref Wasm2AsmBuilder::processAsserts(Element& root,
+ SExpressionWasmBuilder& sexpBuilder) {
+ Builder wasmBuilder(sexpBuilder.getAllocator());
+ Ref ret = ValueBuilder::makeBlock();
+ flattenAppend(ret, makeInstantiation());
+ for (size_t i = 1; i < root.size(); ++i) {
+ Element& e = *root[i];
+ if (!isAssertHandled(e)) {
+ std::cerr << "skipping " << e << std::endl;
+ continue;
+ }
+ Name testFuncName(IString(("check" + std::to_string(i)).c_str(), false));
+ bool isReturn = (e[0]->str() == Name("assert_return"));
+ Element& testOp = *e[1];
+ // Replace "invoke" with "call"
+ testOp[0]->setString(IString("call"), false, false);
+ // Need to claim dollared to get string as function target
+ testOp[1]->setString(testOp[1]->str(), /*dollared=*/true, false);
+
+ Ref testFunc = isReturn ?
+ makeAssertReturnFunc(sexpBuilder, wasmBuilder, e, testFuncName) :
+ makeAssertTrapFunc(sexpBuilder, wasmBuilder, e, testFuncName);
+
+ flattenAppend(ret, testFunc);
+ std::stringstream failFuncName;
+ failFuncName << "fail" << std::to_string(i);
+ flattenAppend(
+ ret,
+ ValueBuilder::makeIf(
+ ValueBuilder::makeUnary(L_NOT, ValueBuilder::makeCall(testFuncName)),
+ ValueBuilder::makeCall(IString(failFuncName.str().c_str(), false)),
+ Ref()
+ )
+ );
+ }
+ return ret;
+}
+
+
} // namespace wasm
#endif // wasm_wasm2asm_h