@@ -94,6 +94,7 @@ using DAEFunctionInfoMap = std::unordered_map<Name, DAEFunctionInfo>;
9494struct DAEScanner
9595 : public WalkerPass<PostWalker<DAEScanner, Visitor<DAEScanner>>> {
9696 bool isFunctionParallel () override { return true ; }
97+ bool modifiesBinaryenIR () override { return false ; }
9798
9899 std::unique_ptr<Pass> create () override {
99100 return std::make_unique<DAEScanner>(infoMap);
@@ -188,6 +189,11 @@ struct DAE : public Pass {
188189
189190 bool optimize = false ;
190191
192+ Index numFunctions;
193+
194+ // Map of function names to indexes. This lets us use indexes below for speed.
195+ std::unordered_map<Name, Index> indexes;
196+
191197 void run (Module* module ) override {
192198 DAEFunctionInfoMap infoMap;
193199 // Ensure all entries exist so the parallel threads don't modify the data
@@ -198,6 +204,12 @@ struct DAE : public Pass {
198204 // The null name represents module-level code (not in a function).
199205 infoMap[Name ()];
200206
207+ numFunctions = module ->functions .size ();
208+
209+ for (Index i = 0 ; i < numFunctions; i++) {
210+ indexes[module ->functions [i]->name ] = i;
211+ }
212+
201213 // Iterate to convergence.
202214 while (1 ) {
203215 if (!iteration (module , infoMap)) {
@@ -233,34 +245,36 @@ struct DAE : public Pass {
233245 Call* call;
234246 Function* func;
235247 };
236- std::map<Name, std::vector<Call*>> allCalls;
237- std::unordered_set<Name> tailCallees;
238- std::unordered_set<Name> hasUnseenCalls;
248+
249+ std::vector<std::vector<Call*>> allCalls (numFunctions);
250+ std::vector<bool > tailCallees (numFunctions);
251+ std::vector<bool > hasUnseenCalls (numFunctions);
252+
239253 // Track the function in which relevant expressions exist. When we modify
240254 // those expressions we will need to mark the function's info as stale.
241255 std::unordered_map<Expression*, Name> expressionFuncs;
242256 for (auto & [func, info] : infoMap) {
243257 for (auto & [name, calls] : info.calls ) {
244- auto & allCallsToName = allCalls[name];
258+ auto & allCallsToName = allCalls[indexes[ name] ];
245259 allCallsToName.insert (allCallsToName.end (), calls.begin (), calls.end ());
246260 for (auto * call : calls) {
247261 expressionFuncs[call] = func;
248262 }
249263 }
250264 for (auto & callee : info.tailCallees ) {
251- tailCallees. insert ( callee) ;
265+ tailCallees[indexes[ callee]] = true ;
252266 }
253267 for (auto & [call, dropp] : info.droppedCalls ) {
254268 allDroppedCalls[call] = dropp;
255269 }
256270 for (auto & name : info.hasUnseenCalls ) {
257- hasUnseenCalls. insert ( name) ;
271+ hasUnseenCalls[indexes[ name]] = true ;
258272 }
259273 }
260274 // Exports are considered unseen calls.
261275 for (auto & curr : module ->exports ) {
262276 if (curr->kind == ExternalKind::Function) {
263- hasUnseenCalls. insert ( *curr->getInternalName ()) ;
277+ hasUnseenCalls[indexes[ *curr->getInternalName ()]] = true ;
264278 }
265279 }
266280
@@ -299,23 +313,32 @@ struct DAE : public Pass {
299313
300314 // We now have a mapping of all call sites for each function, and can look
301315 // for optimization opportunities.
302- for (auto & [name, calls] : allCalls) {
316+ for (Index index = 0 ; index < numFunctions; index++) {
317+ auto * func = module ->functions [index].get ();
318+ if (func->imported ()) {
319+ continue ;
320+ }
303321 // We can only optimize if we see all the calls and can modify them.
304- if (hasUnseenCalls.count (name)) {
322+ if (hasUnseenCalls[index]) {
323+ continue ;
324+ }
325+ auto & calls = allCalls[index];
326+ if (calls.empty ()) {
327+ // Nothing calls this, so it is not worth optimizing.
305328 continue ;
306329 }
307- auto * func = module ->getFunction (name);
308330 // Refine argument types before doing anything else. This does not
309331 // affect whether an argument is used or not, it just refines the type
310332 // where possible.
333+ auto name = func->name ;
311334 if (refineArgumentTypes (func, calls, module , infoMap[name])) {
312335 worthOptimizing.insert (func);
313336 markStale (func->name );
314337 }
315338 // Refine return types as well.
316339 if (refineReturnTypes (func, calls, module )) {
317340 refinedReturnTypes = true ;
318- markStale (func-> name );
341+ markStale (name);
319342 markCallersStale (calls);
320343 }
321344 auto optimizedIndexes =
@@ -336,21 +359,29 @@ struct DAE : public Pass {
336359 ReFinalize ().run (getPassRunner (), module );
337360 }
338361 // We now know which parameters are unused, and can potentially remove them.
339- for (auto & [name, calls] : allCalls) {
340- if (hasUnseenCalls.count (name)) {
362+ for (Index index = 0 ; index < numFunctions; index++) {
363+ auto * func = module ->functions [index].get ();
364+ if (func->imported ()) {
365+ continue ;
366+ }
367+ if (hasUnseenCalls[index]) {
341368 continue ;
342369 }
343- auto * func = module ->getFunction (name);
344370 auto numParams = func->getNumParams ();
345371 if (numParams == 0 ) {
346372 continue ;
347373 }
374+ auto & calls = allCalls[index];
375+ if (calls.empty ()) {
376+ continue ;
377+ }
378+ auto name = func->name ;
348379 auto [removedIndexes, outcome] = ParamUtils::removeParameters (
349380 {func}, infoMap[name].unusedParams , calls, {}, module , getPassRunner ());
350381 if (!removedIndexes.empty ()) {
351382 // Success!
352383 worthOptimizing.insert (func);
353- markStale (func-> name );
384+ markStale (name);
354385 markCallersStale (calls);
355386 }
356387 if (outcome == ParamUtils::RemovalOutcome::Failure) {
@@ -362,25 +393,28 @@ struct DAE : public Pass {
362393 // modified allCalls (we can't modify a call site twice in one iteration,
363394 // once to remove a param, once to drop the return value).
364395 if (worthOptimizing.empty ()) {
365- for (auto & func : module ->functions ) {
396+ for (Index index = 0 ; index < numFunctions; index++) {
397+ auto & func = module ->functions [index];
398+ if (func->imported ()) {
399+ continue ;
400+ }
366401 if (func->getResults () == Type::none) {
367402 continue ;
368403 }
369- auto name = func->name ;
370- if (hasUnseenCalls.count (name)) {
404+ if (hasUnseenCalls[index]) {
371405 continue ;
372406 }
407+ auto name = func->name ;
373408 if (infoMap[name].hasTailCalls ) {
374409 continue ;
375410 }
376- if (tailCallees. count (name) ) {
411+ if (tailCallees[index] ) {
377412 continue ;
378413 }
379- auto iter = allCalls. find (name) ;
380- if (iter == allCalls. end ()) {
414+ auto & calls = allCalls[index] ;
415+ if (calls. empty ()) {
381416 continue ;
382417 }
383- auto & calls = iter->second ;
384418 bool allDropped =
385419 std::all_of (calls.begin (), calls.end (), [&](Call* call) {
386420 return allDroppedCalls.count (call);
@@ -397,7 +431,7 @@ struct DAE : public Pass {
397431 // TODO Removing a drop may also open optimization opportunities in the
398432 // callers.
399433 worthOptimizing.insert (func.get ());
400- markStale (func-> name );
434+ markStale (name);
401435 markCallersStale (calls);
402436 }
403437 }
0 commit comments