- added the ability to attach a constructor or destructor to an internally defined struct.

There are a few which require explicit native construction or destruction that need to be exported to the VM, e.g. FCheckPosition.
The VM cannot handle this directly, it needs two special functions to be attached to handle such elements.
This commit is contained in:
Christoph Oelckers 2017-01-11 23:46:03 +01:00
parent ca2ac4e4fd
commit 5ef9429ae4
10 changed files with 132 additions and 13 deletions

View file

@ -708,11 +708,15 @@ protected:
class PStruct : public PNamedType class PStruct : public PNamedType
{ {
DECLARE_CLASS(PStruct, PNamedType); DECLARE_CLASS(PStruct, PNamedType);
public: public:
PStruct(FName name, PTypeBase *outer); PStruct(FName name, PTypeBase *outer);
TArray<PField *> Fields; TArray<PField *> Fields;
bool HasNativeFields; bool HasNativeFields;
// Some internal structs require explicit construction and destruction of fields the VM cannot handle directly so use thes two functions for it.
VMFunction *mConstructor = nullptr;
VMFunction *mDestructor = nullptr;
virtual PField *AddField(FName name, PType *type, DWORD flags=0); virtual PField *AddField(FName name, PType *type, DWORD flags=0);
virtual PField *AddNativeField(FName name, PType *type, size_t address, DWORD flags = 0, int bitvalue = 0); virtual PField *AddNativeField(FName name, PType *type, size_t address, DWORD flags = 0, int bitvalue = 0);

View file

@ -84,6 +84,28 @@ TArray<spechit_t> spechit;
TArray<spechit_t> portalhit; TArray<spechit_t> portalhit;
// FCheckPosition requires explicit contstruction and destruction when used in the VM
DEFINE_ACTION_FUNCTION(_FCheckPosition, _Constructor)
{
PARAM_SELF_STRUCT_PROLOGUE(FCheckPosition);
new(self) FCheckPosition;
return 0;
}
DEFINE_ACTION_FUNCTION(_FCheckPosition, _Destructor)
{
PARAM_SELF_STRUCT_PROLOGUE(FCheckPosition);
self->~FCheckPosition();
return 0;
}
DEFINE_ACTION_FUNCTION(_FCheckPosition, ClearLastRipped)
{
PARAM_SELF_STRUCT_PROLOGUE(FCheckPosition);
self->LastRipped.Clear();
return 0;
}
//========================================================================== //==========================================================================
// //
// CanCollideWith // CanCollideWith

View file

@ -5160,6 +5160,7 @@ ExpEmit FxRandom::Emit(VMFunctionBuilder *build)
assert(((PSymbolVMFunction *)sym)->Function != nullptr); assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function; callfunc = ((PSymbolVMFunction *)sym)->Function;
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG));
@ -5410,6 +5411,7 @@ ExpEmit FxFRandom::Emit(VMFunctionBuilder *build)
assert(((PSymbolVMFunction *)sym)->Function != nullptr); assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function; callfunc = ((PSymbolVMFunction *)sym)->Function;
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG));
@ -5504,6 +5506,7 @@ ExpEmit FxRandom2::Emit(VMFunctionBuilder *build)
assert(((PSymbolVMFunction *)sym)->Function != nullptr); assert(((PSymbolVMFunction *)sym)->Function != nullptr);
callfunc = ((PSymbolVMFunction *)sym)->Function; callfunc = ((PSymbolVMFunction *)sym)->Function;
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K); int opcode = (EmitTail ? OP_TAIL_K : OP_CALL_K);
build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG)); build->Emit(OP_PARAM, 0, REGT_POINTER | REGT_KONST, build->GetConstantAddress(rng, ATAG_RNG));
@ -7801,6 +7804,7 @@ ExpEmit FxActionSpecialCall::Emit(VMFunctionBuilder *build)
ArgList.DeleteAndClear(); ArgList.DeleteAndClear();
ArgList.ShrinkToFit(); ArgList.ShrinkToFit();
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
if (EmitTail) if (EmitTail)
{ {
build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 0); build->Emit(OP_TAIL_K, build->GetConstantAddress(callfunc, ATAG_OBJECT), 2 + i, 0);
@ -8072,6 +8076,8 @@ ExpEmit FxVMFunctionCall::Emit(VMFunctionBuilder *build)
assert(build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits); assert(build->Registers[REGT_POINTER].GetMostUsed() >= build->NumImplicits);
int count = 0; int count = 0;
if (build->FramePointer.Fixed) EmitTail = false; // do not tail call if the stack is in use
if (count == 1) if (count == 1)
{ {
ExpEmit reg; ExpEmit reg;
@ -9508,6 +9514,19 @@ ExpEmit FxReturnStatement::Emit(VMFunctionBuilder *build)
{ {
ExpEmit out(0, REGT_NIL); ExpEmit out(0, REGT_NIL);
// call the destructors for all structs requiring one.
// go in reverse order of construction
for (int i = build->ConstructedStructs.Size() - 1; i >= 0; i--)
{
auto pstr = static_cast<PStruct*>(build->ConstructedStructs[i]->ValueType);
assert(pstr->mDestructor != nullptr);
ExpEmit reg(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(build->ConstructedStructs[i]->StackOffset));
build->Emit(OP_PARAM, 0, reg.RegType, reg.RegNum);
build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor, ATAG_OBJECT), 1, 0);
reg.Free(build);
}
// If we return nothing, use a regular RET opcode. // If we return nothing, use a regular RET opcode.
// Otherwise just return the value we're given. // Otherwise just return the value we're given.
if (Value == nullptr) if (Value == nullptr)
@ -10173,6 +10192,19 @@ ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
else else
{ {
// Init arrays and structs. // Init arrays and structs.
if (ValueType->IsA(RUNTIME_CLASS(PStruct)))
{
auto pstr = static_cast<PStruct*>(ValueType);
if (pstr->mConstructor != nullptr)
{
ExpEmit reg(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset));
build->Emit(OP_PARAM, 0, reg.RegType, reg.RegNum);
build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mConstructor, ATAG_OBJECT), 1, 0);
reg.Free(build);
}
if (pstr->mDestructor != nullptr) build->ConstructedStructs.Push(this);
}
} }
return ExpEmit(); return ExpEmit();
} }
@ -10184,6 +10216,22 @@ void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build)
{ {
build->Registers[ValueType->GetRegType()].Return(RegNum, RegCount); build->Registers[ValueType->GetRegType()].Return(RegNum, RegCount);
} }
else
{
if (ValueType->IsA(RUNTIME_CLASS(PStruct)))
{
auto pstr = static_cast<PStruct*>(ValueType);
if (pstr->mDestructor != nullptr)
{
ExpEmit reg(build, REGT_POINTER);
build->Emit(OP_ADDA_RK, reg.RegNum, build->FramePointer.RegNum, build->GetConstantInt(StackOffset));
build->Emit(OP_PARAM, 0, reg.RegType, reg.RegNum);
build->Emit(OP_CALL_K, build->GetConstantAddress(pstr->mDestructor, ATAG_OBJECT), 1, 0);
reg.Free(build);
}
build->ConstructedStructs.Delete(build->ConstructedStructs.Find(this));
}
}
// Stack space will not be released because that would make controlled destruction impossible. // Stack space will not be released because that would make controlled destruction impossible.
// For that all local stack variables need to live for the entire execution of a function. // For that all local stack variables need to live for the entire execution of a function.
} }

View file

@ -1993,6 +1993,7 @@ class FxLocalVariableDeclaration : public FxExpression
public: public:
int StackOffset = -1; int StackOffset = -1;
int RegNum = -1; int RegNum = -1;
bool constructed = false;
FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, int varflags, const FScriptPosition &p); FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, int varflags, const FScriptPosition &p);
~FxLocalVariableDeclaration(); ~FxLocalVariableDeclaration();

View file

@ -37,9 +37,7 @@
** **
*/ */
#define private public // this file needs public access to TArray's private 'Count' field so just hack the visibility qualifiers here
#include "tarray.h" #include "tarray.h"
#undef private
#include "dobject.h" #include "dobject.h"
#include "thingdef.h" #include "thingdef.h"
// We need one specific type for each of the 7 integral VM types and instantiate the needed functions for each of them. // We need one specific type for each of the 7 integral VM types and instantiate the needed functions for each of them.
@ -775,10 +773,10 @@ DEFINE_ACTION_FUNCTION(FDynArray_String, Clear)
return 0; return 0;
} }
DEFINE_FIELD_NAMED(FDynArray_I8, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_I8, FArray, Count, Size)
DEFINE_FIELD_NAMED(FDynArray_I16, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_I16, FArray, Count, Size)
DEFINE_FIELD_NAMED(FDynArray_I32, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_I32, FArray, Count, Size)
DEFINE_FIELD_NAMED(FDynArray_F32, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_F32, FArray, Count, Size)
DEFINE_FIELD_NAMED(FDynArray_F64, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_F64, FArray, Count, Size)
DEFINE_FIELD_NAMED(FDynArray_Ptr, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_Ptr, FArray, Count, Size)
DEFINE_FIELD_NAMED(FDynArray_String, Count, Size) // clashes with type 'Inventory'. DEFINE_FIELD_NAMED_X(DynArray_String, FArray, Count, Size)

View file

@ -50,6 +50,8 @@
#include "zstring.h" #include "zstring.h"
#include "d_event.h" #include "d_event.h"
#include "g_levellocals.h" #include "g_levellocals.h"
#include "vm.h"
#include "p_checkposition.h"
static TArray<FPropertyInfo*> properties; static TArray<FPropertyInfo*> properties;
static TArray<AFuncDesc> AFTable; static TArray<AFuncDesc> AFTable;
@ -856,6 +858,14 @@ void InitThingdef()
qsort(&AFTable[0], AFTable.Size(), sizeof(AFTable[0]), funccmp); qsort(&AFTable[0], AFTable.Size(), sizeof(AFTable[0]), funccmp);
} }
// Add the constructor and destructor to FCheckPosition.
auto fcp = NewStruct("FCheckPosition", nullptr);
fcp->mConstructor = *FindFunction(fcp, "_Constructor")->VMPointer;
fcp->mDestructor = *FindFunction(fcp, "_Destructor")->VMPointer;
fcp->Size = sizeof(FCheckPosition);
fcp->Align = alignof(FCheckPosition);
FieldTable.Clear(); FieldTable.Clear();
if (FieldTable.Size() == 0) if (FieldTable.Size() == 0)
{ {

View file

@ -1116,9 +1116,9 @@ struct AFuncDesc
MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##icls##_##name; MSVC_FSEG FieldDesc const *const VMField_##icls##_##name##_HookPtr GCC_FSEG = &VMField_##icls##_##name;
#define DEFINE_FIELD_NAMED_X(cls, icls, name, scriptname) \ #define DEFINE_FIELD_NAMED_X(cls, icls, name, scriptname) \
static const FieldDesc VMField_##icls##_##scriptname = { "A" #cls, #scriptname, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), 0 }; \ static const FieldDesc VMField_##cls##_##scriptname = { "A" #cls, #scriptname, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), 0 }; \
extern FieldDesc const *const VMField_##icls##_##scriptname##_HookPtr; \ extern FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr; \
MSVC_FSEG FieldDesc const *const VMField_##icls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##icls##_##scriptname; MSVC_FSEG FieldDesc const *const VMField_##cls##_##scriptname##_HookPtr GCC_FSEG = &VMField_##cls##_##scriptname;
#define DEFINE_FIELD_X_BIT(cls, icls, name, bitval) \ #define DEFINE_FIELD_X_BIT(cls, icls, name, bitval) \
static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), bitval }; \ static const FieldDesc VMField_##icls##_##name = { "A" #cls, #name, (unsigned)myoffsetof(icls, name), (unsigned)sizeof(icls::name), bitval }; \

View file

@ -5,6 +5,7 @@
class VMFunctionBuilder; class VMFunctionBuilder;
class FxExpression; class FxExpression;
class FxLocalVariableDeclaration;
struct ExpEmit struct ExpEmit
{ {
@ -92,6 +93,7 @@ public:
// keep the frame pointer, if needed, in a register because the LFP opcode is hideously inefficient, requiring more than 20 instructions on x64. // keep the frame pointer, if needed, in a register because the LFP opcode is hideously inefficient, requiring more than 20 instructions on x64.
ExpEmit FramePointer; ExpEmit FramePointer;
TArray<FxLocalVariableDeclaration *> ConstructedStructs;
private: private:
struct AddrKonst struct AddrKonst

View file

@ -71,8 +71,8 @@ protected:
struct FArray struct FArray
{ {
void *Array; void *Array;
unsigned int Most;
unsigned int Count; unsigned int Count;
unsigned int Most;
}; };
// T is the type stored in the array. // T is the type stored in the array.

View file

@ -1,3 +1,37 @@
struct FCheckPosition
{
/*
// in
native Actor thing;
native Vector3 pos;
// out
native sector sector;
native double floorz;
native double ceilingz;
native double dropoffz;
nativeTextureID floorpic;
native int floorterrain;
native sector floorsector;
native TextureID ceilingpic;
native sector ceilingsector;
native bool touchmidtex;
native bool abovemidtex;
native bool floatok;
native bool FromPMove;
native line ceilingline;
native AActor stepthing;
native bool DoRipping;
native bool portalstep;
native int PushTime;
*/
native void ClearLastRipped();
}
class Actor : Thinker native class Actor : Thinker native
{ {
const DEFAULT_HEALTH = 1000; const DEFAULT_HEALTH = 1000;