From f7547064e38bcc8518d6ebf1a2de0d7a76d6ad15 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Wed, 18 Nov 2020 13:20:20 -0800 Subject: [DeadArgumentElimination] Don't DAE a ref.func-ed class (#3380) If we take a reference of a function, it is dangerous to change the function's type (which removing dead arguments does), as that would be an observable different from the outside - the type changes, and some params are now ignored, and others are reordered. In theory we could find out if the reference does not escape, but that's not trivial. Related to #3378 but not quite the same. --- src/passes/DeadArgumentElimination.cpp | 41 ++++++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/passes/DeadArgumentElimination.cpp b/src/passes/DeadArgumentElimination.cpp index a1b99d2cf..89d03f461 100644 --- a/src/passes/DeadArgumentElimination.cpp +++ b/src/passes/DeadArgumentElimination.cpp @@ -72,7 +72,11 @@ struct DAEFunctionInfo { // see inhibits our optimizations, but TODO: an export // could be worked around by exporting a thunk that // adds the parameter. - bool hasUnseenCalls = false; + // This is atomic so that we can write to it from any function at any time + // during the parallel analysis phase which is run in DAEScanner. + std::atomic hasUnseenCalls; + + DAEFunctionInfo() { hasUnseenCalls = false; } }; typedef std::unordered_map DAEFunctionInfoMap; @@ -145,6 +149,17 @@ struct DAEScanner } } + void visitRefFunc(RefFunc* curr) { + // We can't modify another function in parallel. + assert((*infoMap).count(curr->func)); + // Treat a ref.func as an unseen call, preventing us from changing the + // function's type. If we did change it, it could be an observable + // difference from the outside, if the reference escapes, for example. + // TODO: look for actual escaping? + // TODO: create a thunk for external uses that allow internal optimizations + (*infoMap)[curr->func].hasUnseenCalls = true; + } + // main entry point void doWalkFunction(Function* func) { @@ -152,14 +167,22 @@ struct DAEScanner info = &((*infoMap)[func->name]); CFGWalker, DAEBlockInfo>::doWalkFunction( func); - // If there are relevant params, check if they are used. (If - // we can't optimize the function anyhow, there's no point.) + // If there are relevant params, check if they are used. If we can't + // optimize the function anyhow, there's no point (note that our check here + // is technically racy - another thread could update hasUnseenCalls to true + // around when we check it - but that just means that we might or might not + // do some extra work, as we'll ignore the results later if we have unseen + // calls. That is, the check for hasUnseenCalls here is just a minor + // optimization to avoid pointless work. We can avoid that work if either + // we know there is an unseen call before the parallel analysis that we are + // part of, say if we are exported, or if another parallel function finds a + // RefFunc to us and updates it before we check it). if (numParams > 0 && !info->hasUnseenCalls) { - findUnusedParams(func); + findUnusedParams(); } } - void findUnusedParams(Function* func) { + void findUnusedParams() { // Flow the incoming parameter values, see if they reach a read. // Once we've seen a parameter at a block, we need never consider it there // again. @@ -242,8 +265,9 @@ struct DAE : public Pass { DAEFunctionInfoMap infoMap; // Ensure they all exist so the parallel threads don't modify the data // structure. - ModuleUtils::iterDefinedFunctions( - *module, [&](Function* func) { infoMap[func->name]; }); + for (auto& func : module->functions) { + infoMap[func->name]; + } // Check the influence of the table and exports. for (auto& curr : module->exports) { if (curr->kind == ExternalKind::Function) { @@ -326,6 +350,9 @@ struct DAE : public Pass { for (auto& pair : allCalls) { auto name = pair.first; auto& calls = pair.second; + if (infoMap[name].hasUnseenCalls) { + continue; + } auto* func = module->getFunction(name); auto numParams = func->getNumParams(); if (numParams == 0) { -- cgit v1.2.3