@@ -51,7 +51,7 @@ std::ostream& operator<<(std::ostream& o, const WasmException& exn);
5151
5252// Utilities
5353
54- extern Name WASM, RETURN_FLOW, NONCONSTANT_FLOW;
54+ extern Name WASM, RETURN_FLOW, RETURN_CALL_FLOW, NONCONSTANT_FLOW;
5555
5656// Stuff that flows around during executing expressions: a literal, or a change
5757// in control flow.
@@ -63,6 +63,8 @@ class Flow {
6363 Flow (Literals&& values) : values(std::move(values)) {}
6464 Flow (Name breakTo) : values(), breakTo(breakTo) {}
6565 Flow (Name breakTo, Literal value) : values{value}, breakTo(breakTo) {}
66+ Flow (Name breakTo, Literals&& values)
67+ : values(std::move(values)), breakTo(breakTo) {}
6668
6769 Literals values;
6870 Name breakTo; // if non-null, a break is going on
@@ -2997,32 +2999,33 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
29972999 Flow visitCall (Call* curr) {
29983000 NOTE_ENTER (" Call" );
29993001 NOTE_NAME (curr->target );
3002+ Name target = curr->target ;
30003003 Literals arguments;
30013004 Flow flow = self ()->generateArguments (curr->operands , arguments);
30023005 if (flow.breaking ()) {
30033006 return flow;
30043007 }
30053008 auto * func = wasm.getFunction (curr->target );
3006- Flow ret ;
3009+ auto funcType = func-> type ;
30073010 if (Intrinsics (*self ()->getModule ()).isCallWithoutEffects (func)) {
30083011 // The call.without.effects intrinsic is a call to an import that actually
30093012 // calls the given function reference that is the final argument.
3010- auto newArguments = arguments;
3011- auto target = newArguments.back ();
3012- newArguments.pop_back ();
3013- ret.values = callFunctionInternal (target.getFunc (), newArguments);
3014- } else if (func->imported ()) {
3015- ret.values = externalInterface->callImport (func, arguments);
3016- } else {
3017- ret.values = callFunctionInternal (curr->target , arguments);
3013+ target = arguments.back ().getFunc ();
3014+ funcType = arguments.back ().type .getHeapType ();
3015+ arguments.pop_back ();
3016+ }
3017+
3018+ if (curr->isReturn ) {
3019+ // Return calls are represented by their arguments followed by a reference
3020+ // to the function to be called.
3021+ arguments.push_back (Literal::makeFunc (target, funcType));
3022+ return Flow (RETURN_CALL_FLOW, std::move (arguments));
30183023 }
3024+
3025+ Flow ret = callFunctionInternal (target, arguments);
30193026#ifdef WASM_INTERPRETER_DEBUG
30203027 std::cout << " (returned to " << scope->function ->name << " )\n " ;
30213028#endif
3022- // TODO: make this a proper tail call (return first)
3023- if (curr->isReturn ) {
3024- ret.breakTo = RETURN_FLOW;
3025- }
30263029 return ret;
30273030 }
30283031
@@ -3039,18 +3042,28 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
30393042 }
30403043
30413044 Index index = target.getSingleValue ().geti32 ();
3042- Type type = curr->isReturn ? scope->function ->getResults () : curr->type ;
30433045
30443046 auto info = getTableInterfaceInfo (curr->table );
3045- Flow ret = info.interface ->callTable (
3046- info.name , index, curr->heapType , arguments, type, *self ());
30473047
3048- // TODO: make this a proper tail call (return first)
30493048 if (curr->isReturn ) {
3050- ret.breakTo = RETURN_FLOW;
3049+ // Return calls are represented by their arguments followed by a reference
3050+ // to the function to be called.
3051+ auto funcref = info.interface ->tableLoad (info.name , index);
3052+ if (!Type::isSubType (funcref.type , Type (curr->heapType , NonNullable))) {
3053+ trap (" cast failure in call_indirect" );
3054+ }
3055+ arguments.push_back (funcref);
3056+ return Flow (RETURN_CALL_FLOW, std::move (arguments));
30513057 }
3058+
3059+ Flow ret = info.interface ->callTable (
3060+ info.name , index, curr->heapType , arguments, curr->type , *self ());
3061+ #ifdef WASM_INTERPRETER_DEBUG
3062+ std::cout << " (returned to " << scope->function ->name << " )\n " ;
3063+ #endif
30523064 return ret;
30533065 }
3066+
30543067 Flow visitCallRef (CallRef* curr) {
30553068 NOTE_ENTER (" CallRef" );
30563069 Literals arguments;
@@ -3062,24 +3075,22 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
30623075 if (target.breaking ()) {
30633076 return target;
30643077 }
3065- if (target.getSingleValue ().isNull ()) {
3078+ auto targetRef = target.getSingleValue ();
3079+ if (targetRef.isNull ()) {
30663080 trap (" null target in call_ref" );
30673081 }
3068- Name funcName = target.getSingleValue ().getFunc ();
3069- auto * func = wasm.getFunction (funcName);
3070- Flow ret;
3071- if (func->imported ()) {
3072- ret.values = externalInterface->callImport (func, arguments);
3073- } else {
3074- ret.values = callFunctionInternal (funcName, arguments);
3082+
3083+ if (curr->isReturn ) {
3084+ // Return calls are represented by their arguments followed by a reference
3085+ // to the function to be called.
3086+ arguments.push_back (targetRef);
3087+ return Flow (RETURN_CALL_FLOW, std::move (arguments));
30753088 }
3089+
3090+ Flow ret = callFunctionInternal (targetRef.getFunc (), arguments);
30763091#ifdef WASM_INTERPRETER_DEBUG
30773092 std::cout << " (returned to " << scope->function ->name << " )\n " ;
30783093#endif
3079- // TODO: make this a proper tail call (return first)
3080- if (curr->isReturn ) {
3081- ret.breakTo = RETURN_FLOW;
3082- }
30833094 return ret;
30843095 }
30853096
@@ -4130,12 +4141,7 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
41304141
41314142 // Call a function, starting an invocation.
41324143 Literals callFunction (Name name, const Literals& arguments) {
4133- auto * func = wasm.getFunction (name);
4134- if (func->imported ()) {
4135- return externalInterface->callImport (func, arguments);
4136- }
4137-
4138- // if the last call ended in a jump up the stack, it might have left stuff
4144+ // If the last call ended in a jump up the stack, it might have left stuff
41394145 // for us to clean up here
41404146 callDepth = 0 ;
41414147 functionStack.clear ();
@@ -4144,47 +4150,79 @@ class ModuleRunnerBase : public ExpressionRunner<SubType> {
41444150
41454151 // Internal function call. Must be public so that callTable implementations
41464152 // can use it (refactor?)
4147- Literals callFunctionInternal (Name name, const Literals& arguments) {
4153+ Literals callFunctionInternal (Name name, Literals arguments) {
41484154 if (callDepth > maxDepth) {
41494155 externalInterface->trap (" stack limit" );
41504156 }
4151- auto previousCallDepth = callDepth;
4152- callDepth++;
4153- auto previousFunctionStackSize = functionStack.size ();
4154- functionStack.push_back (name);
41554157
4156- Function* function = wasm.getFunction (name);
4157- assert (function);
4158- FunctionScope scope (function, arguments, *self ());
4158+ Flow flow;
4159+ std::optional<Type> resultType;
4160+
4161+ // We may have to call multiple functions in the event of return calls.
4162+ while (true ) {
4163+ Function* function = wasm.getFunction (name);
4164+ assert (function);
4165+
4166+ // Return calls can only make the result type more precise.
4167+ if (resultType) {
4168+ assert (Type::isSubType (function->getResults (), *resultType));
4169+ }
4170+ resultType = function->getResults ();
4171+
4172+ if (function->imported ()) {
4173+ // TODO: Allow imported functions to tail call as well.
4174+ return externalInterface->callImport (function, arguments);
4175+ }
4176+
4177+ auto previousCallDepth = callDepth;
4178+ callDepth++;
4179+ auto previousFunctionStackSize = functionStack.size ();
4180+ functionStack.push_back (name);
4181+
4182+ FunctionScope scope (function, arguments, *self ());
41594183
41604184#ifdef WASM_INTERPRETER_DEBUG
4161- std::cout << " entering " << function->name << " \n with arguments:\n " ;
4162- for (unsigned i = 0 ; i < arguments.size (); ++i) {
4163- std::cout << " $" << i << " : " << arguments[i] << ' \n ' ;
4164- }
4185+ std::cout << " entering " << function->name << " \n with arguments:\n " ;
4186+ for (unsigned i = 0 ; i < arguments.size (); ++i) {
4187+ std::cout << " $" << i << " : " << arguments[i] << ' \n ' ;
4188+ }
4189+ #endif
4190+
4191+ flow = self ()->visit (function->body );
4192+
4193+ // may decrease more than one, if we jumped up the stack
4194+ callDepth = previousCallDepth;
4195+ // if we jumped up the stack, we also need to pop higher frames
4196+ // TODO can FunctionScope handle this automatically?
4197+ while (functionStack.size () > previousFunctionStackSize) {
4198+ functionStack.pop_back ();
4199+ }
4200+ #ifdef WASM_INTERPRETER_DEBUG
4201+ std::cout << " exiting " << function->name << " with " << flow.values
4202+ << ' \n ' ;
41654203#endif
41664204
4167- Flow flow = self ()->visit (function->body );
4205+ if (flow.breakTo != RETURN_CALL_FLOW) {
4206+ break ;
4207+ }
4208+
4209+ // There was a return call, so we need to call the next function before
4210+ // returning to the caller. The flow carries the function arguments and a
4211+ // function reference.
4212+ name = flow.values .back ().getFunc ();
4213+ flow.values .pop_back ();
4214+ arguments = flow.values ;
4215+ }
4216+
41684217 // cannot still be breaking, it means we missed our stop
41694218 assert (!flow.breaking () || flow.breakTo == RETURN_FLOW);
41704219 auto type = flow.getType ();
4171- if (!Type::isSubType (type, function->getResults ())) {
4172- std::cerr << " calling " << function->name << " resulted in " << type
4173- << " but the function type is " << function->getResults ()
4174- << ' \n ' ;
4220+ if (!Type::isSubType (type, *resultType)) {
4221+ std::cerr << " calling " << name << " resulted in " << type
4222+ << " but the function type is " << *resultType << ' \n ' ;
41754223 WASM_UNREACHABLE (" unexpected result type" );
41764224 }
4177- // may decrease more than one, if we jumped up the stack
4178- callDepth = previousCallDepth;
4179- // if we jumped up the stack, we also need to pop higher frames
4180- // TODO can FunctionScope handle this automatically?
4181- while (functionStack.size () > previousFunctionStackSize) {
4182- functionStack.pop_back ();
4183- }
4184- #ifdef WASM_INTERPRETER_DEBUG
4185- std::cout << " exiting " << function->name << " with " << flow.values
4186- << ' \n ' ;
4187- #endif
4225+
41884226 return flow.values ;
41894227 }
41904228
0 commit comments