- 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)
: ElementType(etype)
PDynArray::PDynArray(PType *etype,PStruct *backing)
: ElementType(etype), BackingType(backing)
{
mDescriptiveName.Format("DynArray<%s>", etype->DescriptiveName());
Size = sizeof(FArray);
@ -2061,7 +2061,33 @@ PDynArray *NewDynArray(PType *type)
PType *atype = TypeTable.FindType(RUNTIME_CLASS(PDynArray), (intptr_t)type, 0, &bucket);
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);
}
return (PDynArray *)atype;

View file

@ -6,6 +6,7 @@
#endif
typedef std::pair<const class PType *, unsigned> FTypeAndOffset;
class PStruct;
#include "vm.h"
@ -649,9 +650,10 @@ class PDynArray : public PCompoundType
DECLARE_CLASS(PDynArray, PCompoundType);
HAS_OBJECT_POINTERS;
public:
PDynArray(PType *etype);
PDynArray(PType *etype, PStruct *backing);
PType *ElementType;
PStruct *BackingType;
virtual bool IsMatch(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(Unit)
xx(Size)
xx(Copy)
xx(Move)
xx(Voidptr)
xx(StateLabel)
xx(SpriteID)

View file

@ -6946,7 +6946,18 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
return nullptr;
}
PArray *arraytype = dyn_cast<PArray>(Array->ValueType);
PArray *arraytype = nullptr;
PType *elementtype = nullptr;
if (Array->IsDynamicArray())
{
PDynArray *darraytype = static_cast<PDynArray*>(Array->ValueType);
elementtype = darraytype->ElementType;
Array->ValueType = NewPointer(NewResizableArray(elementtype)); // change type so that this can use the code for resizable arrays unchanged.
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.
@ -6960,6 +6971,8 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
arraytype = static_cast<PArray*>(ptype->PointedType);
arrayispointer = true;
}
elementtype = arraytype->ElementType;
}
if (Array->IsResizableArray())
{
@ -6967,12 +6980,17 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
if (Array->ExprType == EFX_ClassMember || Array->ExprType == EFX_StructMember)
{
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
SizeAddr = parentfield->Offset + parentfield->Type->Align;
SizeAddr = parentfield->Offset + sizeof(void*);
}
else if (Array->ExprType == EFX_GlobalVariable)
{
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
{
@ -6981,24 +6999,23 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
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();
if (indexval >= arraytype->ElementCount && !Array->IsResizableArray())
if (indexval >= arraytype->ElementCount)
{
ScriptPosition.Message(MSG_ERROR, "Array index out of bounds");
delete this;
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)
{
auto parentfield = static_cast<FxStructMember *>(Array)->membervar;
// PFields are garbage collected so this will be automatically taken care of later.
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxStructMember *>(Array)->membervar = newfield;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
@ -7008,7 +7025,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
else if (Array->ExprType == EFX_GlobalVariable)
{
auto parentfield = static_cast<FxGlobalVariable *>(Array)->membervar;
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxGlobalVariable *>(Array)->membervar = newfield;
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
@ -7018,7 +7035,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
else if (Array->ExprType == EFX_StackVariable)
{
auto parentfield = static_cast<FxStackVariable *>(Array)->membervar;
auto newfield = new PField(NAME_None, arraytype->ElementType, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
auto newfield = new PField(NAME_None, elementtype, parentfield->Flags, indexval * arraytype->ElementSize + parentfield->Offset);
static_cast<FxStackVariable *>(Array)->ReplaceField(newfield);
Array->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = Array->Resolve(ctx);
@ -7026,9 +7043,8 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
return x;
}
}
}
ValueType = arraytype->ElementType;
ValueType = elementtype;
if (!Array->RequestAddress(ctx, &AddressWritable))
{
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.
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())
{
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 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 IsDynamicArray() const { return (ValueType->IsKindOf(RUNTIME_CLASS(PDynArray))); }
virtual ExpEmit Emit(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:
if (allowarraytypes)
{
Error(field, "%s: Dynamic array types not implemented yet", name.GetChars());
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;