diff --git a/src/compiler/Generation/QBE/QBEGenerator.cs b/src/compiler/Generation/QBE/QBEGenerator.cs index 9cea4c9..90e221e 100644 --- a/src/compiler/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/Generation/QBE/QBEGenerator.cs @@ -15,7 +15,7 @@ public static class QBEGenerator private static BoundSyntaxTree _syntaxTree = null!; private static BoundDefinitionTable _definitionTable = null!; - private static QBEWriter _writer = new(); + private static QBEWriter _writer = null!; private static List _cStringLiterals = []; private static List _stringLiterals = []; private static Stack _breakLabels = []; @@ -35,7 +35,7 @@ public static class QBEGenerator _syntaxTree = syntaxTree; _definitionTable = definitionTable; - _writer = new QBEWriter(); + _writer = new QBEWriter(file); _cStringLiterals = []; _stringLiterals = []; @@ -51,25 +51,19 @@ public static class QBEGenerator _stringLiteralIndex = 0; _codeIsReachable = true; - _writer.WriteLine($"dbgfile \"{file}\""); - _writer.WriteLine(); - foreach (var structDef in _definitionTable.GetStructs()) { EmitStructDefinition(structDef); - _writer.WriteLine(); } foreach (var funcDef in _syntaxTree.Definitions.OfType()) { EmitFuncDefinition(funcDef, FuncName(funcDef), funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported); - _writer.WriteLine(); } while (_anonymousFunctions.TryDequeue(out var anon)) { EmitFuncDefinition(anon.Func, anon.Name, anon.Func.Parameters, anon.Func.ReturnType, anon.Func.Body, false); - _writer.WriteLine(); } foreach (var cStringLiteral in _cStringLiterals) @@ -141,7 +135,7 @@ public static class QBEGenerator _ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions") }; - _writer.WriteLine($" {store} {value}, {destination}"); + _writer.Code($"{store} {value}, {destination}"); } private static Val EmitLoad(NubType type, string from) @@ -165,38 +159,38 @@ public static class QBEGenerator _ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions") }; - _writer.WriteLine($" {into} {QBEAssign(type)} {load} {from}"); + _writer.Code($"{into} {QBEAssign(type)} {load} {from}"); return new Val(into, type, ValKind.Direct); } private static void EmitMemcpy(string source, string destination, string length) { - _writer.WriteLine($" call $nub_memcpy(l {source}, l {destination}, l {length})"); + _writer.Code($"call $nub_memcpy(l {source}, l {destination}, l {length})"); } private static string EmitArraySizeInBytes(NubArrayType type, string array) { var size = VarName(); - _writer.WriteLine($" {size} =l loadl {array}"); - _writer.WriteLine($" {size} =l mul {size}, {SizeOf(type.ElementType)}"); - _writer.WriteLine($" {size} =l add {size}, 8"); + _writer.Code($"{size} =l loadl {array}"); + _writer.Code($"{size} =l mul {size}, {SizeOf(type.ElementType)}"); + _writer.Code($"{size} =l add {size}, 8"); return size; } private static string EmitCStringSizeInBytes(string cstring) { var size = VarName(); - _writer.WriteLine($" {size} =l call $nub_cstring_length(l {cstring})"); - _writer.WriteLine($" {size} =l add {size}, 1"); + _writer.Code($"{size} =l call $nub_cstring_length(l {cstring})"); + _writer.Code($"{size} =l add {size}, 1"); return size; } private static string EmitStringSizeInBytes(string nubstring) { var size = VarName(); - _writer.WriteLine($" {size} =l loadl {nubstring}"); - _writer.WriteLine($" {size} =l add {size}, 8"); + _writer.Code($"{size} =l loadl {nubstring}"); + _writer.Code($"{size} =l add {size}, 8"); return size; } @@ -254,7 +248,7 @@ public static class QBEGenerator case NubStringType: { var buffer = VarName(); - _writer.WriteLine($" {buffer} =l alloc8 {size}"); + _writer.Code($"{buffer} =l alloc8 {size}"); EmitMemcpy(value, buffer, size); EmitStore(source.Type, buffer, destinationPointer); return; @@ -324,7 +318,7 @@ public static class QBEGenerator _ => throw new ArgumentOutOfRangeException(nameof(complexType)) }; - _writer.WriteLine($" {destination} =l alloc8 {size}"); + _writer.Code($"{destination} =l alloc8 {size}"); EmitMemcpy(value, destination, size); return destination; } @@ -462,8 +456,6 @@ public static class QBEGenerator private static void EmitFuncDefinition(BoundNode debugNode, string name, List parameters, NubType returnType, BoundBlockNode body, bool exported) { - _writer.ResetLineTracker(); - _variables.Clear(); _variableScopes.Clear(); @@ -524,42 +516,11 @@ public static class QBEGenerator return $"{qbeType} %{parameter.Name}"; }); - builder.Append($"({string.Join(", ", parameterStrings)}) {{"); + builder.Append($"({string.Join(", ", parameterStrings)})"); - _writer.WriteLine(builder.ToString()); - _writer.WriteLine("@start"); + _writer.StartFunction(builder.ToString()); - List parameterVars = []; - - foreach (var parameter in parameters) - { - var parameterName = "%" + parameter.Name; - - if (parameter.Type is NubPrimitiveType primitiveType) - { - switch (primitiveType.Kind) - { - case PrimitiveTypeKind.I16: - parameterName = VarName(); - _writer.WriteLine($" {parameterName} =w extsh %{parameter.Name}"); - break; - case PrimitiveTypeKind.I8: - parameterName = VarName(); - _writer.WriteLine($" {parameterName} =w extsb %{parameter.Name}"); - break; - case PrimitiveTypeKind.U16: - parameterName = VarName(); - _writer.WriteLine($" {parameterName} =w extuh %{parameter.Name}"); - break; - case PrimitiveTypeKind.U8: - parameterName = VarName(); - _writer.WriteLine($" {parameterName} =w extub %{parameter.Name}"); - break; - } - } - - parameterVars.Add(new Variable(parameter.Name, new Val(parameterName, parameter.Type, ValKind.Direct))); - } + var parameterVars = parameters.Select(parameter => new Variable(parameter.Name, new Val("%" + parameter.Name, parameter.Type, ValKind.Direct))).ToList(); EmitBlock(body, parameterVars); @@ -567,18 +528,16 @@ public static class QBEGenerator { if (returnType is NubVoidType) { - _writer.WriteLine(" ret"); + _writer.Code("ret"); } } - _writer.WriteLine("}"); + _writer.EndFunction(); } private static void EmitStructDefinition(BoundStructDefinitionNode structDefinition) { - var builder = new StringBuilder(); - - builder.Append($"type {StructName(structDefinition)} = {{ "); + _writer.Write($"type {StructName(structDefinition)} = {{ "); foreach (var structDefinitionField in structDefinition.Fields) { var qbeType = structDefinitionField.Type switch @@ -597,12 +556,10 @@ public static class QBEGenerator NubArrayType or NubPointerType or NubFuncType or NubCStringType or NubStringType => "l", _ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs") }; - builder.Append(qbeType + ", "); + _writer.Write(qbeType + ", "); } - builder.Append("}"); - - _writer.WriteLine(builder.ToString()); + _writer.WriteLine("}"); } private static void EmitStatement(BoundStatementNode statement) @@ -672,13 +629,13 @@ public static class QBEGenerator private static void EmitBreak(BoundBreakNode @break) { - _writer.WriteLine($" jmp {_breakLabels.Peek()}"); + _writer.Code($"jmp {_breakLabels.Peek()}"); _codeIsReachable = false; } private static void EmitContinue(BoundContinueNode @continue) { - _writer.WriteLine($" jmp {_continueLabels.Peek()}"); + _writer.Code($"jmp {_continueLabels.Peek()}"); _codeIsReachable = false; } @@ -689,11 +646,11 @@ public static class QBEGenerator var endLabel = LabelName(); var result = EmitUnwrap(EmitExpression(ifStatement.Condition)); - _writer.WriteLine($" jnz {result}, {trueLabel}, {falseLabel}"); - _writer.WriteLine(trueLabel); + _writer.Code($"jnz {result}, {trueLabel}, {falseLabel}"); + _writer.Code(trueLabel); EmitBlock(ifStatement.Body); - _writer.WriteLine($" jmp {endLabel}"); - _writer.WriteLine(falseLabel); + _writer.Code($"jmp {endLabel}"); + _writer.Code(falseLabel); if (ifStatement.Else.HasValue) { ifStatement.Else.Value.Match @@ -703,7 +660,7 @@ public static class QBEGenerator ); } - _writer.WriteLine(endLabel); + _writer.Code(endLabel); } private static void EmitReturn(BoundReturnNode @return) @@ -711,18 +668,18 @@ public static class QBEGenerator if (@return.Value.HasValue) { var result = EmitUnwrap(EmitExpression(@return.Value.Value)); - _writer.WriteLine($" ret {result}"); + _writer.Code($"ret {result}"); } else { - _writer.WriteLine(" ret"); + _writer.Code("ret"); } } private static void EmitVariableDeclaration(BoundVariableDeclarationNode variableDeclaration) { var variable = VarName(); - _writer.WriteLine($" {variable} =l alloc8 {SizeOf(variableDeclaration.Type)}"); + _writer.Code($"{variable} =l alloc8 {SizeOf(variableDeclaration.Type)}"); if (variableDeclaration.Assignment.HasValue) { @@ -741,13 +698,13 @@ public static class QBEGenerator _breakLabels.Push(endLabel); _continueLabels.Push(conditionLabel); - _writer.WriteLine($" jmp {conditionLabel}"); - _writer.WriteLine(iterationLabel); + _writer.Code($"jmp {conditionLabel}"); + _writer.Code(iterationLabel); EmitBlock(whileStatement.Body); - _writer.WriteLine(conditionLabel); + _writer.Code(conditionLabel); var result = EmitUnwrap(EmitExpression(whileStatement.Condition)); - _writer.WriteLine($" jnz {result}, {iterationLabel}, {endLabel}"); - _writer.WriteLine(endLabel); + _writer.Code($"jnz {result}, {iterationLabel}, {endLabel}"); + _writer.Code(endLabel); _continueLabels.Pop(); _breakLabels.Pop(); @@ -788,34 +745,34 @@ public static class QBEGenerator var elementType = ((NubArrayType)arrayIndexAccess.Array.Type).ElementType; var pointer = VarName(); - _writer.WriteLine($" {pointer} =l mul {index}, {SizeOf(elementType)}"); - _writer.WriteLine($" {pointer} =l add {pointer}, 8"); - _writer.WriteLine($" {pointer} =l add {array}, {pointer}"); + _writer.Code($"{pointer} =l mul {index}, {SizeOf(elementType)}"); + _writer.Code($"{pointer} =l add {pointer}, 8"); + _writer.Code($"{pointer} =l add {array}, {pointer}"); return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer); } private static void EmitArrayBoundsCheck(string array, string index) { var count = VarName(); - _writer.WriteLine($" {count} =l loadl {array}"); + _writer.Code($"{count} =l loadl {array}"); var isNegative = VarName(); - _writer.WriteLine($" {isNegative} =w csltl {index}, 0"); + _writer.Code($"{isNegative} =w csltl {index}, 0"); var isOob = VarName(); - _writer.WriteLine($" {isOob} =w csgel {index}, {count}"); + _writer.Code($"{isOob} =w csgel {index}, {count}"); var anyOob = VarName(); - _writer.WriteLine($" {anyOob} =w or {isNegative}, {isOob}"); + _writer.Code($"{anyOob} =w or {isNegative}, {isOob}"); var oobLabel = LabelName(); var notOobLabel = LabelName(); - _writer.WriteLine($" jnz {anyOob}, {oobLabel}, {notOobLabel}"); + _writer.Code($"jnz {anyOob}, {oobLabel}, {notOobLabel}"); - _writer.WriteLine(oobLabel); - _writer.WriteLine($" call $nub_panic_array_oob()"); + _writer.Code(oobLabel); + _writer.Code($"call $nub_panic_array_oob()"); - _writer.WriteLine(notOobLabel); + _writer.Code(notOobLabel); } private static Val EmitArrayInitializer(BoundArrayInitializerNode arrayInitializer) @@ -824,17 +781,17 @@ public static class QBEGenerator var elementSize = SizeOf(arrayInitializer.ElementType); var capacityInBytes = VarName(); - _writer.WriteLine($" {capacityInBytes} =l mul {capacity}, {elementSize}"); + _writer.Code($"{capacityInBytes} =l mul {capacity}, {elementSize}"); var totalSize = VarName(); - _writer.WriteLine($" {totalSize} =l add {capacityInBytes}, 8"); + _writer.Code($"{totalSize} =l add {capacityInBytes}, 8"); var arrayPointer = VarName(); - _writer.WriteLine($" {arrayPointer} =l alloc8 {totalSize}"); - _writer.WriteLine($" storel {capacity}, {arrayPointer}"); + _writer.Code($"{arrayPointer} =l alloc8 {totalSize}"); + _writer.Code($"storel {capacity}, {arrayPointer}"); var dataPointer = VarName(); - _writer.WriteLine($" {dataPointer} =l add {arrayPointer}, 8"); - _writer.WriteLine($" call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); + _writer.Code($"{dataPointer} =l add {arrayPointer}, 8"); + _writer.Code($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); return new Val(arrayPointer, arrayInitializer.Type, ValKind.Direct); } @@ -870,7 +827,7 @@ public static class QBEGenerator var instruction = EmitBinaryInstructionFor(binaryExpression.Operator, binaryExpression.Left.Type, left, right); - _writer.WriteLine($" {outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); + _writer.Code($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); return new Val(outputName, binaryExpression.Type, ValKind.Direct); } @@ -890,13 +847,13 @@ public static class QBEGenerator { if (type.IsSignedInteger) { - _writer.WriteLine($" {left} =w extsb {left}"); - _writer.WriteLine($" {right} =w extsb {right}"); + _writer.Code($"{left} =w extsb {left}"); + _writer.Code($"{right} =w extsb {right}"); } else { - _writer.WriteLine($" {left} =w extub {left}"); - _writer.WriteLine($" {right} =w extub {right}"); + _writer.Code($"{left} =w extub {left}"); + _writer.Code($"{right} =w extub {right}"); } suffix = 'w'; @@ -905,13 +862,13 @@ public static class QBEGenerator { if (type.IsSignedInteger) { - _writer.WriteLine($" {left} =w extsh {left}"); - _writer.WriteLine($" {right} =w extsh {right}"); + _writer.Code($"{left} =w extsh {left}"); + _writer.Code($"{right} =w extsh {right}"); } else { - _writer.WriteLine($" {left} =w extuh {left}"); - _writer.WriteLine($" {right} =w extuh {right}"); + _writer.Code($"{left} =w extuh {left}"); + _writer.Code($"{right} =w extuh {right}"); } suffix = 'w'; @@ -1083,7 +1040,7 @@ public static class QBEGenerator { destination = VarName(); var size = SizeOf(structInitializer.StructType); - _writer.WriteLine($" {destination} =l alloc8 {size}"); + _writer.Code($"{destination} =l alloc8 {size}"); } foreach (var field in structDefinition.Fields) @@ -1096,7 +1053,7 @@ public static class QBEGenerator Debug.Assert(valueExpression != null); var offset = VarName(); - _writer.WriteLine($" {offset} =l add {destination}, {OffsetOf(structDefinition, field.Name)}"); + _writer.Code($"{offset} =l add {destination}, {OffsetOf(structDefinition, field.Name)}"); EmitCopyIntoOrInitialize(valueExpression, offset); } @@ -1115,16 +1072,16 @@ public static class QBEGenerator switch (unaryExpression.Operand.Type) { case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: - _writer.WriteLine($" {outputName} =l neg {operand}"); + _writer.Code($"{outputName} =l neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }: - _writer.WriteLine($" {outputName} =w neg {operand}"); + _writer.Code($"{outputName} =w neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: - _writer.WriteLine($" {outputName} =d neg {operand}"); + _writer.Code($"{outputName} =d neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: - _writer.WriteLine($" {outputName} =s neg {operand}"); + _writer.Code($"{outputName} =s neg {operand}"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); } @@ -1135,7 +1092,7 @@ public static class QBEGenerator switch (unaryExpression.Operand.Type) { case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }: - _writer.WriteLine($" {outputName} =w xor {operand}, 1"); + _writer.Code($"{outputName} =w xor {operand}, 1"); return new Val(outputName, unaryExpression.Type, ValKind.Direct); } @@ -1161,7 +1118,7 @@ public static class QBEGenerator { if (memberAccess.Member == "count") { - _writer.WriteLine($" {output} =l loadl {item}"); + _writer.Code($"{output} =l loadl {item}"); return new Val(output, memberAccess.Type, ValKind.Direct); } @@ -1171,7 +1128,7 @@ public static class QBEGenerator { if (memberAccess.Member == "count") { - _writer.WriteLine($" {output} =l call $nub_string_length(l {item})"); + _writer.Code($"{output} =l call $nub_string_length(l {item})"); return new Val(output, memberAccess.Type, ValKind.Direct); } @@ -1181,7 +1138,7 @@ public static class QBEGenerator { if (memberAccess.Member == "count") { - _writer.WriteLine($" {output} =l call $nub_cstring_length(l {item})"); + _writer.Code($"{output} =l call $nub_cstring_length(l {item})"); return new Val(output, memberAccess.Type, ValKind.Direct); } @@ -1192,7 +1149,7 @@ public static class QBEGenerator var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue(); var offset = OffsetOf(structDefinition, memberAccess.Member); - _writer.WriteLine($" {output} =l add {item}, {offset}"); + _writer.Code($"{output} =l add {item}, {offset}"); return new Val(output, memberAccess.Type, ValKind.Pointer); } } @@ -1237,12 +1194,12 @@ public static class QBEGenerator { var outputName = VarName(); - _writer.WriteLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); + _writer.Code($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); return new Val(outputName, funcCall.Type, ValKind.Direct); } else { - _writer.WriteLine($" call {funcPointer}({string.Join(", ", parameterStrings)})"); + _writer.Code($"call {funcPointer}({string.Join(", ", parameterStrings)})"); return new Val(string.Empty, funcCall.Type, ValKind.Direct); } } diff --git a/src/compiler/Generation/QBE/QBEWriter.cs b/src/compiler/Generation/QBE/QBEWriter.cs index 8103f14..6c672e2 100644 --- a/src/compiler/Generation/QBE/QBEWriter.cs +++ b/src/compiler/Generation/QBE/QBEWriter.cs @@ -6,12 +6,27 @@ namespace Generation.QBE; public class QBEWriter { - private int _currentLine = -1; private readonly StringBuilder _builder = new(); + private int _currentLine = -1; - public void ResetLineTracker() + public QBEWriter(string debugFile) + { + _builder.AppendLine($"dbgfile \"{debugFile}\""); + _builder.AppendLine(); + } + + public void StartFunction(string signature) { _currentLine = -1; + _builder.AppendLine(); + _builder.Append(signature); + _builder.AppendLine(" {"); + _builder.AppendLine("@start"); + } + + public void EndFunction() + { + _builder.AppendLine("}"); } private void WriteDebugLocation(SourceSpan span) @@ -37,23 +52,30 @@ public class QBEWriter } } - public QBEWriter WriteLine(BoundNode node, string value) - { - WriteDebugLocation(node); - WriteLine(value); - return this; - } - - public QBEWriter WriteLine(string value) + public void Code(string value) { + _builder.Append('\t'); _builder.AppendLine(value); - return this; } - public QBEWriter WriteLine() + public void Comment(string comment) + { + _builder.AppendLine("# " + comment); + } + + public void WriteLine(string text) + { + _builder.AppendLine(text); + } + + public void Write(string text) + { + _builder.Append(text); + } + + public void NewLine() { _builder.AppendLine(); - return this; } public override string ToString()