diff options
author | Thomas Lively <7121787+tlively@users.noreply.github.com> | 2021-07-01 01:56:23 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-06-30 18:56:23 -0700 |
commit | ca27f40a2f1070a16ee7c0efc18ff35d342d8027 (patch) | |
tree | ab0f2b1b731737bc409db21f677b97be16f67c0f /src/tools | |
parent | 10ef52d62468aec5762742930630e882dc5e5c0b (diff) | |
download | binaryen-ca27f40a2f1070a16ee7c0efc18ff35d342d8027.tar.gz binaryen-ca27f40a2f1070a16ee7c0efc18ff35d342d8027.tar.bz2 binaryen-ca27f40a2f1070a16ee7c0efc18ff35d342d8027.zip |
Preserve Function HeapTypes (#3952)
When using nominal types, func.ref of two functions with identical signatures
but different HeapTypes will yield different types. To preserve these semantics,
Functions need to track their HeapTypes, not just their Signatures.
This PR replaces the Signature field in Function with a HeapType field and adds
new utility methods to make it almost as simple to update and query the function
HeapType as it was to update and query the Function Signature.
Diffstat (limited to 'src/tools')
-rw-r--r-- | src/tools/execution-results.h | 6 | ||||
-rw-r--r-- | src/tools/fuzzing.h | 72 | ||||
-rw-r--r-- | src/tools/js-wrapper.h | 10 | ||||
-rw-r--r-- | src/tools/spec-wrapper.h | 4 | ||||
-rw-r--r-- | src/tools/wasm-ctor-eval.cpp | 2 | ||||
-rw-r--r-- | src/tools/wasm-reduce.cpp | 8 | ||||
-rw-r--r-- | src/tools/wasm2c-wrapper.h | 10 | ||||
-rw-r--r-- | src/tools/wasm2js.cpp | 10 |
8 files changed, 59 insertions, 63 deletions
diff --git a/src/tools/execution-results.h b/src/tools/execution-results.h index 50f627baf..aabbb4819 100644 --- a/src/tools/execution-results.h +++ b/src/tools/execution-results.h @@ -106,14 +106,14 @@ struct ExecutionResults { } std::cout << "[fuzz-exec] calling " << exp->name << "\n"; auto* func = wasm.getFunction(exp->value); - if (func->sig.results != Type::none) { + if (func->getResults() != Type::none) { // this has a result Literals ret = run(func, wasm, instance); results[exp->name] = ret; // ignore the result if we hit an unreachable and returned no value if (ret.size() > 0) { std::cout << "[fuzz-exec] note result: " << exp->name << " => "; - auto resultType = func->sig.results; + auto resultType = func->getResults(); if (resultType.isRef()) { // Don't print reference values, as funcref(N) contains an index // for example, which is not guaranteed to remain identical after @@ -227,7 +227,7 @@ struct ExecutionResults { instance.callFunction(ex->value, arguments); } // call the method - for (const auto& param : func->sig.params) { + for (const auto& param : func->getParams()) { // zeros in arguments TODO: more? if (!param.isDefaultable()) { std::cout << "[trap fuzzer can only send defaultable parameters to " diff --git a/src/tools/fuzzing.h b/src/tools/fuzzing.h index 03efd8134..dcdfd391f 100644 --- a/src/tools/fuzzing.h +++ b/src/tools/fuzzing.h @@ -592,7 +592,7 @@ private: auto funcName = Names::getValidFunctionName(wasm, exportName); auto* func = new Function; func->name = funcName; - func->sig = Signature(Type::none, Type::none); + func->type = Signature(Type::none, Type::none); func->body = builder.makeGlobalSet(HANG_LIMIT_GLOBAL, builder.makeConst(int32_t(HANG_LIMIT))); wasm.addFunction(func); @@ -616,7 +616,7 @@ private: func->name = name; func->module = "fuzzing-support"; func->base = name; - func->sig = Signature(type, Type::none); + func->type = Signature(type, Type::none); wasm.addFunction(func); } } @@ -684,7 +684,7 @@ private: funcContext->typeLocals[type].push_back(params.size()); params.push_back(type); } - func->sig = Signature(Type(params), getControlFlowType()); + func->type = Signature(Type(params), getControlFlowType()); Index numVars = upToSquared(MAX_VARS); for (Index i = 0; i < numVars; i++) { auto type = getConcreteType(); @@ -698,7 +698,7 @@ private: func->vars.push_back(type); } // with small chance, make the body unreachable - auto bodyType = func->sig.results; + auto bodyType = func->getResults(); if (oneIn(10)) { bodyType = Type::unreachable; } @@ -737,7 +737,7 @@ private: } // add some to an elem segment while (oneIn(3) && !finishedInput) { - auto type = Type(HeapType(func->sig), NonNullable); + auto type = Type(func->type, NonNullable); std::vector<ElementSegment*> compatibleSegments; ModuleUtils::iterActiveElementSegments( wasm, [&](ElementSegment* segment) { @@ -746,7 +746,7 @@ private: } }); auto& randomElem = compatibleSegments[upTo(compatibleSegments.size())]; - randomElem->data.push_back(builder.makeRefFunc(func->name, func->sig)); + randomElem->data.push_back(builder.makeRefFunc(func->name, func->type)); } numAddedFunctions++; return func; @@ -760,8 +760,8 @@ private: builder.makeSequence(makeHangLimitCheck(), loop->body, loop->type); } // recursion limit - func->body = - builder.makeSequence(makeHangLimitCheck(), func->body, func->sig.results); + func->body = builder.makeSequence( + makeHangLimitCheck(), func->body, func->getResults()); } // Recombination and mutation can replace a node with another node of the same @@ -967,7 +967,7 @@ private: // We can't allow extra imports, as the fuzzing infrastructure wouldn't // know what to provide. func->module = func->base = Name(); - func->body = make(func->sig.results); + func->body = make(func->getResults()); } // Optionally, fuzz the function contents. if (upTo(RESOLUTION) >= chance) { @@ -1033,12 +1033,12 @@ private: std::vector<Expression*> invocations; while (oneIn(2) && !finishedInput) { std::vector<Expression*> args; - for (const auto& type : func->sig.params) { + for (const auto& type : func->getParams()) { args.push_back(makeConst(type)); } Expression* invoke = - builder.makeCall(func->name, args, func->sig.results); - if (func->sig.results.isConcrete()) { + builder.makeCall(func->name, args, func->getResults()); + if (func->getResults().isConcrete()) { invoke = builder.makeDrop(invoke); } invocations.push_back(invoke); @@ -1052,7 +1052,7 @@ private: } auto* invoker = new Function; invoker->name = name; - invoker->sig = Signature(Type::none, Type::none); + invoker->type = Signature(Type::none, Type::none); invoker->body = builder.makeBlock(invocations); wasm.addFunction(invoker); auto* export_ = new Export; @@ -1236,8 +1236,8 @@ private: } assert(type == Type::unreachable); Expression* ret = nullptr; - if (funcContext->func->sig.results.isConcrete()) { - ret = makeTrivial(funcContext->func->sig.results); + if (funcContext->func->getResults().isConcrete()) { + ret = makeTrivial(funcContext->func->getResults()); } return builder.makeReturn(ret); } @@ -1437,13 +1437,13 @@ private: target = pick(wasm.functions).get(); } isReturn = type == Type::unreachable && wasm.features.hasTailCall() && - funcContext->func->sig.results == target->sig.results; - if (target->sig.results != type && !isReturn) { + funcContext->func->getResults() == target->getResults(); + if (target->getResults() != type && !isReturn) { continue; } // we found one! std::vector<Expression*> args; - for (const auto& argType : target->sig.params) { + for (const auto& argType : target->getParams()) { args.push_back(make(argType)); } return builder.makeCall(target->name, args, type, isReturn); @@ -1468,8 +1468,8 @@ private: if (auto* get = data[i]->dynCast<RefFunc>()) { targetFn = wasm.getFunction(get->func); isReturn = type == Type::unreachable && wasm.features.hasTailCall() && - funcContext->func->sig.results == targetFn->sig.results; - if (targetFn->sig.results == type || isReturn) { + funcContext->func->getResults() == targetFn->getResults(); + if (targetFn->getResults() == type || isReturn) { break; } } @@ -1490,12 +1490,12 @@ private: target = make(Type::i32); } std::vector<Expression*> args; - for (const auto& type : targetFn->sig.params) { + for (const auto& type : targetFn->getParams()) { args.push_back(make(type)); } // TODO: use a random table return builder.makeCallIndirect( - funcrefTableName, target, args, targetFn->sig, isReturn); + funcrefTableName, target, args, targetFn->getSig(), isReturn); } Expression* makeCallRef(Type type) { @@ -1511,19 +1511,19 @@ private: // TODO: handle unreachable target = wasm.functions[upTo(wasm.functions.size())].get(); isReturn = type == Type::unreachable && wasm.features.hasTailCall() && - funcContext->func->sig.results == target->sig.results; - if (target->sig.results == type || isReturn) { + funcContext->func->getResults() == target->getResults(); + if (target->getResults() == type || isReturn) { break; } i++; } std::vector<Expression*> args; - for (const auto& type : target->sig.params) { + for (const auto& type : target->getParams()) { args.push_back(make(type)); } // TODO: half the time make a completely random item with that type. return builder.makeCallRef( - builder.makeRefFunc(target->name, target->sig), args, type, isReturn); + builder.makeRefFunc(target->name, target->type), args, type, isReturn); } Expression* makeLocalGet(Type type) { @@ -2112,7 +2112,7 @@ private: if (!wasm.functions.empty() && !oneIn(wasm.functions.size())) { target = pick(wasm.functions).get(); } - return builder.makeRefFunc(target->name, target->sig); + return builder.makeRefFunc(target->name, target->type); } if (type == Type::i31ref) { return builder.makeI31New(makeConst(Type::i32)); @@ -2133,8 +2133,8 @@ private: } // TODO: randomize the order for (auto& func : wasm.functions) { - if (type == Type(HeapType(func->sig), NonNullable)) { - return builder.makeRefFunc(func->name, func->sig); + if (type == Type(func->type, NonNullable)) { + return builder.makeRefFunc(func->name, func->type); } } // We failed to find a function, so create a null reference if we can. @@ -2143,17 +2143,13 @@ private: } // Last resort: create a function. auto heapType = type.getHeapType(); - Signature sig; - if (heapType.isSignature()) { - sig = heapType.getSignature(); - } else { - assert(heapType == HeapType::func); + if (heapType == HeapType::func) { // The specific signature does not matter. - sig = Signature(Type::none, Type::none); + heapType = Signature(Type::none, Type::none); } auto* func = wasm.addFunction(builder.makeFunction( Names::getValidFunctionName(wasm, "ref_func_target"), - sig, + heapType, {}, builder.makeUnreachable())); return builder.makeRefFunc(func->name, heapType); @@ -2681,8 +2677,8 @@ private: } Expression* makeReturn(Type type) { - return builder.makeReturn(funcContext->func->sig.results.isConcrete() - ? make(funcContext->func->sig.results) + return builder.makeReturn(funcContext->func->getResults().isConcrete() + ? make(funcContext->func->getResults()) : nullptr); } diff --git a/src/tools/js-wrapper.h b/src/tools/js-wrapper.h index e6f553124..b93948e74 100644 --- a/src/tools/js-wrapper.h +++ b/src/tools/js-wrapper.h @@ -25,7 +25,7 @@ namespace wasm { -static std::string generateJSWrapper(Module& wasm) { +inline std::string generateJSWrapper(Module& wasm) { std::string ret; ret += "if (typeof console === 'undefined') {\n" " console = { log: print };\n" @@ -96,7 +96,7 @@ static std::string generateJSWrapper(Module& wasm) { ret += "try {\n"; ret += std::string(" console.log('[fuzz-exec] calling ") + exp->name.str + "');\n"; - if (func->sig.results != Type::none) { + if (func->getResults() != Type::none) { ret += std::string(" console.log('[fuzz-exec] note result: ") + exp->name.str + " => ' + literal("; } else { @@ -104,7 +104,7 @@ static std::string generateJSWrapper(Module& wasm) { } ret += std::string("instance.exports.") + exp->name.str + "("; bool first = true; - for (auto param : func->sig.params) { + for (auto param : func->getParams()) { // zeros in arguments TODO more? if (first) { first = false; @@ -121,8 +121,8 @@ static std::string generateJSWrapper(Module& wasm) { } } ret += ")"; - if (func->sig.results != Type::none) { - ret += ", '" + func->sig.results.toString() + "'))"; + if (func->getResults() != Type::none) { + ret += ", '" + func->getResults().toString() + "'))"; // TODO: getTempRet } ret += ";\n"; diff --git a/src/tools/spec-wrapper.h b/src/tools/spec-wrapper.h index 32e547f1b..3a490c954 100644 --- a/src/tools/spec-wrapper.h +++ b/src/tools/spec-wrapper.h @@ -24,7 +24,7 @@ namespace wasm { -static std::string generateSpecWrapper(Module& wasm) { +inline std::string generateSpecWrapper(Module& wasm) { std::string ret; for (auto& exp : wasm.exports) { auto* func = wasm.getFunctionOrNull(exp->value); @@ -33,7 +33,7 @@ static std::string generateSpecWrapper(Module& wasm) { } ret += std::string("(invoke \"hangLimitInitializer\") (invoke \"") + exp->name.str + "\" "; - for (const auto& param : func->sig.params) { + for (const auto& param : func->getParams()) { // zeros in arguments TODO more? TODO_SINGLE_COMPOUND(param); switch (param.getBasic()) { diff --git a/src/tools/wasm-ctor-eval.cpp b/src/tools/wasm-ctor-eval.cpp index 5bede4444..bd370a58a 100644 --- a/src/tools/wasm-ctor-eval.cpp +++ b/src/tools/wasm-ctor-eval.cpp @@ -322,7 +322,7 @@ struct CtorEvalExternalInterface : EvallingModuleInstance::ExternalInterface { // if this is one of our functions, we can call it; if it was // imported, fail auto* func = wasm->getFunction(name); - if (func->sig != sig) { + if (func->getSig() != sig) { throw FailToEvalException( std::string("callTable signature mismatch: ") + name.str); } diff --git a/src/tools/wasm-reduce.cpp b/src/tools/wasm-reduce.cpp index 48435fac0..ed2b9b564 100644 --- a/src/tools/wasm-reduce.cpp +++ b/src/tools/wasm-reduce.cpp @@ -483,7 +483,7 @@ struct Reducer auto* save = curr; Unreachable un; Nop nop; - bool useUnreachable = getFunction()->sig.results != Type::none; + bool useUnreachable = getFunction()->getResults() != Type::none; if (useUnreachable) { replaceCurrent(&un); } else { @@ -977,7 +977,7 @@ struct Reducer auto* func = module->functions[0].get(); // We can't remove something that might have breaks to it. if (!func->imported() && !Properties::isNamedControlFlow(func->body)) { - auto funcSig = func->sig; + auto funcType = func->type; auto* funcBody = func->body; for (auto* child : ChildIterator(func->body)) { if (!(child->type.isConcrete() || child->type == Type::none)) { @@ -985,7 +985,7 @@ struct Reducer } // Try to replace the body with the child, fixing up the function // to accept it. - func->sig.results = child->type; + func->type = Signature(funcType.getSignature().params, child->type); func->body = child; if (writeAndTestReduction()) { // great, we succeeded! @@ -994,7 +994,7 @@ struct Reducer break; } // Undo. - func->sig = funcSig; + func->type = funcType; func->body = funcBody; } } diff --git a/src/tools/wasm2c-wrapper.h b/src/tools/wasm2c-wrapper.h index de235c35e..ae32ec744 100644 --- a/src/tools/wasm2c-wrapper.h +++ b/src/tools/wasm2c-wrapper.h @@ -27,7 +27,7 @@ namespace wasm { // Mangle a name in (hopefully) exactly the same way wasm2c does. -static std::string wasm2cMangle(Name name, Signature sig) { +inline std::string wasm2cMangle(Name name, Signature sig) { const char escapePrefix = 'Z'; std::string mangled = "Z_"; const char* original = name.str; @@ -78,7 +78,7 @@ static std::string wasm2cMangle(Name name, Signature sig) { return mangled; } -static std::string generateWasm2CWrapper(Module& wasm) { +inline std::string generateWasm2CWrapper(Module& wasm) { // First, emit implementations of the wasm's imports so that the wasm2c code // can call them. The names use wasm2c's name mangling. std::string ret = R"( @@ -169,7 +169,7 @@ int main(int argc, char** argv) { ret += std::string(" puts(\"[fuzz-exec] calling ") + exp->name.str + "\");\n"; - auto result = func->sig.results; + auto result = func->getResults(); // Emit the call itself. ret += " "; @@ -199,13 +199,13 @@ int main(int argc, char** argv) { ret += "(*"; // Emit the callee's name with wasm2c name mangling. - ret += wasm2cMangle(exp->name, func->sig); + ret += wasm2cMangle(exp->name, func->getSig()); ret += ")("; // Emit the parameters (all 0s, like the other wrappers). bool first = true; - for (const auto& param : func->sig.params) { + for (const auto& param : func->getParams()) { WASM_UNUSED(param); if (!first) { ret += ", "; diff --git a/src/tools/wasm2js.cpp b/src/tools/wasm2js.cpp index 06b24ea26..4736dee08 100644 --- a/src/tools/wasm2js.cpp +++ b/src/tools/wasm2js.cpp @@ -604,7 +604,7 @@ Expression* AssertionEmitter::parseInvoke(Builder& wasmBuilder, for (size_t i = 2; i < e.size(); ++i) { args.push_back(sexpBuilder.parseExpression(e[i])); } - Type type = module.getFunction(module.getExport(target)->value)->sig.results; + Type type = module.getFunction(module.getExport(target)->value)->getResults(); return wasmBuilder.makeCall(target, args, type); } @@ -658,7 +658,7 @@ Ref AssertionEmitter::emitAssertReturnFunc(Builder& wasmBuilder, std::unique_ptr<Function> testFunc( wasmBuilder.makeFunction(testFuncName, std::vector<NameType>{}, - body->type, + Signature(Type::none, body->type), std::vector<NameType>{}, body)); Ref jsFunc = processFunction(testFunc.get()); @@ -676,7 +676,7 @@ Ref AssertionEmitter::emitAssertReturnNanFunc(Builder& wasmBuilder, std::unique_ptr<Function> testFunc( wasmBuilder.makeFunction(testFuncName, std::vector<NameType>{}, - body->type, + Signature(Type::none, body->type), std::vector<NameType>{}, body)); Ref jsFunc = processFunction(testFunc.get()); @@ -695,7 +695,7 @@ Ref AssertionEmitter::emitAssertTrapFunc(Builder& wasmBuilder, std::unique_ptr<Function> exprFunc( wasmBuilder.makeFunction(innerFuncName, std::vector<NameType>{}, - expr->type, + Signature(Type::none, expr->type), std::vector<NameType>{}, expr)); IString expectedErr = e[2]->str(); @@ -729,7 +729,7 @@ Ref AssertionEmitter::emitInvokeFunc(Builder& wasmBuilder, std::unique_ptr<Function> testFunc( wasmBuilder.makeFunction(testFuncName, std::vector<NameType>{}, - body->type, + Signature(Type::none, body->type), std::vector<NameType>{}, body)); Ref jsFunc = processFunction(testFunc.get()); |