- major work on dynamic array support. Mostly working, some issues still exist with Move and Copy methods and with assignments in stack variables.

This commit is contained in:
Christoph Oelckers 2017-02-06 21:39:21 +01:00
parent 12477216ef
commit ca48a687f8
6 changed files with 175 additions and 55 deletions

View file

@ -2012,8 +2012,8 @@ PDynArray::PDynArray()
// //
//========================================================================== //==========================================================================
PDynArray::PDynArray(PType *etype) PDynArray::PDynArray(PType *etype,PStruct *backing)
: ElementType(etype) : ElementType(etype), BackingType(backing)
{ {
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName()); mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
Size = sizeof(FArray); Size = sizeof(FArray);
@ -2061,7 +2061,33 @@ PDynArray *NewDynArray(PType *type)
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket); PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket);
if (atype == NULL) if (atype == NULL)
{ {
atype = new PDynArray(type); FString backingname;
switch (type->GetRegType())
{
case REGT_INT:
backingname.Format("DynArray_I%d", type->Size * 8);
break;
case REGT_FLOAT:
backingname.Format("DynArray_F%d", type->Size * 8);
break;
case REGT_STRING:
backingname = "DynArray_String";
break;
case REGT_POINTER:
backingname = "DynArray_Ptr";
break;
default:
I_Error("Unsupported dynamic array requested");
break;
}
auto backing = NewNativeStruct(backingname, nullptr);
atype = new PDynArray(type, backing);
TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket); TypeTable.AddType(atype, RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, bucket);
} }
return (PDynArray *)atype; return (PDynArray *)atype;

View file

@ -6,6 +6,7 @@
#endif #endif
typedef std::pair<const class PType *, unsigned> FTypeAndOffset; typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
class PStruct;
#include "vm.h" #include "vm.h"
@ -649,9 +650,10 @@ class PDynArray : public PCompoundType
DECLARE_CLASS(PDynArray, PCompoundType); DECLARE_CLASS(PDynArray, PCompoundType);
HAS_OBJECT_POINTERS; HAS_OBJECT_POINTERS;
public: public:
PDynArray(PType *etype); PDynArray(PType *etype, PStruct *backing);
PType *ElementType; PType *ElementType;
PStruct *BackingType;
virtual bool IsMatch(intptr_t id1, intptr_t id2) const; virtual bool IsMatch(intptr_t id1, intptr_t id2) const;
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const; virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;

View file

@ -821,6 +821,8 @@ xx(DamageFunction)
xx(Length) xx(Length)
xx(Unit) xx(Unit)
xx(Size) xx(Size)
xx(Copy)
xx(Move)
xx(Voidptr) xx(Voidptr)
xx(StateLabel) xx(StateLabel)
xx(SpriteID) xx(SpriteID)

View file

@ -6946,20 +6946,33 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
return nullptr; return nullptr;
} }
PArray *arraytype = dyn_cast<PArray>(Array->ValueType); PArray *arraytype = nullptr;
if (arraytype == nullptr) PType *elementtype = nullptr;
if (Array->IsDynamicArray())
{ {
// Check if we got a pointer to an array. Some native data structures (like the line list in sectors) use this. PDynArray *darraytype = static_cast<PDynArray*>(Array->ValueType);
PPointer *ptype = dyn_cast<PPointer>(Array->ValueType); elementtype = darraytype->ElementType;
if (ptype == nullptr || !ptype->PointedType->IsKindOf(RUNTIME_CLASS(PArray))) Array->ValueType = NewPointer(NewResizableArray(elementtype)); // change type so that this can use the code for resizable arrays unchanged.
{
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
delete this;
return nullptr;
}
arraytype = static_cast<PArray*>(ptype->PointedType);
arrayispointer = true; arrayispointer = true;
} }
else
{
arraytype = dyn_cast<PArray>(Array->ValueType);
if (arraytype == nullptr)
{
// Check if we got a pointer to an array. Some native data structures (like the line list in sectors) use this.
PPointer *ptype = dyn_cast<PPointer>(Array->ValueType);
if (ptype == nullptr || !ptype->PointedType->IsKindOf(RUNTIME_CLASS(PArray)))
{
ScriptPosition.Message(MSG_ERROR, "'[]' can only be used with arrays.");
delete this;
return nullptr;
}
arraytype = static_cast<PArray*>(ptype->PointedType);
arrayispointer = true;
}
elementtype = arraytype->ElementType;
}
if (Array->IsResizableArray()) if (Array->IsResizableArray())
{ {
@ -6967,12 +6980,17 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember) if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
{ {
auto parentfield = static_cast<FxStructMember *>(Array)->membervar; auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
SizeAddr = parentfield->Offset + parentfield->Type->Align; SizeAddr = parentfield->Offset + sizeof(void*);
} }
else if (Array->ExprType == EFX_GlobalVariable) else if (Array->ExprType == EFX_GlobalVariable)
{ {
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar; auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
SizeAddr = parentfield->Offset + parentfield->Type->Align; SizeAddr = parentfield->Offset + sizeof(void*);
}
else if (Array->ExprType == EFX_StackVariable)
{
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
SizeAddr = parentfield->Offset + sizeof(void*);
} }
else else
{ {
@ -6981,54 +6999,52 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
return nullptr; return nullptr;
} }
} }
else if (index->isConstant()) // constant indices can only be resolved at compile time for statically sized arrays.
else if (index->isConstant() && arraytype != nullptr && !arrayispointer)
{ {
unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt(); unsigned indexval = static_cast<FxConstant *>(index)->GetValue().GetInt();
if (indexval >= arraytype->ElementCount && !Array->IsResizableArray()) if (indexval >= arraytype->ElementCount)
{ {
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds"); ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
delete this; delete this;
return nullptr; return nullptr;
} }
if (!arrayispointer) // if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset.
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
{ {
// if this is an array within a class or another struct we can simplify the expression by creating a new PField with a cumulative offset. auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember) // PFields are garbage collected so this will be automatically taken care of later.
{ auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
auto parentfield = static_cast<FxStructMember *>(Array)->membervar; static_cast<FxStructMember *>(Array)->membervar = newfield;
// PFields are garbage collected so this will be automatically taken care of later. Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset); auto x = Array->Resolve(ctx);
static_cast<FxStructMember *>(Array)->membervar = newfield; Array = nullptr;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. return x;
auto x = Array->Resolve(ctx); }
Array = nullptr; else if (Array->ExprType == EFX_GlobalVariable)
return x; {
} auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
else if (Array->ExprType == EFX_GlobalVariable) auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
{ static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar; Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset); auto x = Array->Resolve(ctx);
static_cast<FxGlobalVariable *>(Array)->membervar = newfield; Array = nullptr;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. return x;
auto x = Array->Resolve(ctx); }
Array = nullptr; else if (Array->ExprType == EFX_StackVariable)
return x; {
} auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
else if (Array->ExprType == EFX_StackVariable) auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
{ static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar; Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset); auto x = Array->Resolve(ctx);
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield); Array = nullptr;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away. return x;
auto x = Array->Resolve(ctx);
Array = nullptr;
return x;
}
} }
} }
ValueType = arraytype->ElementType; ValueType = elementtype;
if (!Array->RequestAddress(ctx, &AddressWritable)) if (!Array->RequestAddress(ctx, &AddressWritable))
{ {
ScriptPosition.Message(MSG_ERROR, "Unable to dereference array."); ScriptPosition.Message(MSG_ERROR, "Unable to dereference array.");
@ -7804,6 +7820,72 @@ FxExpression *FxMemberFunctionCall::Resolve(FCompileContext& ctx)
// same for String methods. It also uses a hidden struct type to define them. // same for String methods. It also uses a hidden struct type to define them.
Self->ValueType = TypeStringStruct; Self->ValueType = TypeStringStruct;
} }
else if (Self->IsDynamicArray())
{
if (MethodName == NAME_Size)
{
FxExpression *x = new FxMemberIdentifier(Self, NAME_Size, ScriptPosition); // todo: obfuscate the name to prevent direct access.
Self = nullptr;
delete this;
return x->Resolve(ctx);
}
else
{
auto elementType = static_cast<PDynArray*>(Self->ValueType)->ElementType;
Self->ValueType = static_cast<PDynArray*>(Self->ValueType)->BackingType;
// this requires some added type checks for the passed types.
for (auto &a : ArgList)
{
a = a->Resolve(ctx);
if (a == nullptr)
{
delete this;
return nullptr;
}
if (a->IsDynamicArray())
{
// Copy and Move must turn their parameter into a pointer to the backing struct type.
auto backingtype = static_cast<PDynArray*>(a->ValueType)->BackingType;
a->ValueType = NewPointer(backingtype);
// Also change the field's type so the code generator can work with this (actually this requires swapping out the entire field.)
if (Self->ExprType == EFX_StructMember || Self->ExprType == EFX_ClassMember)
{
auto member = static_cast<FxStructMember*>(a);
auto newfield = new PField(NAME_None, backingtype, 0, member->membervar->Offset);
member->membervar = newfield;
Self = nullptr;
delete this;
member->ValueType = TypeUInt32;
return member;
}
else if (Self->ExprType == EFX_StackVariable)
{
auto member = static_cast<FxStackVariable*>(Self);
auto newfield = new PField(NAME_None, backingtype, 0, member->membervar->Offset);
member->membervar = newfield;
Self = nullptr;
delete this;
member->ValueType = TypeUInt32;
return member;
}
}
else if (a->IsPointer() && Self->ValueType->IsKindOf(RUNTIME_CLASS(PPointer)))
{
// the only case which must be checked up front is for pointer arrays receiving a new element.
// Since there is only one native backing class it uses a neutral void pointer as its argument,
// meaning that FxMemberFunctionCall is unable to do a proper check. So we have to do it here.
if (a->ValueType != elementType)
{
ScriptPosition.Message(MSG_ERROR, "Type mismatch in function argument. Got %s, expected %s", a->ValueType->DescriptiveName(), elementType->DescriptiveName());
delete this;
return nullptr;
}
}
}
}
}
else if (Self->IsArray()) else if (Self->IsArray())
{ {
if (MethodName == NAME_Size) if (MethodName == NAME_Size)

View file

@ -333,6 +333,7 @@ public:
bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); } bool IsObject() const { return ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && !ValueType->IsKindOf(RUNTIME_CLASS(PClassPointer)) && ValueType != TypeNullPtr && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PClass)); }
bool IsArray() const { return ValueType->IsKindOf(RUNTIME_CLASS(PArray)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PArray))); } bool IsArray() const { return ValueType->IsKindOf(RUNTIME_CLASS(PArray)) || (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PArray))); }
bool IsResizableArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PResizableArray))); } // can only exist in pointer form. bool IsResizableArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PPointer)) && static_cast<PPointer*>(ValueType)->PointedType->IsKindOf(RUNTIME_CLASS(PResizableArray))); } // can only exist in pointer form.
bool IsDynamicArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PDynArray))); }
virtual ExpEmit Emit(VMFunctionBuilder *build); virtual ExpEmit Emit(VMFunctionBuilder *build);
void EmitStatement(VMFunctionBuilder *build); void EmitStatement(VMFunctionBuilder *build);

View file

@ -1358,9 +1358,16 @@ PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName n
case AST_DynArrayType: case AST_DynArrayType:
if (allowarraytypes) if (allowarraytypes)
{ {
Error(field, "%s: Dynamic array types not implemented yet", name.GetChars());
auto atype = static_cast<ZCC_DynArrayType *>(ztype); auto atype = static_cast<ZCC_DynArrayType *>(ztype);
retval = NewDynArray(DetermineType(outertype, field, name, atype->ElementType, false, false)); auto ftype = DetermineType(outertype, field, name, atype->ElementType, false, true);
if (ftype->GetRegType() == REGT_NIL || ftype->GetRegCount() > 1)
{
Error(field, "%s: Base type for dynamic array types nust be integral, but got %s", name.GetChars(), ftype->DescriptiveName());
}
else
{
retval = NewDynArray(ftype);
}
break; break;
} }
break; break;