diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 0a02e2763..a1350086b 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -276,7 +276,7 @@ static void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, static void FinishThingdef() { int errorcount = 0; - unsigned i, j; + unsigned i; int codesize = 0; #if 1 @@ -289,34 +289,26 @@ static void FinishThingdef() FStateTempCall *tcall = StateTempCalls[i]; VMFunction *func; - assert(tcall->Function != NULL); - if (tcall->Parameters.Size() == 0) + assert(tcall->Call != NULL); + if (tcall->Call->GetArgCount() == 0) { - func = tcall->Function; + // There are no arguments, so we can call this function directly + // without wrapping it in an anonymous function. + func = tcall->Call->GetVMFunction(); } else { FCompileContext ctx(tcall->ActorClass); - for (j = 0; j < tcall->Parameters.Size(); ++j) - { - tcall->Parameters[j]->Resolve(ctx); - } + tcall->Call->Resolve(ctx); VMFunctionBuilder buildit; + // Allocate registers used to pass parameters in. // self, stateowner, state (all are pointers) buildit.Registers[REGT_POINTER].Get(3); - // Emit code to pass the standard action function parameters. - buildit.Emit(OP_PARAM, 0, REGT_POINTER, 0); - buildit.Emit(OP_PARAM, 0, REGT_POINTER, 1); - buildit.Emit(OP_PARAM, 0, REGT_POINTER, 2); - // Emit code for action parameters. - for (j = 0; j < tcall->Parameters.Size(); ++j) - { - FxExpression *p = /*new FxParameter*/(tcall->Parameters[j]); - p->Emit(&buildit); - delete p; - } - buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(tcall->Function, ATAG_OBJECT), NAP + j, 0); + + // Emit a tail call via FxVMFunctionCall + tcall->Call->Emit(&buildit, true); + VMScriptFunction *sfunc = buildit.MakeFunction(); sfunc->NumArgs = NAP; func = sfunc; @@ -330,6 +322,8 @@ static void FinishThingdef() codesize += sfunc->CodeSize; } } + delete tcall->Call; + tcall->Call = NULL; for (int k = 0; k < tcall->NumStates; ++k) { tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func); diff --git a/src/thingdef/thingdef.h b/src/thingdef/thingdef.h index 90dd83750..de8d524a1 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -119,11 +119,10 @@ public: struct FStateTempCall { - FStateTempCall() : ActorClass(NULL), Function(NULL), FirstState(0), NumStates(0) {} + FStateTempCall() : ActorClass(NULL), Call(NULL), FirstState(0), NumStates(0) {} PClassActor *ActorClass; - VMFunction *Function; - TArray Parameters; + class FxVMFunctionCall *Call; int FirstState; int NumStates; }; diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 8d3c162df..8608557ef 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -798,7 +798,10 @@ public: FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos); ~FxVMFunctionCall(); FxExpression *Resolve(FCompileContext&); - ExpEmit Emit(VMFunctionBuilder *build); + virtual ExpEmit Emit(VMFunctionBuilder *build); + ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); + unsigned GetArgCount() { return ArgList == NULL ? 0 : ArgList->Size(); } + VMFunction *GetVMFunction() { return Function->Variants[0].Implementation; } }; //========================================================================== diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 5fec0cf45..af6f7b52a 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -3053,17 +3053,23 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) return NULL; } TArray &rets = Function->Variants[0].Proto->ReturnTypes; - assert(rets.Size() == 1); - ReturnType = rets[0]; - // If more types are added to ParseNativeVariable(), add them here too. - if (rets[0] == TypeSInt32) ValueType = VAL_Int; - else if (rets[0] == TypeFloat64) ValueType = VAL_Float; - else if (rets[0] == TypeAngle) ValueType = VAL_Angle; - else if (rets[0] == TypeFixed) ValueType = VAL_Fixed; + if (rets.Size() == NULL) + { + ReturnType = TypeVoid; + } else { - ValueType = VAL_Int; - assert(0 && "Unhandled return type in FxVMFunctionCall::Resolve"); + ReturnType = rets[0]; + // If more types are added to ParseNativeVariable(), add them here too. + if (rets[0] == TypeSInt32) ValueType = VAL_Int; + else if (rets[0] == TypeFloat64) ValueType = VAL_Float; + else if (rets[0] == TypeAngle) ValueType = VAL_Angle; + else if (rets[0] == TypeFixed) ValueType = VAL_Fixed; + else + { + ValueType = VAL_Int; + assert(0 && "Unhandled return type in FxVMFunctionCall::Resolve"); + } } return this; } @@ -3077,6 +3083,11 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) //========================================================================== ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) +{ + return Emit(build, false); +} + +ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) { assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); int count = ArgList->Size(); @@ -3098,13 +3109,27 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build) { (*ArgList)[i]->Emit(build); } - // Get a register to store the return value in - assert(ReturnType != NULL); - ExpEmit reg(build, ReturnType->GetRegType()); - // Emit the call itself - build->Emit(OP_CALL_K, build->GetConstantAddress(Function->Variants[0].Implementation, ATAG_OBJECT), count, 1); - build->Emit(OP_RESULT, 0, reg.RegType, reg.RegNum); - return reg; + // Get a constant register for this function + int funcaddr = build->GetConstantAddress(Function->Variants[0].Implementation, ATAG_OBJECT); + // Emit the call + if (tailcall) + { // Tail call + build->Emit(OP_TAIL_K, funcaddr, count, 0); + return ExpEmit(); + } + else if (ReturnType != TypeVoid) + { // Call, expecting one result + assert(ReturnType != NULL); + ExpEmit reg(build, ReturnType->GetRegType()); + build->Emit(OP_CALL_K, funcaddr, count, 1); + build->Emit(OP_RESULT, 0, reg.RegType, reg.RegNum); + return reg; + } + else + { // Call, expecting no results + build->Emit(OP_CALL_K, funcaddr, count, 0); + return ExpEmit(); + } } //========================================================================== diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 041d3790e..d1c1f8209 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -72,24 +72,25 @@ bool DoActionSpecials(FScanner &sc, FState & state, Baggage &bag, FStateTempCall int min_args, max_args; FString specname = sc.String; + FArgumentList *args = new FArgumentList; int special = P_FindLineSpecial(sc.String, &min_args, &max_args); if (special > 0 && min_args >= 0) { - tcall->Parameters.Push(new FxParameter(new FxConstant(special, sc))); + args->Push(new FxParameter(new FxConstant(special, sc))); + i = 0; // Make this consistent with all other parameter parsing if (sc.CheckToken('(')) { - for (i = 0; i < 5;) + while (i < 5) { - tcall->Parameters.Push(new FxParameter(new FxIntCast(ParseExpression(sc, bag.Info)))); + args->Push(new FxParameter(new FxIntCast(ParseExpression(sc, bag.Info)))); i++; if (!sc.CheckToken (',')) break; } sc.MustGetToken (')'); } - else i=0; if (i < min_args) { @@ -100,7 +101,7 @@ bool DoActionSpecials(FScanner &sc, FState & state, Baggage &bag, FStateTempCall sc.ScriptError ("Too many arguments to %s", specname.GetChars()); } - tcall->Function = FindGlobalActionFunction("A_CallSpecial")->Variants[0].Implementation; + tcall->Call = new FxVMFunctionCall(FindGlobalActionFunction("A_CallSpecial"), args, sc); return true; } return false; @@ -143,6 +144,7 @@ void ParseStates(FScanner &sc, PClassActor * actor, AActor * defaults, Baggage & FState state; char lastsprite[5] = ""; FStateTempCall *tcall = NULL; + FArgumentList *args = NULL; sc.MustGetStringName ("{"); sc.SetEscape(false); // disable escape sequences in the state parser @@ -325,8 +327,19 @@ do_stop: PFunction *afd = dyn_cast(bag.Info->Symbols.FindSymbol(FName(sc.String, true), true)); if (afd != NULL) { - tcall->Function = afd->Variants[0].Implementation; - ParseFunctionParameters(sc, bag.Info, tcall->Parameters, afd, statestring, &bag.statedef); + // When creating the FxVMFunctionCall, we only pass args if there are any. + // So if the previous call had no args, then we can use the argument list + // allocated for it. Otherwise, we need to create a new one. + if (args == NULL) + { + args = new FArgumentList; + } + ParseFunctionParameters(sc, bag.Info, *args, afd, statestring, &bag.statedef); + tcall->Call = new FxVMFunctionCall(afd, args->Size() > 0 ? args : NULL, sc); + if (args->Size() > 0) + { + args = NULL; + } goto endofstate; } sc.ScriptError("Invalid state parameter %s\n", sc.String); @@ -339,7 +352,7 @@ endofstate: sc.ScriptError ("Invalid frame character string '%s'", statestring.GetChars()); count = -count; } - if (tcall->Function != NULL) + if (tcall->Call != NULL) { tcall->ActorClass = actor; tcall->FirstState = bag.statedef.GetStateCount() - count; @@ -353,6 +366,10 @@ endofstate: { delete tcall; } + if (args != NULL) + { + delete args; + } sc.SetEscape(true); // re-enable escape sequences }