defer and struct hooks
This commit is contained in:
@@ -16,11 +16,14 @@ public class QBEGenerator
|
||||
private readonly List<StringLiteral> _stringLiterals = [];
|
||||
private readonly Stack<string> _breakLabels = [];
|
||||
private readonly Stack<string> _continueLabels = [];
|
||||
private readonly Stack<Scope> _scopes = new();
|
||||
private int _tmpIndex;
|
||||
private int _labelIndex;
|
||||
private int _cStringLiteralIndex;
|
||||
private int _stringLiteralIndex;
|
||||
|
||||
private Scope Scope => _scopes.Peek();
|
||||
|
||||
public QBEGenerator(List<DefinitionNode> definitions, HashSet<StructTypeNode> structTypes)
|
||||
{
|
||||
_definitions = definitions;
|
||||
@@ -34,6 +37,7 @@ public class QBEGenerator
|
||||
_stringLiterals.Clear();
|
||||
_breakLabels.Clear();
|
||||
_continueLabels.Clear();
|
||||
_scopes.Clear();
|
||||
_tmpIndex = 0;
|
||||
_labelIndex = 0;
|
||||
_cStringLiteralIndex = 0;
|
||||
@@ -307,6 +311,16 @@ public class QBEGenerator
|
||||
{
|
||||
var value = EmitExpression(source);
|
||||
_writer.Indented($"blit {value}, {destination}, {SizeOf(source.Type)}");
|
||||
|
||||
if (source.Type is StructTypeNode structType)
|
||||
{
|
||||
var copyFunc = structType.Functions.FirstOrDefault(x => x.Hook == "oncopy");
|
||||
if (copyFunc != null)
|
||||
{
|
||||
_writer.Indented($"call {StructFuncName(structType.Module, structType.Name, copyFunc.Name)}(l {destination})");
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -349,48 +363,6 @@ public class QBEGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private string EmitCopy(ExpressionNode source)
|
||||
{
|
||||
if (source is RValueExpressionNode || source.Type.IsScalar)
|
||||
{
|
||||
return EmitExpression(source);
|
||||
}
|
||||
|
||||
var value = EmitExpression(source);
|
||||
var destination = TmpName();
|
||||
|
||||
if (source.Type.IsValueType)
|
||||
{
|
||||
var size = SizeOf(source.Type);
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
_writer.Indented($"blit {value}, {destination}, {size}");
|
||||
return destination;
|
||||
}
|
||||
|
||||
switch (source.Type)
|
||||
{
|
||||
case ArrayTypeNode arrayType:
|
||||
var arraySize = EmitArraySizeInBytes(arrayType, value);
|
||||
_writer.Indented($"{destination} =l alloc8 {arraySize}");
|
||||
EmitMemcpy(value, destination, arraySize);
|
||||
break;
|
||||
case CStringTypeNode:
|
||||
var cstrSize = EmitCStringSizeInBytes(value);
|
||||
_writer.Indented($"{destination} =l alloc8 {cstrSize}");
|
||||
EmitMemcpy(value, destination, cstrSize);
|
||||
break;
|
||||
case StringTypeNode:
|
||||
var strSize = EmitStringSizeInBytes(value);
|
||||
_writer.Indented($"{destination} =l alloc8 {strSize}");
|
||||
EmitMemcpy(value, destination, strSize);
|
||||
break;
|
||||
default:
|
||||
throw new InvalidOperationException($"Cannot copy type {source.Type}");
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
private void EmitStructType(StructTypeNode structType)
|
||||
{
|
||||
// todo(nub31): qbe expects structs to be declared in order. We must Check the dependencies of the struct to see if a type need to be declared before this one
|
||||
@@ -450,7 +422,13 @@ public class QBEGenerator
|
||||
_writer.WriteLine(") {");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
EmitBlock(function.Body);
|
||||
var scope = new Scope();
|
||||
foreach (var parameter in function.Signature.Parameters)
|
||||
{
|
||||
scope.Variables.Push(new Variable(parameter.Name, parameter.Type));
|
||||
}
|
||||
|
||||
EmitBlock(function.Body, scope);
|
||||
|
||||
// Implicit return for void functions if no explicit return has been set
|
||||
if (function.Signature.ReturnType is VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode)
|
||||
@@ -488,18 +466,31 @@ public class QBEGenerator
|
||||
_writer.WriteLine(") {");
|
||||
_writer.WriteLine("@start");
|
||||
|
||||
EmitBlock(funcDef.Body);
|
||||
var scope = new Scope();
|
||||
foreach (var parameter in funcDef.Signature.Parameters)
|
||||
{
|
||||
scope.Variables.Push(new Variable(parameter.Name, parameter.Type));
|
||||
}
|
||||
|
||||
EmitBlock(funcDef.Body, scope);
|
||||
|
||||
_writer.WriteLine("}");
|
||||
_writer.NewLine();
|
||||
}
|
||||
|
||||
private void EmitBlock(BlockNode block)
|
||||
private void EmitBlock(BlockNode block, Scope? scope = null)
|
||||
{
|
||||
scope ??= Scope.SubScope();
|
||||
_scopes.Push(scope);
|
||||
|
||||
foreach (var statement in block.Statements)
|
||||
{
|
||||
EmitStatement(statement);
|
||||
}
|
||||
|
||||
EmitScopeCleanup();
|
||||
|
||||
_scopes.Pop();
|
||||
}
|
||||
|
||||
private void EmitStatement(StatementNode statement)
|
||||
@@ -510,9 +501,11 @@ public class QBEGenerator
|
||||
EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target));
|
||||
break;
|
||||
case BreakNode:
|
||||
EmitScopeCleanup();
|
||||
_writer.Indented($"jmp {_breakLabels.Peek()}");
|
||||
break;
|
||||
case ContinueNode:
|
||||
EmitScopeCleanup();
|
||||
_writer.Indented($"jmp {_continueLabels.Peek()}");
|
||||
break;
|
||||
case IfNode ifStatement:
|
||||
@@ -527,6 +520,9 @@ public class QBEGenerator
|
||||
case VariableDeclarationNode variableDeclaration:
|
||||
EmitVariableDeclaration(variableDeclaration);
|
||||
break;
|
||||
case DeferNode defer:
|
||||
Scope.DeferredStatements.Push(defer.Statement);
|
||||
break;
|
||||
case WhileNode whileStatement:
|
||||
EmitWhile(whileStatement);
|
||||
break;
|
||||
@@ -538,6 +534,26 @@ public class QBEGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitScopeCleanup()
|
||||
{
|
||||
while (Scope.DeferredStatements.TryPop(out var deferredStatement))
|
||||
{
|
||||
EmitStatement(deferredStatement);
|
||||
}
|
||||
|
||||
while (Scope.Variables.TryPop(out var variable))
|
||||
{
|
||||
if (variable.Type is StructTypeNode structType)
|
||||
{
|
||||
var destroyFunc = structType.Functions.FirstOrDefault(x => x.Hook == "ondestroy");
|
||||
if (destroyFunc != null)
|
||||
{
|
||||
_writer.Indented($"call {StructFuncName(structType.Module, structType.Name, destroyFunc.Name)}(l %{variable.Name})");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void EmitIf(IfNode ifStatement)
|
||||
{
|
||||
var trueLabel = LabelName();
|
||||
@@ -552,7 +568,7 @@ public class QBEGenerator
|
||||
_writer.WriteLine(falseLabel);
|
||||
if (ifStatement.Else.HasValue)
|
||||
{
|
||||
ifStatement.Else.Value.Match(EmitIf, EmitBlock);
|
||||
ifStatement.Else.Value.Match(EmitIf, b => EmitBlock(b));
|
||||
}
|
||||
|
||||
_writer.WriteLine(endLabel);
|
||||
@@ -563,10 +579,12 @@ public class QBEGenerator
|
||||
if (@return.Value.HasValue)
|
||||
{
|
||||
var result = EmitExpression(@return.Value.Value);
|
||||
EmitScopeCleanup();
|
||||
_writer.Indented($"ret {result}");
|
||||
}
|
||||
else
|
||||
{
|
||||
EmitScopeCleanup();
|
||||
_writer.Indented("ret");
|
||||
}
|
||||
}
|
||||
@@ -580,6 +598,8 @@ public class QBEGenerator
|
||||
{
|
||||
EmitCopyInto(variableDeclaration.Assignment.Value, name);
|
||||
}
|
||||
|
||||
Scope.Variables.Push(new Variable(variableDeclaration.Name, variableDeclaration.Type));
|
||||
}
|
||||
|
||||
private void EmitWhile(WhileNode whileStatement)
|
||||
@@ -677,7 +697,6 @@ public class QBEGenerator
|
||||
BinaryExpressionNode expr => EmitBinaryExpression(expr),
|
||||
ConvertFloatNode expr => EmitConvertFloat(expr),
|
||||
ConvertIntNode expr => EmitConvertInt(expr),
|
||||
DereferenceNode expr => EmitLoad(expr.Type, EmitExpression(expr.Expression)),
|
||||
FuncCallNode expr => EmitFuncCall(expr),
|
||||
FuncIdentifierNode expr => FuncName(expr.Module, expr.Name, expr.ExternSymbol),
|
||||
FuncParameterIdentifierNode expr => $"%{expr.Name}",
|
||||
@@ -740,6 +759,7 @@ public class QBEGenerator
|
||||
return lval switch
|
||||
{
|
||||
ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess),
|
||||
DereferenceNode dereference => EmitExpression(dereference.Expression),
|
||||
StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess),
|
||||
VariableIdentifierNode variableIdent => $"%{variableIdent.Name}",
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(lval))
|
||||
@@ -948,6 +968,12 @@ public class QBEGenerator
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
_writer.Indented($"call {StructCtorName(structInitializer.StructType.Module, structInitializer.StructType.Name)}(l {destination})");
|
||||
|
||||
var createFunc = structInitializer.StructType.Functions.FirstOrDefault(x => x.Hook == "oncreate");
|
||||
if (createFunc != null)
|
||||
{
|
||||
_writer.Indented($"call {StructFuncName(structInitializer.StructType.Module, structInitializer.StructType.Name, createFunc.Name)}(l {destination})");
|
||||
}
|
||||
|
||||
foreach (var (field, value) in structInitializer.Initializers)
|
||||
{
|
||||
var offset = TmpName();
|
||||
@@ -1015,8 +1041,18 @@ public class QBEGenerator
|
||||
|
||||
foreach (var parameter in structFuncCall.Parameters)
|
||||
{
|
||||
var copy = EmitCopy(parameter);
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
|
||||
var value = EmitExpression(parameter);
|
||||
|
||||
if (parameter.Type is StructTypeNode structType)
|
||||
{
|
||||
var copyFunc = structType.Functions.FirstOrDefault(x => x.Hook == "oncopy");
|
||||
if (copyFunc != null)
|
||||
{
|
||||
_writer.Indented($"call {StructFuncName(structType.Module, structType.Name, copyFunc.Name)}(l {value})");
|
||||
}
|
||||
}
|
||||
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {value}");
|
||||
}
|
||||
|
||||
if (structFuncCall.Type is VoidTypeNode)
|
||||
@@ -1093,8 +1129,18 @@ public class QBEGenerator
|
||||
|
||||
foreach (var parameter in funcCall.Parameters)
|
||||
{
|
||||
var copy = EmitCopy(parameter);
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
|
||||
var value = EmitExpression(parameter);
|
||||
|
||||
if (parameter.Type is StructTypeNode structType)
|
||||
{
|
||||
var copyFunc = structType.Functions.FirstOrDefault(x => x.Hook == "oncopy");
|
||||
if (copyFunc != null)
|
||||
{
|
||||
_writer.Indented($"call {StructFuncName(structType.Module, structType.Name, copyFunc.Name)}(l {value})");
|
||||
}
|
||||
}
|
||||
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {value}");
|
||||
}
|
||||
|
||||
if (funcCall.Type is VoidTypeNode)
|
||||
@@ -1242,6 +1288,20 @@ public class QBEGenerator
|
||||
}
|
||||
}
|
||||
|
||||
// todo(nub31): Parent is not used when getting variables and deferred statements
|
||||
public class Scope(Scope? parent = null)
|
||||
{
|
||||
public readonly Stack<StatementNode> DeferredStatements = [];
|
||||
public readonly Stack<Variable> Variables = [];
|
||||
|
||||
public Scope SubScope()
|
||||
{
|
||||
return new Scope(this);
|
||||
}
|
||||
}
|
||||
|
||||
public record Variable(string Name, TypeNode Type);
|
||||
|
||||
public class StringLiteral(string value, string name)
|
||||
{
|
||||
public string Value { get; } = value;
|
||||
|
||||
Reference in New Issue
Block a user