- added processing of compound statements to the compiler. This means that anonymous functions without control statements are generating code now.

- added local variable declarations to the code generator. This is not tested yet, that will come with the next commit.
This commit is contained in:
Christoph Oelckers 2016-10-19 19:05:48 +02:00
parent 8a6230d64a
commit 2d85efce2a
4 changed files with 232 additions and 5 deletions

View file

@ -5023,14 +5023,18 @@ ExpEmit FxFlopFunctionCall::Emit(VMFunctionBuilder *build)
FxExpression *FxCompoundStatement::Resolve(FCompileContext &ctx)
{
CHECKRESOLVED();
Outer = ctx.Block;
ctx.Block = this;
for (unsigned i = 0; i < Expressions.Size(); ++i)
{
if (NULL == (Expressions[i] = Expressions[i]->Resolve(ctx)))
{
ctx.Block = Outer;
delete this;
return NULL;
return nullptr;
}
}
ctx.Block = Outer;
return this;
}
@ -5048,6 +5052,11 @@ ExpEmit FxCompoundStatement::Emit(VMFunctionBuilder *build)
// Throw away any result. We don't care about it.
v.Free(build);
}
// Release all local variables in this block.
for (auto l : LocalVars)
{
l->Release(build);
}
return ExpEmit();
}
@ -5066,6 +5075,52 @@ VMFunction *FxCompoundStatement::GetDirectFunction()
return NULL;
}
//==========================================================================
//
// FxCompoundStatement :: FindLocalVariable
//
// Looks for a variable name in any of the containing compound statements
//
//==========================================================================
FxLocalVariableDeclaration *FxCompoundStatement::FindLocalVariable(FName name)
{
auto block = this;
while (block != nullptr)
{
for (auto l : block->LocalVars)
{
if (l->Name == name)
{
return l;
}
}
block = block->Outer;
}
return nullptr;
}
//==========================================================================
//
// FxCompoundStatement :: CheckLocalVariable
//
// Checks if the current block already contains a local variable
// of the given name.
//
//==========================================================================
bool FxCompoundStatement::CheckLocalVariable(FName name)
{
for (auto l : LocalVars)
{
if (l->Name == name)
{
return true;
}
}
return false;
}
//==========================================================================
//
// FxIfStatement
@ -6142,3 +6197,87 @@ ExpEmit FxDamageValue::Emit(VMFunctionBuilder *build)
return ExpEmit();
}
//==========================================================================
//
// declares a single local variable (no arrays)
//
//==========================================================================
FxLocalVariableDeclaration::FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, const FScriptPosition &p)
:FxExpression(p)
{
ValueType = type;
Name = name;
Init = initval == nullptr? nullptr : new FxTypeCast(initval, type, false);
}
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;
}
ctx.Block->LocalVars.Push(this);
return this;
}
ExpEmit FxLocalVariableDeclaration::Emit(VMFunctionBuilder *build)
{
if (Init == nullptr)
{
RegNum = build->Registers[ValueType->GetRegType()].Get(1);
}
else
{
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, build->GetConstantInt(constval->GetValue().GetInt()), RegNum);
break;
case REGT_FLOAT:
build->Emit(OP_LKF, build->GetConstantFloat(constval->GetValue().GetFloat()), RegNum);
break;
case REGT_POINTER:
build->Emit(OP_LKP, build->GetConstantAddress(constval->GetValue().GetPointer(), ATAG_GENERIC), RegNum);
break;
case REGT_STRING:
build->Emit(OP_LKS, build->GetConstantString(constval->GetValue().GetString()), RegNum);
}
emitval.Free(build);
}
else
{
// take over the register that got allocated while emitting the Init expression.
RegNum = emitval.RegNum;
}
}
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, 1);
}

View file

@ -63,10 +63,12 @@ class FxJumpStatement;
//==========================================================================
struct FScriptPosition;
class FxLoopStatement;
class FxCompoundStatement;
struct FCompileContext
{
FxLoopStatement *Loop;
FxLoopStatement *Loop = nullptr;
FxCompoundStatement *Block = nullptr;
PPrototype *ReturnProto;
PFunction *Function; // The function that is currently being compiled (or nullptr for constant evaluation.)
PClass *Class; // The type of the owning class.
@ -162,6 +164,12 @@ struct ExpVal
return regtype == REGT_INT ? double(Int) : regtype == REGT_FLOAT ? Float : 0;
}
void *GetPointer() const
{
int regtype = Type->GetRegType();
return regtype == REGT_POINTER ? pointer : nullptr;
}
const FString GetString() const
{
return Type == TypeString ? *(FString *)&pointer : Type == TypeName ? FString(FName(ENamedName(Int)).GetChars()) : "";
@ -1082,10 +1090,15 @@ public:
// FxCompoundStatement
//
//==========================================================================
class FxLocalVariableDeclaration;
class FxCompoundStatement : public FxExpression
{
TArray<FxLocalVariableDeclaration *> LocalVars;
TDeletingArray<FxExpression *> Expressions;
FxCompoundStatement *Outer = nullptr;
friend class FxLocalVariableDeclaration;
public:
FxCompoundStatement(const FScriptPosition &pos) : FxExpression(pos) {}
@ -1093,6 +1106,8 @@ public:
ExpEmit Emit(VMFunctionBuilder *build);
void Add(FxExpression *expr) { if (expr != NULL) Expressions.Push(expr); }
VMFunction *GetDirectFunction();
FxLocalVariableDeclaration *FindLocalVariable(FName name);
bool CheckLocalVariable(FName name);
};
//==========================================================================
@ -1341,7 +1356,26 @@ public:
}
};
FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve = false);
//==========================================================================
//
//
//
//==========================================================================
class FxLocalVariableDeclaration : public FxExpression
{
friend class FxCompoundStatement;
FName Name;
FxExpression *Init;
int RegNum = -1;
public:
FxLocalVariableDeclaration(PType *type, FName name, FxExpression *initval, const FScriptPosition &p);
FxExpression *Resolve(FCompileContext&);
ExpEmit Emit(VMFunctionBuilder *build);
void Release(VMFunctionBuilder *build);
};
#endif

View file

@ -144,6 +144,7 @@ inline void ResetBaggage (Baggage *bag, PClassActor *stateclass)
AFuncDesc *FindFunction(const char * string);
FxExpression *ParseExpression(FScanner &sc, PClassActor *cls, bool mustresolve = false);
void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag);
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
PFunction *afd, FString statestring, FStateDefinitions *statedef);

View file

@ -2313,8 +2313,27 @@ void ZCCCompiler::CompileStates()
FxExpression *ZCCCompiler::ConvertAST(ZCC_TreeNode *ast)
{
// FxReturnStatement will have to be done more intelligently, of course.
return new FxReturnStatement(ConvertNode(ast), *ast);
// there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return.
if (ast->NodeType == AST_ExprFuncCall)
{
return new FxReturnStatement(ConvertNode(ast), *ast);
}
else
{
// This must be done here so that we can check for a trailing return statement.
auto x = new FxCompoundStatement(*ast);
auto compound = static_cast<ZCC_CompoundStmt *>(ast);
bool isreturn = false;
auto node = compound->Content;
if (node != nullptr) do
{
x->Add(ConvertNode(node));
isreturn = node->NodeType == AST_ReturnStmt;
node = static_cast<decltype(node)>(node->SiblingNext);
} while (node != compound->Content);
if (!isreturn) x->Add(new FxReturnStatement(nullptr, *ast));
return x;
}
}
FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
@ -2514,8 +2533,42 @@ FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
return new FxConditional(condition, left, right);
}
case AST_ExpressionStmt:
return ConvertNode(static_cast<ZCC_ExpressionStmt *>(ast)->Expression);
case AST_ReturnStmt:
{
auto ret = static_cast<ZCC_ReturnStmt *>(ast);
FArgumentList *args = ConvertNodeList(ret->Values);
if (args->Size() == 0)
{
return new FxReturnStatement(nullptr, *ast);
}
else if (args->Size() == 1)
{
return new FxReturnStatement((*args)[0], *ast);
}
else
{
Error(ast, "Return with multiple values not implemented yet.");
return new FxReturnStatement(nullptr, *ast);
}
}
case AST_CompoundStmt:
{
auto x = new FxCompoundStatement(*ast);
auto compound = static_cast<ZCC_CompoundStmt *>(ast);
auto node = compound->Content;
if (node != nullptr) do
{
x->Add(ConvertNode(node));
node = static_cast<decltype(node)>(node->SiblingNext);
} while (node != compound->Content);
return x;
}
}
// only for development. I_Error is more convenient here than a normal error.
I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType);
return nullptr;