- implemented code generation for stack variables.

- fixed code generation for using local variables as array index. This must use a different register for the array element offset because the original register may not be overwritten.
This commit is contained in:
Christoph Oelckers 2016-11-17 16:44:41 +01:00
commit 2cc48ec378
9 changed files with 306 additions and 92 deletions

View file

@ -5084,9 +5084,18 @@ FxExpression *FxIdentifier::Resolve(FCompileContext& ctx)
FxLocalVariableDeclaration *local = ctx.FindLocalVariable(Identifier);
if (local != nullptr)
{
auto x = new FxLocalVariable(local, ScriptPosition);
delete this;
return x->Resolve(ctx);
if (local->ValueType->GetRegType() != REGT_NIL)
{
auto x = new FxLocalVariable(local, ScriptPosition);
delete this;
return x->Resolve(ctx);
}
else
{
auto x = new FxStackVariable(local->ValueType, local->StackOffset, ScriptPosition);
delete this;
return x->Resolve(ctx);
}
}
if (Identifier == NAME_Default)
@ -5584,6 +5593,106 @@ ExpEmit FxGlobalVariable::Emit(VMFunctionBuilder *build)
}
//==========================================================================
//
//
//
//==========================================================================
FxStackVariable::FxStackVariable(PType *type, int offset, const FScriptPosition &pos)
: FxExpression(EFX_StackVariable, pos)
{
membervar = new PField(NAME_None, type, 0, offset);
AddressRequested = false;
AddressWritable = true; // must be true unless classx tells us otherwise if requested.
}
//==========================================================================
//
// force delete the PField because we know we won't need it anymore
// and it won't get GC'd until the compiler finishes.
//
//==========================================================================
FxStackVariable::~FxStackVariable()
{
// Q: Is this good or bad? Needs testing if this is fine or better left to the GC anyway. DObject's destructor is anything but cheap.
membervar->ObjectFlags |= OF_YesReallyDelete;
delete membervar;
}
//==========================================================================
//
//
//==========================================================================
void FxStackVariable::ReplaceField(PField *newfield)
{
membervar->ObjectFlags |= OF_YesReallyDelete;
delete membervar;
membervar = newfield;
}
//==========================================================================
//
//
//
//==========================================================================
bool FxStackVariable::RequestAddress(FCompileContext &ctx, bool *writable)
{
AddressRequested = true;
if (writable != nullptr) *writable = AddressWritable && !ctx.CheckReadOnly(membervar->Flags);
return true;
}
//==========================================================================
//
//
//
//==========================================================================
FxExpression *FxStackVariable::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
ValueType = membervar->Type;
return this;
}
ExpEmit FxStackVariable::Emit(VMFunctionBuilder *build)
{
int offsetreg = -1;
if (membervar->Offset != 0) offsetreg = build->GetConstantInt((int)membervar->Offset);
if (AddressRequested)
{
ExpEmit obj(build, REGT_POINTER);
if (offsetreg >= 0)
{
build->Emit(OP_ADDA_RK, obj.RegNum, build->FramePointer.RegNum, offsetreg);
return obj;
}
else return build->FramePointer;
}
ExpEmit loc(build, membervar->Type->GetRegType(), membervar->Type->GetRegCount());
if (membervar->BitValue == -1)
{
if (offsetreg == -1) offsetreg = build->GetConstantInt(0);
build->Emit(membervar->Type->GetLoadOp(), loc.RegNum, build->FramePointer.RegNum, offsetreg);
}
else
{
ExpEmit obj(build, REGT_POINTER);
if (offsetreg >= 0) build->Emit(OP_ADDA_RK, obj.RegNum, build->FramePointer.RegNum, offsetreg);
obj.Free(build);
build->Emit(OP_LBIT, loc.RegNum, obj.RegNum, 1 << membervar->BitValue);
}
return loc;
}
//==========================================================================
//
//
@ -5684,6 +5793,16 @@ FxExpression *FxStructMember::Resolve(FCompileContext &ctx)
classx = nullptr;
return x;
}
else if (classx->ExprType == EFX_StackVariable)
{
auto parentfield = static_cast<FxStackVariable *>(classx)->membervar;
auto newfield = new PField(membervar->SymbolName, membervar->Type, membervar->Flags | parentfield->Flags, membervar->Offset + parentfield->Offset, membervar->BitValue);
static_cast<FxStackVariable *>(classx)->ReplaceField(newfield);
classx->isresolved = false; // re-resolve the parent so it can also check if it can be optimized away.
auto x = classx->Resolve(ctx);
classx = nullptr;
return x;
}
else if (classx->ExprType == EFX_LocalVariable && classx->IsVector()) // vectors are a special case because they are held in registers
{
// since this is a vector, all potential things that may get here are single float or an xy-vector.
@ -5831,7 +5950,7 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
return nullptr;
}
}
if (index->ValueType->GetRegType() != REGT_INT && index->ValueType != TypeName)
if (!index->IsInteger())
{
ScriptPosition.Message(MSG_ERROR, "Array index must be integer");
delete this;
@ -5854,16 +5973,10 @@ FxExpression *FxArrayElement::Resolve(FCompileContext &ctx)
delete this;
return nullptr;
}
// Todo: optimize out the array.
}
ValueType = arraytype->ElementType;
if (ValueType->GetRegType() != REGT_INT && ValueType->GetRegType() != REGT_FLOAT)
{
// int arrays only for now
ScriptPosition.Message(MSG_ERROR, "Only numeric arrays are supported.");
delete this;
return nullptr;
}
if (!Array->RequestAddress(ctx, &AddressWritable))
{
ScriptPosition.Message(MSG_ERROR, "Unable to dereference array.");
@ -5902,7 +6015,18 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
{
if (indexval != 0)
{
build->Emit(OP_ADDA_RK, start.RegNum, start.RegNum, build->GetConstantInt(indexval));
if (!start.Fixed)
{
build->Emit(OP_ADDA_RK, start.RegNum, start.RegNum, build->GetConstantInt(indexval));
}
else
{
// do not clobber local variables.
ExpEmit temp(build, start.RegType);
build->Emit(OP_ADDA_RK, temp.RegNum, start.RegNum, build->GetConstantInt(indexval));
start.Free(build);
start = temp;
}
}
}
else
@ -5915,21 +6039,40 @@ ExpEmit FxArrayElement::Emit(VMFunctionBuilder *build)
{
ExpEmit indexv(index->Emit(build));
ExpEmit indexwork = indexv.Fixed ? ExpEmit(build, indexv.RegType) : indexv;
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
int shiftbits = 0;
while (1u << shiftbits < arraytype->ElementSize)
{
shiftbits++;
}
assert(1u << shiftbits == arraytype->ElementSize && "Element sizes other than power of 2 are not implemented");
build->Emit(OP_BOUND, indexv.RegNum, arraytype->ElementCount);
if (shiftbits > 0)
if (1u << shiftbits == arraytype->ElementSize)
{
build->Emit(OP_SLL_RI, indexwork.RegNum, indexv.RegNum, shiftbits);
if (shiftbits > 0)
{
build->Emit(OP_SLL_RI, indexwork.RegNum, indexv.RegNum, shiftbits);
}
}
else
{
// A shift won't do, so use a multiplication
build->Emit(OP_MUL_RK, indexwork.RegNum, indexv.RegNum, build->GetConstantInt(arraytype->ElementSize));
}
if (AddressRequested)
{
build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexwork.RegNum);
if (!start.Fixed)
{
build->Emit(OP_ADDA_RR, start.RegNum, start.RegNum, indexwork.RegNum);
}
else
{
// do not clobber local variables.
ExpEmit temp(build, start.RegType);
build->Emit(OP_ADDA_RR, temp.RegNum, start.RegNum, indexwork.RegNum);
start.Free(build);
start = temp;
}
}
else
{
@ -8413,7 +8556,7 @@ FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name,
VarFlags = varflags;
Name = name;
RegCount = type == TypeVector2 ? 2 : type == TypeVector3 ? 3 : 1;
Init = initval == nullptr? nullptr : new FxTypeCast(initval, type, false);
Init = initval;
}
FxLocalVariableDeclaration::~FxLocalVariableDeclaration()
@ -8424,75 +8567,96 @@ FxLocalVariableDeclaration::~FxLocalVariableDeclaration()
FxExpression *FxLocalVariableDeclaration::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
SAFE_RESOLVE_OPT(Init, ctx);
if (ctx.Block == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "Variable declaration outside compound statement");
delete this;
return nullptr;
}
if (ValueType->RegType == REGT_NIL)
{
auto sfunc = static_cast<VMScriptFunction *>(ctx.Function->Variants[0].Implementation);
StackOffset = sfunc->AllocExtraStack(ValueType);
// Todo: Process the compound initializer once implemented.
}
else
{
if (Init) Init = new FxTypeCast(Init, ValueType, false);
SAFE_RESOLVE_OPT(Init, ctx);
}
ctx.Block->LocalVars.Push(this);
return this;
}
ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
{
if (Init == nullptr)
if (ValueType->RegType != REGT_NIL)
{
RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount);
}
else
{
ExpEmit emitval = Init->Emit(build);
int regtype = emitval.RegType;
if (regtype < REGT_INT || regtype > REGT_TYPE)
if (Init == nullptr)
{
ScriptPosition.Message(MSG_ERROR, "Attempted to assign a non-value");
return ExpEmit();
}
if (emitval.Konst)
{
auto constval = static_cast<FxConstant *>(Init);
RegNum = build->Registers[regtype].Get(1);
switch (regtype)
{
default:
case REGT_INT:
build->Emit(OP_LK, RegNum, build->GetConstantInt(constval->GetValue().GetInt()));
break;
case REGT_FLOAT:
build->Emit(OP_LKF, RegNum, build->GetConstantFloat(constval->GetValue().GetFloat()));
break;
case REGT_POINTER:
build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC));
break;
case REGT_STRING:
build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString()));
}
emitval.Free(build);
}
else if (Init->ExprType != EFX_LocalVariable)
{
// take over the register that got allocated while emitting the Init expression.
RegNum = emitval.RegNum;
RegNum = build->Registers[ValueType->GetRegType()].Get(RegCount);
}
else
{
ExpEmit out(build, emitval.RegType, emitval.RegCount);
build->Emit(ValueType->GetMoveOp(), out.RegNum, emitval.RegNum);
RegNum = out.RegNum;
ExpEmit emitval = Init->Emit(build);
int regtype = emitval.RegType;
if (regtype < REGT_INT || regtype > REGT_TYPE)
{
ScriptPosition.Message(MSG_ERROR, "Attempted to assign a non-value");
return ExpEmit();
}
if (emitval.Konst)
{
auto constval = static_cast<FxConstant *>(Init);
RegNum = build->Registers[regtype].Get(1);
switch (regtype)
{
default:
case REGT_INT:
build->Emit(OP_LK, RegNum, build->GetConstantInt(constval->GetValue().GetInt()));
break;
case REGT_FLOAT:
build->Emit(OP_LKF, RegNum, build->GetConstantFloat(constval->GetValue().GetFloat()));
break;
case REGT_POINTER:
build->Emit(OP_LKP, RegNum, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC));
break;
case REGT_STRING:
build->Emit(OP_LKS, RegNum, build->GetConstantString(constval->GetValue().GetString()));
}
emitval.Free(build);
}
else if (Init->ExprType != EFX_LocalVariable)
{
// take over the register that got allocated while emitting the Init expression.
RegNum = emitval.RegNum;
}
else
{
ExpEmit out(build, emitval.RegType, emitval.RegCount);
build->Emit(ValueType->GetMoveOp(), out.RegNum, emitval.RegNum);
RegNum = out.RegNum;
}
}
}
else
{
// Init arrays and structs.
}
return ExpEmit();
}
void FxLocalVariableDeclaration::Release(VMFunctionBuilder *build)
{
// Release the register after the containing block gets closed
assert(RegNum != -1);
build->Registers[ValueType->GetRegType()].Return(RegNum, RegCount);
if(RegNum != -1)
{
build->Registers[ValueType->GetRegType()].Return(RegNum, RegCount);
}
// 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.
}