From 5b079dd40bbbd83617e62b66c62a04c26dd57023 Mon Sep 17 00:00:00 2001 From: "alexey.lysiuk" Date: Sat, 6 Aug 2016 17:47:33 +0300 Subject: [PATCH 01/11] Fixed wrong height of player coordinates text --- src/g_shared/shared_hud.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/g_shared/shared_hud.cpp b/src/g_shared/shared_hud.cpp index c9e87d6421..cddc86ee46 100644 --- a/src/g_shared/shared_hud.cpp +++ b/src/g_shared/shared_hud.cpp @@ -891,16 +891,16 @@ static void DrawCoordinates(player_t * CPlayer) default: case 0: vwidth = SCREENWIDTH; - vheight = SCREENWIDTH; + vheight = SCREENHEIGHT; break; case 1: case 2: vwidth = SCREENWIDTH/2; - vheight = SCREENWIDTH/2; + vheight = SCREENHEIGHT/2; break; case 3: vwidth = SCREENWIDTH/4; - vheight = SCREENWIDTH/4; + vheight = SCREENHEIGHT/4; break; } From 5a66fdf9be3f2dfac07d80b7761b82a8802ef76d Mon Sep 17 00:00:00 2001 From: Edoardo Prezioso Date: Sun, 7 Aug 2016 20:55:16 +0200 Subject: [PATCH 02/11] - Hide Clang -Winconsistent-missing-override warnings in non-Apple targets, too. --- src/CMakeLists.txt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5fe947ef82..f6caa2f5da 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -484,8 +484,10 @@ if( ZD_CMAKE_COMPILER_IS_GNUCXX_COMPATIBLE ) set( CMAKE_C_FLAGS "-Wno-unused-result ${CMAKE_C_FLAGS}" ) set( CMAKE_CXX_FLAGS "-Wno-unused-result ${CMAKE_CXX_FLAGS}" ) endif() - if(APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - set( CMAKE_CXX_FLAGS "-Wno-inconsistent-missing-override ${CMAKE_CXX_FLAGS}" ) + if( CMAKE_CXX_COMPILER_ID STREQUAL "Clang" ) + if( APPLE OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER "3.6" ) + set( CMAKE_CXX_FLAGS "-Wno-inconsistent-missing-override ${CMAKE_CXX_FLAGS}" ) + endif() endif() set( CMAKE_C_FLAGS "-Wall -Wextra -Wno-unused -Wno-unused-parameter -Wno-missing-field-initializers -ffp-contract=off ${CMAKE_C_FLAGS}" ) set( CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-unused -Wno-unused-parameter -Wno-missing-field-initializers -ffp-contract=off ${CMAKE_CXX_FLAGS}" ) From ab837b608d2ad3dba4f9be2a1749a53f8048d0c9 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 7 Aug 2016 22:03:36 +0200 Subject: [PATCH 03/11] - compatibility optioned triggering sector actions by indirectly initiated teleports There's several old maps depending on this not happening. - Set the option for Hell's Twisted Influence Part 1. --- src/compatibility.cpp | 1 + src/d_main.cpp | 3 ++- src/doomdef.h | 1 + src/g_mapinfo.cpp | 1 + src/p_map.cpp | 3 ++- wadsrc/static/compatibility.txt | 5 +++++ 6 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/compatibility.cpp b/src/compatibility.cpp index ae3b905d07..870d2ede6d 100644 --- a/src/compatibility.cpp +++ b/src/compatibility.cpp @@ -147,6 +147,7 @@ static FCompatOption Options[] = { "soundcutoff", COMPATF2_SOUNDCUTOFF, SLOT_COMPAT2 }, { "pointonline", COMPATF2_POINTONLINE, SLOT_COMPAT2 }, { "multiexit", COMPATF2_MULTIEXIT, SLOT_COMPAT2 }, + { "teleport", COMPATF2_TELEPORT, SLOT_COMPAT2 }, { NULL, 0, 0 } }; diff --git a/src/d_main.cpp b/src/d_main.cpp index d6d53dd77a..4e360a38e4 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -574,7 +574,7 @@ CUSTOM_CVAR(Int, compatmode, 0, CVAR_ARCHIVE|CVAR_NOINITCALL) case 4: // Old ZDoom compat mode v = COMPATF_SOUNDTARGET | COMPATF_LIGHT; - w = COMPATF2_MULTIEXIT; + w = COMPATF2_MULTIEXIT | COMPATF2_TELEPORT; break; case 5: // MBF compat mode @@ -630,6 +630,7 @@ CVAR (Flag, compat_floormove, compatflags2, COMPATF2_FLOORMOVE); CVAR (Flag, compat_soundcutoff, compatflags2, COMPATF2_SOUNDCUTOFF); CVAR (Flag, compat_pointonline, compatflags2, COMPATF2_POINTONLINE); CVAR (Flag, compat_multiexit, compatflags2, COMPATF2_MULTIEXIT); +CVAR (Flag, compat_teleport, compatflags2, COMPATF2_TELEPORT); //========================================================================== // diff --git a/src/doomdef.h b/src/doomdef.h index 56b69fe7c7..9ebe45cf7f 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -342,6 +342,7 @@ enum : unsigned int COMPATF2_SOUNDCUTOFF = 1 << 2, // Cut off sounds when an actor vanishes instead of making it owner-less COMPATF2_POINTONLINE = 1 << 3, // Use original but buggy P_PointOnLineSide() and P_PointOnDivlineSideCompat() COMPATF2_MULTIEXIT = 1 << 4, // Level exit can be triggered multiple times (required by Daedalus's travel tubes, thanks to a faulty script) + COMPATF2_TELEPORT = 1 << 5, // Don't let indirect teleports trigger sector actions }; // Emulate old bugs for select maps. These are not exposed by a cvar diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 78ba9142b8..7d3df69e01 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1341,6 +1341,7 @@ MapFlagHandlers[] = { "compat_soundcutoff", MITYPE_COMPATFLAG, 0, COMPATF2_SOUNDCUTOFF }, { "compat_pointonline", MITYPE_COMPATFLAG, 0, COMPATF2_POINTONLINE }, { "compat_multiexit", MITYPE_COMPATFLAG, 0, COMPATF2_MULTIEXIT }, + { "compat_teleport", MITYPE_COMPATFLAG, 0, COMPATF2_TELEPORT }, { "cd_start_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end1_track", MITYPE_EATNEXT, 0, 0 }, { "cd_end2_track", MITYPE_EATNEXT, 0, 0 }, diff --git a/src/p_map.cpp b/src/p_map.cpp index 110ae5235f..e7aceba781 100644 --- a/src/p_map.cpp +++ b/src/p_map.cpp @@ -434,7 +434,8 @@ bool P_TeleportMove(AActor* thing, const DVector3 &pos, bool telefrag, bool modi // If this teleport was caused by a move, P_TryMove() will handle the // sector transition messages better than we can here. - if (!(thing->flags6 & MF6_INTRYMOVE)) + // This needs to be compatibility optioned because some older maps exploited this missing feature. + if (!(thing->flags6 & MF6_INTRYMOVE) && !(i_compatflags2 & COMPATF2_TELEPORT)) { thing->CheckSectorTransition(oldsec); } diff --git a/wadsrc/static/compatibility.txt b/wadsrc/static/compatibility.txt index a2c3794ae4..25395dad69 100644 --- a/wadsrc/static/compatibility.txt +++ b/wadsrc/static/compatibility.txt @@ -430,6 +430,11 @@ C98F79709BD7E0E4C19026AB9575EC6F // cc-cod.zip:codlev.wad map07 maskedmidtex } +7B82B12A6990E09553B12FDB4E3824A0 // hti.wad map01 +{ + teleport +} + D7F6E9F08C39A17026349A04F8C0B0BE // Return to Hadron, e1m9 19D03FFC875589E21EDBB7AB74EF4AEF // Return to Hadron, e1m9, 2016.01.03 update { From 078881a941958b92d76e10d87370fdc00971801d Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Fri, 5 Aug 2016 17:07:51 +0200 Subject: [PATCH 04/11] Parse statements as expressions in sequences This will be needed for assignment operators to work --- src/thingdef/thingdef_states.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index ecc62bf29d..652bdf9e9e 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -539,7 +539,8 @@ static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, B sc.MustGetString(); if (!sc.Compare(";")) { - init = ParseAction(sc, state, statestring, bag); // That's all DECORATE can handle for now. + sc.UnGet(); + init = ParseExpression(sc, bag.Info); sc.MustGetStringName(";"); } sc.MustGetString(); @@ -552,7 +553,8 @@ static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, B sc.MustGetString(); if (!sc.Compare(")")) { - iter = ParseAction(sc, state, statestring, bag); + sc.UnGet(); + iter = ParseExpression(sc, bag.Info); sc.MustGetStringName(")"); } @@ -641,8 +643,9 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg sc.MustGetString(); } else - { // Handle a regular action function call - add = ParseAction(sc, state, statestring, bag); + { // Handle a regular expression + sc.UnGet(); + add = ParseExpression(sc, bag.Info); sc.MustGetStringName(";"); sc.MustGetString(); } From a4142ad9fba4fb26eea2b60277012d6f78dc7766 Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Thu, 4 Aug 2016 02:39:00 +0200 Subject: [PATCH 05/11] Re-allow action function calls with no argument list in sequences --- src/thingdef/thingdef_exp.cpp | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 6a756d9b5d..b837968604 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -374,6 +374,29 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) case NAME_Random2: return ParseRandom2(sc, cls); default: + if (cls != nullptr) + { + func = dyn_cast(cls->Symbols.FindSymbol(identifier, true)); + + // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. + if (func != nullptr && identifier != NAME_ACS_NamedExecuteWithResult) + { + args = new FArgumentList; + if (sc.CheckToken('(')) + { + sc.UnGet(); + ParseFunctionParameters(sc, cls, *args, func, "", nullptr); + } + if (args->Size() == 0) + { + delete args; + args = nullptr; + } + + return new FxVMFunctionCall(func, args, sc); + } + } + break; } if (sc.CheckToken('(')) @@ -392,17 +415,9 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) return ParseAtan2(sc, identifier, cls); default: args = new FArgumentList; - func = (cls == nullptr) ? nullptr : dyn_cast(cls->Symbols.FindSymbol(identifier, true)); try { - // There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work. - if (func != NULL && identifier != NAME_ACS_NamedExecuteWithResult) - { - sc.UnGet(); - ParseFunctionParameters(sc, cls, *args, func, "", NULL); - return new FxVMFunctionCall(func, args, sc); - } - else if (!sc.CheckToken(')')) + if (!sc.CheckToken(')')) { do { From db9f4c13857f785b589bfdb01684c38557611a79 Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Fri, 5 Aug 2016 17:26:33 +0200 Subject: [PATCH 06/11] Re-allow indexes as state parameter in sequences --- src/namedef.h | 1 + src/thingdef/thingdef_exp.h | 18 ++++++ src/thingdef/thingdef_expression.cpp | 83 ++++++++++++++++++++++++++++ src/thingdef/thingdef_parse.cpp | 29 ++++++---- 4 files changed, 120 insertions(+), 11 deletions(-) diff --git a/src/namedef.h b/src/namedef.h index cd0d5615b8..e5b6c452a7 100644 --- a/src/namedef.h +++ b/src/namedef.h @@ -654,6 +654,7 @@ xx(DecoCallLineSpecial) xx(DecoNameToClass) xx(DecoFindMultiNameState) xx(DecoFindSingleNameState) +xx(DecoHandleRuntimeState) xx(Damage) // basic type names diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index b878e50f16..c1e8863e86 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -1058,6 +1058,24 @@ public: FxExpression *Resolve(FCompileContext&); }; +//========================================================================== +// +// Same as above except for expressions which means it will have to be +// evaluated at runtime +// +//========================================================================== + +class FxRuntimeStateIndex : public FxExpression +{ + FxExpression *Index; + +public: + FxRuntimeStateIndex(FxExpression *index); + ~FxRuntimeStateIndex(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index b57618575a..d40b7605c1 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -4428,7 +4428,90 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx) delete this; return x; } + +//========================================================================== +// +// +// +//========================================================================== + +FxRuntimeStateIndex::FxRuntimeStateIndex(FxExpression *index) +: FxExpression(index->ScriptPosition), Index(index) +{ + ValueType = TypeState; +} + +FxRuntimeStateIndex::~FxRuntimeStateIndex() +{ + SAFE_DELETE(Index); +} + +FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Index, ctx); + + if (!Index->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + else if (Index->ValueType->GetRegType() != REGT_INT) + { // Float. + Index = new FxIntCast(Index); + SAFE_RESOLVE(Index, ctx); + } + + return this; +} + +static int DecoHandleRuntimeState(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) +{ + PARAM_PROLOGUE; + PARAM_OBJECT(stateowner, AActor); + PARAM_POINTER(stateinfo, FStateParamInfo); + PARAM_INT(index); + + if (index == 0 || !stateowner->GetClass()->OwnsState(stateinfo->mCallingState + index)) + { + // Null is returned if the location was invalid which means that no jump will be performed + // if used as return value + // 0 always meant the same thing so we handle it here for compatibility + ret->SetPointer(nullptr, ATAG_STATE); + } + else + { + ret->SetPointer(stateinfo->mCallingState + index, ATAG_STATE); + } + + return 1; +} + +ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build) +{ + assert(build->Registers[REGT_POINTER].GetMostUsed() >= 3); + + ExpEmit out(build, REGT_POINTER); + + build->Emit(OP_PARAM, 0, REGT_POINTER, 1); // stateowner + build->Emit(OP_PARAM, 0, REGT_POINTER, 2); // stateinfo + ExpEmit id = Index->Emit(build); + build->Emit(OP_PARAM, 0, REGT_INT | (id.Konst ? REGT_KONST : 0), id.RegNum); // index + + VMFunction *callfunc; + PSymbol *sym; + sym = FindDecorateBuiltinFunction(NAME_DecoHandleRuntimeState, DecoHandleRuntimeState); + assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolVMFunction))); + assert(((PSymbolVMFunction *)sym)->Function != nullptr); + callfunc = ((PSymbolVMFunction *)sym)->Function; + + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum); + + return out; +} //========================================================================== // diff --git a/src/thingdef/thingdef_parse.cpp b/src/thingdef/thingdef_parse.cpp index 566643b8e3..b99f1a3842 100644 --- a/src/thingdef/thingdef_parse.cpp +++ b/src/thingdef/thingdef_parse.cpp @@ -138,23 +138,30 @@ FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool c else if (type == TypeState) { // This forces quotation marks around the state name. - sc.MustGetToken(TK_StringConst); - if (sc.String[0] == 0 || sc.Compare("None")) + if (sc.CheckToken(TK_StringConst)) { - x = new FxConstant((FState*)NULL, sc); - } - else if (sc.Compare("*")) - { - if (constant) + if (sc.String[0] == 0 || sc.Compare("None")) { - x = new FxConstant((FState*)(intptr_t)-1, sc); + x = new FxConstant((FState*)NULL, sc); + } + else if (sc.Compare("*")) + { + if (constant) + { + x = new FxConstant((FState*)(intptr_t)-1, sc); + } + else sc.ScriptError("Invalid state name '*'"); + } + else + { + x = new FxMultiNameState(sc.String, sc); } - else sc.ScriptError("Invalid state name '*'"); } - else + else if (!constant) { - x = new FxMultiNameState(sc.String, sc); + x = new FxRuntimeStateIndex(ParseExpression(sc, cls)); } + else sc.MustGetToken(TK_StringConst); // This is for the error. } else if (type->GetClass() == RUNTIME_CLASS(PClassPointer)) { // Actor name From 2ef51d0d501dccf4d2151f0a2cc85802e7758153 Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Fri, 5 Aug 2016 17:32:01 +0200 Subject: [PATCH 07/11] Re-allow casts in sequences --- src/thingdef/thingdef_exp.cpp | 43 +++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index b837968604..2659171754 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -357,6 +357,49 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) // a cheap way to get them working when people use "name" instead of 'name'. return new FxConstant(FName(sc.String), scpos); } + else if (sc.CheckToken(TK_Bool)) + { + sc.MustGetToken('('); + FxExpression *exp = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + return new FxBoolCast(exp); + } + else if (sc.CheckToken(TK_Int)) + { + sc.MustGetToken('('); + FxExpression *exp = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + return new FxIntCast(exp); + } + else if (sc.CheckToken(TK_Float)) + { + sc.MustGetToken('('); + FxExpression *exp = ParseExpressionM(sc, cls); + sc.MustGetToken(')'); + return new FxFloatCast(exp); + } + else if (sc.CheckToken(TK_State)) + { + sc.MustGetToken('('); + FxExpression *exp; + if (sc.CheckToken(TK_StringConst)) + { + if (sc.String[0] == 0 || sc.Compare("None")) + { + exp = new FxConstant((FState*)nullptr, sc); + } + else + { + exp = new FxMultiNameState(sc.String, sc); + } + } + else + { + exp = new FxRuntimeStateIndex(ParseExpressionM(sc, cls)); + } + sc.MustGetToken(')'); + return exp; + } else if (sc.CheckToken(TK_Identifier)) { FName identifier = FName(sc.String); From 8437313e7c2d5d8c2db8419da8c00cf4b0759cdf Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Fri, 5 Aug 2016 17:33:29 +0200 Subject: [PATCH 08/11] Added post/pre increment/decrement operators to DECORATE --- src/dobjtype.h | 1 + src/thingdef/thingdef_data.cpp | 68 ++++---- src/thingdef/thingdef_exp.cpp | 15 +- src/thingdef/thingdef_exp.h | 48 +++++- src/thingdef/thingdef_expression.cpp | 240 ++++++++++++++++++++++++--- 5 files changed, 306 insertions(+), 66 deletions(-) diff --git a/src/dobjtype.h b/src/dobjtype.h index 7edf8ca6ab..d0ccee7465 100644 --- a/src/dobjtype.h +++ b/src/dobjtype.h @@ -18,6 +18,7 @@ typedef std::pair FTypeAndOffset; #define VARF_Method (1<<1) // func has an implied self parameter #define VARF_Action (1<<2) // func has implied owner and state parameters #define VARF_Native (1<<3) // func is native code/don't auto serialize field +#define VARF_ReadOnly (1<<4) // field is read only, do not write to it // Symbol information ------------------------------------------------------- diff --git a/src/thingdef/thingdef_data.cpp b/src/thingdef/thingdef_data.cpp index 3b114fbeef..8fceca8ee4 100644 --- a/src/thingdef/thingdef_data.cpp +++ b/src/thingdef/thingdef_data.cpp @@ -631,38 +631,38 @@ void InitThingdef() // Define some member variables we feel like exposing to the user PSymbolTable &symt = RUNTIME_CLASS(AActor)->Symbols; PType *array5 = NewArray(TypeSInt32, 5); - symt.AddSymbol(new PField(NAME_Alpha, TypeFloat64, VARF_Native, myoffsetof(AActor,Alpha))); - symt.AddSymbol(new PField(NAME_Angle, TypeFloat64, VARF_Native, myoffsetof(AActor,Angles.Yaw))); - symt.AddSymbol(new PField(NAME_Args, array5, VARF_Native, myoffsetof(AActor,args))); - symt.AddSymbol(new PField(NAME_CeilingZ, TypeFloat64, VARF_Native, myoffsetof(AActor,ceilingz))); - symt.AddSymbol(new PField(NAME_FloorZ, TypeFloat64, VARF_Native, myoffsetof(AActor,floorz))); - symt.AddSymbol(new PField(NAME_Health, TypeSInt32, VARF_Native, myoffsetof(AActor,health))); - symt.AddSymbol(new PField(NAME_Mass, TypeSInt32, VARF_Native, myoffsetof(AActor,Mass))); - symt.AddSymbol(new PField(NAME_Pitch, TypeFloat64, VARF_Native, myoffsetof(AActor,Angles.Pitch))); - symt.AddSymbol(new PField(NAME_Roll, TypeFloat64, VARF_Native, myoffsetof(AActor,Angles.Roll))); - symt.AddSymbol(new PField(NAME_Special, TypeSInt32, VARF_Native, myoffsetof(AActor,special))); - symt.AddSymbol(new PField(NAME_TID, TypeSInt32, VARF_Native, myoffsetof(AActor,tid))); - symt.AddSymbol(new PField(NAME_TIDtoHate, TypeSInt32, VARF_Native, myoffsetof(AActor,TIDtoHate))); - symt.AddSymbol(new PField(NAME_WaterLevel, TypeSInt32, VARF_Native, myoffsetof(AActor,waterlevel))); - symt.AddSymbol(new PField(NAME_X, TypeFloat64, VARF_Native, myoffsetof(AActor,__Pos.X))); // must remain read-only! - symt.AddSymbol(new PField(NAME_Y, TypeFloat64, VARF_Native, myoffsetof(AActor,__Pos.Y))); // must remain read-only! - symt.AddSymbol(new PField(NAME_Z, TypeFloat64, VARF_Native, myoffsetof(AActor,__Pos.Z))); // must remain read-only! - symt.AddSymbol(new PField(NAME_VelX, TypeFloat64, VARF_Native, myoffsetof(AActor,Vel.X))); - symt.AddSymbol(new PField(NAME_VelY, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Y))); - symt.AddSymbol(new PField(NAME_VelZ, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Z))); - symt.AddSymbol(new PField(NAME_MomX, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.X))); - symt.AddSymbol(new PField(NAME_MomY, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Y))); - symt.AddSymbol(new PField(NAME_MomZ, TypeFloat64, VARF_Native, myoffsetof(AActor, Vel.Z))); - symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.X))); - symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.Y))); - symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor,Score))); - symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor,accuracy))); - symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor,stamina))); - symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native, myoffsetof(AActor,Height))); - symt.AddSymbol(new PField(NAME_Radius, TypeFloat64, VARF_Native, myoffsetof(AActor,radius))); - symt.AddSymbol(new PField(NAME_ReactionTime,TypeSInt32, VARF_Native, myoffsetof(AActor,reactiontime))); - symt.AddSymbol(new PField(NAME_MeleeRange, TypeFloat64, VARF_Native, myoffsetof(AActor,meleerange))); - symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor,Speed))); - symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native, myoffsetof(AActor,threshold))); - symt.AddSymbol(new PField(NAME_DefThreshold,TypeSInt32, VARF_Native, myoffsetof(AActor,DefThreshold))); + symt.AddSymbol(new PField(NAME_Alpha, TypeFloat64, VARF_Native, myoffsetof(AActor, Alpha))); + symt.AddSymbol(new PField(NAME_Angle, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Yaw))); + symt.AddSymbol(new PField(NAME_Args, array5, VARF_Native, myoffsetof(AActor, args))); + symt.AddSymbol(new PField(NAME_CeilingZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, ceilingz))); + symt.AddSymbol(new PField(NAME_FloorZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, floorz))); + symt.AddSymbol(new PField(NAME_Health, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, health))); + symt.AddSymbol(new PField(NAME_Mass, TypeSInt32, VARF_Native, myoffsetof(AActor, Mass))); + symt.AddSymbol(new PField(NAME_Pitch, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Pitch))); + symt.AddSymbol(new PField(NAME_Roll, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Roll))); + symt.AddSymbol(new PField(NAME_Special, TypeSInt32, VARF_Native, myoffsetof(AActor, special))); + symt.AddSymbol(new PField(NAME_TID, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, tid))); + symt.AddSymbol(new PField(NAME_TIDtoHate, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, TIDtoHate))); + symt.AddSymbol(new PField(NAME_WaterLevel, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, waterlevel))); + symt.AddSymbol(new PField(NAME_X, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.X))); // must remain read-only! + symt.AddSymbol(new PField(NAME_Y, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.Y))); // must remain read-only! + symt.AddSymbol(new PField(NAME_Z, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.Z))); // must remain read-only! + symt.AddSymbol(new PField(NAME_VelX, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.X))); + symt.AddSymbol(new PField(NAME_VelY, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Y))); + symt.AddSymbol(new PField(NAME_VelZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Z))); + symt.AddSymbol(new PField(NAME_MomX, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.X))); + symt.AddSymbol(new PField(NAME_MomY, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Y))); + symt.AddSymbol(new PField(NAME_MomZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Z))); + symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.X))); + symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.Y))); + symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor, Score))); + symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor, accuracy))); + symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor, stamina))); + symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Height))); + symt.AddSymbol(new PField(NAME_Radius, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, radius))); + symt.AddSymbol(new PField(NAME_ReactionTime, TypeSInt32, VARF_Native, myoffsetof(AActor, reactiontime))); + symt.AddSymbol(new PField(NAME_MeleeRange, TypeFloat64, VARF_Native, myoffsetof(AActor, meleerange))); + symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor, Speed))); + symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold))); + symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold))); } diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 2659171754..12dd5957ef 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -63,6 +63,7 @@ static FxExpression *ParseClamp(FScanner &sc, PClassActor *cls); // // ParseExpression // [GRB] Parses an expression and stores it into Expression array +// It's worth mentioning that this is using C++ operator precedence // static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls); @@ -257,6 +258,10 @@ static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls) case '+': return new FxPlusSign(ParseExpressionA (sc, cls)); + case TK_Incr: + case TK_Decr: + return new FxPreIncrDecr(ParseExpressionA(sc, cls), sc.TokenType); + default: sc.UnGet(); return ParseExpressionA (sc, cls); @@ -312,6 +317,14 @@ static FxExpression *ParseExpressionA (FScanner &sc, PClassActor *cls) sc.MustGetToken(']'); base_expr = new FxArrayElement(base_expr, index); } + else if (sc.CheckToken(TK_Incr)) + { + return new FxPostIncrDecr(base_expr, TK_Incr); + } + else if (sc.CheckToken(TK_Decr)) + { + return new FxPostIncrDecr(base_expr, TK_Decr); + } else break; } @@ -478,7 +491,7 @@ static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls) } break; } - } + } else { return new FxIdentifier(identifier, sc); diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index c1e8863e86..25c4b3afb1 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -201,7 +201,7 @@ public: virtual FxExpression *Resolve(FCompileContext &ctx); virtual bool isConstant() const; - virtual void RequestAddress(); + virtual bool RequestAddress(); virtual VMFunction *GetDirectFunction(); bool IsNumeric() const { return ValueType != TypeName && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT); } bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; } @@ -469,6 +469,45 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxPreIncrDecr +// +//========================================================================== + +class FxPreIncrDecr : public FxExpression +{ + int Token; + FxExpression *Base; + bool AddressRequested; + bool AddressWritable; + +public: + FxPreIncrDecr(FxExpression *base, int token); + ~FxPreIncrDecr(); + FxExpression *Resolve(FCompileContext&); + bool RequestAddress(); + ExpEmit Emit(VMFunctionBuilder *build); +}; + +//========================================================================== +// +// FxPostIncrDecr +// +//========================================================================== + +class FxPostIncrDecr : public FxExpression +{ + int Token; + FxExpression *Base; + +public: + FxPostIncrDecr(FxExpression *base, int token); + ~FxPostIncrDecr(); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxBinary @@ -753,7 +792,7 @@ public: FxClassMember(FxExpression*, PField*, const FScriptPosition&); ~FxClassMember(); FxExpression *Resolve(FCompileContext&); - void RequestAddress(); + bool RequestAddress(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -796,12 +835,13 @@ class FxArrayElement : public FxExpression public: FxExpression *Array; FxExpression *index; - //bool AddressRequested; + bool AddressRequested; + bool AddressWritable; FxArrayElement(FxExpression*, FxExpression*); ~FxArrayElement(); FxExpression *Resolve(FCompileContext&); - //void RequestAddress(); + bool RequestAddress(); ExpEmit Emit(VMFunctionBuilder *build); }; diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index d40b7605c1..63b6be9ca9 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -218,13 +218,14 @@ FxExpression *FxExpression::Resolve(FCompileContext &ctx) //========================================================================== // -// +// Returns true if we can write to the address. // //========================================================================== -void FxExpression::RequestAddress() +bool FxExpression::RequestAddress() { ScriptPosition.Message(MSG_ERROR, "invalid dereference\n"); + return false; } //========================================================================== @@ -887,6 +888,166 @@ ExpEmit FxUnaryNotBoolean::Emit(VMFunctionBuilder *build) return from; } +//========================================================================== +// +// FxPreIncrDecr +// +//========================================================================== + +FxPreIncrDecr::FxPreIncrDecr(FxExpression *base, int token) +: FxExpression(base->ScriptPosition), Base(base), Token(token) +{ + AddressRequested = false; + AddressWritable = false; +} + +FxPreIncrDecr::~FxPreIncrDecr() +{ + SAFE_DELETE(Base); +} + +bool FxPreIncrDecr::RequestAddress() +{ + AddressRequested = true; + return AddressWritable; +} + +FxExpression *FxPreIncrDecr::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Base, ctx); + + ValueType = Base->ValueType; + + if (!Base->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + else if (Base->ValueType == TypeBool) + { + ScriptPosition.Message(MSG_ERROR, "%s is not allowed on type bool", FScanner::TokenName(Token).GetChars()); + delete this; + return nullptr; + } + if (!(AddressWritable = Base->RequestAddress())) + { + ScriptPosition.Message(MSG_ERROR, "Expression must be a modifiable value"); + delete this; + return nullptr; + } + + return this; +} + +ExpEmit FxPreIncrDecr::Emit(VMFunctionBuilder *build) +{ + assert(Token == TK_Incr || Token == TK_Decr); + assert(ValueType == Base->ValueType && IsNumeric()); + + int zero = build->GetConstantInt(0); + int regtype = ValueType->GetRegType(); + ExpEmit pointer = Base->Emit(build); + + ExpEmit value(build, regtype); + build->Emit(ValueType->GetLoadOp(), value.RegNum, pointer.RegNum, zero); + + if (regtype == REGT_INT) + { + build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, value.RegNum, value.RegNum, build->GetConstantInt(1)); + } + else + { + build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, value.RegNum, value.RegNum, build->GetConstantFloat(1.)); + } + + build->Emit(ValueType->GetStoreOp(), pointer.RegNum, value.RegNum, zero); + + if (AddressRequested) + { + value.Free(build); + return pointer; + } + + pointer.Free(build); + return value; +} + +//========================================================================== +// +// FxPostIncrDecr +// +//========================================================================== + +FxPostIncrDecr::FxPostIncrDecr(FxExpression *base, int token) +: FxExpression(base->ScriptPosition), Base(base), Token(token) +{ +} + +FxPostIncrDecr::~FxPostIncrDecr() +{ + SAFE_DELETE(Base); +} + +FxExpression *FxPostIncrDecr::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Base, ctx); + + ValueType = Base->ValueType; + + if (!Base->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + else if (Base->ValueType == TypeBool) + { + ScriptPosition.Message(MSG_ERROR, "%s is not allowed on type bool", FScanner::TokenName(Token).GetChars()); + delete this; + return nullptr; + } + if (!Base->RequestAddress()) + { + ScriptPosition.Message(MSG_ERROR, "Expression must be a modifiable value"); + delete this; + return nullptr; + } + + return this; +} + +ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build) +{ + assert(Token == TK_Incr || Token == TK_Decr); + assert(ValueType == Base->ValueType && IsNumeric()); + + int zero = build->GetConstantInt(0); + int regtype = ValueType->GetRegType(); + ExpEmit pointer = Base->Emit(build); + + ExpEmit out(build, regtype); + build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, zero); + + ExpEmit assign(build, regtype); + if (regtype == REGT_INT) + { + build->Emit((Token == TK_Incr) ? OP_ADD_RK : OP_SUB_RK, assign.RegNum, out.RegNum, build->GetConstantInt(1)); + } + else + { + build->Emit((Token == TK_Incr) ? OP_ADDF_RK : OP_SUBF_RK, assign.RegNum, out.RegNum, build->GetConstantFloat(1.)); + } + + build->Emit(ValueType->GetStoreOp(), pointer.RegNum, assign.RegNum, zero); + + pointer.Free(build); + assign.Free(build); + return out; +} + //========================================================================== // // @@ -2995,9 +3156,10 @@ FxClassMember::~FxClassMember() // //========================================================================== -void FxClassMember::RequestAddress() +bool FxClassMember::RequestAddress() { AddressRequested = true; + return !!(~membervar->Flags & VARF_ReadOnly); } //========================================================================== @@ -3027,6 +3189,17 @@ ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) ExpEmit obj = classx->Emit(build); assert(obj.RegType == REGT_POINTER); + if (obj.Konst) + { + // If the situation where we are dereferencing a constant + // pointer is common, then it would probably be worthwhile + // to add new opcodes for those. But as of right now, I + // don't expect it to be a particularly common case. + ExpEmit newobj(build, REGT_POINTER); + build->Emit(OP_LKP, newobj.RegNum, obj.RegNum); + obj = newobj; + } + if (AddressRequested) { if (membervar->Offset == 0) @@ -3040,20 +3213,8 @@ ExpEmit FxClassMember::Emit(VMFunctionBuilder *build) } int offsetreg = build->GetConstantInt((int)membervar->Offset); - ExpEmit loc, tmp; + ExpEmit loc(build, membervar->Type->GetRegType()); - if (obj.Konst) - { - // If the situation where we are dereferencing a constant - // pointer is common, then it would probably be worthwhile - // to add new opcodes for those. But as of right now, I - // don't expect it to be a particularly common case. - ExpEmit newobj(build, REGT_POINTER); - build->Emit(OP_LKP, newobj.RegNum, obj.RegNum); - obj = newobj; - } - - loc = ExpEmit(build, membervar->Type->GetRegType()); build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, obj.RegNum, offsetreg); obj.Free(build); return loc; @@ -3071,7 +3232,8 @@ FxArrayElement::FxArrayElement(FxExpression *base, FxExpression *_index) { Array=base; index = _index; - //AddressRequested = false; + AddressRequested = false; + AddressWritable = false; } //========================================================================== @@ -3092,12 +3254,11 @@ FxArrayElement::~FxArrayElement() // //========================================================================== -/* -void FxArrayElement::RequestAddress() +bool FxArrayElement::RequestAddress() { AddressRequested = true; + return AddressWritable; } -*/ //========================================================================== // @@ -3145,7 +3306,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx) delete this; return NULL; } - Array->RequestAddress(); + AddressWritable = Array->RequestAddress(); return this; } @@ -3159,13 +3320,13 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { ExpEmit start = Array->Emit(build); PArray *const arraytype = static_cast(Array->ValueType); - PType *const elementtype = arraytype->ElementType; - ExpEmit dest(build, elementtype->GetRegType()); + ExpEmit dest(build, arraytype->ElementType->GetRegType()); if (start.Konst) { ExpEmit tmpstart(build, REGT_POINTER); build->Emit(OP_LKP, tmpstart.RegNum, start.RegNum); + start.Free(build); start = tmpstart; } if (index->isConstant()) @@ -3176,8 +3337,19 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) I_Error("Array index out of bounds"); } indexval *= arraytype->ElementSize; - build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, - start.RegNum, build->GetConstantInt(indexval)); + + if (AddressRequested) + { + if (indexval != 0) + { + build->Emit(OP_ADDA_RK, start.RegNum, start.RegNum, build->GetConstantInt(indexval)); + } + } + else + { + build->Emit(arraytype->ElementType->GetLoadOp(), dest.RegNum, + start.RegNum, build->GetConstantInt(indexval)); + } } else { @@ -3193,10 +3365,24 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build) { build->Emit(OP_SLL_RI, indexv.RegNum, indexv.RegNum, shiftbits); } - build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that - dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register + + if (AddressRequested) + { + build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexv.RegNum); + } + else + { + build->Emit(arraytype->ElementType->GetLoadOp() + 1, // added 1 to use the *_R version that + dest.RegNum, start.RegNum, indexv.RegNum); // takes the offset from a register + } indexv.Free(build); } + if (AddressRequested) + { + dest.Free(build); + return start; + } + start.Free(build); return dest; } From 90cc79a902002c68f5a910dccee1b9e018024459 Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Mon, 1 Aug 2016 00:32:02 +0200 Subject: [PATCH 09/11] Added the direct assignment operator to DECORATE --- src/thingdef/thingdef_exp.cpp | 11 +++- src/thingdef/thingdef_exp.h | 21 +++++++ src/thingdef/thingdef_expression.cpp | 94 ++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index 12dd5957ef..ffe7bf11ae 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -96,18 +96,23 @@ FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve) static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls) { - FxExpression *condition = ParseExpressionL (sc, cls); + FxExpression *base = ParseExpressionL (sc, cls); if (sc.CheckToken('?')) { FxExpression *truex = ParseExpressionM (sc, cls); sc.MustGetToken(':'); FxExpression *falsex = ParseExpressionM (sc, cls); - return new FxConditional(condition, truex, falsex); + return new FxConditional(base, truex, falsex); + } + else if (sc.CheckToken('=')) + { + FxExpression *right = ParseExpressionM(sc, cls); + return new FxAssign(base, right); } else { - return condition; + return base; } } diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 25c4b3afb1..1284f364d2 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -508,6 +508,27 @@ public: ExpEmit Emit(VMFunctionBuilder *build); }; +//========================================================================== +// +// FxAssign +// +//========================================================================== + +class FxAssign : public FxExpression +{ + FxExpression *Base; + FxExpression *Right; + bool AddressRequested; + bool AddressWritable; + +public: + FxAssign(FxExpression *base, FxExpression *right); + ~FxAssign(); + FxExpression *Resolve(FCompileContext&); + bool RequestAddress(); + ExpEmit Emit(VMFunctionBuilder *build); +}; + //========================================================================== // // FxBinary diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 63b6be9ca9..34a2c0f95b 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1048,6 +1048,100 @@ ExpEmit FxPostIncrDecr::Emit(VMFunctionBuilder *build) return out; } +//========================================================================== +// +// FxAssign +// +//========================================================================== + +FxAssign::FxAssign(FxExpression *base, FxExpression *right) +: FxExpression(base->ScriptPosition), Base(base), Right(right) +{ + AddressRequested = false; + AddressWritable = false; +} + +FxAssign::~FxAssign() +{ + SAFE_DELETE(Base); + SAFE_DELETE(Right); +} + +bool FxAssign::RequestAddress() +{ + AddressRequested = true; + return AddressWritable; +} + +FxExpression *FxAssign::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + SAFE_RESOLVE(Base, ctx); + SAFE_RESOLVE(Right, ctx); + + ValueType = Base->ValueType; + + if (!Base->IsNumeric() || !Right->IsNumeric()) + { + ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); + delete this; + return nullptr; + } + if (!(AddressWritable = Base->RequestAddress())) + { + ScriptPosition.Message(MSG_ERROR, "Expression must be a modifiable value"); + delete this; + return nullptr; + } + + if (Right->ValueType != ValueType) + { + if (ValueType == TypeBool) + { + Right = new FxBoolCast(Right); + } + else if (ValueType->GetRegType() == REGT_INT) + { + Right = new FxIntCast(Right); + } + else + { + Right = new FxFloatCast(Right); + } + SAFE_RESOLVE(Right, ctx); + } + + return this; +} + +ExpEmit FxAssign::Emit(VMFunctionBuilder *build) +{ + assert(ValueType == Base->ValueType && IsNumeric()); + assert(ValueType->GetRegType() == Right->ValueType->GetRegType()); + + ExpEmit pointer = Base->Emit(build); + ExpEmit result = Right->Emit(build); + + if (result.Konst) + { + ExpEmit temp(build, result.RegType); + build->Emit(result.RegType == REGT_FLOAT ? OP_LKF : OP_LK, temp.RegNum, result.RegNum); + result.Free(build); + result = temp; + } + + build->Emit(ValueType->GetStoreOp(), pointer.RegNum, result.RegNum, build->GetConstantInt(0)); + + if (AddressRequested) + { + result.Free(build); + return pointer; + } + + pointer.Free(build); + return result; +} + //========================================================================== // // From e79c0225ed7a635870f1d22e31cc9ad45be5fb2f Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Mon, 1 Aug 2016 03:32:02 +0200 Subject: [PATCH 10/11] Added all compound assignment operators to DECORATE --- src/thingdef/thingdef_exp.cpp | 62 +++++++++++++++++++++++++++- src/thingdef/thingdef_exp.h | 18 ++++++++ src/thingdef/thingdef_expression.cpp | 40 +++++++++++++++++- 3 files changed, 117 insertions(+), 3 deletions(-) diff --git a/src/thingdef/thingdef_exp.cpp b/src/thingdef/thingdef_exp.cpp index ffe7bf11ae..991e9b947a 100644 --- a/src/thingdef/thingdef_exp.cpp +++ b/src/thingdef/thingdef_exp.cpp @@ -112,7 +112,67 @@ static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls) } else { - return base; + FxBinary *exp; + FxAssignSelf *left = new FxAssignSelf(sc); + + sc.GetToken(); + switch (sc.TokenType) + { + case TK_AddEq: + exp = new FxAddSub('+', left, nullptr); + break; + + case TK_SubEq: + exp = new FxAddSub('-', left, nullptr); + break; + + case TK_MulEq: + exp = new FxMulDiv('*', left, nullptr); + break; + + case TK_DivEq: + exp = new FxMulDiv('/', left, nullptr); + break; + + case TK_ModEq: + exp = new FxMulDiv('%', left, nullptr); + break; + + case TK_LShiftEq: + exp = new FxBinaryInt(TK_LShift, left, nullptr); + break; + + case TK_RShiftEq: + exp = new FxBinaryInt(TK_RShift, left, nullptr); + break; + + case TK_URShiftEq: + exp = new FxBinaryInt(TK_URShift, left, nullptr); + break; + + case TK_AndEq: + exp = new FxBinaryInt('&', left, nullptr); + break; + + case TK_XorEq: + exp = new FxBinaryInt('^', left, nullptr); + break; + + case TK_OrEq: + exp = new FxBinaryInt('|', left, nullptr); + break; + + default: + sc.UnGet(); + delete left; + return base; + } + + exp->right = ParseExpressionM(sc, cls); + + FxAssign *ret = new FxAssign(base, exp); + left->Assignment = ret; + return ret; } } diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 1284f364d2..7ec542772c 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -527,6 +527,24 @@ public: FxExpression *Resolve(FCompileContext&); bool RequestAddress(); ExpEmit Emit(VMFunctionBuilder *build); + + ExpEmit Address; +}; + +//========================================================================== +// +// FxAssignSelf +// +//========================================================================== + +class FxAssignSelf : public FxExpression +{ +public: + FxAssign *Assignment; + + FxAssignSelf(const FScriptPosition &pos); + FxExpression *Resolve(FCompileContext&); + ExpEmit Emit(VMFunctionBuilder *build); }; //========================================================================== diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 34a2c0f95b..07f4d9fdf1 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -1077,10 +1077,11 @@ FxExpression *FxAssign::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); SAFE_RESOLVE(Base, ctx); - SAFE_RESOLVE(Right, ctx); ValueType = Base->ValueType; + SAFE_RESOLVE(Right, ctx); + if (!Base->IsNumeric() || !Right->IsNumeric()) { ScriptPosition.Message(MSG_ERROR, "Numeric type expected"); @@ -1120,8 +1121,10 @@ ExpEmit FxAssign::Emit(VMFunctionBuilder *build) assert(ValueType->GetRegType() == Right->ValueType->GetRegType()); ExpEmit pointer = Base->Emit(build); - ExpEmit result = Right->Emit(build); + Address = pointer; + ExpEmit result = Right->Emit(build); + if (result.Konst) { ExpEmit temp(build, result.RegType); @@ -1142,6 +1145,39 @@ ExpEmit FxAssign::Emit(VMFunctionBuilder *build) return result; } +//========================================================================== +// +// FxAssignSelf +// +//========================================================================== + +FxAssignSelf::FxAssignSelf(const FScriptPosition &pos) +: FxExpression(pos) +{ + Assignment = nullptr; +} + +FxExpression *FxAssignSelf::Resolve(FCompileContext &ctx) +{ + CHECKRESOLVED(); + + // This should never happen if FxAssignSelf is used correctly + assert(Assignment != nullptr); + + ValueType = Assignment->ValueType; + + return this; +} + +ExpEmit FxAssignSelf::Emit(VMFunctionBuilder *build) +{ + assert(ValueType = Assignment->ValueType); + ExpEmit pointer = Assignment->Address; // FxAssign should have already emitted it + ExpEmit out(build, ValueType->GetRegType()); + build->Emit(ValueType->GetLoadOp(), out.RegNum, pointer.RegNum, build->GetConstantInt(0)); + return out; +} + //========================================================================== // // From b97024b710ff796391f0c7dda9606b38d8e4608b Mon Sep 17 00:00:00 2001 From: Leonard2 Date: Tue, 2 Aug 2016 18:50:34 +0200 Subject: [PATCH 11/11] The return statement now accepts any expression as its return value So something like 'return ++user_x;' is now possible Admittedly this needed quite a bit of refactoring mainly due to the fact that return types now have to be checked after resolving the function rather than before --- src/thingdef/thingdef.cpp | 41 ++-- src/thingdef/thingdef.h | 4 +- src/thingdef/thingdef_exp.h | 37 ++-- src/thingdef/thingdef_expression.cpp | 314 ++++++++++++++++++++++----- src/thingdef/thingdef_states.cpp | 152 ++----------- 5 files changed, 327 insertions(+), 221 deletions(-) diff --git a/src/thingdef/thingdef.cpp b/src/thingdef/thingdef.cpp index 04ac218905..4878adfdf1 100644 --- a/src/thingdef/thingdef.cpp +++ b/src/thingdef/thingdef.cpp @@ -270,23 +270,28 @@ static void FinishThingdef() for (i = 0; i < StateTempCalls.Size(); ++i) { FStateTempCall *tcall = StateTempCalls[i]; - VMFunction *func; + VMFunction *func = nullptr; assert(tcall->Code != NULL); - // Can we call this function directly without wrapping it in an - // anonymous function? e.g. Are we passing any parameters to it? - func = tcall->Code->GetDirectFunction(); - if (func == NULL) - { - FCompileContext ctx(tcall->ActorClass); - tcall->Code = tcall->Code->Resolve(ctx); + // We don't know the return type in advance for anonymous functions. + FCompileContext ctx(tcall->ActorClass, nullptr); + tcall->Code = tcall->Code->Resolve(ctx); + tcall->Proto = ctx.ReturnProto; - // Make sure resolving it didn't obliterate it. - if (tcall->Code != NULL) + // Make sure resolving it didn't obliterate it. + if (tcall->Code != nullptr) + { + // Can we call this function directly without wrapping it in an + // anonymous function? e.g. Are we passing any parameters to it? + func = tcall->Code->GetDirectFunction(); + + if (func == nullptr) { VMFunctionBuilder buildit; + assert(tcall->Proto != nullptr); + // Allocate registers used to pass parameters in. // self, stateowner, state (all are pointers) buildit.Registers[REGT_POINTER].Get(3); @@ -300,15 +305,7 @@ static void FinishThingdef() // Generate prototype for this anonymous function TArray args(3); SetImplicitArgs(&args, NULL, tcall->ActorClass, VARF_Method | VARF_Action); - if (tcall->Proto != NULL) - { - sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args); - } - else - { - TArray norets(0); - sfunc->Proto = NewPrototype(norets, args); - } + sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args); func = sfunc; @@ -321,11 +318,9 @@ static void FinishThingdef() codesize += sfunc->CodeSize; } } - } - if (tcall->Code != NULL) - { + delete tcall->Code; - tcall->Code = NULL; + tcall->Code = nullptr; 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 8ad28f0985..296383a3d5 100644 --- a/src/thingdef/thingdef.h +++ b/src/thingdef/thingdef.h @@ -190,10 +190,9 @@ AFuncDesc *FindFunction(const char * string); void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag); void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray &out_params, PFunction *afd, FString statestring, FStateDefinitions *statedef); -FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, PPrototype *&proto, bool &endswithret); +FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret); class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag); FName CheckCastKludges(FName in); -void AddImplicitReturn(class FxSequence *code, const PPrototype *proto, FScanner &sc); void SetImplicitArgs(TArray *args, TArray *argflags, PClassActor *cls, DWORD funcflags); PFunction *FindGlobalActionFunction(const char *name); @@ -356,6 +355,7 @@ int MatchString (const char *in, const char **strings); #define ACTION_RETURN_STATE(v) do { FState *state = v; if (numret > 0) { assert(ret != NULL); ret->SetPointer(state, ATAG_STATE); return 1; } return 0; } while(0) +#define ACTION_RETURN_FLOAT(v) do { double u = v; if (numret > 0) { assert(ret != nullptr); ret->SetFloat(u); return 1; } return 0; } while(0) #define ACTION_RETURN_INT(v) do { int u = v; if (numret > 0) { assert(ret != NULL); ret->SetInt(u); return 1; } return 0; } while(0) #define ACTION_RETURN_BOOL(v) ACTION_RETURN_INT(v) diff --git a/src/thingdef/thingdef_exp.h b/src/thingdef/thingdef_exp.h index 7ec542772c..2f06e085e2 100644 --- a/src/thingdef/thingdef_exp.h +++ b/src/thingdef/thingdef_exp.h @@ -62,14 +62,16 @@ class FxJumpStatement; struct FCompileContext { TArray Jumps; + PPrototype *ReturnProto; PClassActor *Class; - FCompileContext(PClassActor *cls = nullptr); + FCompileContext(PClassActor *cls = nullptr, PPrototype *ret = nullptr); PSymbol *FindInClass(FName identifier); PSymbol *FindGlobal(FName identifier); void HandleJumps(int token, FxExpression *handler); + void CheckReturn(PPrototype *proto, FScriptPosition &pos); }; //========================================================================== @@ -170,14 +172,14 @@ struct ExpVal struct ExpEmit { - ExpEmit() : RegNum(0), RegType(REGT_NIL), Konst(false), Fixed(false) {} - ExpEmit(int reg, int type) : RegNum(reg), RegType(type), Konst(false), Fixed(false) {} - ExpEmit(int reg, int type, bool konst) : RegNum(reg), RegType(type), Konst(konst), Fixed(false) {} + ExpEmit() : RegNum(0), RegType(REGT_NIL), Konst(false), Fixed(false), Final(false) {} + ExpEmit(int reg, int type) : RegNum(reg), RegType(type), Konst(false), Fixed(false), Final(false) {} + ExpEmit(int reg, int type, bool konst) : RegNum(reg), RegType(type), Konst(konst), Fixed(false), Final(false) {} ExpEmit(VMFunctionBuilder *build, int type); void Free(VMFunctionBuilder *build); void Reuse(VMFunctionBuilder *build); - BYTE RegNum, RegType, Konst:1, Fixed:1; + BYTE RegNum, RegType, Konst:1, Fixed:1, Final:1; }; //========================================================================== @@ -202,6 +204,7 @@ public: virtual bool isConstant() const; virtual bool RequestAddress(); + virtual PPrototype *ReturnProto(); virtual VMFunction *GetDirectFunction(); bool IsNumeric() const { return ValueType != TypeName && (ValueType->GetRegType() == REGT_INT || ValueType->GetRegType() == REGT_FLOAT); } bool IsPointer() const { return ValueType->GetRegType() == REGT_POINTER; } @@ -748,6 +751,7 @@ public: class FxRandom : public FxExpression { protected: + bool EmitTail; FRandom *rng; FxExpression *min, *max; @@ -756,7 +760,7 @@ public: FxRandom(FRandom *, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos); ~FxRandom(); FxExpression *Resolve(FCompileContext&); - + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -802,6 +806,7 @@ public: class FxRandom2 : public FxExpression { + bool EmitTail; FRandom * rng; FxExpression *mask; @@ -810,7 +815,7 @@ public: FxRandom2(FRandom *, FxExpression *m, const FScriptPosition &pos); ~FxRandom2(); FxExpression *Resolve(FCompileContext&); - + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -915,8 +920,9 @@ public: class FxActionSpecialCall : public FxExpression { - FxExpression *Self; int Special; + bool EmitTail; + FxExpression *Self; FArgumentList *ArgList; public: @@ -924,6 +930,7 @@ public: FxActionSpecialCall(FxExpression *self, int special, FArgumentList *args, const FScriptPosition &pos); ~FxActionSpecialCall(); FxExpression *Resolve(FCompileContext&); + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; @@ -954,6 +961,7 @@ public: class FxVMFunctionCall : public FxExpression { + bool EmitTail; PFunction *Function; FArgumentList *ArgList; @@ -961,13 +969,10 @@ public: FxVMFunctionCall(PFunction *func, FArgumentList *args, const FScriptPosition &pos); ~FxVMFunctionCall(); FxExpression *Resolve(FCompileContext&); + PPrototype *ReturnProto(); + VMFunction *GetDirectFunction(); ExpEmit Emit(VMFunctionBuilder *build); - ExpEmit Emit(VMFunctionBuilder *build, bool tailcall); bool CheckEmitCast(VMFunctionBuilder *build, bool returnit, ExpEmit ®); - unsigned GetArgCount() const { return ArgList == NULL ? 0 : ArgList->Size(); } - PFunction *GetFunction() const { return Function; } - VMFunction *GetVMFunction() const { return Function->Variants[0].Implementation; } - bool IsDirectFunction(); }; //========================================================================== @@ -1089,10 +1094,10 @@ public: class FxReturnStatement : public FxExpression { - FxVMFunctionCall *Call; + FxExpression *Value; public: - FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos); + FxReturnStatement(FxExpression *value, const FScriptPosition &pos); ~FxReturnStatement(); FxExpression *Resolve(FCompileContext&); ExpEmit Emit(VMFunctionBuilder *build); @@ -1146,12 +1151,14 @@ public: class FxRuntimeStateIndex : public FxExpression { + bool EmitTail; FxExpression *Index; public: FxRuntimeStateIndex(FxExpression *index); ~FxRuntimeStateIndex(); FxExpression *Resolve(FCompileContext&); + PPrototype *ReturnProto(); ExpEmit Emit(VMFunctionBuilder *build); }; diff --git a/src/thingdef/thingdef_expression.cpp b/src/thingdef/thingdef_expression.cpp index 07f4d9fdf1..4af9e33c94 100644 --- a/src/thingdef/thingdef_expression.cpp +++ b/src/thingdef/thingdef_expression.cpp @@ -91,7 +91,7 @@ static const FLOP FxFlops[] = // //========================================================================== -FCompileContext::FCompileContext(PClassActor *cls) : Class(cls) +FCompileContext::FCompileContext(PClassActor *cls, PPrototype *ret) : Class(cls), ReturnProto(ret) { } @@ -118,6 +118,50 @@ void FCompileContext::HandleJumps(int token, FxExpression *handler) } } +void FCompileContext::CheckReturn(PPrototype *proto, FScriptPosition &pos) +{ + assert(proto != nullptr); + bool fail = false; + + if (ReturnProto == nullptr) + { + ReturnProto = proto; + return; + } + + // A prototype that defines fewer return types can be compatible with + // one that defines more if the shorter one matches the initial types + // for the longer one. + if (ReturnProto->ReturnTypes.Size() < proto->ReturnTypes.Size()) + { // Make proto the shorter one to avoid code duplication below. + swapvalues(proto, ReturnProto); + } + // If one prototype returns nothing, they both must. + if (proto->ReturnTypes.Size() == 0) + { + if (ReturnProto->ReturnTypes.Size() != 0) + { + fail = true; + } + } + else + { + for (unsigned i = 0; i < proto->ReturnTypes.Size(); i++) + { + if (ReturnProto->ReturnTypes[i] != proto->ReturnTypes[i]) + { // Incompatible + fail = true; + break; + } + } + } + + if (fail) + { + pos.Message(MSG_ERROR, "All return expressions must deduce to the same type"); + } +} + //========================================================================== // // ExpEmit @@ -125,7 +169,7 @@ void FCompileContext::HandleJumps(int token, FxExpression *handler) //========================================================================== ExpEmit::ExpEmit(VMFunctionBuilder *build, int type) -: RegNum(build->Registers[type].Get(1)), RegType(type), Konst(false), Fixed(false) +: RegNum(build->Registers[type].Get(1)), RegType(type), Konst(false), Fixed(false), Final(false) { } @@ -228,6 +272,26 @@ bool FxExpression::RequestAddress() return false; } +//========================================================================== +// +// Called by return statements. +// +//========================================================================== + +PPrototype *FxExpression::ReturnProto() +{ + assert(ValueType != nullptr); + + TArray ret(0); + TArray none(0); + if (ValueType != TypeVoid) + { + ret.Push(ValueType); + } + + return NewPrototype(ret, none); +} + //========================================================================== // // @@ -2651,6 +2715,7 @@ ExpEmit FxMinMax::Emit(VMFunctionBuilder *build) FxRandom::FxRandom(FRandom * r, FxExpression *mi, FxExpression *ma, const FScriptPosition &pos) : FxExpression(pos) { + EmitTail = false; if (mi != NULL && ma != NULL) { min = new FxIntCast(mi); @@ -2679,6 +2744,18 @@ FxRandom::~FxRandom() // //========================================================================== +PPrototype *FxRandom::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +//========================================================================== +// +// +// +//========================================================================== + FxExpression *FxRandom::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -2706,12 +2783,12 @@ int DecoRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, FRandom *rng = reinterpret_cast(param[0].a); if (numparam == 1) { - ret->SetInt((*rng)()); + ACTION_RETURN_INT((*rng)()); } else if (numparam == 2) { int maskval = param[1].i; - ret->SetInt(rng->Random2(maskval)); + ACTION_RETURN_INT(rng->Random2(maskval)); } else if (numparam == 3) { @@ -2720,9 +2797,11 @@ int DecoRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, { swapvalues(max, min); } - ret->SetInt((*rng)(max - min + 1) + min); + ACTION_RETURN_INT((*rng)(max - min + 1) + min); } - return 1; + + // Shouldn't happen + return 0; } ExpEmit FxRandom::Emit(VMFunctionBuilder *build) @@ -2735,17 +2814,27 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); if (min != NULL && max != NULL) { EmitParameter(build, min, ScriptPosition); EmitParameter(build, max, ScriptPosition); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); } else { - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); } + + if (EmitTail) + { + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); return out; @@ -2952,13 +3041,12 @@ int DecoFRandom(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret { swapvalues(max, min); } - ret->SetFloat(frandom * (max - min) + min); + ACTION_RETURN_FLOAT(frandom * (max - min) + min); } else { - ret->SetFloat(frandom); + ACTION_RETURN_FLOAT(frandom); } - return 1; } ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) @@ -2971,17 +3059,27 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); if (min != NULL && max != NULL) { EmitParameter(build, min, ScriptPosition); EmitParameter(build, max, ScriptPosition); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); } else { - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 1, 1); } + + if (EmitTail) + { + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit out(build, REGT_FLOAT); build->Emit(OP_RESULT, 0, REGT_FLOAT, out.RegNum); return out; @@ -2996,6 +3094,7 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build) FxRandom2::FxRandom2(FRandom *r, FxExpression *m, const FScriptPosition &pos) : FxExpression(pos) { + EmitTail = false; rng = r; if (m) mask = new FxIntCast(m); else mask = new FxConstant(-1, pos); @@ -3019,6 +3118,18 @@ FxRandom2::~FxRandom2() // //========================================================================== +PPrototype *FxRandom2::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +//========================================================================== +// +// +// +//========================================================================== + FxExpression *FxRandom2::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -3042,9 +3153,19 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); + build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); EmitParameter(build, mask, ScriptPosition); - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + build->Emit(opcode, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2, 1); + + if (EmitTail) + { + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit out(build, REGT_INT); build->Emit(OP_RESULT, 0, REGT_INT, out.RegNum); return out; @@ -3623,6 +3744,7 @@ FxActionSpecialCall::FxActionSpecialCall(FxExpression *self, int special, FArgum Self = self; Special = special; ArgList = args; + EmitTail = false; } //========================================================================== @@ -3643,6 +3765,18 @@ FxActionSpecialCall::~FxActionSpecialCall() // //========================================================================== +PPrototype *FxActionSpecialCall::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + +//========================================================================== +// +// +// +//========================================================================== + FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) { CHECKRESOLVED(); @@ -3695,7 +3829,6 @@ FxExpression *FxActionSpecialCall::Resolve(FCompileContext& ctx) int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, int numparam, VMReturn *ret, int numret) { assert(numparam > 2 && numparam < 8); - assert(numret == 1); assert(param[0].Type == REGT_INT); assert(param[1].Type == REGT_POINTER); int v[5] = { 0 }; @@ -3704,8 +3837,7 @@ int DecoCallLineSpecial(VMFrameStack *stack, VMValue *param, int numparam, VMRet { v[i - 2] = param[i].i; } - ret->SetInt(P_ExecuteSpecial(param[0].i, NULL, reinterpret_cast(param[1].a), false, v[0], v[1], v[2], v[3], v[4])); - return 1; + ACTION_RETURN_INT(P_ExecuteSpecial(param[0].i, NULL, reinterpret_cast(param[1].a), false, v[0], v[1], v[2], v[3], v[4])); } ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) @@ -3750,6 +3882,14 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != NULL); callfunc = ((PSymbolVMFunction *)sym)->Function; + if (EmitTail) + { + build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 0); + ExpEmit call; + call.Final = true; + return call; + } + ExpEmit dest(build, REGT_INT); build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 1); build->Emit(OP_RESULT, 0, REGT_INT, dest.RegNum); @@ -3767,6 +3907,7 @@ FxVMFunctionCall::FxVMFunctionCall(PFunction *func, FArgumentList *args, const F { Function = func; ArgList = args; + EmitTail = false; } //========================================================================== @@ -3780,6 +3921,38 @@ FxVMFunctionCall::~FxVMFunctionCall() SAFE_DELETE(ArgList); } +//========================================================================== +// +// +// +//========================================================================== + +PPrototype *FxVMFunctionCall::ReturnProto() +{ + EmitTail = true; + return Function->Variants[0].Implementation->Proto; +} + +//========================================================================== +// +// +// +//========================================================================== + +VMFunction *FxVMFunctionCall::GetDirectFunction() +{ + // If this return statement calls a function with no arguments, + // then it can be a "direct" function. That is, the DECORATE + // definition can call that function directly without wrapping + // it inside VM code. + if (EmitTail && (ArgList ? ArgList->Size() : 0) == 0 && (Function->Flags & VARF_Action)) + { + return Function->Variants[0].Implementation; + } + + return nullptr; +} + //========================================================================== // // FxVMFunctionCall :: Resolve @@ -3809,6 +3982,11 @@ FxExpression *FxVMFunctionCall::Resolve(FCompileContext& ctx) { ValueType = rets[0]; } + else + { + ValueType = TypeVoid; + } + return this; } @@ -3821,19 +3999,14 @@ 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 = GetArgCount(); + int count = (ArgList ? ArgList->Size() : 0); if (count == 1) { ExpEmit reg; - if (CheckEmitCast(build, tailcall, reg)) + if (CheckEmitCast(build, EmitTail, reg)) { return reg; } @@ -3862,10 +4035,12 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build, bool tailcall) VMFunction *vmfunc = Function->Variants[0].Implementation; int funcaddr = build->GetConstantAddress(vmfunc, ATAG_OBJECT); // Emit the call - if (tailcall) + if (EmitTail) { // Tail call build->Emit(OP_TAIL_K, funcaddr, count, 0); - return ExpEmit(); + ExpEmit call; + call.Final = true; + return call; } else if (vmfunc->Proto->ReturnTypes.Size() > 0) { // Call, expecting one result @@ -3913,6 +4088,7 @@ bool FxVMFunctionCall::CheckEmitCast(VMFunctionBuilder *build, bool returnit, Ex where.Free(build); } reg = ExpEmit(); + reg.Final = true; } else { @@ -4540,54 +4716,77 @@ ExpEmit FxJumpStatement::Emit(VMFunctionBuilder *build) // //========================================================================== -FxReturnStatement::FxReturnStatement(FxVMFunctionCall *call, const FScriptPosition &pos) -: FxExpression(pos), Call(call) +FxReturnStatement::FxReturnStatement(FxExpression *value, const FScriptPosition &pos) +: FxExpression(pos), Value(value) { + ValueType = TypeVoid; } FxReturnStatement::~FxReturnStatement() { - SAFE_DELETE(Call); + SAFE_DELETE(Value); } FxExpression *FxReturnStatement::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); - if (Call != NULL) + SAFE_RESOLVE_OPT(Value, ctx); + + PPrototype *retproto; + if (Value == nullptr) { - Call = static_cast(Call->Resolve(ctx)); - ABORT(Call); + TArray none(0); + retproto = NewPrototype(none, none); } + else + { + retproto = Value->ReturnProto(); + } + + ctx.CheckReturn(retproto, ScriptPosition); + return this; } ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build) { - // If we return nothing, use a regular RET opcode. If we return - // something, use TAIL to call the function. Our return type - // should be compatible with the called function's return type. - if (Call == NULL) + // If we return nothing, use a regular RET opcode. + // Otherwise just return the value we're given. + if (Value == nullptr) { build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); } else { - Call->Emit(build, true); + ExpEmit ret = Value->Emit(build); + + // Check if it is a function call that simplified itself + // into a tail call in which case we don't emit anything. + if (!ret.Final) + { + if (Value->ValueType == TypeVoid) + { // Nothing is returned. + build->Emit(OP_RET, RET_FINAL, REGT_NIL, 0); + } + else + { + build->Emit(OP_RET, RET_FINAL, ret.RegType | (ret.Konst ? REGT_KONST : 0), ret.RegNum); + } + } } - return ExpEmit(); + + ExpEmit out; + out.Final = true; + return out; } VMFunction *FxReturnStatement::GetDirectFunction() { - // If this return statement calls a function with no arguments, - // then it can be a "direct" function. That is, the DECORATE - // definition can call that function directly without wrapping - // it inside VM code. - if (Call != NULL && Call->GetArgCount() == 0 && (Call->GetFunction()->Flags & VARF_Action)) + if (Value != nullptr) { - return Call->GetVMFunction(); + return Value->GetDirectFunction(); } - return NULL; + return nullptr; } //========================================================================== @@ -4754,6 +4953,7 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx) FxRuntimeStateIndex::FxRuntimeStateIndex(FxExpression *index) : FxExpression(index->ScriptPosition), Index(index) { + EmitTail = false; ValueType = TypeState; } @@ -4762,6 +4962,12 @@ FxRuntimeStateIndex::~FxRuntimeStateIndex() SAFE_DELETE(Index); } +PPrototype *FxRuntimeStateIndex::ReturnProto() +{ + EmitTail = true; + return FxExpression::ReturnProto(); +} + FxExpression *FxRuntimeStateIndex::Resolve(FCompileContext &ctx) { CHECKRESOLVED(); @@ -4794,14 +5000,12 @@ static int DecoHandleRuntimeState(VMFrameStack *stack, VMValue *param, int numpa // Null is returned if the location was invalid which means that no jump will be performed // if used as return value // 0 always meant the same thing so we handle it here for compatibility - ret->SetPointer(nullptr, ATAG_STATE); + ACTION_RETURN_STATE(nullptr); } else { - ret->SetPointer(stateinfo->mCallingState + index, ATAG_STATE); + ACTION_RETURN_STATE(stateinfo->mCallingState + index); } - - return 1; } ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build) @@ -4823,8 +5027,16 @@ ExpEmit FxRuntimeStateIndex::Emit(VMFunctionBuilder *build) assert(((PSymbolVMFunction *)sym)->Function != nullptr); callfunc = ((PSymbolVMFunction *)sym)->Function; - build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); - build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum); + if (EmitTail) + { + build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + out.Final = true; + } + else + { + build->Emit(OP_CALL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 3, 1); + build->Emit(OP_RESULT, 0, REGT_POINTER, out.RegNum); + } return out; } diff --git a/src/thingdef/thingdef_states.cpp b/src/thingdef/thingdef_states.cpp index 652bdf9e9e..707de30451 100644 --- a/src/thingdef/thingdef_states.cpp +++ b/src/thingdef/thingdef_states.cpp @@ -315,10 +315,10 @@ do_stop: } bool hasfinalret; - tcall->Code = ParseActions(sc, state, statestring, bag, tcall->Proto, hasfinalret); - if (!hasfinalret) + tcall->Code = ParseActions(sc, state, statestring, bag, hasfinalret); + if (!hasfinalret && tcall->Code != nullptr) { - AddImplicitReturn(static_cast(tcall->Code), tcall->Proto, sc); + static_cast(tcall->Code)->Add(new FxReturnStatement(nullptr, sc)); } goto endofstate; } @@ -351,125 +351,37 @@ endofstate: sc.SetEscape(true); // re-enable escape sequences } -//========================================================================== -// -// AddImplicitReturn -// -// Adds an implied return; statement to the end of a code sequence. -// -//========================================================================== - -void AddImplicitReturn(FxSequence *code, const PPrototype *proto, FScanner &sc) -{ - if (code == NULL) - { - return; - } - if (proto == NULL || proto->ReturnTypes.Size() == 0) - { // Returns nothing. Good. We can safely add an implied return. - code->Add(new FxReturnStatement(NULL, sc)); - } - else - { // Something was returned earlier in the sequence. Make it an error - // instead of adding an implicit one. - sc.ScriptError("Not all paths return a value"); - } -} - -//========================================================================== -// -// ReturnCheck -// -// If proto1 is NULL, returns proto2. If proto2 is NULL, returns proto1. -// If neither is null, checks if both prototypes define the same return -// types. If not, an error is flagged. -// -//========================================================================== - -static PPrototype *ReturnCheck(PPrototype *proto1, PPrototype *proto2, FScanner &sc) -{ - if (proto1 == NULL) - { - return proto2; - } - if (proto2 == NULL) - { - return proto1; - } - // A prototype that defines fewer return types can be compatible with - // one that defines more if the shorter one matches the initial types - // for the longer one. - if (proto2->ReturnTypes.Size() < proto1->ReturnTypes.Size()) - { // Make proto1 the shorter one to avoid code duplication below. - swapvalues(proto1, proto2); - } - // If one prototype returns nothing, they both must. - if (proto1->ReturnTypes.Size() == 0) - { - if (proto2->ReturnTypes.Size() == 0) - { - return proto1; - } - proto1 = NULL; - } - else - { - for (unsigned i = 0; i < proto1->ReturnTypes.Size(); ++i) - { - if (proto1->ReturnTypes[i] != proto2->ReturnTypes[i]) - { // Incompatible - proto1 = NULL; - break; - } - } - } - if (proto1 == NULL) - { - sc.ScriptError("Return types are incompatible"); - } - return proto1; -} - //========================================================================== // // ParseActions // -// If this action block contains any return statements, the prototype for -// one of them will be returned. This is used for deducing the return type -// of anonymous functions. All called functions passed to return must have -// matching return types. -// //========================================================================== -static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *add, *cond; - FxExpression *true_part, *false_part = NULL; - PPrototype *true_proto, *false_proto = NULL; + FxExpression *true_part, *false_part = nullptr; bool true_ret, false_ret = false; sc.MustGetStringName("("); cond = ParseExpression(sc, bag.Info); sc.MustGetStringName(")"); sc.MustGetStringName("{"); // braces are mandatory - true_part = ParseActions(sc, state, statestring, bag, true_proto, true_ret); + true_part = ParseActions(sc, state, statestring, bag, true_ret); sc.MustGetString(); if (sc.Compare("else")) { if (sc.CheckString("if")) { - false_part = ParseIf(sc, state, statestring, bag, false_proto, false_ret); + false_part = ParseIf(sc, state, statestring, bag, false_ret); } else { sc.MustGetStringName("{"); // braces are still mandatory - false_part = ParseActions(sc, state, statestring, bag, false_proto, false_ret); + false_part = ParseActions(sc, state, statestring, bag, false_ret); sc.MustGetString(); } } add = new FxIfStatement(cond, true_part, false_part, sc); - retproto = ReturnCheck(retproto, true_proto, sc); - retproto = ReturnCheck(retproto, false_proto, sc); // If one side does not end with a return, we don't consider the if statement // to end with a return. If the else case is missing, it can never be considered // as ending with a return. @@ -480,11 +392,9 @@ static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Ba return add; } -static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *cond, *code; - PPrototype *proto; bool ret; sc.MustGetStringName("("); @@ -492,24 +402,21 @@ static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, sc.MustGetStringName(")"); sc.MustGetStringName("{"); // Enforce braces like for if statements. - code = ParseActions(sc, state, statestring, bag, proto, ret); + code = ParseActions(sc, state, statestring, bag, ret); sc.MustGetString(); - retproto = ReturnCheck(retproto, proto, sc); lastwasret = false; // A while loop always jumps back. return new FxWhileLoop(cond, code, sc); } -static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *cond, *code; - PPrototype *proto; bool ret; sc.MustGetStringName("{"); // Enforce braces like for if statements. - code = ParseActions(sc, state, statestring, bag, proto, ret); + code = ParseActions(sc, state, statestring, bag, ret); sc.MustGetStringName("while"); sc.MustGetStringName("("); @@ -518,20 +425,17 @@ static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestrin sc.MustGetStringName(";"); sc.MustGetString(); - retproto = ReturnCheck(retproto, proto, sc); lastwasret = false; return new FxDoWhileLoop(cond, code, sc); } -static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &lastwasret) +static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret) { FxExpression *init = nullptr; FxExpression *cond = nullptr; FxExpression *iter = nullptr; FxExpression *code = nullptr; - PPrototype *proto; bool ret; // Parse the statements. @@ -560,24 +464,21 @@ static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, B // Now parse the loop's content. sc.MustGetStringName("{"); // Enforce braces like for if statements. - code = ParseActions(sc, state, statestring, bag, proto, ret); + code = ParseActions(sc, state, statestring, bag, ret); sc.MustGetString(); - retproto = ReturnCheck(retproto, proto, sc); lastwasret = false; return new FxForLoop(init, cond, iter, code, sc); } -FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, - PPrototype *&retproto, bool &endswithret) +FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret) { // If it's not a '{', then it should be a single action. // Otherwise, it's a sequence of actions. if (!sc.Compare("{")) { FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag); - retproto = call->GetVMFunction()->Proto; endswithret = true; return new FxReturnStatement(call, sc); } @@ -585,7 +486,6 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg const FScriptPosition pos(sc); FxSequence *seq = NULL; - PPrototype *proto = NULL; bool lastwasret = false; sc.MustGetString(); @@ -595,38 +495,31 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg lastwasret = false; if (sc.Compare("if")) { // Handle an if statement - add = ParseIf(sc, state, statestring, bag, proto, lastwasret); + add = ParseIf(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("while")) { // Handle a while loop - add = ParseWhile(sc, state, statestring, bag, proto, lastwasret); + add = ParseWhile(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("do")) { // Handle a do-while loop - add = ParseDoWhile(sc, state, statestring, bag, proto, lastwasret); + add = ParseDoWhile(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("for")) { // Handle a for loop - add = ParseFor(sc, state, statestring, bag, proto, lastwasret); + add = ParseFor(sc, state, statestring, bag, lastwasret); } else if (sc.Compare("return")) { // Handle a return statement lastwasret = true; - FxVMFunctionCall *retexp = NULL; - PPrototype *retproto; + FxExpression *retexp = nullptr; sc.MustGetString(); if (!sc.Compare(";")) { - retexp = ParseAction(sc, state, statestring, bag); + sc.UnGet(); + retexp = ParseExpression(sc, bag.Info); sc.MustGetStringName(";"); - retproto = retexp->GetVMFunction()->Proto; } - else - { // Returning nothing; we still need a prototype for that. - TArray notypes(0); - retproto = NewPrototype(notypes, notypes); - } - proto = ReturnCheck(proto, retproto, sc); sc.MustGetString(); add = new FxReturnStatement(retexp, sc); } @@ -660,7 +553,6 @@ FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Bagg } } endswithret = lastwasret; - retproto = proto; return seq; }