From 3c2de4bd59361605c41a1f63139b870532888815 Mon Sep 17 00:00:00 2001 From: albertodemichelis Date: Fri, 17 Nov 2017 03:55:35 +0800 Subject: [PATCH] added sq_tailcall() --- include/squirrel.h | 1 + squirrel/sqapi.cpp | 19 +++++++++++++++++++ squirrel/sqbaselib.cpp | 7 ++++++- squirrel/sqvm.cpp | 40 ++++++++++++++++++++++++++++++++-------- squirrel/sqvm.h | 4 +++- 5 files changed, 61 insertions(+), 10 deletions(-) diff --git a/include/squirrel.h b/include/squirrel.h index 28d9102..d20ddb9 100644 --- a/include/squirrel.h +++ b/include/squirrel.h @@ -332,6 +332,7 @@ SQUIRREL_API SQRESULT sq_throwerror(HSQUIRRELVM v,const SQChar *err); SQUIRREL_API SQRESULT sq_throwobject(HSQUIRRELVM v); SQUIRREL_API void sq_reseterror(HSQUIRRELVM v); SQUIRREL_API void sq_getlasterror(HSQUIRRELVM v); +SQUIRREL_API SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams); /*raw object handling*/ SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po); diff --git a/squirrel/sqapi.cpp b/squirrel/sqapi.cpp index 21039fc..3e93520 100644 --- a/squirrel/sqapi.cpp +++ b/squirrel/sqapi.cpp @@ -1188,6 +1188,25 @@ SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool raiseerror) return sq_throwerror(v,_SC("call failed")); } +SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) +{ + SQObjectPtr &res = v->GetUp(-(nparams + 1)); + if (type(res) != OT_CLOSURE) { + return sq_throwerror(v, _SC("only closure can be tail called")); + } + SQClosure *clo = _closure(res); + if (clo->_function->_bgenerator) + { + return sq_throwerror(v, _SC("generators cannot be tail called")); + } + + SQInteger stackbase = (v->_top - nparams) - v->_stackbase; + if (!v->TailCall(clo, stackbase, nparams)) { + return SQ_ERROR; + } + return SQ_TAILCALL_FLAG; +} + SQRESULT sq_suspendvm(HSQUIRRELVM v) { return v->Suspend(); diff --git a/squirrel/sqbaselib.cpp b/squirrel/sqbaselib.cpp index 662aeac..37c7ab5 100644 --- a/squirrel/sqbaselib.cpp +++ b/squirrel/sqbaselib.cpp @@ -885,7 +885,12 @@ static SQInteger closure_pcall(HSQUIRRELVM v) static SQInteger closure_call(HSQUIRRELVM v) { - return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQTrue))?1:SQ_ERROR; + SQObjectPtr &c = stack_get(v, -1); + if (type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) + { + return sq_tailcall(v, sq_gettop(v) - 1); + } + return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; } static SQInteger _closure_acall(HSQUIRRELVM v,SQBool raiseerror) diff --git a/squirrel/sqvm.cpp b/squirrel/sqvm.cpp index b20df9a..9e23f16 100644 --- a/squirrel/sqvm.cpp +++ b/squirrel/sqvm.cpp @@ -744,7 +744,8 @@ exception_restore: continue; case OT_NATIVECLOSURE: { bool suspend; - _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo,suspend)); + bool tailcall; + _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo, (SQInt32)sarg0, suspend, tailcall)); if(suspend){ _suspended = SQTrue; _suspended_target = sarg0; @@ -753,7 +754,7 @@ exception_restore: outres = clo; return true; } - if(sarg0 != -1) { + if(sarg0 != -1 && !tailcall) { STK(arg0) = clo; } } @@ -772,10 +773,10 @@ exception_restore: _GUARD(StartCall(_closure(clo), -1, arg3, stkbase, false)); break; case OT_NATIVECLOSURE: - bool suspend; + bool dummy; stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; - _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo,suspend)); + _GUARD(CallNative(_nativeclosure(clo), arg3, stkbase, clo, -1, dummy, dummy)); break; default: break; //shutup GCC 4.x } @@ -1139,7 +1140,7 @@ void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) _debughook = true; } -bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, bool &suspend) +bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target,bool &suspend, bool &tailcall) { SQInteger nparamscheck = nclosure->_nparamscheck; SQInteger newtop = newbase + nargs + nclosure->_noutervalues; @@ -1169,6 +1170,7 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb if(!EnterFrame(newbase, newtop, false)) return false; ci->_closure = nclosure; + ci->_target = target; SQInteger outers = nclosure->_noutervalues; for (SQInteger i = 0; i < outers; i++) { @@ -1183,7 +1185,12 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb _nnativecalls--; suspend = false; - if (ret == SQ_SUSPEND_FLAG) { + tailcall = false; + if (ret == SQ_TAILCALL_FLAG) { + tailcall = true; + return true; + } + else if (ret == SQ_SUSPEND_FLAG) { suspend = true; } else if (ret < 0) { @@ -1202,6 +1209,23 @@ bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newb return true; } +bool SQVM::TailCall(SQClosure *closure, SQInteger parambase,SQInteger nparams) +{ + SQInteger last_top = _top; + SQObjectPtr clo = closure; + if (ci->_root) + { + Raise_Error("root calls cannot invoke tailcalls"); + return false; + } + for (SQInteger i = 0; i < nparams; i++) STK(i) = STK(parambase + i); + bool ret = StartCall(closure, ci->_target, nparams, _stackbase, true); + if (last_top >= _top) { + _top = last_top; + } + return ret; +} + #define FALLBACK_OK 0 #define FALLBACK_NO_MATCH 1 #define FALLBACK_ERROR 2 @@ -1551,8 +1575,8 @@ SQInteger prevstackbase = _stackbase; return Execute(closure, nparams, stackbase, outres, raiseerror); break; case OT_NATIVECLOSURE:{ - bool suspend; - return CallNative(_nativeclosure(closure), nparams, stackbase, outres,suspend); + bool dummy; + return CallNative(_nativeclosure(closure), nparams, stackbase, outres, -1, dummy, dummy); } break; diff --git a/squirrel/sqvm.h b/squirrel/sqvm.h index 35fef5b..a75524d 100644 --- a/squirrel/sqvm.h +++ b/squirrel/sqvm.h @@ -8,6 +8,7 @@ #define MIN_STACK_OVERHEAD 15 #define SQ_SUSPEND_FLAG -666 +#define SQ_TAILCALL_FLAG -777 #define DONT_FALL_BACK 666 //#define EXISTS_FALL_BACK -1 @@ -56,7 +57,8 @@ public: bool Init(SQVM *friendvm, SQInteger stacksize); bool Execute(SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool raiseerror, ExecutionType et = ET_CALL); //starts a native call return when the NATIVE closure returns - bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval,bool &suspend); + bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); + bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); //starts a SQUIRREL call in the same "Execution loop" bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor);