From 041c25d3ded6f48712ac28cd7c4367d94ef49dc7 Mon Sep 17 00:00:00 2001 From: nub31 Date: Mon, 18 Aug 2025 12:53:35 +0200 Subject: [PATCH] Do not split generator to expr and stmt --- .../Generation/QBE/QBEGenerator.Expression.cs | 558 -------------- .../Generation/QBE/QBEGenerator.Statement.cs | 135 ---- .../NubLang/Generation/QBE/QBEGenerator.cs | 680 +++++++++++++++++- 3 files changed, 679 insertions(+), 694 deletions(-) delete mode 100644 src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs delete mode 100644 src/compiler/NubLang/Generation/QBE/QBEGenerator.Statement.cs diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs deleted file mode 100644 index 161db2a..0000000 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Expression.cs +++ /dev/null @@ -1,558 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using NubLang.Tokenization; -using NubLang.TypeChecking.Node; - -namespace NubLang.Generation.QBE; - -public partial class QBEGenerator -{ - private Val EmitExpression(ExpressionNode expression) - { - return expression switch - { - ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), - StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), - AddressOfNode addressOf => EmitAddressOf(addressOf), - DereferenceNode dereference => EmitDereference(dereference), - BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), - FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), - InterfaceFuncAccessNode interfaceFuncAccess => EmitInterfaceFuncAccess(interfaceFuncAccess), - InterfaceFuncCallNode interfaceFuncCall => EmitInterfaceFuncCall(interfaceFuncCall), - InterfaceInitializerNode interfaceInitializer => EmitInterfaceInitializer(interfaceInitializer), - ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent), - LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent), - VariableIdentNode variableIdent => EmitVariableIdent(variableIdent), - LiteralNode literal => EmitLiteral(literal), - UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), - StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess), - StructFuncAccessNode structFuncAccess => EmitStructFuncAccess(structFuncAccess), - StructFuncCallNode structFuncCall => EmitStructFuncCall(structFuncCall), - ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), - _ => throw new ArgumentOutOfRangeException(nameof(expression)) - }; - } - - private Val EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) - { - var array = EmitUnwrap(EmitExpression(arrayIndexAccess.Target)); - var index = EmitUnwrap(EmitExpression(arrayIndexAccess.Index)); - - EmitArraysCheck(array, index); - - var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType; - - var pointer = TmpName(); - _writer.Indented($"{pointer} =l mul {index}, {SizeOf(elementType)}"); - _writer.Indented($"{pointer} =l add {pointer}, 8"); - _writer.Indented($"{pointer} =l add {array}, {pointer}"); - return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer); - } - - private void EmitArraysCheck(string array, string index) - { - var count = TmpName(); - _writer.Indented($"{count} =l loadl {array}"); - - var isNegative = TmpName(); - _writer.Indented($"{isNegative} =w csltl {index}, 0"); - - var isOob = TmpName(); - _writer.Indented($"{isOob} =w csgel {index}, {count}"); - - var anyOob = TmpName(); - _writer.Indented($"{anyOob} =w or {isNegative}, {isOob}"); - - var oobLabel = LabelName(); - var notOobLabel = LabelName(); - _writer.Indented($"jnz {anyOob}, {oobLabel}, {notOobLabel}"); - - _writer.Indented(oobLabel); - _writer.Indented($"call $nub_panic_array_oob()"); - - _writer.Indented(notOobLabel); - } - - private Val EmitArrayInitializer(ArrayInitializerNode arrayInitializer) - { - var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity)); - var elementSize = SizeOf(arrayInitializer.ElementType); - - var capacityInBytes = TmpName(); - _writer.Indented($"{capacityInBytes} =l mul {capacity}, {elementSize}"); - var totalSize = TmpName(); - _writer.Indented($"{totalSize} =l add {capacityInBytes}, 8"); - - var arrayPointer = TmpName(); - _writer.Indented($"{arrayPointer} =l alloc8 {totalSize}"); - _writer.Indented($"storel {capacity}, {arrayPointer}"); - - var dataPointer = TmpName(); - _writer.Indented($"{dataPointer} =l add {arrayPointer}, 8"); - _writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); - - return new Val(arrayPointer, arrayInitializer.Type, ValKind.Direct); - } - - private Val EmitDereference(DereferenceNode dereference) - { - return EmitLoad(dereference.Type, EmitUnwrap(EmitExpression(dereference.Expression))); - } - - private Val EmitAddressOf(AddressOfNode addressOf) - { - var value = EmitExpression(addressOf.Expression); - if (value.Kind != ValKind.Pointer) - { - throw new UnreachableException("Tried to take address of non-pointer type. This should have been caught in the type checker"); - } - - return new Val(value.Name, addressOf.Type, ValKind.Direct); - } - - private Val EmitBinaryExpression(BinaryExpressionNode binaryExpression) - { - var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); - var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); - - var outputName = TmpName(); - - var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right); - - _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); - return new Val(outputName, binaryExpression.Type, ValKind.Direct); - } - - private string EmitBinaryInstructionFor(BinaryOperator op, TypeNode type, string left, string right) - { - if (op is - BinaryOperator.Equal or - BinaryOperator.NotEqual or - BinaryOperator.GreaterThan or - BinaryOperator.GreaterThanOrEqual or - BinaryOperator.LessThan or - BinaryOperator.LessThanOrEqual) - { - char suffix; - - if (!type.IsSimpleType(out var simpleType, out _)) - { - throw new NotSupportedException("Binary operations is only supported for simple types."); - } - - switch (simpleType.StorageSize) - { - case StorageSize.I8: - _writer.Indented($"{left} =w extsb {left}"); - _writer.Indented($"{right} =w extsb {right}"); - suffix = 'w'; - break; - case StorageSize.U8: - _writer.Indented($"{left} =w extub {left}"); - _writer.Indented($"{right} =w extub {right}"); - suffix = 'w'; - break; - case StorageSize.I16: - _writer.Indented($"{left} =w extsh {left}"); - _writer.Indented($"{right} =w extsh {right}"); - suffix = 'w'; - break; - case StorageSize.U16: - _writer.Indented($"{left} =w extuh {left}"); - _writer.Indented($"{right} =w extuh {right}"); - suffix = 'w'; - break; - case StorageSize.I32 or StorageSize.U32: - suffix = 'w'; - break; - case StorageSize.I64 or StorageSize.U64: - suffix = 'l'; - break; - default: - throw new NotSupportedException($"Unsupported type '{simpleType}' for binary operator '{op}'"); - } - - if (op is BinaryOperator.Equal) - { - return "ceq" + suffix; - } - - if (op is BinaryOperator.NotEqual) - { - return "cne" + suffix; - } - - string sign; - - if (simpleType is IntTypeNode { Signed: true }) - { - sign = "s"; - } - else if (simpleType is IntTypeNode { Signed: false }) - { - sign = "u"; - } - else - { - throw new NotSupportedException($"Unsupported type '{type}' for binary operator '{op}'"); - } - - return op switch - { - BinaryOperator.GreaterThan => 'c' + sign + "gt" + suffix, - BinaryOperator.GreaterThanOrEqual => 'c' + sign + "ge" + suffix, - BinaryOperator.LessThan => 'c' + sign + "lt" + suffix, - BinaryOperator.LessThanOrEqual => 'c' + sign + "le" + suffix, - _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) - }; - } - - return op switch - { - BinaryOperator.Plus => "add", - BinaryOperator.Minus => "sub", - BinaryOperator.Multiply => "mul", - BinaryOperator.Divide => "div", - _ => throw new ArgumentOutOfRangeException(nameof(op)) - }; - } - - private Val EmitExternFuncIdent(ExternFuncIdentNode externFuncIdent) - { - var func = _definitionTable.LookupExternFunc(externFuncIdent.Name); - return new Val(ExternFuncName(func), externFuncIdent.Type, ValKind.Direct); - } - - private Val EmitLocalFuncIdent(LocalFuncIdentNode localFuncIdent) - { - var func = _definitionTable.LookupLocalFunc(localFuncIdent.Name); - return new Val(LocalFuncName(func), localFuncIdent.Type, ValKind.Direct); - } - - private Val EmitVariableIdent(VariableIdentNode variableIdent) - { - return Scope.Lookup(variableIdent.Name); - } - - private Val EmitLiteral(LiteralNode literal) - { - switch (literal.Kind) - { - case LiteralKind.Integer: - { - if (literal.Type is FloatTypeNode { Width: 32 }) - { - var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); - } - - if (literal.Type is FloatTypeNode { Width: 64 }) - { - var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); - } - - if (literal.Type is IntTypeNode) - { - return new Val(literal.Value, literal.Type, ValKind.Direct); - } - - break; - } - case LiteralKind.Float: - { - if (literal.Type is IntTypeNode) - { - return new Val(literal.Value.Split(".").First(), literal.Type, ValKind.Direct); - } - - if (literal.Type is FloatTypeNode { Width: 32 }) - { - var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); - } - - if (literal.Type is FloatTypeNode { Width: 64 }) - { - var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); - var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Direct); - } - - break; - } - case LiteralKind.String: - { - if (literal.Type is StringTypeNode) - { - var stringLiteral = new StringLiteral(literal.Value, StringName()); - _stringLiterals.Add(stringLiteral); - return new Val(stringLiteral.Name, literal.Type, ValKind.Direct); - } - - if (literal.Type is CStringTypeNode) - { - var cStringLiteral = new CStringLiteral(literal.Value, CStringName()); - _cStringLiterals.Add(cStringLiteral); - return new Val(cStringLiteral.Name, literal.Type, ValKind.Direct); - } - - break; - } - case LiteralKind.Bool: - { - if (literal.Type is BoolTypeNode) - { - return new Val(bool.Parse(literal.Value) ? "1" : "0", literal.Type, ValKind.Direct); - } - - break; - } - } - - throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}"); - } - - private Val EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null) - { - var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name); - - if (destination == null) - { - destination = TmpName(); - var size = SizeOf(structInitializer.StructType); - _writer.Indented($"{destination} =l alloc8 {size}"); - } - - foreach (var field in structDef.Fields) - { - if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression)) - { - valueExpression = field.Value.Value; - } - - if (valueExpression == null) - { - throw new UnreachableException("Value of field in uninitialized. This should have been caught in the type checker"); - } - - var offset = TmpName(); - _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}"); - EmitCopyIntoOrInitialize(valueExpression, offset); - } - - return new Val(destination, structInitializer.StructType, ValKind.Direct); - } - - private Val EmitUnaryExpression(UnaryExpressionNode unaryExpression) - { - var operand = EmitUnwrap(EmitExpression(unaryExpression.Operand)); - var outputName = TmpName(); - - switch (unaryExpression.Operator) - { - case UnaryOperator.Negate: - { - switch (unaryExpression.Operand.Type) - { - case IntTypeNode { Signed: true, Width: 64 }: - _writer.Indented($"{outputName} =l neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); - case IntTypeNode { Signed: true, Width: 8 or 16 or 32 }: - _writer.Indented($"{outputName} =w neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); - case FloatTypeNode { Width: 64 }: - _writer.Indented($"{outputName} =d neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); - case FloatTypeNode { Width: 32 }: - _writer.Indented($"{outputName} =s neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); - } - - break; - } - case UnaryOperator.Invert: - { - switch (unaryExpression.Operand.Type) - { - case BoolTypeNode: - _writer.Indented($"{outputName} =w xor {operand}, 1"); - return new Val(outputName, unaryExpression.Type, ValKind.Direct); - } - - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - - throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); - } - - private Val EmitStructFieldAccess(StructFieldAccessNode structFieldAccess) - { - var target = EmitUnwrap(EmitExpression(structFieldAccess.Target)); - - var structDef = _definitionTable.LookupStruct(structFieldAccess.StructType.Name); - var offset = OffsetOf(structDef, structFieldAccess.Field); - - var output = TmpName(); - _writer.Indented($"{output} =l add {target}, {offset}"); - - // If the accessed member is an inline struct, it will not be a pointer - if (structFieldAccess.Type is StructTypeNode) - { - return new Val(output, structFieldAccess.Type, ValKind.Direct); - } - - return new Val(output, structFieldAccess.Type, ValKind.Pointer); - } - - private Val EmitStructFuncAccess(StructFuncAccessNode structFuncAccess) - { - var structDef = _definitionTable.LookupStruct(structFuncAccess.StructType.Name); - var func = StructFuncName(structDef.Name, structFuncAccess.Func); - - return new Val(func, structFuncAccess.Type, ValKind.Direct); - } - - private Val EmitStructFuncCall(StructFuncCallNode structFuncCall) - { - var expression = EmitExpression(structFuncCall.Expression); - var thisParameter = EmitUnwrap(EmitExpression(structFuncCall.StructExpression)); - - List parameterStrings = [$"l {thisParameter}"]; - - foreach (var parameter in structFuncCall.Parameters) - { - var copy = EmitCreateCopyOrInitialize(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); - } - - var funcPointer = EmitUnwrap(expression); - if (structFuncCall.Type is VoidTypeNode) - { - _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, structFuncCall.Type, ValKind.Direct); - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(structFuncCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, structFuncCall.Type, ValKind.Direct); - } - } - - private Val EmitInterfaceFuncAccess(InterfaceFuncAccessNode interfaceFuncAccess) - { - var target = EmitUnwrap(EmitExpression(interfaceFuncAccess.Target)); - - var interfaceDef = _definitionTable.LookupInterface(interfaceFuncAccess.InterfaceType.Name); - var functionIndex = interfaceDef.Functions.ToList().FindIndex(x => x.Name == interfaceFuncAccess.FuncName); - var offset = functionIndex * 8; - - var vtable = TmpName(); - _writer.Indented($"{vtable} =l loadl {target}"); - - var funcOffset = TmpName(); - _writer.Indented($"{funcOffset} =l add {vtable}, {offset}"); - - var func = TmpName(); - _writer.Indented($"{func} =l loadl {funcOffset}"); - - return new Val(func, interfaceFuncAccess.Type, ValKind.Direct); - } - - private Val EmitInterfaceFuncCall(InterfaceFuncCallNode interfaceFuncCall) - { - var expression = EmitExpression(interfaceFuncCall.Expression); - - var thisParameter = EmitUnwrap(EmitExpression(interfaceFuncCall.InterfaceExpression)); - _writer.Indented($"{thisParameter} =l add {thisParameter}, 8"); - _writer.Indented($"{thisParameter} =l loadl {thisParameter}"); - - List parameterStrings = [$"l {thisParameter}"]; - - foreach (var parameter in interfaceFuncCall.Parameters) - { - var copy = EmitCreateCopyOrInitialize(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); - } - - var funcPointer = EmitUnwrap(expression); - if (interfaceFuncCall.Type is VoidTypeNode) - { - _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, interfaceFuncCall.Type, ValKind.Direct); - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(interfaceFuncCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, interfaceFuncCall.Type, ValKind.Direct); - } - } - - private Val EmitInterfaceInitializer(InterfaceInitializerNode interfaceInitializer, string? destination = null) - { - var implementation = EmitUnwrap(EmitExpression(interfaceInitializer.Implementation)); - - var vtableOffset = 0; - foreach (var interfaceImplementation in interfaceInitializer.StructType.InterfaceImplementations) - { - if (interfaceImplementation == interfaceInitializer.InterfaceType) - { - break; - } - - vtableOffset += interfaceImplementation.Functions.Count * 8; - } - - if (destination == null) - { - destination = TmpName(); - _writer.Indented($"{destination} =l alloc8 {SizeOf(interfaceInitializer.InterfaceType)}"); - } - - var interfaceVtablePointer = TmpName(); - _writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(interfaceInitializer.StructType.Name)}, {vtableOffset}"); - _writer.Indented($"storel {interfaceVtablePointer}, {destination}"); - - var objectPointer = TmpName(); - _writer.Indented($"{objectPointer} =l add {destination}, 8"); - _writer.Indented($"storel {implementation}, {objectPointer}"); - - return new Val(destination, interfaceInitializer.InterfaceType, ValKind.Direct); - } - - private Val EmitFuncCall(FuncCallNode funcCall) - { - var expression = EmitExpression(funcCall.Expression); - - var parameterStrings = new List(); - - foreach (var parameter in funcCall.Parameters) - { - var copy = EmitCreateCopyOrInitialize(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); - } - - var funcPointer = EmitUnwrap(expression); - if (funcCall.Type is VoidTypeNode) - { - _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, funcCall.Type, ValKind.Direct); - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, funcCall.Type, ValKind.Direct); - } - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Statement.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.Statement.cs deleted file mode 100644 index 0871558..0000000 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.Statement.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System.Diagnostics; -using NubLang.TypeChecking.Node; - -namespace NubLang.Generation.QBE; - -public partial class QBEGenerator -{ - private void EmitStatement(StatementNode statement) - { - switch (statement) - { - case AssignmentNode assignment: - EmitAssignment(assignment); - break; - case BreakNode: - EmitBreak(); - break; - case ContinueNode: - EmitContinue(); - break; - case IfNode ifStatement: - EmitIf(ifStatement); - break; - case ReturnNode @return: - EmitReturn(@return); - break; - case StatementExpressionNode statementExpression: - EmitExpression(statementExpression.Expression); - break; - case VariableDeclarationNode variableDeclaration: - EmitVariableDeclaration(variableDeclaration); - break; - case WhileNode whileStatement: - EmitWhile(whileStatement); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statement)); - } - } - - private void EmitAssignment(AssignmentNode assignment) - { - var destination = EmitExpression(assignment.Target); - if (destination.Kind != ValKind.Pointer) - { - throw new UnreachableException("Destination of assignment must be a pointer. This should be caught in the type checker"); - } - - EmitCopyIntoOrInitialize(assignment.Value, destination.Name); - } - - private void EmitBreak() - { - _writer.Indented($"jmp {_breakLabels.Peek()}"); - _codeIsReachable = false; - } - - private void EmitContinue() - { - _writer.Indented($"jmp {_continueLabels.Peek()}"); - _codeIsReachable = false; - } - - private void EmitIf(IfNode ifStatement) - { - var trueLabel = LabelName(); - var falseLabel = LabelName(); - var endLabel = LabelName(); - - var result = EmitUnwrap(EmitExpression(ifStatement.Condition)); - _writer.Indented($"jnz {result}, {trueLabel}, {falseLabel}"); - _writer.WriteLine(trueLabel); - EmitBlock(ifStatement.Body); - _writer.Indented($"jmp {endLabel}"); - _writer.WriteLine(falseLabel); - if (ifStatement.Else.HasValue) - { - ifStatement.Else.Value.Match - ( - elseIfNode => EmitIf(elseIfNode), - elseNode => EmitBlock(elseNode) - ); - } - - _writer.WriteLine(endLabel); - } - - private void EmitReturn(ReturnNode @return) - { - if (@return.Value.HasValue) - { - var result = EmitUnwrap(EmitExpression(@return.Value.Value)); - _writer.Indented($"ret {result}"); - } - else - { - _writer.Indented("ret"); - } - } - - private void EmitVariableDeclaration(VariableDeclarationNode variableDeclaration) - { - var name = $"%{variableDeclaration.Name}"; - _writer.Indented($"{name} =l alloc8 8"); - - if (variableDeclaration.Assignment.HasValue) - { - var value = EmitCreateCopyOrInitialize(variableDeclaration.Assignment.Value); - EmitStore(variableDeclaration.Assignment.Value.Type, value, name); - } - - Scope.Declare(variableDeclaration.Name, new Val(name, variableDeclaration.Type, ValKind.Pointer)); - } - - private void EmitWhile(WhileNode whileStatement) - { - var conditionLabel = LabelName(); - var iterationLabel = LabelName(); - var endLabel = LabelName(); - - _breakLabels.Push(endLabel); - _continueLabels.Push(conditionLabel); - - _writer.Indented($"jmp {conditionLabel}"); - _writer.WriteLine(iterationLabel); - EmitBlock(whileStatement.Body); - _writer.WriteLine(conditionLabel); - var result = EmitUnwrap(EmitExpression(whileStatement.Condition)); - _writer.Indented($"jnz {result}, {iterationLabel}, {endLabel}"); - _writer.WriteLine(endLabel); - - _continueLabels.Pop(); - _breakLabels.Pop(); - } -} \ No newline at end of file diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 92bcfd0..f353be2 100644 --- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -1,12 +1,13 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Text; using NubLang.Tokenization; using NubLang.TypeChecking.Node; namespace NubLang.Generation.QBE; -public partial class QBEGenerator +public class QBEGenerator { private readonly TypedSyntaxTree _syntaxTree; private readonly TypedDefinitionTable _definitionTable; @@ -495,6 +496,683 @@ public partial class QBEGenerator _codeIsReachable = true; } + private void EmitStatement(StatementNode statement) + { + switch (statement) + { + case AssignmentNode assignment: + EmitAssignment(assignment); + break; + case BreakNode: + EmitBreak(); + break; + case ContinueNode: + EmitContinue(); + break; + case IfNode ifStatement: + EmitIf(ifStatement); + break; + case ReturnNode @return: + EmitReturn(@return); + break; + case StatementExpressionNode statementExpression: + EmitExpression(statementExpression.Expression); + break; + case VariableDeclarationNode variableDeclaration: + EmitVariableDeclaration(variableDeclaration); + break; + case WhileNode whileStatement: + EmitWhile(whileStatement); + break; + default: + throw new ArgumentOutOfRangeException(nameof(statement)); + } + } + + private void EmitAssignment(AssignmentNode assignment) + { + var destination = EmitExpression(assignment.Target); + if (destination.Kind != ValKind.Pointer) + { + throw new UnreachableException("Destination of assignment must be a pointer. This should be caught in the type checker"); + } + + EmitCopyIntoOrInitialize(assignment.Value, destination.Name); + } + + private void EmitBreak() + { + _writer.Indented($"jmp {_breakLabels.Peek()}"); + _codeIsReachable = false; + } + + private void EmitContinue() + { + _writer.Indented($"jmp {_continueLabels.Peek()}"); + _codeIsReachable = false; + } + + private void EmitIf(IfNode ifStatement) + { + var trueLabel = LabelName(); + var falseLabel = LabelName(); + var endLabel = LabelName(); + + var result = EmitUnwrap(EmitExpression(ifStatement.Condition)); + _writer.Indented($"jnz {result}, {trueLabel}, {falseLabel}"); + _writer.WriteLine(trueLabel); + EmitBlock(ifStatement.Body); + _writer.Indented($"jmp {endLabel}"); + _writer.WriteLine(falseLabel); + if (ifStatement.Else.HasValue) + { + ifStatement.Else.Value.Match + ( + elseIfNode => EmitIf(elseIfNode), + elseNode => EmitBlock(elseNode) + ); + } + + _writer.WriteLine(endLabel); + } + + private void EmitReturn(ReturnNode @return) + { + if (@return.Value.HasValue) + { + var result = EmitUnwrap(EmitExpression(@return.Value.Value)); + _writer.Indented($"ret {result}"); + } + else + { + _writer.Indented("ret"); + } + } + + private void EmitVariableDeclaration(VariableDeclarationNode variableDeclaration) + { + var name = $"%{variableDeclaration.Name}"; + _writer.Indented($"{name} =l alloc8 8"); + + if (variableDeclaration.Assignment.HasValue) + { + var value = EmitCreateCopyOrInitialize(variableDeclaration.Assignment.Value); + EmitStore(variableDeclaration.Assignment.Value.Type, value, name); + } + + Scope.Declare(variableDeclaration.Name, new Val(name, variableDeclaration.Type, ValKind.Pointer)); + } + + private void EmitWhile(WhileNode whileStatement) + { + var conditionLabel = LabelName(); + var iterationLabel = LabelName(); + var endLabel = LabelName(); + + _breakLabels.Push(endLabel); + _continueLabels.Push(conditionLabel); + + _writer.Indented($"jmp {conditionLabel}"); + _writer.WriteLine(iterationLabel); + EmitBlock(whileStatement.Body); + _writer.WriteLine(conditionLabel); + var result = EmitUnwrap(EmitExpression(whileStatement.Condition)); + _writer.Indented($"jnz {result}, {iterationLabel}, {endLabel}"); + _writer.WriteLine(endLabel); + + _continueLabels.Pop(); + _breakLabels.Pop(); + } + + private Val EmitExpression(ExpressionNode expression) + { + return expression switch + { + ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer), + StructInitializerNode structInitializer => EmitStructInitializer(structInitializer), + AddressOfNode addressOf => EmitAddressOf(addressOf), + DereferenceNode dereference => EmitDereference(dereference), + BinaryExpressionNode binaryExpression => EmitBinaryExpression(binaryExpression), + FuncCallNode funcCallExpression => EmitFuncCall(funcCallExpression), + InterfaceFuncAccessNode interfaceFuncAccess => EmitInterfaceFuncAccess(interfaceFuncAccess), + InterfaceFuncCallNode interfaceFuncCall => EmitInterfaceFuncCall(interfaceFuncCall), + InterfaceInitializerNode interfaceInitializer => EmitInterfaceInitializer(interfaceInitializer), + ExternFuncIdentNode externFuncIdent => EmitExternFuncIdent(externFuncIdent), + LocalFuncIdentNode localFuncIdent => EmitLocalFuncIdent(localFuncIdent), + VariableIdentNode variableIdent => EmitVariableIdent(variableIdent), + LiteralNode literal => EmitLiteral(literal), + UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression), + StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess), + StructFuncAccessNode structFuncAccess => EmitStructFuncAccess(structFuncAccess), + StructFuncCallNode structFuncCall => EmitStructFuncCall(structFuncCall), + ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex), + _ => throw new ArgumentOutOfRangeException(nameof(expression)) + }; + } + + private Val EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) + { + var array = EmitUnwrap(EmitExpression(arrayIndexAccess.Target)); + var index = EmitUnwrap(EmitExpression(arrayIndexAccess.Index)); + + EmitArraysCheck(array, index); + + var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType; + + var pointer = TmpName(); + _writer.Indented($"{pointer} =l mul {index}, {SizeOf(elementType)}"); + _writer.Indented($"{pointer} =l add {pointer}, 8"); + _writer.Indented($"{pointer} =l add {array}, {pointer}"); + return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer); + } + + private void EmitArraysCheck(string array, string index) + { + var count = TmpName(); + _writer.Indented($"{count} =l loadl {array}"); + + var isNegative = TmpName(); + _writer.Indented($"{isNegative} =w csltl {index}, 0"); + + var isOob = TmpName(); + _writer.Indented($"{isOob} =w csgel {index}, {count}"); + + var anyOob = TmpName(); + _writer.Indented($"{anyOob} =w or {isNegative}, {isOob}"); + + var oobLabel = LabelName(); + var notOobLabel = LabelName(); + _writer.Indented($"jnz {anyOob}, {oobLabel}, {notOobLabel}"); + + _writer.Indented(oobLabel); + _writer.Indented($"call $nub_panic_array_oob()"); + + _writer.Indented(notOobLabel); + } + + private Val EmitArrayInitializer(ArrayInitializerNode arrayInitializer) + { + var capacity = EmitUnwrap(EmitExpression(arrayInitializer.Capacity)); + var elementSize = SizeOf(arrayInitializer.ElementType); + + var capacityInBytes = TmpName(); + _writer.Indented($"{capacityInBytes} =l mul {capacity}, {elementSize}"); + var totalSize = TmpName(); + _writer.Indented($"{totalSize} =l add {capacityInBytes}, 8"); + + var arrayPointer = TmpName(); + _writer.Indented($"{arrayPointer} =l alloc8 {totalSize}"); + _writer.Indented($"storel {capacity}, {arrayPointer}"); + + var dataPointer = TmpName(); + _writer.Indented($"{dataPointer} =l add {arrayPointer}, 8"); + _writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); + + return new Val(arrayPointer, arrayInitializer.Type, ValKind.Direct); + } + + private Val EmitDereference(DereferenceNode dereference) + { + return EmitLoad(dereference.Type, EmitUnwrap(EmitExpression(dereference.Expression))); + } + + private Val EmitAddressOf(AddressOfNode addressOf) + { + var value = EmitExpression(addressOf.Expression); + if (value.Kind != ValKind.Pointer) + { + throw new UnreachableException("Tried to take address of non-pointer type. This should have been caught in the type checker"); + } + + return new Val(value.Name, addressOf.Type, ValKind.Direct); + } + + private Val EmitBinaryExpression(BinaryExpressionNode binaryExpression) + { + var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); + var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); + + var outputName = TmpName(); + + var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right); + + _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); + return new Val(outputName, binaryExpression.Type, ValKind.Direct); + } + + private string EmitBinaryInstructionFor(BinaryOperator op, TypeNode type, string left, string right) + { + if (op is + BinaryOperator.Equal or + BinaryOperator.NotEqual or + BinaryOperator.GreaterThan or + BinaryOperator.GreaterThanOrEqual or + BinaryOperator.LessThan or + BinaryOperator.LessThanOrEqual) + { + char suffix; + + if (!type.IsSimpleType(out var simpleType, out _)) + { + throw new NotSupportedException("Binary operations is only supported for simple types."); + } + + switch (simpleType.StorageSize) + { + case StorageSize.I8: + _writer.Indented($"{left} =w extsb {left}"); + _writer.Indented($"{right} =w extsb {right}"); + suffix = 'w'; + break; + case StorageSize.U8: + _writer.Indented($"{left} =w extub {left}"); + _writer.Indented($"{right} =w extub {right}"); + suffix = 'w'; + break; + case StorageSize.I16: + _writer.Indented($"{left} =w extsh {left}"); + _writer.Indented($"{right} =w extsh {right}"); + suffix = 'w'; + break; + case StorageSize.U16: + _writer.Indented($"{left} =w extuh {left}"); + _writer.Indented($"{right} =w extuh {right}"); + suffix = 'w'; + break; + case StorageSize.I32 or StorageSize.U32: + suffix = 'w'; + break; + case StorageSize.I64 or StorageSize.U64: + suffix = 'l'; + break; + default: + throw new NotSupportedException($"Unsupported type '{simpleType}' for binary operator '{op}'"); + } + + if (op is BinaryOperator.Equal) + { + return "ceq" + suffix; + } + + if (op is BinaryOperator.NotEqual) + { + return "cne" + suffix; + } + + string sign; + + if (simpleType is IntTypeNode { Signed: true }) + { + sign = "s"; + } + else if (simpleType is IntTypeNode { Signed: false }) + { + sign = "u"; + } + else + { + throw new NotSupportedException($"Unsupported type '{type}' for binary operator '{op}'"); + } + + return op switch + { + BinaryOperator.GreaterThan => 'c' + sign + "gt" + suffix, + BinaryOperator.GreaterThanOrEqual => 'c' + sign + "ge" + suffix, + BinaryOperator.LessThan => 'c' + sign + "lt" + suffix, + BinaryOperator.LessThanOrEqual => 'c' + sign + "le" + suffix, + _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) + }; + } + + return op switch + { + BinaryOperator.Plus => "add", + BinaryOperator.Minus => "sub", + BinaryOperator.Multiply => "mul", + BinaryOperator.Divide => "div", + _ => throw new ArgumentOutOfRangeException(nameof(op)) + }; + } + + private Val EmitExternFuncIdent(ExternFuncIdentNode externFuncIdent) + { + var func = _definitionTable.LookupExternFunc(externFuncIdent.Name); + return new Val(ExternFuncName(func), externFuncIdent.Type, ValKind.Direct); + } + + private Val EmitLocalFuncIdent(LocalFuncIdentNode localFuncIdent) + { + var func = _definitionTable.LookupLocalFunc(localFuncIdent.Name); + return new Val(LocalFuncName(func), localFuncIdent.Type, ValKind.Direct); + } + + private Val EmitVariableIdent(VariableIdentNode variableIdent) + { + return Scope.Lookup(variableIdent.Name); + } + + private Val EmitLiteral(LiteralNode literal) + { + switch (literal.Kind) + { + case LiteralKind.Integer: + { + if (literal.Type is FloatTypeNode { Width: 32 }) + { + var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); + var bits = BitConverter.SingleToInt32Bits(value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); + } + + if (literal.Type is FloatTypeNode { Width: 64 }) + { + var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); + var bits = BitConverter.DoubleToInt64Bits(value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); + } + + if (literal.Type is IntTypeNode) + { + return new Val(literal.Value, literal.Type, ValKind.Direct); + } + + break; + } + case LiteralKind.Float: + { + if (literal.Type is IntTypeNode) + { + return new Val(literal.Value.Split(".").First(), literal.Type, ValKind.Direct); + } + + if (literal.Type is FloatTypeNode { Width: 32 }) + { + var value = float.Parse(literal.Value, CultureInfo.InvariantCulture); + var bits = BitConverter.SingleToInt32Bits(value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); + } + + if (literal.Type is FloatTypeNode { Width: 64 }) + { + var value = double.Parse(literal.Value, CultureInfo.InvariantCulture); + var bits = BitConverter.DoubleToInt64Bits(value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); + } + + break; + } + case LiteralKind.String: + { + if (literal.Type is StringTypeNode) + { + var stringLiteral = new StringLiteral(literal.Value, StringName()); + _stringLiterals.Add(stringLiteral); + return new Val(stringLiteral.Name, literal.Type, ValKind.Direct); + } + + if (literal.Type is CStringTypeNode) + { + var cStringLiteral = new CStringLiteral(literal.Value, CStringName()); + _cStringLiterals.Add(cStringLiteral); + return new Val(cStringLiteral.Name, literal.Type, ValKind.Direct); + } + + break; + } + case LiteralKind.Bool: + { + if (literal.Type is BoolTypeNode) + { + return new Val(bool.Parse(literal.Value) ? "1" : "0", literal.Type, ValKind.Direct); + } + + break; + } + } + + throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}"); + } + + private Val EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null) + { + var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name); + + if (destination == null) + { + destination = TmpName(); + var size = SizeOf(structInitializer.StructType); + _writer.Indented($"{destination} =l alloc8 {size}"); + } + + foreach (var field in structDef.Fields) + { + if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression)) + { + valueExpression = field.Value.Value; + } + + if (valueExpression == null) + { + throw new UnreachableException("Value of field in uninitialized. This should have been caught in the type checker"); + } + + var offset = TmpName(); + _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}"); + EmitCopyIntoOrInitialize(valueExpression, offset); + } + + return new Val(destination, structInitializer.StructType, ValKind.Direct); + } + + private Val EmitUnaryExpression(UnaryExpressionNode unaryExpression) + { + var operand = EmitUnwrap(EmitExpression(unaryExpression.Operand)); + var outputName = TmpName(); + + switch (unaryExpression.Operator) + { + case UnaryOperator.Negate: + { + switch (unaryExpression.Operand.Type) + { + case IntTypeNode { Signed: true, Width: 64 }: + _writer.Indented($"{outputName} =l neg {operand}"); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); + case IntTypeNode { Signed: true, Width: 8 or 16 or 32 }: + _writer.Indented($"{outputName} =w neg {operand}"); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); + case FloatTypeNode { Width: 64 }: + _writer.Indented($"{outputName} =d neg {operand}"); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); + case FloatTypeNode { Width: 32 }: + _writer.Indented($"{outputName} =s neg {operand}"); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); + } + + break; + } + case UnaryOperator.Invert: + { + switch (unaryExpression.Operand.Type) + { + case BoolTypeNode: + _writer.Indented($"{outputName} =w xor {operand}, 1"); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); + } + + break; + } + default: + { + throw new ArgumentOutOfRangeException(); + } + } + + throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); + } + + private Val EmitStructFieldAccess(StructFieldAccessNode structFieldAccess) + { + var target = EmitUnwrap(EmitExpression(structFieldAccess.Target)); + + var structDef = _definitionTable.LookupStruct(structFieldAccess.StructType.Name); + var offset = OffsetOf(structDef, structFieldAccess.Field); + + var output = TmpName(); + _writer.Indented($"{output} =l add {target}, {offset}"); + + // If the accessed member is an inline struct, it will not be a pointer + if (structFieldAccess.Type is StructTypeNode) + { + return new Val(output, structFieldAccess.Type, ValKind.Direct); + } + + return new Val(output, structFieldAccess.Type, ValKind.Pointer); + } + + private Val EmitStructFuncAccess(StructFuncAccessNode structFuncAccess) + { + var structDef = _definitionTable.LookupStruct(structFuncAccess.StructType.Name); + var func = StructFuncName(structDef.Name, structFuncAccess.Func); + + return new Val(func, structFuncAccess.Type, ValKind.Direct); + } + + private Val EmitStructFuncCall(StructFuncCallNode structFuncCall) + { + var expression = EmitExpression(structFuncCall.Expression); + var thisParameter = EmitUnwrap(EmitExpression(structFuncCall.StructExpression)); + + List parameterStrings = [$"l {thisParameter}"]; + + foreach (var parameter in structFuncCall.Parameters) + { + var copy = EmitCreateCopyOrInitialize(parameter); + parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); + } + + var funcPointer = EmitUnwrap(expression); + if (structFuncCall.Type is VoidTypeNode) + { + _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); + return new Val(string.Empty, structFuncCall.Type, ValKind.Direct); + } + else + { + var outputName = TmpName(); + _writer.Indented($"{outputName} {QBEAssign(structFuncCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); + return new Val(outputName, structFuncCall.Type, ValKind.Direct); + } + } + + private Val EmitInterfaceFuncAccess(InterfaceFuncAccessNode interfaceFuncAccess) + { + var target = EmitUnwrap(EmitExpression(interfaceFuncAccess.Target)); + + var interfaceDef = _definitionTable.LookupInterface(interfaceFuncAccess.InterfaceType.Name); + var functionIndex = interfaceDef.Functions.ToList().FindIndex(x => x.Name == interfaceFuncAccess.FuncName); + var offset = functionIndex * 8; + + var vtable = TmpName(); + _writer.Indented($"{vtable} =l loadl {target}"); + + var funcOffset = TmpName(); + _writer.Indented($"{funcOffset} =l add {vtable}, {offset}"); + + var func = TmpName(); + _writer.Indented($"{func} =l loadl {funcOffset}"); + + return new Val(func, interfaceFuncAccess.Type, ValKind.Direct); + } + + private Val EmitInterfaceFuncCall(InterfaceFuncCallNode interfaceFuncCall) + { + var expression = EmitExpression(interfaceFuncCall.Expression); + + var thisParameter = EmitUnwrap(EmitExpression(interfaceFuncCall.InterfaceExpression)); + _writer.Indented($"{thisParameter} =l add {thisParameter}, 8"); + _writer.Indented($"{thisParameter} =l loadl {thisParameter}"); + + List parameterStrings = [$"l {thisParameter}"]; + + foreach (var parameter in interfaceFuncCall.Parameters) + { + var copy = EmitCreateCopyOrInitialize(parameter); + parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); + } + + var funcPointer = EmitUnwrap(expression); + if (interfaceFuncCall.Type is VoidTypeNode) + { + _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); + return new Val(string.Empty, interfaceFuncCall.Type, ValKind.Direct); + } + else + { + var outputName = TmpName(); + _writer.Indented($"{outputName} {QBEAssign(interfaceFuncCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); + return new Val(outputName, interfaceFuncCall.Type, ValKind.Direct); + } + } + + private Val EmitInterfaceInitializer(InterfaceInitializerNode interfaceInitializer, string? destination = null) + { + var implementation = EmitUnwrap(EmitExpression(interfaceInitializer.Implementation)); + + var vtableOffset = 0; + foreach (var interfaceImplementation in interfaceInitializer.StructType.InterfaceImplementations) + { + if (interfaceImplementation == interfaceInitializer.InterfaceType) + { + break; + } + + vtableOffset += interfaceImplementation.Functions.Count * 8; + } + + if (destination == null) + { + destination = TmpName(); + _writer.Indented($"{destination} =l alloc8 {SizeOf(interfaceInitializer.InterfaceType)}"); + } + + var interfaceVtablePointer = TmpName(); + _writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(interfaceInitializer.StructType.Name)}, {vtableOffset}"); + _writer.Indented($"storel {interfaceVtablePointer}, {destination}"); + + var objectPointer = TmpName(); + _writer.Indented($"{objectPointer} =l add {destination}, 8"); + _writer.Indented($"storel {implementation}, {objectPointer}"); + + return new Val(destination, interfaceInitializer.InterfaceType, ValKind.Direct); + } + + private Val EmitFuncCall(FuncCallNode funcCall) + { + var expression = EmitExpression(funcCall.Expression); + + var parameterStrings = new List(); + + foreach (var parameter in funcCall.Parameters) + { + var copy = EmitCreateCopyOrInitialize(parameter); + parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); + } + + var funcPointer = EmitUnwrap(expression); + if (funcCall.Type is VoidTypeNode) + { + _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); + return new Val(string.Empty, funcCall.Type, ValKind.Direct); + } + else + { + var outputName = TmpName(); + _writer.Indented($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); + return new Val(outputName, funcCall.Type, ValKind.Direct); + } + } + private string EmitUnwrap(Val val) { return val.Kind switch