mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-02-17 17:51:11 +00:00
- separated the code generation from the DECORATE parser and cleaned up the interface to the code generator. Most importantly, the VMScriptFunctions are now preallocated when being added to the list of functions to compile and will be filled in later by the code generator. This allowed the removal of some ugly maintenance code.
This commit is contained in:
parent
59ed26c0b6
commit
a72fbb771f
16 changed files with 287 additions and 274 deletions
|
@ -815,11 +815,15 @@ file( GLOB HEADER_FILES
|
||||||
posix/cocoa/*.h
|
posix/cocoa/*.h
|
||||||
posix/sdl/*.h
|
posix/sdl/*.h
|
||||||
r_data/*.h
|
r_data/*.h
|
||||||
|
rapidjson/*.h
|
||||||
resourcefiles/*.h
|
resourcefiles/*.h
|
||||||
sfmt/*.h
|
sfmt/*.h
|
||||||
sound/*.h
|
sound/*.h
|
||||||
textures/*.h
|
textures/*.h
|
||||||
scripting/*.h
|
scripting/*.h
|
||||||
|
scripting/codegeneration/*.h
|
||||||
|
scripting/decorate/*.h
|
||||||
|
scripting/zscript/*.h
|
||||||
scripting/vm/*.h
|
scripting/vm/*.h
|
||||||
xlat/*.h
|
xlat/*.h
|
||||||
*.h
|
*.h
|
||||||
|
|
|
@ -820,7 +820,8 @@ void SetDehParams(FState *state, int codepointer)
|
||||||
int argcount = MBFCodePointerFactories[codepointer](buildit, value1, value2);
|
int argcount = MBFCodePointerFactories[codepointer](buildit, value1, value2);
|
||||||
buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation, ATAG_OBJECT), NAP + argcount, 0);
|
buildit.Emit(OP_TAIL_K, buildit.GetConstantAddress(sym->Variants[0].Implementation, ATAG_OBJECT), NAP + argcount, 0);
|
||||||
// Attach it to the state.
|
// Attach it to the state.
|
||||||
VMScriptFunction *sfunc = buildit.MakeFunction();
|
VMScriptFunction *sfunc = new VMScriptFunction;
|
||||||
|
buildit.MakeFunction(sfunc);
|
||||||
sfunc->NumArgs = NAP;
|
sfunc->NumArgs = NAP;
|
||||||
state->SetAction(sfunc);
|
state->SetAction(sfunc);
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "m_random.h"
|
#include "m_random.h"
|
||||||
|
#include "sc_man.h"
|
||||||
|
#include "s_sound.h"
|
||||||
|
#include "actor.h"
|
||||||
|
|
||||||
|
|
||||||
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
#define CHECKRESOLVED() if (isresolved) return this; isresolved=true;
|
||||||
|
@ -58,14 +61,15 @@ class FxJumpStatement;
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
struct FScriptPosition;
|
||||||
|
|
||||||
struct FCompileContext
|
struct FCompileContext
|
||||||
{
|
{
|
||||||
TArray<FxJumpStatement *> Jumps;
|
TArray<FxJumpStatement *> Jumps;
|
||||||
PPrototype *ReturnProto;
|
PPrototype *ReturnProto;
|
||||||
PClassActor *Class;
|
PClass *Class;
|
||||||
|
|
||||||
FCompileContext(PClassActor *cls = nullptr, PPrototype *ret = nullptr);
|
FCompileContext(PClass *cls = nullptr, PPrototype *ret = nullptr);
|
||||||
|
|
||||||
PSymbol *FindInClass(FName identifier);
|
PSymbol *FindInClass(FName identifier);
|
||||||
PSymbol *FindGlobal(FName identifier);
|
PSymbol *FindGlobal(FName identifier);
|
||||||
|
@ -1175,7 +1179,6 @@ public:
|
||||||
class FxDamageValue : public FxExpression
|
class FxDamageValue : public FxExpression
|
||||||
{
|
{
|
||||||
FxExpression *val;
|
FxExpression *val;
|
||||||
VMScriptFunction *MyFunction;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -1184,8 +1187,6 @@ public:
|
||||||
FxExpression *Resolve(FCompileContext&);
|
FxExpression *Resolve(FCompileContext&);
|
||||||
|
|
||||||
ExpEmit Emit(VMFunctionBuilder *build);
|
ExpEmit Emit(VMFunctionBuilder *build);
|
||||||
VMScriptFunction *GetFunction() const { return MyFunction; }
|
|
||||||
void SetFunction(VMScriptFunction *func) { MyFunction = func; }
|
|
||||||
};
|
};
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
|
@ -91,7 +91,7 @@ static const FLOP FxFlops[] =
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
FCompileContext::FCompileContext(PClassActor *cls, PPrototype *ret) : Class(cls), ReturnProto(ret)
|
FCompileContext::FCompileContext(PClass *cls, PPrototype *ret) : Class(cls), ReturnProto(ret)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3902,7 +3902,7 @@ VMFunction *FxVMFunctionCall::GetDirectFunction()
|
||||||
// then it can be a "direct" function. That is, the DECORATE
|
// then it can be a "direct" function. That is, the DECORATE
|
||||||
// definition can call that function directly without wrapping
|
// definition can call that function directly without wrapping
|
||||||
// it inside VM code.
|
// it inside VM code.
|
||||||
if (EmitTail && (ArgList ? ArgList->Size() : 0) == 0 && (Function->Flags & VARF_Action))
|
if ((ArgList ? ArgList->Size() : 0) == 0 && (Function->Flags & VARF_Action))
|
||||||
{
|
{
|
||||||
return Function->Variants[0].Implementation;
|
return Function->Variants[0].Implementation;
|
||||||
}
|
}
|
||||||
|
@ -4922,20 +4922,19 @@ FxExpression *FxStateByIndex::Resolve(FCompileContext &ctx)
|
||||||
{
|
{
|
||||||
CHECKRESOLVED();
|
CHECKRESOLVED();
|
||||||
ABORT(ctx.Class);
|
ABORT(ctx.Class);
|
||||||
|
auto aclass = dyn_cast<PClassActor>(ctx.Class);
|
||||||
|
|
||||||
if (ctx.Class->NumOwnedStates == 0)
|
// This expression type can only be used from DECORATE, so there's no need to consider the possibility of calling it from a non-actor.
|
||||||
{
|
assert(aclass != nullptr && aclass->NumOwnedStates > 0);
|
||||||
// This can't really happen
|
|
||||||
assert(false);
|
if (aclass->NumOwnedStates <= index)
|
||||||
}
|
|
||||||
if (ctx.Class->NumOwnedStates <= index)
|
|
||||||
{
|
{
|
||||||
ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d",
|
ScriptPosition.Message(MSG_ERROR, "%s: Attempt to jump to non existing state index %d",
|
||||||
ctx.Class->TypeName.GetChars(), index);
|
ctx.Class->TypeName.GetChars(), index);
|
||||||
delete this;
|
delete this;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
FxExpression *x = new FxConstant(ctx.Class->OwnedStates + index, ScriptPosition);
|
FxExpression *x = new FxConstant(aclass->OwnedStates + index, ScriptPosition);
|
||||||
delete this;
|
delete this;
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
@ -5245,7 +5244,6 @@ FxDamageValue::FxDamageValue(FxExpression *v)
|
||||||
{
|
{
|
||||||
val = v;
|
val = v;
|
||||||
ValueType = TypeVoid;
|
ValueType = TypeVoid;
|
||||||
MyFunction = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
FxDamageValue::~FxDamageValue()
|
FxDamageValue::~FxDamageValue()
|
||||||
|
|
|
@ -441,42 +441,6 @@ static void ParseArgListDef(FScanner &sc, PClassActor *cls,
|
||||||
sc.MustGetToken(';');
|
sc.MustGetToken(';');
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
// SetImplicitArgs
|
|
||||||
//
|
|
||||||
// Adds the parameters implied by the function flags.
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClass *cls, DWORD funcflags)
|
|
||||||
{
|
|
||||||
// Must be called before adding any other arguments.
|
|
||||||
assert(args == NULL || args->Size() == 0);
|
|
||||||
assert(argflags == NULL || argflags->Size() == 0);
|
|
||||||
|
|
||||||
if (funcflags & VARF_Method)
|
|
||||||
{
|
|
||||||
// implied self pointer
|
|
||||||
if (args != NULL) args->Push(NewClassPointer(RUNTIME_CLASS(AActor)));
|
|
||||||
if (argflags != NULL) argflags->Push(VARF_Implicit);
|
|
||||||
}
|
|
||||||
if (funcflags & VARF_Action)
|
|
||||||
{
|
|
||||||
// implied stateowner and callingstate pointers
|
|
||||||
if (args != NULL)
|
|
||||||
{
|
|
||||||
args->Push(NewClassPointer(cls));
|
|
||||||
args->Push(TypeState);
|
|
||||||
}
|
|
||||||
if (argflags != NULL)
|
|
||||||
{
|
|
||||||
argflags->Push(VARF_Implicit);
|
|
||||||
argflags->Push(VARF_Implicit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// ParseFunctionDef
|
// ParseFunctionDef
|
||||||
|
@ -1559,3 +1523,14 @@ void ParseDecorate (FScanner &sc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ParseAllDecorate()
|
||||||
|
{
|
||||||
|
int lastlump, lump;
|
||||||
|
|
||||||
|
while ((lump = Wads.FindLump("DECORATE", &lastlump)) != -1)
|
||||||
|
{
|
||||||
|
FScanner sc(lump);
|
||||||
|
ParseDecorate(sc);
|
||||||
|
}
|
||||||
|
}
|
|
@ -57,8 +57,7 @@
|
||||||
#include "codegeneration/thingdef_exp.h"
|
#include "codegeneration/thingdef_exp.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "templates.h"
|
#include "templates.h"
|
||||||
|
#include "vmbuilder.h"
|
||||||
TDeletingArray<FStateTempCall *> StateTempCalls;
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//***
|
//***
|
||||||
|
@ -141,13 +140,14 @@ void ParseStates(FScanner &sc, PClassActor * actor, AActor * defaults, Baggage &
|
||||||
FString statestring;
|
FString statestring;
|
||||||
FState state;
|
FState state;
|
||||||
char lastsprite[5] = "";
|
char lastsprite[5] = "";
|
||||||
FStateTempCall *tcall = NULL;
|
FxExpression *ScriptCode;
|
||||||
FArgumentList *args = NULL;
|
FArgumentList *args = nullptr;
|
||||||
|
|
||||||
sc.MustGetStringName ("{");
|
sc.MustGetStringName ("{");
|
||||||
sc.SetEscape(false); // disable escape sequences in the state parser
|
sc.SetEscape(false); // disable escape sequences in the state parser
|
||||||
while (!sc.CheckString ("}") && !sc.End)
|
while (!sc.CheckString ("}") && !sc.End)
|
||||||
{
|
{
|
||||||
|
ScriptCode = nullptr;
|
||||||
memset(&state,0,sizeof(state));
|
memset(&state,0,sizeof(state));
|
||||||
statestring = ParseStateString(sc);
|
statestring = ParseStateString(sc);
|
||||||
if (!statestring.CompareNoCase("GOTO"))
|
if (!statestring.CompareNoCase("GOTO"))
|
||||||
|
@ -224,10 +224,6 @@ do_stop:
|
||||||
sc.MustGetString();
|
sc.MustGetString();
|
||||||
statestring = sc.String;
|
statestring = sc.String;
|
||||||
|
|
||||||
if (tcall == NULL)
|
|
||||||
{
|
|
||||||
tcall = new FStateTempCall;
|
|
||||||
}
|
|
||||||
if (sc.CheckString("RANDOM"))
|
if (sc.CheckString("RANDOM"))
|
||||||
{
|
{
|
||||||
int min, max;
|
int min, max;
|
||||||
|
@ -315,35 +311,27 @@ do_stop:
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasfinalret;
|
bool hasfinalret;
|
||||||
tcall->Code = ParseActions(sc, state, statestring, bag, hasfinalret);
|
ScriptCode = ParseActions(sc, state, statestring, bag, hasfinalret);
|
||||||
if (!hasfinalret && tcall->Code != nullptr)
|
if (!hasfinalret && ScriptCode != nullptr)
|
||||||
{
|
{
|
||||||
static_cast<FxSequence *>(tcall->Code)->Add(new FxReturnStatement(nullptr, sc));
|
static_cast<FxSequence *>(ScriptCode)->Add(new FxReturnStatement(nullptr, sc));
|
||||||
}
|
}
|
||||||
goto endofstate;
|
goto endofstate;
|
||||||
}
|
}
|
||||||
sc.UnGet();
|
sc.UnGet();
|
||||||
endofstate:
|
endofstate:
|
||||||
|
if (ScriptCode != nullptr)
|
||||||
|
{
|
||||||
|
state.ActionFunc = FunctionBuildList.AddFunction(actor, ScriptCode, FStringf("%s.StateFunction.%d", actor->TypeName.GetChars(), bag.statedef.GetStateCount()), true);
|
||||||
|
}
|
||||||
int count = bag.statedef.AddStates(&state, statestring);
|
int count = bag.statedef.AddStates(&state, statestring);
|
||||||
if (count < 0)
|
if (count < 0)
|
||||||
{
|
{
|
||||||
sc.ScriptError("Invalid frame character string '%s'", statestring.GetChars());
|
sc.ScriptError("Invalid frame character string '%s'", statestring.GetChars());
|
||||||
count = -count;
|
count = -count;
|
||||||
}
|
}
|
||||||
if (tcall->Code != NULL)
|
|
||||||
{
|
|
||||||
tcall->ActorClass = actor;
|
|
||||||
tcall->FirstState = bag.statedef.GetStateCount() - count;
|
|
||||||
tcall->NumStates = count;
|
|
||||||
StateTempCalls.Push(tcall);
|
|
||||||
tcall = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (tcall != NULL)
|
|
||||||
{
|
|
||||||
delete tcall;
|
|
||||||
}
|
|
||||||
if (args != NULL)
|
if (args != NULL)
|
||||||
{
|
{
|
||||||
delete args;
|
delete args;
|
||||||
|
|
|
@ -66,174 +66,48 @@
|
||||||
#include "vmbuilder.h"
|
#include "vmbuilder.h"
|
||||||
#include "stats.h"
|
#include "stats.h"
|
||||||
|
|
||||||
TDeletingArray<class FxExpression *> ActorDamageFuncs;
|
|
||||||
|
|
||||||
|
|
||||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||||
void InitThingdef();
|
void InitThingdef();
|
||||||
void ParseDecorate(FScanner &ctx);
|
|
||||||
|
|
||||||
// STATIC FUNCTION PROTOTYPES --------------------------------------------
|
// STATIC FUNCTION PROTOTYPES --------------------------------------------
|
||||||
PClassActor *QuestItemClasses[31];
|
PClassActor *QuestItemClasses[31];
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Do some postprocessing after everything has been defined
|
// SetImplicitArgs
|
||||||
|
//
|
||||||
|
// Adds the parameters implied by the function flags.
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
static void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen)
|
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClass *cls, DWORD funcflags)
|
||||||
{
|
{
|
||||||
const char *marks = "=======================================================";
|
// Must be called before adding any other arguments.
|
||||||
fprintf(dump, "\n%.*s %s %.*s", MAX(3, 38 - labellen / 2), marks, label, MAX(3, 38 - labellen / 2), marks);
|
assert(args == NULL || args->Size() == 0);
|
||||||
fprintf(dump, "\nInteger regs: %-3d Float regs: %-3d Address regs: %-3d String regs: %-3d\nStack size: %d\n",
|
assert(argflags == NULL || argflags->Size() == 0);
|
||||||
sfunc->NumRegD, sfunc->NumRegF, sfunc->NumRegA, sfunc->NumRegS, sfunc->MaxParam);
|
|
||||||
VMDumpConstants(dump, sfunc);
|
if (funcflags & VARF_Method)
|
||||||
fprintf(dump, "\nDisassembly @ %p:\n", sfunc->Code);
|
{
|
||||||
VMDisasm(dump, sfunc->Code, sfunc->CodeSize, sfunc);
|
// implied self pointer
|
||||||
|
if (args != NULL) args->Push(NewClassPointer(cls));
|
||||||
|
if (argflags != NULL) argflags->Push(VARF_Implicit);
|
||||||
}
|
}
|
||||||
|
if (funcflags & VARF_Action)
|
||||||
static void FinishThingdef()
|
|
||||||
{
|
{
|
||||||
int errorcount = 0;
|
// implied caller and callingstate pointers
|
||||||
unsigned i;
|
if (args != NULL)
|
||||||
int codesize = 0;
|
|
||||||
FILE *dump = NULL;
|
|
||||||
|
|
||||||
if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w");
|
|
||||||
|
|
||||||
for (i = 0; i < StateTempCalls.Size(); ++i)
|
|
||||||
{
|
{
|
||||||
FStateTempCall *tcall = StateTempCalls[i];
|
args->Insert(0, NewClassPointer(RUNTIME_CLASS(AActor))); // the caller must go before self due to an old design mistake.
|
||||||
VMFunction *func = nullptr;
|
args->Push(TypeState);
|
||||||
|
|
||||||
assert(tcall->Code != NULL);
|
|
||||||
|
|
||||||
// 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 != 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(true);
|
|
||||||
|
|
||||||
assert(tcall->Proto != nullptr);
|
|
||||||
|
|
||||||
// Allocate registers used to pass parameters in.
|
|
||||||
// self, stateowner, state (all are pointers)
|
|
||||||
buildit.Registers[REGT_POINTER].Get(3);
|
|
||||||
|
|
||||||
// Emit code
|
|
||||||
tcall->Code->Emit(&buildit);
|
|
||||||
|
|
||||||
VMScriptFunction *sfunc = buildit.MakeFunction();
|
|
||||||
sfunc->NumArgs = NAP;
|
|
||||||
|
|
||||||
// Generate prototype for this anonymous function
|
|
||||||
TArray<PType *> args(3);
|
|
||||||
SetImplicitArgs(&args, NULL, tcall->ActorClass, VARF_Method | VARF_Action);
|
|
||||||
sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args);
|
|
||||||
|
|
||||||
func = sfunc;
|
|
||||||
|
|
||||||
if (dump != NULL)
|
|
||||||
{
|
|
||||||
char label[64];
|
|
||||||
int labellen = mysnprintf(label, countof(label), "Function %s.States[%d] (*%d)",
|
|
||||||
tcall->ActorClass->TypeName.GetChars(), tcall->FirstState, tcall->NumStates);
|
|
||||||
DumpFunction(dump, sfunc, label, labellen);
|
|
||||||
codesize += sfunc->CodeSize;
|
|
||||||
}
|
}
|
||||||
}
|
if (argflags != NULL)
|
||||||
|
|
||||||
delete tcall->Code;
|
|
||||||
tcall->Code = nullptr;
|
|
||||||
for (int k = 0; k < tcall->NumStates; ++k)
|
|
||||||
{
|
{
|
||||||
tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func);
|
argflags->Push(VARF_Implicit);
|
||||||
|
argflags->Push(VARF_Implicit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < PClassActor::AllActorClasses.Size(); i++)
|
|
||||||
{
|
|
||||||
PClassActor *ti = PClassActor::AllActorClasses[i];
|
|
||||||
|
|
||||||
if (ti->Size == TentativeClass)
|
|
||||||
{
|
|
||||||
Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars());
|
|
||||||
errorcount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
AActor *def = GetDefaultByType(ti);
|
|
||||||
|
|
||||||
if (!def)
|
|
||||||
{
|
|
||||||
Printf(TEXTCOLOR_RED "No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars());
|
|
||||||
errorcount++;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (def->DamageFunc != nullptr)
|
|
||||||
{
|
|
||||||
FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->DamageFunc - 1];
|
|
||||||
VMScriptFunction *sfunc;
|
|
||||||
sfunc = dmg->GetFunction();
|
|
||||||
if (sfunc == nullptr)
|
|
||||||
{
|
|
||||||
FCompileContext ctx(ti);
|
|
||||||
dmg = static_cast<FxDamageValue *>(dmg->Resolve(ctx));
|
|
||||||
|
|
||||||
if (dmg != nullptr)
|
|
||||||
{
|
|
||||||
VMFunctionBuilder buildit;
|
|
||||||
buildit.Registers[REGT_POINTER].Get(1); // The self pointer
|
|
||||||
dmg->Emit(&buildit);
|
|
||||||
sfunc = buildit.MakeFunction();
|
|
||||||
sfunc->NumArgs = 1;
|
|
||||||
sfunc->Proto = nullptr; ///FIXME: Need a proper prototype here
|
|
||||||
// Save this function in case this damage value was reused
|
|
||||||
// (which happens quite easily with inheritance).
|
|
||||||
dmg->SetFunction(sfunc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def->DamageFunc = sfunc;
|
|
||||||
|
|
||||||
if (dump != nullptr && sfunc != nullptr)
|
|
||||||
{
|
|
||||||
char label[64];
|
|
||||||
int labellen = mysnprintf(label, countof(label), "Function %s.Damage",
|
|
||||||
ti->TypeName.GetChars());
|
|
||||||
DumpFunction(dump, sfunc, label, labellen);
|
|
||||||
codesize += sfunc->CodeSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (dump != NULL)
|
|
||||||
{
|
|
||||||
fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4);
|
|
||||||
fclose(dump);
|
|
||||||
}
|
|
||||||
if (errorcount > 0)
|
|
||||||
{
|
|
||||||
I_Error("%d errors during actor postprocessing", errorcount);
|
|
||||||
}
|
|
||||||
|
|
||||||
ActorDamageFuncs.DeleteAndClear();
|
|
||||||
StateTempCalls.DeleteAndClear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// LoadActors
|
// LoadActors
|
||||||
|
@ -242,29 +116,48 @@ static void FinishThingdef()
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
void ParseScripts();
|
void ParseScripts();
|
||||||
|
void ParseAllDecorate();
|
||||||
|
|
||||||
void LoadActors ()
|
void LoadActors ()
|
||||||
{
|
{
|
||||||
int lastlump, lump;
|
|
||||||
cycle_t timer;
|
cycle_t timer;
|
||||||
|
|
||||||
timer.Reset(); timer.Clock();
|
timer.Reset(); timer.Clock();
|
||||||
ActorDamageFuncs.Clear();
|
|
||||||
InitThingdef();
|
|
||||||
lastlump = 0;
|
|
||||||
ParseScripts();
|
|
||||||
|
|
||||||
FScriptPosition::ResetErrorCounter();
|
FScriptPosition::ResetErrorCounter();
|
||||||
while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
|
|
||||||
{
|
InitThingdef();
|
||||||
FScanner sc(lump);
|
ParseScripts();
|
||||||
ParseDecorate (sc);
|
ParseAllDecorate();
|
||||||
}
|
|
||||||
FinishThingdef();
|
FunctionBuildList.Build();
|
||||||
|
|
||||||
if (FScriptPosition::ErrorCounter > 0)
|
if (FScriptPosition::ErrorCounter > 0)
|
||||||
{
|
{
|
||||||
I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
|
I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int errorcount = 0;
|
||||||
|
for (auto ti : PClassActor::AllActorClasses)
|
||||||
|
{
|
||||||
|
if (ti->Size == TentativeClass)
|
||||||
|
{
|
||||||
|
Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars());
|
||||||
|
errorcount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetDefaultByType(ti) == nullptr)
|
||||||
|
{
|
||||||
|
Printf(TEXTCOLOR_RED "No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars());
|
||||||
|
errorcount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (errorcount > 0)
|
||||||
|
{
|
||||||
|
I_Error("%d errors during actor postprocessing", errorcount);
|
||||||
|
}
|
||||||
|
|
||||||
timer.Unclock();
|
timer.Unclock();
|
||||||
if (!batchrun) Printf("DECORATE parsing took %.2f ms\n", timer.TimeMS());
|
if (!batchrun) Printf("DECORATE parsing took %.2f ms\n", timer.TimeMS());
|
||||||
// Base time: ~52 ms
|
// Base time: ~52 ms
|
||||||
|
|
|
@ -101,25 +101,6 @@ public:
|
||||||
int GetStateCount() const { return StateArray.Size(); }
|
int GetStateCount() const { return StateArray.Size(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
//==========================================================================
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//
|
|
||||||
//==========================================================================
|
|
||||||
|
|
||||||
struct FStateTempCall
|
|
||||||
{
|
|
||||||
FStateTempCall() : ActorClass(NULL), Code(NULL), FirstState(0), NumStates(0) {}
|
|
||||||
|
|
||||||
PClassActor *ActorClass;
|
|
||||||
class FxExpression *Code;
|
|
||||||
class PPrototype *Proto;
|
|
||||||
int FirstState;
|
|
||||||
int NumStates;
|
|
||||||
};
|
|
||||||
extern TDeletingArray<FStateTempCall *> StateTempCalls;
|
|
||||||
extern TDeletingArray<class FxExpression *> ActorDamageFuncs;
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Extra info maintained while defining an actor.
|
// Extra info maintained while defining an actor.
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
#include "teaminfo.h"
|
#include "teaminfo.h"
|
||||||
#include "v_video.h"
|
#include "v_video.h"
|
||||||
#include "r_data/colormaps.h"
|
#include "r_data/colormaps.h"
|
||||||
|
#include "vmbuilder.h"
|
||||||
|
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -656,7 +657,7 @@ DEFINE_PROPERTY(damage, X, Actor)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
defaults->DamageFunc = (VMFunction *)(uintptr_t)(ActorDamageFuncs.Push(id) + 1);
|
defaults->DamageFunc = FunctionBuildList.AddFunction(bag.Info, id, FStringf("%s.DamageFunction", bag.Info->TypeName.GetChars()), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1020,4 +1020,5 @@ class PFunction;
|
||||||
PFunction *FindGlobalActionFunction(const char *name);
|
PFunction *FindGlobalActionFunction(const char *name);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -32,6 +32,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "vmbuilder.h"
|
#include "vmbuilder.h"
|
||||||
|
#include "codegeneration/thingdef_exp.h"
|
||||||
|
#include "info.h"
|
||||||
|
#include "m_argv.h"
|
||||||
|
#include "thingdef.h"
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
|
@ -68,10 +72,8 @@ VMFunctionBuilder::~VMFunctionBuilder()
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
VMScriptFunction *VMFunctionBuilder::MakeFunction()
|
void VMFunctionBuilder::MakeFunction(VMScriptFunction *func)
|
||||||
{
|
{
|
||||||
VMScriptFunction *func = new VMScriptFunction;
|
|
||||||
|
|
||||||
func->Alloc(Code.Size(), NumIntConstants, NumFloatConstants, NumStringConstants, NumAddressConstants);
|
func->Alloc(Code.Size(), NumIntConstants, NumFloatConstants, NumStringConstants, NumAddressConstants);
|
||||||
|
|
||||||
// Copy code block.
|
// Copy code block.
|
||||||
|
@ -106,8 +108,6 @@ VMScriptFunction *VMFunctionBuilder::MakeFunction()
|
||||||
// entries on the parameter stack, but it means the caller probably
|
// entries on the parameter stack, but it means the caller probably
|
||||||
// did something wrong.
|
// did something wrong.
|
||||||
assert(ActiveParam == 0);
|
assert(ActiveParam == 0);
|
||||||
|
|
||||||
return func;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -634,3 +634,113 @@ void VMFunctionBuilder::BackpatchToHere(size_t loc)
|
||||||
{
|
{
|
||||||
Backpatch(loc, Code.Size());
|
Backpatch(loc, Code.Size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// FFunctionBuildList
|
||||||
|
//
|
||||||
|
// This list contains all functions yet to build.
|
||||||
|
// All adding functions return a VMFunction - either a complete one
|
||||||
|
// for native functions or an empty VMScriptFunction for scripted ones
|
||||||
|
// This VMScriptFunction object later gets filled in with the actual
|
||||||
|
// info, but we get the pointer right after registering the function
|
||||||
|
// with the builder.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
FFunctionBuildList FunctionBuildList;
|
||||||
|
|
||||||
|
VMFunction *FFunctionBuildList::AddFunction(PClass *cls, FxExpression *code, const FString &name, bool statecall)
|
||||||
|
{
|
||||||
|
auto func = code->GetDirectFunction();
|
||||||
|
if (func != nullptr)
|
||||||
|
{
|
||||||
|
delete code;
|
||||||
|
return func;
|
||||||
|
}
|
||||||
|
|
||||||
|
Printf("Adding %s\n", name.GetChars());
|
||||||
|
|
||||||
|
Item it;
|
||||||
|
it.Class = cls;
|
||||||
|
it.Code = code;
|
||||||
|
it.DumpName = name;
|
||||||
|
it.Function = new VMScriptFunction;
|
||||||
|
it.Proto = nullptr;
|
||||||
|
it.type = statecall;
|
||||||
|
mItems.Push(it);
|
||||||
|
return it.Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FFunctionBuildList::Build()
|
||||||
|
{
|
||||||
|
int errorcount = 0;
|
||||||
|
int codesize = 0;
|
||||||
|
FILE *dump = nullptr;
|
||||||
|
|
||||||
|
if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w");
|
||||||
|
|
||||||
|
for (auto &item : mItems)
|
||||||
|
{
|
||||||
|
assert(item.Code != NULL);
|
||||||
|
|
||||||
|
// We don't know the return type in advance for anonymous functions.
|
||||||
|
FCompileContext ctx(item.Class, nullptr);
|
||||||
|
item.Code = item.Code->Resolve(ctx);
|
||||||
|
item.Proto = ctx.ReturnProto;
|
||||||
|
|
||||||
|
// Make sure resolving it didn't obliterate it.
|
||||||
|
if (item.Code != nullptr)
|
||||||
|
{
|
||||||
|
VMFunctionBuilder buildit(true);
|
||||||
|
|
||||||
|
assert(item.Proto != nullptr);
|
||||||
|
|
||||||
|
int numargs;
|
||||||
|
int flags;
|
||||||
|
|
||||||
|
// Kludge alert. This needs to be done in a more universal fashion.
|
||||||
|
// Right now there's only action and damage functions, so for the time being
|
||||||
|
// this will do to get the whole thing started first.
|
||||||
|
|
||||||
|
if (item.type == 1) // anonymous action function
|
||||||
|
{
|
||||||
|
numargs = NAP;
|
||||||
|
flags = VARF_Method | VARF_Action;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
numargs = 1;
|
||||||
|
flags = VARF_Method;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate prototype for this anonymous function
|
||||||
|
buildit.Registers[REGT_POINTER].Get(numargs);
|
||||||
|
TArray<PType *> args(numargs);
|
||||||
|
SetImplicitArgs(&args, nullptr, item.Class, flags);
|
||||||
|
|
||||||
|
VMScriptFunction *sfunc = item.Function;
|
||||||
|
item.Function->Proto = NewPrototype(item.Proto->ReturnTypes, args);
|
||||||
|
|
||||||
|
// Emit code
|
||||||
|
item.Code->Emit(&buildit);
|
||||||
|
buildit.MakeFunction(item.Function);
|
||||||
|
item.Function->NumArgs = numargs;
|
||||||
|
|
||||||
|
if (dump != nullptr)
|
||||||
|
{
|
||||||
|
char label[64];
|
||||||
|
int labellen = mysnprintf(label, countof(label), item.DumpName,
|
||||||
|
item.Class->TypeName.GetChars());
|
||||||
|
DumpFunction(dump, sfunc, label, labellen);
|
||||||
|
codesize += sfunc->CodeSize;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
delete item.Code;
|
||||||
|
}
|
||||||
|
if (dump != nullptr)
|
||||||
|
{
|
||||||
|
fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4);
|
||||||
|
fclose(dump);
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ public:
|
||||||
VMFunctionBuilder(bool checkself = false);
|
VMFunctionBuilder(bool checkself = false);
|
||||||
~VMFunctionBuilder();
|
~VMFunctionBuilder();
|
||||||
|
|
||||||
VMScriptFunction *MakeFunction();
|
void MakeFunction(VMScriptFunction *func);
|
||||||
|
|
||||||
// Returns the constant register holding the value.
|
// Returns the constant register holding the value.
|
||||||
int GetConstantInt(int val);
|
int GetConstantInt(int val);
|
||||||
|
@ -87,4 +87,34 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen);
|
||||||
|
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
class FxExpression;
|
||||||
|
|
||||||
|
class FFunctionBuildList
|
||||||
|
{
|
||||||
|
struct Item
|
||||||
|
{
|
||||||
|
PClass *Class = nullptr;
|
||||||
|
FxExpression *Code = nullptr;
|
||||||
|
PPrototype *Proto = nullptr;
|
||||||
|
VMScriptFunction *Function = nullptr;
|
||||||
|
FString DumpName;
|
||||||
|
int type; // temporary kludge
|
||||||
|
};
|
||||||
|
|
||||||
|
TArray<Item> mItems;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VMFunction *AddFunction(PClass *cls, FxExpression *code, const FString &name, bool statecall = false);
|
||||||
|
void Build();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern FFunctionBuildList FunctionBuildList;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
|
|
||||||
#include "vm.h"
|
#include "vm.h"
|
||||||
#include "c_console.h"
|
#include "c_console.h"
|
||||||
|
#include "templates.h"
|
||||||
|
|
||||||
#define NOP MODE_AUNUSED | MODE_BUNUSED | MODE_CUNUSED
|
#define NOP MODE_AUNUSED | MODE_BUNUSED | MODE_CUNUSED
|
||||||
|
|
||||||
|
@ -598,3 +599,21 @@ static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const
|
||||||
}
|
}
|
||||||
return col;
|
return col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Do some postprocessing after everything has been defined
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen)
|
||||||
|
{
|
||||||
|
const char *marks = "=======================================================";
|
||||||
|
fprintf(dump, "\n%.*s %s %.*s", MAX(3, 38 - labellen / 2), marks, label, MAX(3, 38 - labellen / 2), marks);
|
||||||
|
fprintf(dump, "\nInteger regs: %-3d Float regs: %-3d Address regs: %-3d String regs: %-3d\nStack size: %d\n",
|
||||||
|
sfunc->NumRegD, sfunc->NumRegF, sfunc->NumRegA, sfunc->NumRegS, sfunc->MaxParam);
|
||||||
|
VMDumpConstants(dump, sfunc);
|
||||||
|
fprintf(dump, "\nDisassembly @ %p:\n", sfunc->Code);
|
||||||
|
VMDisasm(dump, sfunc->Code, sfunc->CodeSize, sfunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include "p_lnspec.h"
|
#include "p_lnspec.h"
|
||||||
#include "gdtoa.h"
|
#include "gdtoa.h"
|
||||||
#include "codegeneration/thingdef_exp.h"
|
#include "codegeneration/thingdef_exp.h"
|
||||||
|
#include "vmbuilder.h"
|
||||||
|
|
||||||
#define DEFINING_CONST ((PSymbolConst *)(void *)1)
|
#define DEFINING_CONST ((PSymbolConst *)(void *)1)
|
||||||
|
|
||||||
|
@ -2165,12 +2166,7 @@ void ZCCCompiler::CompileStates()
|
||||||
auto code = SetupActionFunction(static_cast<PClassActor *>(c->Type()), sl->Action);
|
auto code = SetupActionFunction(static_cast<PClassActor *>(c->Type()), sl->Action);
|
||||||
if (code != nullptr)
|
if (code != nullptr)
|
||||||
{
|
{
|
||||||
auto tcall = new FStateTempCall;
|
state.ActionFunc = FunctionBuildList.AddFunction(c->Type(), code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), true);
|
||||||
tcall->Code = code;
|
|
||||||
tcall->ActorClass = static_cast<PClassActor *>(c->Type());
|
|
||||||
tcall->FirstState = statedef.GetStateCount() - count;
|
|
||||||
tcall->NumStates = count;
|
|
||||||
StateTempCalls.Push(tcall);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -1192,3 +1192,11 @@ FStringData *FStringData::MakeCopy ()
|
||||||
FString::StrCopy (copy->Chars(), Chars(), Len);
|
FString::StrCopy (copy->Chars(), Chars(), Len);
|
||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FStringf::FStringf(const char *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
VFormat(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
|
@ -308,6 +308,13 @@ private:
|
||||||
bool operator >= (const FString &illegal) const;
|
bool operator >= (const FString &illegal) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FStringf : public FString
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FStringf(const char *fmt, ...);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
namespace StringFormat
|
namespace StringFormat
|
||||||
{
|
{
|
||||||
enum
|
enum
|
||||||
|
|
Loading…
Reference in a new issue