2525#include < memory>
2626
2727#include " asmjs/shared-constants.h"
28+ #include " ir/find_all.h"
2829#include " ir/gc-type-utils.h"
2930#include " ir/global-utils.h"
3031#include " ir/import-utils.h"
@@ -1061,40 +1062,45 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance,
10611062 params.push_back (Literal::makeZero (type));
10621063 }
10631064
1064- // We want to handle the form of the global constructor function in LLVM. That
1065- // looks like this:
1066- //
1067- // (func $__wasm_call_ctors
1068- // (call $ctor.1)
1069- // (call $ctor.2)
1070- // (call $ctor.3)
1071- // )
1072- //
1073- // Some of those ctors may be inlined, however, which would mean that the
1074- // function could have locals, control flow, etc. However, we assume for now
1075- // that it does not have parameters at least (whose values we can't tell).
1076- // And for now we look for a toplevel block and process its children one at a
1077- // time. This allows us to eval some of the $ctor.* functions (or their
1078- // inlined contents) even if not all.
1079- //
1080- // TODO: Support complete partial evalling, that is, evaluate parts of an
1081- // arbitrary function, and not just a sequence in a single toplevel
1082- // block.
1065+ // After we successfully eval a line we will store the operations to set up
1066+ // the locals here. That is, we need to save the local state in the function,
1067+ // which we do by setting up at the entry. We update this list of expressions
1068+ // at the same time as applyToModule() - we must only do it after an entire
1069+ // atomic "chunk" has been processed succesfully, we do not want partial
1070+ // updates from an item in the block that we only partially evalled. When we
1071+ // construct the (partially) evalled function, we will create local.sets of
1072+ // these expressions at the beginning.
1073+ std::vector<Expression*> localExprs;
1074+
1075+ // We might have to evaluate multiple functions due to return calls.
1076+ start_eval:
1077+ while (true ) {
1078+ // We want to handle the form of the global constructor function in LLVM.
1079+ // That looks like this:
1080+ //
1081+ // (func $__wasm_call_ctors
1082+ // (call $ctor.1)
1083+ // (call $ctor.2)
1084+ // (call $ctor.3)
1085+ // )
1086+ //
1087+ // Some of those ctors may be inlined, however, which would mean that the
1088+ // function could have locals, control flow, etc. However, we assume for now
1089+ // that it does not have parameters at least (whose values we can't tell).
1090+ // And for now we look for a toplevel block and process its children one at
1091+ // a time. This allows us to eval some of the $ctor.* functions (or their
1092+ // inlined contents) even if not all.
1093+ //
1094+ // TODO: Support complete partial evalling, that is, evaluate parts of an
1095+ // arbitrary function, and not just a sequence in a single toplevel
1096+ // block.
1097+ Builder builder (wasm);
1098+ auto * block = builder.blockify (func->body );
10831099
1084- if (auto * block = func->body ->dynCast <Block>()) {
10851100 // Go through the items in the block and try to execute them. We do all this
10861101 // in a single function scope for all the executions.
10871102 EvallingModuleRunner::FunctionScope scope (func, params, instance);
10881103
1089- // After we successfully eval a line we will store the operations to set up
1090- // the locals here. That is, we need to save the local state in the
1091- // function, which we do by setting up at the entry. We update this list of
1092- // local.sets at the same time as applyToModule() - we must only do it after
1093- // an entire atomic "chunk" has been processed succesfully, we do not want
1094- // partial updates from an item in the block that we only partially evalled.
1095- std::vector<Expression*> localSets;
1096-
1097- Builder builder (wasm);
10981104 Literals results;
10991105 Index successes = 0 ;
11001106
@@ -1116,6 +1122,22 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance,
11161122 break ;
11171123 }
11181124
1125+ if (flow.breakTo == RETURN_CALL_FLOW) {
1126+ // The return-called function is stored in the last value.
1127+ func = wasm.getFunction (flow.values .back ().getFunc ());
1128+ flow.values .pop_back ();
1129+ params = std::move (flow.values );
1130+
1131+ // Serialize the arguments for the new function and save the module
1132+ // state in case we fail to eval the new function.
1133+ localExprs.clear ();
1134+ for (auto & param : params) {
1135+ localExprs.push_back (interface.getSerialization (param));
1136+ }
1137+ interface.applyToModule ();
1138+ goto start_eval;
1139+ }
1140+
11191141 // So far so good! Serialize the values of locals, and apply to the
11201142 // module. Note that we must serialize the locals now as doing so may
11211143 // cause changes that must be applied to the module (e.g. GC data may
@@ -1128,11 +1150,9 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance,
11281150 // of them, and leave it to the optimizer to remove redundant or
11291151 // unnecessary operations. We just recompute the entire local
11301152 // serialization sets from scratch each time here, for all locals.
1131- localSets .clear ();
1153+ localExprs .clear ();
11321154 for (Index i = 0 ; i < func->getNumLocals (); i++) {
1133- auto value = scope.locals [i];
1134- localSets.push_back (
1135- builder.makeLocalSet (i, interface.getSerialization (value)));
1155+ localExprs.push_back (interface.getSerialization (scope.locals [i]));
11361156 }
11371157 interface.applyToModule ();
11381158 successes++;
@@ -1144,41 +1164,92 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance,
11441164 if (flow.breaking ()) {
11451165 // We are returning out of the function (either via a return, or via a
11461166 // break to |block|, which has the same outcome. That means we don't
1147- // need to execute any more lines, and can consider them to be executed.
1167+ // need to execute any more lines, and can consider them to be
1168+ // executed.
11481169 if (!quiet) {
11491170 std::cout << " ...stopping in block due to break\n " ;
11501171 }
11511172
11521173 // Mark us as having succeeded on the entire block, since we have: we
1153- // are skipping the rest, which means there is no problem there. We must
1154- // set this here so that lower down we realize that we've evalled
1174+ // are skipping the rest, which means there is no problem there. We
1175+ // must set this here so that lower down we realize that we've evalled
11551176 // everything.
11561177 successes = block->list .size ();
11571178 break ;
11581179 }
11591180 }
11601181
1161- if (successes > 0 && successes < block->list .size ()) {
1162- // We managed to eval some but not all. That means we can't just remove
1163- // the entire function, but need to keep parts of it - the parts we have
1164- // not evalled - around. To do so, we create a copy of the function with
1165- // the partially-evalled contents and make the export use that (as the
1166- // function may be used in other places than the export, which we do not
1167- // want to affect).
1182+ if ((localExprs.size () && func->getParams () != Type::none) ||
1183+ func->name != funcName ||
1184+ (successes > 0 && successes < block->list .size ())) {
1185+ auto originalFuncType = wasm.getFunction (funcName)->type ;
11681186 auto copyName = Names::getValidFunctionName (wasm, funcName);
1169- auto * copyFunc = ModuleUtils::copyFunction (func, wasm, copyName);
11701187 wasm.getExport (exportName)->value = copyName;
11711188
1189+ if (func->imported ()) {
1190+ // We must have return-called this imported function. Generate a new
1191+ // function that return-calls the import with the arguments we have
1192+ // evalled.
1193+ auto copyFunc = builder.makeFunction (
1194+ copyName,
1195+ originalFuncType,
1196+ {},
1197+ builder.makeCall (func->name , localExprs, func->getResults (), true ));
1198+ wasm.addFunction (std::move (copyFunc));
1199+ return EvalCtorOutcome ();
1200+ }
1201+
1202+ // We may have managed to eval some but not all. That means we can't just
1203+ // remove the entire function, but need to keep parts of it - the parts we
1204+ // have not evalled - around. To do so, we create a copy of the function
1205+ // with the partially-evalled contents and make the export use that (as
1206+ // the function may be used in other places than the export, which we do
1207+ // not want to affect).
1208+ auto * copyBody =
1209+ builder.blockify (ExpressionManipulator::copy (func->body , wasm));
1210+
11721211 // Remove the items we've evalled.
1173- auto * copyBlock = copyFunc->body ->cast <Block>();
11741212 for (Index i = 0 ; i < successes; i++) {
1175- copyBlock ->list [i] = builder.makeNop ();
1213+ copyBody ->list [i] = builder.makeNop ();
11761214 }
11771215
1178- // Put the local sets at the front of the block. We know there must be a
1179- // nop in that position (since we've evalled at least one item in the
1180- // block, and replaced it with a nop), so we can overwrite it.
1181- copyBlock->list [0 ] = builder.makeBlock (localSets);
1216+ // Put the local sets at the front of the function body.
1217+ auto * setsBlock = builder.makeBlock ();
1218+ for (Index i = 0 ; i < localExprs.size (); ++i) {
1219+ setsBlock->list .push_back (builder.makeLocalSet (i, localExprs[i]));
1220+ }
1221+ copyBody = builder.makeSequence (setsBlock, copyBody, copyBody->type );
1222+
1223+ // We may have return-called into a function with different parameter
1224+ // types, but we ultimately need to export a function with the original
1225+ // signature. If there is a mismatch, shift the local indices to make room
1226+ // for the unused parameters.
1227+ std::vector<Type> localTypes;
1228+ auto originalParams = originalFuncType.getSignature ().params ;
1229+ if (originalParams != func->getParams ()) {
1230+ // Add locals for the body to use instead of using the params.
1231+ for (auto type : func->getParams ()) {
1232+ localTypes.push_back (type);
1233+ }
1234+
1235+ // Shift indices in the body so they will refer to the new locals.
1236+ auto localShift = originalParams.size ();
1237+ if (localShift != 0 ) {
1238+ for (auto * get : FindAll<LocalGet>(copyBody).list ) {
1239+ get->index += localShift;
1240+ }
1241+ for (auto * set : FindAll<LocalSet>(copyBody).list ) {
1242+ set->index += localShift;
1243+ }
1244+ }
1245+ }
1246+
1247+ // Add vars from current function.
1248+ localTypes.insert (localTypes.end (), func->vars .begin (), func->vars .end ());
1249+
1250+ // Create and add the new function.
1251+ auto * copyFunc = wasm.addFunction (builder.makeFunction (
1252+ copyName, originalFuncType, std::move (localTypes), copyBody));
11821253
11831254 // Interesting optimizations may be possible both due to removing some but
11841255 // not all of the code, and due to the locals we just added.
@@ -1196,24 +1267,6 @@ EvalCtorOutcome evalCtor(EvallingModuleRunner& instance,
11961267 return EvalCtorOutcome ();
11971268 }
11981269 }
1199-
1200- // Otherwise, we don't recognize a pattern that allows us to do partial
1201- // evalling. So simply call the entire function at once and see if we can
1202- // optimize that.
1203-
1204- Literals results;
1205- try {
1206- results = instance.callFunction (funcName, params);
1207- } catch (FailToEvalException& fail) {
1208- if (!quiet) {
1209- std::cout << " ...stopping since could not eval: " << fail.why << " \n " ;
1210- }
1211- return EvalCtorOutcome ();
1212- }
1213-
1214- // Success! Apply the results.
1215- interface.applyToModule ();
1216- return EvalCtorOutcome (results);
12171270}
12181271
12191272// Eval all ctors in a module.
0 commit comments