- 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:
parent
d86f03e2e0
commit
2cc48ec378
9 changed files with 306 additions and 92 deletions
|
|
@ -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.
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue