summaryrefslogtreecommitdiff
path: root/src/tools
diff options
context:
space:
mode:
authorThomas Lively <7121787+tlively@users.noreply.github.com>2021-07-01 01:56:23 +0000
committerGitHub <noreply@github.com>2021-06-30 18:56:23 -0700
commitca27f40a2f1070a16ee7c0efc18ff35d342d8027 (patch)
treeab0f2b1b731737bc409db21f677b97be16f67c0f /src/tools
parent10ef52d62468aec5762742930630e882dc5e5c0b (diff)
downloadbinaryen-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.h6
-rw-r--r--src/tools/fuzzing.h72
-rw-r--r--src/tools/js-wrapper.h10
-rw-r--r--src/tools/spec-wrapper.h4
-rw-r--r--src/tools/wasm-ctor-eval.cpp2
-rw-r--r--src/tools/wasm-reduce.cpp8
-rw-r--r--src/tools/wasm2c-wrapper.h10
-rw-r--r--src/tools/wasm2js.cpp10
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());