From bbbdf42c4726d59dc19c944c226148278777a980 Mon Sep 17 00:00:00 2001 From: nub31 Date: Thu, 16 Oct 2025 15:48:04 +0200 Subject: [PATCH] Some cleanup --- compiler/NubLang.CLI/Archive.cs | 29 - compiler/NubLang.CLI/GCC.cs | 54 - compiler/NubLang.CLI/Program.cs | 63 +- compiler/NubLang.CLI/QBE.cs | 30 - compiler/NubLang/Ast/Node.cs | 2 +- .../{CGenerator.cs => Generator.cs} | 110 +- .../{CWriter.cs => IndentedTextWriter.cs} | 6 +- compiler/NubLang/Generation/QBEGenerator.cs | 1270 ----------------- compiler/NubLang/Generation/QBEWriter.cs | 39 - examples/raylib/makefile | 2 +- 10 files changed, 95 insertions(+), 1510 deletions(-) delete mode 100644 compiler/NubLang.CLI/Archive.cs delete mode 100644 compiler/NubLang.CLI/GCC.cs delete mode 100644 compiler/NubLang.CLI/QBE.cs rename compiler/NubLang/Generation/{CGenerator.cs => Generator.cs} (82%) rename compiler/NubLang/Generation/{CWriter.cs => IndentedTextWriter.cs} (89%) delete mode 100644 compiler/NubLang/Generation/QBEGenerator.cs delete mode 100644 compiler/NubLang/Generation/QBEWriter.cs diff --git a/compiler/NubLang.CLI/Archive.cs b/compiler/NubLang.CLI/Archive.cs deleted file mode 100644 index b9d8d51..0000000 --- a/compiler/NubLang.CLI/Archive.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Diagnostics; - -namespace NubLang.CLI; - -public static class Archive -{ - public static async Task Invoke(string fileName, params IEnumerable objectFiles) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("ar", ["rcs", fileName, ..objectFiles]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync(errors); - } - - return process.ExitCode == 0; - } -} \ No newline at end of file diff --git a/compiler/NubLang.CLI/GCC.cs b/compiler/NubLang.CLI/GCC.cs deleted file mode 100644 index 5d3399b..0000000 --- a/compiler/NubLang.CLI/GCC.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Diagnostics; - -namespace NubLang.CLI; - -public static class GCC -{ - public static async Task Assemble(string asmPath, string outPath) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("x86_64-elf-as", ["-nostartfiles", "-o", outPath, asmPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync(errors); - } - - return process.ExitCode == 0; - } - - public static async Task Compile(string cPath, string outPath) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("gcc", ["-ffreestanding", "-nostartfiles", "-c", "-o", outPath, cPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync(errors); - } - - return process.ExitCode == 0; - } -} \ No newline at end of file diff --git a/compiler/NubLang.CLI/Program.cs b/compiler/NubLang.CLI/Program.cs index e20def1..9a478de 100644 --- a/compiler/NubLang.CLI/Program.cs +++ b/compiler/NubLang.CLI/Program.cs @@ -6,8 +6,6 @@ using NubLang.Generation; using NubLang.Modules; using NubLang.Syntax; -var sw = Stopwatch.StartNew(); - var options = new Options(); for (var i = 0; i < args.Length; i++) @@ -34,9 +32,6 @@ for (var i = 0; i < args.Length; i++) } } -Console.WriteLine($"Parse cli args: {sw.ElapsedMilliseconds}ms"); -sw.Restart(); - foreach (var file in options.Files) { if (!File.Exists(file)) @@ -46,9 +41,6 @@ foreach (var file in options.Files) } } -Console.WriteLine($"Check file exists: {sw.ElapsedMilliseconds}ms"); -sw.Restart(); - var diagnostics = new List(); var syntaxTrees = new List(); @@ -58,26 +50,15 @@ foreach (var file in options.Files) tokenizer.Tokenize(); diagnostics.AddRange(tokenizer.Diagnostics); - Console.WriteLine($" Tokenize: {Path.GetFileName(file)}: {sw.ElapsedMilliseconds}ms"); - sw.Restart(); - var parser = new Parser(); var syntaxTree = parser.Parse(tokenizer.Tokens); diagnostics.AddRange(parser.Diagnostics); - Console.WriteLine($" Parse: {Path.GetFileName(file)}: {sw.ElapsedMilliseconds}ms"); - sw.Restart(); - syntaxTrees.Add(syntaxTree); } -sw.Restart(); - var moduleRepository = new ModuleRepository(syntaxTrees); -Console.WriteLine($"Create module repository: {sw.ElapsedMilliseconds}ms"); -sw.Restart(); - var definitions = new List(); var referencedStructTypes = new HashSet(); @@ -87,9 +68,6 @@ foreach (var syntaxTree in syntaxTrees) var typeChecker = new TypeChecker(syntaxTree, moduleRepository); typeChecker.Check(); - Console.WriteLine($" Type check {syntaxTree.Metadata.ModuleName}: {sw.ElapsedMilliseconds}ms"); - sw.Restart(); - definitions.AddRange(typeChecker.Definitions); diagnostics.AddRange(typeChecker.Diagnostics); @@ -99,16 +77,11 @@ foreach (var syntaxTree in syntaxTrees) } } -sw.Restart(); - foreach (var diagnostic in diagnostics) { Console.Error.WriteLine(diagnostic.FormatANSI()); } -Console.WriteLine($"Print diagnostics: {sw.ElapsedMilliseconds}ms"); -sw.Restart(); - if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Error)) { return 1; @@ -116,38 +89,20 @@ if (diagnostics.Any(diagnostic => diagnostic.Severity == DiagnosticSeverity.Erro Directory.CreateDirectory(".build"); -var generator = new CGenerator(definitions, referencedStructTypes); +var generator = new Generator(definitions, referencedStructTypes); var c = generator.Emit(); var cFilePath = Path.Combine(".build", "out.c"); File.WriteAllText(cFilePath, c); -var objFilePath = Path.Combine(".build", "out.o"); -var asmSuccess = await GCC.Compile(cFilePath, objFilePath); -if (!asmSuccess) return 1; +using var process = Process.Start("gcc", ["-ffreestanding", "-nostartfiles", "-c", "-o", Path.Combine(".build", "out.o"), cFilePath]); -// sw.Restart(); -// -// var generator = new QBEGenerator(definitions, referencedStructTypes); -// var ssa = generator.Emit(); -// var ssaFilePath = Path.Combine(".build", "out.ssa"); -// File.WriteAllText(ssaFilePath, ssa); -// -// Console.WriteLine($"Emit ssa: {sw.ElapsedMilliseconds}ms"); -// sw.Restart(); -// -// var asmFilePath = Path.Combine(".build", "out.asm"); -// var qbeSuccess = await QBE.Invoke(ssaFilePath, asmFilePath); -// if (!qbeSuccess) return 1; -// -// Console.WriteLine($"Emit asm: {sw.ElapsedMilliseconds}ms"); -// sw.Restart(); -// -// var objFilePath = Path.Combine(".build", "out.o"); -// var asmSuccess = await GCC.Assemble(asmFilePath, objFilePath); -// if (!asmSuccess) return 1; -// -// Console.WriteLine($"Assemble: {sw.ElapsedMilliseconds}ms"); -sw.Restart(); +process.WaitForExit(); + +if (process.ExitCode != 0) +{ + Console.Error.WriteLine($"gcc failed with exit code {process.ExitCode}"); + return 1; +} return 0; \ No newline at end of file diff --git a/compiler/NubLang.CLI/QBE.cs b/compiler/NubLang.CLI/QBE.cs deleted file mode 100644 index a4aab64..0000000 --- a/compiler/NubLang.CLI/QBE.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Diagnostics; - -namespace NubLang.CLI; - -public static class QBE -{ - public static async Task Invoke(string ssaPath, string outPath) - { - using var process = new Process(); - process.StartInfo = new ProcessStartInfo("qbe", ["-o", outPath, ssaPath]) - { - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - }; - - process.Start(); - - await process.WaitForExitAsync(); - - var errors = await process.StandardError.ReadToEndAsync(); - if (!string.IsNullOrWhiteSpace(errors)) - { - await Console.Error.WriteLineAsync(errors); - } - - return process.ExitCode == 0; - } -} \ No newline at end of file diff --git a/compiler/NubLang/Ast/Node.cs b/compiler/NubLang/Ast/Node.cs index e92b362..9eed5f1 100644 --- a/compiler/NubLang/Ast/Node.cs +++ b/compiler/NubLang/Ast/Node.cs @@ -126,7 +126,7 @@ public record StructFieldAccessNode(NubType Type, ExpressionNode Target, string public record StructInitializerNode(NubStructType StructType, Dictionary Initializers) : RValueExpressionNode(StructType); -public record DereferenceNode(NubType Type, ExpressionNode Expression) : LValueExpressionNode(Type); +public record DereferenceNode(NubType Type, ExpressionNode Target) : LValueExpressionNode(Type); public record ConvertIntNode(NubType Type, ExpressionNode Value, NubIntType ValueType, NubIntType TargetType) : RValueExpressionNode(Type); diff --git a/compiler/NubLang/Generation/CGenerator.cs b/compiler/NubLang/Generation/Generator.cs similarity index 82% rename from compiler/NubLang/Generation/CGenerator.cs rename to compiler/NubLang/Generation/Generator.cs index 6adf460..e73c482 100644 --- a/compiler/NubLang/Generation/CGenerator.cs +++ b/compiler/NubLang/Generation/Generator.cs @@ -2,17 +2,17 @@ using NubLang.Ast; namespace NubLang.Generation; -public class CGenerator +public class Generator { private readonly List _definitions; private readonly HashSet _structTypes; - private readonly CWriter _writer; + private readonly IndentedTextWriter _writer; - public CGenerator(List definitions, HashSet structTypes) + public Generator(List definitions, HashSet structTypes) { _definitions = definitions; _structTypes = structTypes; - _writer = new CWriter(); + _writer = new IndentedTextWriter(); } private static string MapType(NubType nubType) @@ -114,7 +114,6 @@ public class CGenerator """); _writer.WriteLine(); - _writer.WriteLine("// Struct definitions"); foreach (var structType in _structTypes) { _writer.WriteLine("typedef struct"); @@ -131,9 +130,11 @@ public class CGenerator _writer.WriteLine(); } - _writer.WriteLine("// Function declarations"); + var appendNewLine = false; + foreach (var funcNode in _definitions.OfType()) { + appendNewLine = true; var parameters = funcNode.Signature.Parameters.Count != 0 ? string.Join(", ", funcNode.Signature.Parameters.Select(x => $"{MapType(x.Type)} {x.Name}")) : "void"; @@ -142,12 +143,13 @@ public class CGenerator _writer.WriteLine($"{MapType(funcNode.Signature.ReturnType)} {name}({parameters});"); } - _writer.WriteLine(); + if (appendNewLine) + { + _writer.WriteLine(); + } - _writer.WriteLine("// Struct function implementations"); foreach (var structNode in _definitions.OfType()) { - _writer.WriteLine($"// {structNode.Module}::{structNode.Name}"); foreach (var structFuncNode in structNode.Functions) { var parameters = structFuncNode.Signature.Parameters.Count != 0 @@ -157,12 +159,10 @@ public class CGenerator var name = StructFuncName(structNode.Module, structNode.Name, structFuncNode.Name); _writer.WriteLine($"{MapType(structFuncNode.Signature.ReturnType)} {name}({parameters})"); EmitBlock(structFuncNode.Body); + _writer.WriteLine(); } } - _writer.WriteLine(); - - _writer.WriteLine("// Function implementations"); foreach (var funcNode in _definitions.OfType()) { if (funcNode.Body == null) continue; @@ -229,7 +229,9 @@ public class CGenerator private void EmitAssignment(AssignmentNode assignmentNode) { - throw new NotImplementedException(); + var target = EmitExpression(assignmentNode.Target); + var value = EmitExpression(assignmentNode.Value); + _writer.WriteLine($"{target} = {value};"); } private void EmitBreak(BreakNode breakNode) @@ -314,7 +316,7 @@ public class CGenerator private string EmitExpression(ExpressionNode expressionNode) { - return expressionNode switch + var expr = expressionNode switch { ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode), ArrayInitializerNode arrayInitializerNode => EmitArrayInitializer(arrayInitializerNode), @@ -342,21 +344,53 @@ public class CGenerator UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(unaryExpressionNode), _ => throw new ArgumentOutOfRangeException(nameof(expressionNode)) }; + + return $"({expr})"; } private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode) { - throw new NotImplementedException(); + var array = EmitExpression(arrayIndexAccessNode.Target); + var index = EmitExpression(arrayIndexAccessNode.Index); + return $"(({MapType(arrayIndexAccessNode.Type)}*){array})[{index}]"; } private string EmitArrayInitializer(ArrayInitializerNode arrayInitializerNode) { - throw new NotImplementedException(); + var capacity = EmitExpression(arrayInitializerNode.Capacity); + var elementType = MapType(arrayInitializerNode.ElementType); + return $"({elementType}[{capacity}]){{0}}"; } private string EmitBinaryExpression(BinaryExpressionNode binaryExpressionNode) { - throw new NotImplementedException(); + var left = EmitExpression(binaryExpressionNode.Left); + var right = EmitExpression(binaryExpressionNode.Right); + + var op = binaryExpressionNode.Operator switch + { + BinaryOperator.Plus => "+", + BinaryOperator.Minus => "-", + BinaryOperator.Multiply => "*", + BinaryOperator.Divide => "/", + BinaryOperator.Modulo => "%", + BinaryOperator.Equal => "==", + BinaryOperator.NotEqual => "!=", + BinaryOperator.LessThan => "<", + BinaryOperator.LessThanOrEqual => "<=", + BinaryOperator.GreaterThan => ">", + BinaryOperator.GreaterThanOrEqual => ">=", + BinaryOperator.LogicalAnd => "&&", + BinaryOperator.LogicalOr => "||", + BinaryOperator.BitwiseAnd => "&", + BinaryOperator.BitwiseOr => "|", + BinaryOperator.BitwiseXor => "^", + BinaryOperator.LeftShift => "<<", + BinaryOperator.RightShift => ">>", + _ => throw new ArgumentOutOfRangeException() + }; + + return $"{left} {op} {right}"; } private string EmitBoolLiteral(BoolLiteralNode boolLiteralNode) @@ -366,12 +400,14 @@ public class CGenerator private string EmitConvertFloat(ConvertFloatNode convertFloatNode) { - throw new NotImplementedException(); + var value = EmitExpression(convertFloatNode.Value); + return $"({MapType(convertFloatNode.Type)}){value}"; } private string EmitConvertInt(ConvertIntNode convertIntNode) { - throw new NotImplementedException(); + var value = EmitExpression(convertIntNode.Value); + return $"({MapType(convertIntNode.Type)}){value}"; } private string EmitCStringLiteral(CStringLiteralNode cStringLiteralNode) @@ -381,22 +417,36 @@ public class CGenerator private string EmitDereference(DereferenceNode dereferenceNode) { - throw new NotImplementedException(); + var pointer = EmitExpression(dereferenceNode.Target); + return $"*({MapType(dereferenceNode.Type)}*){pointer}"; } private string EmitFloat32Literal(Float32LiteralNode float32LiteralNode) { - throw new NotImplementedException(); + var str = float32LiteralNode.Value.ToString("G9", System.Globalization.CultureInfo.InvariantCulture); + if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E')) + { + str += ".0"; + } + + return str + "f"; } private string EmitFloat64Literal(Float64LiteralNode float64LiteralNode) { - throw new NotImplementedException(); + var str = float64LiteralNode.Value.ToString("G17", System.Globalization.CultureInfo.InvariantCulture); + if (!str.Contains('.') && !str.Contains('e') && !str.Contains('E')) + { + str += ".0"; + } + + return str; } private string EmitFloatToIntBuiltin(FloatToIntBuiltinNode floatToIntBuiltinNode) { - throw new NotImplementedException(); + var value = EmitExpression(floatToIntBuiltinNode.Value); + return $"({MapType(floatToIntBuiltinNode.Type)}){value}"; } private string EmitFuncCall(FuncCallNode funcCallNode) @@ -426,22 +476,23 @@ public class CGenerator private string EmitAddressOf(AddressOfNode addressOfNode) { - throw new NotImplementedException(); + var value = EmitExpression(addressOfNode.LValue); + return $"(uintptr_t)&{value}"; } private string EmitLValueIdentifier(LValueIdentifierNode lValueIdentifierNode) { - throw new NotImplementedException(); + return lValueIdentifierNode.Name; } private string EmitRValueIdentifier(RValueIdentifierNode rValueIdentifierNode) { - throw new NotImplementedException(); + return rValueIdentifierNode.Name; } private string EmitSizeBuiltin(SizeBuiltinNode sizeBuiltinNode) { - throw new NotImplementedException(); + return $"sizeof({MapType(sizeBuiltinNode.TargetType)})"; } private string EmitStringLiteral(StringLiteralNode stringLiteralNode) @@ -451,7 +502,8 @@ public class CGenerator private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccessNode) { - throw new NotImplementedException(); + var structExpr = EmitExpression(structFieldAccessNode.Target); + return $"{structExpr}.{structFieldAccessNode.Field}"; } private string EmitStructFuncCall(StructFuncCallNode structFuncCallNode) @@ -474,7 +526,7 @@ public class CGenerator ? "0" : string.Join(", ", initValues); - return $"({MapType(structInitializerNode.Type)}){{ {initString} }}"; + return $"({MapType(structInitializerNode.Type)}){{{initString}}}"; } private string EmitUIntLiteral(UIntLiteralNode uIntLiteralNode) diff --git a/compiler/NubLang/Generation/CWriter.cs b/compiler/NubLang/Generation/IndentedTextWriter.cs similarity index 89% rename from compiler/NubLang/Generation/CWriter.cs rename to compiler/NubLang/Generation/IndentedTextWriter.cs index d74157a..5ec5274 100644 --- a/compiler/NubLang/Generation/CWriter.cs +++ b/compiler/NubLang/Generation/IndentedTextWriter.cs @@ -2,7 +2,7 @@ using System.Text; namespace NubLang.Generation; -internal class CWriter +internal class IndentedTextWriter { private readonly StringBuilder _builder = new(); private int _indentLevel; @@ -52,10 +52,10 @@ internal class CWriter private class IndentScope : IDisposable { - private readonly CWriter _writer; + private readonly IndentedTextWriter _writer; private bool _disposed; - public IndentScope(CWriter writer) + public IndentScope(IndentedTextWriter writer) { _writer = writer; } diff --git a/compiler/NubLang/Generation/QBEGenerator.cs b/compiler/NubLang/Generation/QBEGenerator.cs deleted file mode 100644 index e6d30cb..0000000 --- a/compiler/NubLang/Generation/QBEGenerator.cs +++ /dev/null @@ -1,1270 +0,0 @@ -using System.Diagnostics; -using System.Text; -using NubLang.Ast; - -namespace NubLang.Generation; - -public class QBEGenerator -{ - private const int PTR_SIZE = 8; - - private readonly QBEWriter _writer; - private readonly List _definitions; - private readonly HashSet _structTypes; - - private readonly List _cStringLiterals = []; - private readonly List _stringLiterals = []; - private readonly Stack _breakLabels = []; - private readonly Stack _continueLabels = []; - private readonly Stack _scopes = new(); - private int _tmpIndex; - private int _labelIndex; - private int _cStringLiteralIndex; - private int _stringLiteralIndex; - - private Scope Scope => _scopes.Peek(); - - public QBEGenerator(List definitions, HashSet structTypes) - { - _definitions = definitions; - _structTypes = structTypes; - _writer = new QBEWriter(); - } - - public string Emit() - { - _cStringLiterals.Clear(); - _stringLiterals.Clear(); - _breakLabels.Clear(); - _continueLabels.Clear(); - _scopes.Clear(); - _tmpIndex = 0; - _labelIndex = 0; - _cStringLiteralIndex = 0; - _stringLiteralIndex = 0; - - _writer.Comment("========== Builtin functions =========="); - - _writer.WriteLine(""" - function l $.cstring_len(l %str) { - @start - %count =l copy 0 - @loop - %address =l add %str, %count - %value =w loadub %address - jnz %value, @continue, @end - @continue - %count =l add %count, 1 - jmp @loop - @end - ret %count - } - - function $.memcpy(l %source, l %destination, l %length) { - @start - %count =l copy 0 - @loop - %condition =w cultl %count, %length - jnz %condition, @continue, @end - @continue - %source_address =l add %source, %count - %destination_address =l add %destination, %count - %value =w loadub %source_address - storeb %value, %destination_address - %count =l add %count, 1 - jmp @loop - @end - ret - } - - function $.memset(l %destination, l %value, l %length) { - @start - %count =l copy 0 - @loop - %condition =w cultl %count, %length - jnz %condition, @continue, @end - @continue - %destination_address =l add %destination, %count - storeb %value, %destination_address - %count =l add %count, 1 - jmp @loop - @end - ret - } - - function l $.array_size(l %array) { - @start - %size =l loadl %array - ret %size - } - - """); - - _writer.Comment("========== Referenced structs =========="); - - foreach (var structType in _structTypes) - { - EmitStructType(structType); - } - - _writer.NewLine(); - - _writer.Comment("========== Struct definitions =========="); - - foreach (var structDef in _definitions.OfType()) - { - EmitStructDefinition(structDef); - } - - _writer.Comment("========== Function definitions =========="); - - foreach (var funcDef in _definitions.OfType()) - { - EmitFuncDefinition(funcDef); - } - - _writer.Comment("========== cstring literals =========="); - - foreach (var cStringLiteral in _cStringLiterals) - { - _writer.WriteLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}"); - } - - _writer.NewLine(); - _writer.Comment("========== string literals =========="); - - foreach (var stringLiteral in _stringLiterals) - { - var bytes = Encoding.UTF8.GetBytes(stringLiteral.Value).Select(b => $"b {b}"); - _writer.WriteLine($"data {stringLiteral.Name} = {{ l {stringLiteral.Value.Length}, {string.Join(", ", bytes)} }}"); - } - - return _writer.ToString(); - } - - private static string QBEAssign(NubType type) - { - return type switch - { - NubIntType { Width: <= 32 } => "=w", - NubIntType { Width: 64 } => "=l", - NubFloatType { Width: 32 } => "=s", - NubFloatType { Width: 64 } => "=d", - NubBoolType => "=w", - NubPointerType => "=l", - NubFuncType => "=l", - NubCStringType => "=l", - NubStringType => "=l", - NubArrayType => "=l", - NubStructType => "=l", - NubVoidType => throw new InvalidOperationException("Void has no assignment"), - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}") - }; - } - - private void EmitStore(NubType type, string value, string destination) - { - var store = type switch - { - NubBoolType => "storeb", - NubIntType { Width: 8 } => "storeb", - NubIntType { Width: 16 } => "storeh", - NubIntType { Width: 32 } => "storew", - NubIntType { Width: 64 } => "storel", - NubFloatType { Width: 32 } => "stores", - NubFloatType { Width: 64 } => "stored", - NubPointerType => "storel", - NubFuncType => "storel", - NubCStringType => "storel", - NubStringType => "storel", - NubArrayType => "storel", - NubStructType => throw new InvalidOperationException("Struct stores must use blit/memcpy"), - NubVoidType => throw new InvalidOperationException("Cannot store void"), - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}") - }; - - _writer.Indented($"{store} {value}, {destination}"); - } - - private string EmitLoad(NubType type, string from) - { - string load = type switch - { - NubBoolType => "loadub", - NubIntType { Signed: true, Width: 8 } => "loadsb", - NubIntType { Signed: true, Width: 16 } => "loadsh", - NubIntType { Signed: true, Width: 32 } => "loadw", - NubIntType { Signed: true, Width: 64 } => "loadl", - NubIntType { Signed: false, Width: 8 } => "loadsb", - NubIntType { Signed: false, Width: 16 } => "loadsh", - NubIntType { Signed: false, Width: 32 } => "loadw", - NubIntType { Signed: false, Width: 64 } => "loadl", - NubFloatType { Width: 32 } => "loads", - NubFloatType { Width: 64 } => "loadd", - NubPointerType => "loadl", - NubFuncType => "loadl", - NubCStringType => "loadl", - NubStringType => "loadl", - NubArrayType => "loadl", - NubStructType => throw new InvalidOperationException("Struct loads must use blit/memcpy"), - NubVoidType => throw new InvalidOperationException("Cannot load void"), - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}") - }; - - var into = TmpName(); - _writer.Indented($"{into} {QBEAssign(type)} {load} {from}"); - return into; - } - - - private static string StructDefQBEType(NubType type) - { - return type switch - { - NubBoolType => "b", - NubIntType { Width: 8 } => "b", - NubIntType { Width: 16 } => "h", - NubIntType { Width: 32 } => "w", - NubIntType { Width: 64 } => "l", - NubFloatType { Width: 32 } => "s", - NubFloatType { Width: 64 } => "d", - NubPointerType => "l", - NubFuncType => "l", - NubCStringType => "l", - NubStringType => "l", - NubArrayType => "l", - NubStructType st => StructTypeName(st.Module, st.Name), - NubVoidType => throw new InvalidOperationException("Void has no QBE type"), - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}") - }; - } - - private string FuncQBETypeName(NubType type) - { - return type switch - { - NubBoolType => "ub", - NubIntType { Signed: true, Width: 8 } => "sb", - NubIntType { Signed: true, Width: 16 } => "sh", - NubIntType { Signed: false, Width: 8 } => "ub", - NubIntType { Signed: false, Width: 16 } => "uh", - NubIntType { Width: 32 } => "w", - NubIntType { Width: 64 } => "l", - NubFloatType { Width: 32 } => "s", - NubFloatType { Width: 64 } => "d", - NubPointerType => "l", - NubFuncType => "l", - NubCStringType => "l", - NubStringType => "l", - NubArrayType => "l", - NubStructType st => StructTypeName(st.Module, st.Name), - NubVoidType => throw new InvalidOperationException("Void has no QBE type"), - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}") - }; - } - - private void EmitMemset(string destination, int value, string length) - { - _writer.Indented($"call $.memset(l {destination}, l {value}, l {length})"); - } - - private void EmitMemcpy(string source, string destination, string length) - { - _writer.Indented($"call $.memcpy(l {source}, l {destination}, l {length})"); - } - - private string EmitCStringSizeInBytes(string cstring) - { - var result = TmpName(); - _writer.Indented($"{result} =l call $.cstring_len(l {cstring})"); - _writer.Indented($"{result} =l add {result}, 1"); - return result; - } - - private string EmitStringSizeInBytes(string nubstring) - { - var size = TmpName(); - _writer.Indented($"{size} =l loadl {nubstring}"); - _writer.Indented($"{size} =l add {size}, 8"); - return size; - } - - // todo(nub31): EmitRValue should take a destination parameter so we can skip copying rvalues - private void EmitCopyInto(ExpressionNode source, string destinationAddress, bool tryMove = true) - { - if (tryMove && source is RValueExpressionNode rValue) - { - EmitRValueInto(rValue, destinationAddress); - return; - } - - if (source.Type.IsScalar) - { - EmitStore(source.Type, EmitExpression(source), destinationAddress); - return; - } - - if (source.Type.IsValueType) - { - var value = EmitExpression(source); - _writer.Indented($"blit {value}, {destinationAddress}, {SizeOf(source.Type)}"); - return; - } - - switch (source.Type) - { - case NubCStringType: - { - var value = EmitExpression(source); - var size = EmitCStringSizeInBytes(value); - var buffer = TmpName(); - _writer.Indented($"{buffer} =l alloc8 {size}"); - EmitMemcpy(value, buffer, size); - EmitStore(source.Type, buffer, destinationAddress); - return; - } - case NubStringType: - { - var value = EmitExpression(source); - var size = EmitStringSizeInBytes(value); - var buffer = TmpName(); - _writer.Indented($"{buffer} =l alloc8 {size}"); - EmitMemcpy(value, buffer, size); - EmitStore(source.Type, buffer, destinationAddress); - return; - } - case NubVoidType: - throw new InvalidOperationException("Cannot copy void"); - default: - throw new ArgumentOutOfRangeException(nameof(source.Type), $"Unknown type {source.Type}"); - } - } - - private void EmitStructType(NubStructType 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 - // qbe allows multiple declarations of the same struct, but we should keep track and only emit an new one if necessary - _writer.Write($"type {StructTypeName(structType.Module, structType.Name)} = {{ "); - - foreach (var field in structType.Fields) - { - _writer.Write($"{StructDefQBEType(field.Type)},"); - } - - _writer.WriteLine(" }"); - } - - private void EmitStructDefinition(StructNode structDef) - { - _writer.Comment($"{structDef.Module}::{structDef.Name}"); - _writer.WriteLine($"export function {StructCtorName(structDef.Module, structDef.Name)}(l %struct) {{"); - _writer.WriteLine("@start"); - - foreach (var field in structDef.Fields) - { - if (field.Value != null) - { - var offset = OffsetOf(TypeOfStruct(structDef), field.Name); - var destination = TmpName(); - _writer.Indented($"{destination} =l add %struct, {offset}"); - EmitCopyInto(field.Value, destination); - } - } - - _writer.Indented("ret"); - _writer.WriteLine("}"); - _writer.NewLine(); - - foreach (var function in structDef.Functions) - { - _writer.Comment($"{structDef.Module}::{structDef.Name}.{function.Name}"); - _labelIndex = 0; - _tmpIndex = 0; - - _writer.Write("export function "); - - if (function.Signature.ReturnType is not NubVoidType) - { - _writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' '); - } - - _writer.Write(StructFuncName(structDef.Module, structDef.Name, function.Name)); - - _writer.Write("(l %this, "); - foreach (var parameter in function.Signature.Parameters) - { - _writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}, "); - } - - _writer.WriteLine(") {"); - _writer.WriteLine("@start"); - - EmitBlock(function.Body); - - // Implicit return for void functions if no explicit return has been set - if (function.Signature.ReturnType is NubVoidType && function.Body.Statements.LastOrDefault() is not ReturnNode) - { - _writer.Indented("ret"); - } - - _writer.WriteLine("}"); - _writer.NewLine(); - } - } - - private void EmitFuncDefinition(FuncNode funcDef) - { - if (funcDef.Body == null) return; - - _labelIndex = 0; - _tmpIndex = 0; - - _writer.Write(funcDef.ExternSymbol != null ? "export function " : "function "); - - if (funcDef.Signature.ReturnType is not NubVoidType) - { - _writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' '); - } - - _writer.Write(FuncName(funcDef.Module, funcDef.Name, funcDef.ExternSymbol)); - - _writer.Write("("); - foreach (var parameter in funcDef.Signature.Parameters) - { - _writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}"); - } - - _writer.WriteLine(") {"); - _writer.WriteLine("@start"); - - EmitBlock(funcDef.Body); - - _writer.WriteLine("}"); - _writer.NewLine(); - } - - private void EmitBlock(BlockNode block, Scope? scope = null) - { - scope ??= new Scope(); - _scopes.Push(scope); - - foreach (var statement in block.Statements) - { - EmitStatement(statement); - } - - EmitScopeCleanup(); - - _scopes.Pop(); - } - - private void EmitStatement(StatementNode statement) - { - switch (statement) - { - case AssignmentNode assignment: - EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target)); - break; - case BlockNode block: - EmitBlock(block); - break; - case BreakNode: - EmitScopeCleanup(); - _writer.Indented($"jmp {_breakLabels.Peek()}"); - break; - case ContinueNode: - EmitScopeCleanup(); - _writer.Indented($"jmp {_continueLabels.Peek()}"); - break; - case DeferNode defer: - Scope.Defers.Push(defer); - break; - case IfNode ifStatement: - EmitIf(ifStatement); - break; - case ReturnNode @return: - EmitReturn(@return); - break; - case StatementFuncCallNode statementFuncCall: - EmitFuncCall(statementFuncCall.FuncCall); - break; - case StatementStructFuncCallNode statementStructFuncCall: - EmitStructFuncCall(statementStructFuncCall.StructFuncCall); - break; - case VariableDeclarationNode variableDeclaration: - EmitVariableDeclaration(variableDeclaration); - break; - case WhileNode whileStatement: - EmitWhile(whileStatement); - break; - default: - throw new ArgumentOutOfRangeException(nameof(statement)); - } - } - - private void EmitScopeCleanup() - { - while (Scope.Defers.TryPop(out var defer)) - { - EmitStatement(defer.Statement); - } - } - - private void EmitIf(IfNode ifStatement) - { - var trueLabel = LabelName(); - var falseLabel = LabelName(); - var endLabel = LabelName(); - - var result = 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(EmitIf, b => EmitBlock(b)); - } - - _writer.WriteLine(endLabel); - } - - private void EmitReturn(ReturnNode @return) - { - if (@return.Value != null) - { - var result = EmitExpression(@return.Value); - EmitScopeCleanup(); - _writer.Indented($"ret {result}"); - } - else - { - EmitScopeCleanup(); - _writer.Indented("ret"); - } - } - - private void EmitVariableDeclaration(VariableDeclarationNode variableDeclaration) - { - var name = $"%{variableDeclaration.Name}"; - _writer.Indented($"{name} =l alloc8 {SizeOf(variableDeclaration.Type)}"); - - if (variableDeclaration.Assignment != null) - { - EmitCopyInto(variableDeclaration.Assignment, name); - } - } - - 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 = EmitExpression(whileStatement.Condition); - _writer.Indented($"jnz {result}, {iterationLabel}, {endLabel}"); - _writer.WriteLine(endLabel); - - _continueLabels.Pop(); - _breakLabels.Pop(); - } - - private string EmitExpression(ExpressionNode expr) - { - return expr switch - { - RValueExpressionNode rValue => EmitRValue(rValue), - LValueExpressionNode lValue => EmitLValue(lValue), - _ => throw new ArgumentOutOfRangeException(nameof(expr)) - }; - } - - private string EmitRValue(RValueExpressionNode rValue) - { - return rValue switch - { - StringLiteralNode expr => EmitStringLiteral(expr), - CStringLiteralNode expr => EmitCStringLiteral(expr), - IntLiteralNode expr => expr.Value.ToString(), - UIntLiteralNode expr => expr.Value.ToString(), - Float32LiteralNode expr => BitConverter.SingleToInt32Bits(expr.Value).ToString(), - Float64LiteralNode expr => BitConverter.DoubleToInt64Bits(expr.Value).ToString(), - BoolLiteralNode expr => expr.Value ? "1" : "0", - AddressOfNode expr => EmitAddressOf(expr.LValue), - ArrayInitializerNode expr => EmitArrayInitializer(expr), - BinaryExpressionNode expr => EmitBinaryExpression(expr), - ConvertFloatNode expr => EmitConvertFloat(expr), - ConvertIntNode expr => EmitConvertInt(expr), - FuncCallNode expr => EmitFuncCall(expr), - FuncIdentifierNode expr => FuncName(expr.Module, expr.Name, expr.ExternSymbol), - RValueIdentifierNode expr => $"%{expr.Name}", - StructFuncCallNode expr => EmitStructFuncCall(expr), - StructInitializerNode expr => EmitStructInitializer(expr), - UnaryExpressionNode expr => EmitUnaryExpression(expr), - SizeBuiltinNode expr => $"{SizeOf(expr.TargetType)}", - FloatToIntBuiltinNode expr => EmitFloatToIntBuiltin(expr), - _ => throw new ArgumentOutOfRangeException(nameof(rValue)) - }; - } - - private void EmitRValueInto(RValueExpressionNode rValue, string destinationAddress) - { - if (rValue.Type.IsScalar) - { - EmitStore(rValue.Type, EmitRValue(rValue), destinationAddress); - return; - } - - switch (rValue) - { - case StructInitializerNode expr: - { - EmitStructInitializer(expr, destinationAddress); - return; - } - case StringLiteralNode expr: - { - // String literals are read only, so safe to use without copy - EmitStore(expr.Type, EmitStringLiteral(expr), destinationAddress); - return; - } - case CStringLiteralNode expr: - { - // String literals are read only, so safe to use without copy - EmitStore(expr.Type, EmitCStringLiteral(expr), destinationAddress); - return; - } - default: - { - // If no special case is provided, fall back to copying - Console.WriteLine($"No fast initialization for {rValue.GetType().Name}. Using copy fallback"); - EmitCopyInto(rValue, destinationAddress, false); - break; - } - } - } - - private string EmitLValue(LValueExpressionNode lValue) - { - var address = EmitAddressOf(lValue); - - if (lValue.Type is { IsValueType: true, IsScalar: false }) - { - return address; - } - - return EmitLoad(lValue.Type, address); - } - - private string EmitStringLiteral(StringLiteralNode expr) - { - var stringLiteral = new StringLiteral(expr.Value, StringName()); - _stringLiterals.Add(stringLiteral); - return stringLiteral.Name; - } - - private string EmitCStringLiteral(CStringLiteralNode expr) - { - var cStringLiteral = new CStringLiteral(expr.Value, CStringName()); - _cStringLiterals.Add(cStringLiteral); - return cStringLiteral.Name; - } - - private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer) - { - var capacity = 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"); - EmitMemset(dataPointer, 0, capacityInBytes); - - return arrayPointer; - } - - private string EmitAddressOf(LValueExpressionNode lval) - { - return lval switch - { - ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess), - DereferenceNode dereference => EmitExpression(dereference.Expression), - StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess), - LValueIdentifierNode variableIdent => $"%{variableIdent.Name}", - _ => throw new ArgumentOutOfRangeException(nameof(lval)) - }; - } - - private string EmitAddressOfArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess) - { - var array = EmitExpression(arrayIndexAccess.Target); - var index = EmitExpression(arrayIndexAccess.Index); - - var elementType = ((NubArrayType)arrayIndexAccess.Target.Type).ElementType; - - var offset = TmpName(); - _writer.Indented($"{offset} =l mul {index}, {SizeOf(elementType)}"); - _writer.Indented($"{offset} =l add {offset}, 8"); - _writer.Indented($"{offset} =l add {array}, {offset}"); - return offset; - } - - private string EmitAddressOfStructFieldAccess(StructFieldAccessNode structFieldAccess) - { - var structType = (NubStructType)structFieldAccess.Target.Type; - var target = EmitExpression(structFieldAccess.Target); - var offset = OffsetOf(structType, structFieldAccess.Field); - - var address = TmpName(); - _writer.Indented($"{address} =l add {target}, {offset}"); - return address; - } - - private string EmitBinaryExpression(BinaryExpressionNode binaryExpression) - { - var left = EmitExpression(binaryExpression.Left); - var right = EmitExpression(binaryExpression.Right); - - var outputName = TmpName(); - - var instruction = EmitBinaryInstructionForOperator(binaryExpression.Operator, binaryExpression.Left.Type); - - _writer.Indented($"{outputName} {QBEAssign(binaryExpression.Left.Type)} {instruction} {left}, {right}"); - return outputName; - } - - private static string EmitBinaryInstructionForOperator(BinaryOperator op, NubType type) - { - // todo(nub31): Add support for string concatenation. Currently this expects ints or floats and will treat strings as ints - return op switch - { - BinaryOperator.RightShift => type switch - { - NubIntType { Signed: true } => "sar", - NubIntType { Signed: false } => "shr", - _ => throw new NotSupportedException($"Right shift not supported for type '{type}'") - }, - BinaryOperator.BitwiseAnd => "and", - BinaryOperator.BitwiseOr => "or", - BinaryOperator.BitwiseXor => "xor", - BinaryOperator.LeftShift => "shl", - BinaryOperator.Divide => type switch - { - NubIntType { Signed: true } => "div", - NubIntType { Signed: false } => "udiv", - NubFloatType => "div", - _ => throw new NotSupportedException($"Division not supported for type '{type}'") - }, - BinaryOperator.Modulo => type switch - { - NubIntType { Signed: true } => "rem", - NubIntType { Signed: false } => "urem", - _ => throw new NotSupportedException($"Modulo not supported for type '{type}'") - }, - BinaryOperator.Plus => "add", - BinaryOperator.Minus => "sub", - BinaryOperator.Multiply => "mul", - BinaryOperator.Equal => type switch - { - NubIntType intType => intType.Width switch - { - <= 32 => "ceqw", - 64 => "ceql", - _ => throw new ArgumentOutOfRangeException() - }, - NubFloatType floatType => floatType.Width switch - { - 32 => "ceqs", - 64 => "ceqd", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Equality comparison not supported for type '{type}'") - }, - BinaryOperator.NotEqual => type switch - { - NubIntType intType => intType.Width switch - { - <= 32 => "cnew", - 64 => "cnel", - _ => throw new ArgumentOutOfRangeException() - }, - NubFloatType floatType => floatType.Width switch - { - 32 => "cnes", - 64 => "cned", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Inequality comparison not supported for type '{type}'") - }, - BinaryOperator.LessThan => type switch - { - NubIntType { Signed: true } intType => intType.Width switch - { - <= 32 => "csltw", - 64 => "csltl", - _ => throw new ArgumentOutOfRangeException() - }, - NubIntType { Signed: false } intType => intType.Width switch - { - <= 32 => "cultw", - 64 => "cultl", - _ => throw new ArgumentOutOfRangeException() - }, - NubFloatType floatType => floatType.Width switch - { - 32 => "clts", - 64 => "cltd", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Less than comparison not supported for type '{type}'") - }, - BinaryOperator.LessThanOrEqual => type switch - { - NubIntType { Signed: true } intType => intType.Width switch - { - <= 32 => "cslew", - 64 => "cslel", - _ => throw new ArgumentOutOfRangeException() - }, - NubIntType { Signed: false } intType => intType.Width switch - { - <= 32 => "culew", - 64 => "culel", - _ => throw new ArgumentOutOfRangeException() - }, - NubFloatType floatType => floatType.Width switch - { - 32 => "cles", - 64 => "cled", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Less than or equal comparison not supported for type '{type}'") - }, - BinaryOperator.GreaterThan => type switch - { - NubIntType { Signed: true } intType => intType.Width switch - { - <= 32 => "csgtw", - 64 => "csgtl", - _ => throw new ArgumentOutOfRangeException() - }, - NubIntType { Signed: false } intType => intType.Width switch - { - <= 32 => "cugtw", - 64 => "cugtl", - _ => throw new ArgumentOutOfRangeException() - }, - NubFloatType floatType => floatType.Width switch - { - 32 => "cgts", - 64 => "cgtd", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Greater than comparison not supported for type '{type}'") - }, - BinaryOperator.GreaterThanOrEqual => type switch - { - NubIntType { Signed: true } intType => intType.Width switch - { - <= 32 => "csgew", - 64 => "csgel", - _ => throw new ArgumentOutOfRangeException() - }, - NubIntType { Signed: false } intType => intType.Width switch - { - <= 32 => "cugew", - 64 => "cugel", - _ => throw new ArgumentOutOfRangeException() - }, - NubFloatType floatType => floatType.Width switch - { - 32 => "cges", - 64 => "cged", - _ => throw new ArgumentOutOfRangeException() - }, - _ => throw new NotSupportedException($"Greater than or equal comparison not supported for type '{type}'") - }, - // todo(nub31): Implement short circuiting - BinaryOperator.LogicalAnd => "and", - BinaryOperator.LogicalOr => "or", - _ => throw new ArgumentOutOfRangeException(nameof(op)) - }; - } - - private string EmitStructInitializer(StructInitializerNode structInitializer) - { - var destinationAddress = TmpName(); - var size = SizeOf(structInitializer.StructType); - _writer.Indented($"{destinationAddress} =l alloc8 {size}"); - EmitStructInitializer(structInitializer, destinationAddress); - return destinationAddress; - } - - private void EmitStructInitializer(StructInitializerNode structInitializer, string destinationAddress) - { - _writer.Indented($"call {StructCtorName(structInitializer.StructType.Module, structInitializer.StructType.Name)}(l {destinationAddress})"); - - foreach (var (field, value) in structInitializer.Initializers) - { - var offset = TmpName(); - _writer.Indented($"{offset} =l add {destinationAddress}, {OffsetOf(structInitializer.StructType, field)}"); - EmitCopyInto(value, offset); - } - } - - private string EmitUnaryExpression(UnaryExpressionNode unaryExpression) - { - var operand = EmitExpression(unaryExpression.Operand); - var outputName = TmpName(); - - switch (unaryExpression.Operator) - { - case UnaryOperator.Negate: - { - switch (unaryExpression.Operand.Type) - { - case NubIntType { Signed: true, Width: 64 }: - _writer.Indented($"{outputName} =l neg {operand}"); - return outputName; - case NubIntType { Signed: true, Width: 8 or 16 or 32 }: - _writer.Indented($"{outputName} =w neg {operand}"); - return outputName; - case NubFloatType { Width: 64 }: - _writer.Indented($"{outputName} =d neg {operand}"); - return outputName; - case NubFloatType { Width: 32 }: - _writer.Indented($"{outputName} =s neg {operand}"); - return outputName; - } - - break; - } - case UnaryOperator.Invert: - { - switch (unaryExpression.Operand.Type) - { - case NubBoolType: - _writer.Indented($"{outputName} =w xor {operand}, 1"); - return outputName; - } - - break; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } - - throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); - } - - private string EmitStructFuncCall(StructFuncCallNode structFuncCall) - { - var func = StructFuncName(structFuncCall.Module, structFuncCall.StructName, structFuncCall.FuncName); - - var thisParameter = EmitExpression(structFuncCall.StructExpression); - - List parameterStrings = [$"l {thisParameter}"]; - - foreach (var parameter in structFuncCall.Parameters) - { - var value = EmitExpression(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {value}"); - } - - if (structFuncCall.Type is NubVoidType) - { - _writer.Indented($"call {func}({string.Join(", ", parameterStrings)})"); - return string.Empty; - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(structFuncCall.Type)} call {func}({string.Join(", ", parameterStrings)})"); - return outputName; - } - } - - private string EmitConvertInt(ConvertIntNode convertInt) - { - var value = EmitExpression(convertInt.Value); - - if (convertInt.ValueType.Width >= convertInt.TargetType.Width) - { - return value; - } - - var method = convertInt.ValueType.Signed switch - { - true => convertInt.ValueType.Width switch - { - 8 => "extsb", - 16 => "extsh", - 32 => "extsw", - _ => throw new ArgumentOutOfRangeException() - }, - false => convertInt.ValueType.Width switch - { - 8 => "extub", - 16 => "extuh", - 32 => "extuw", - _ => throw new ArgumentOutOfRangeException() - } - }; - - var result = TmpName(); - _writer.Indented($"{result} {QBEAssign(convertInt.TargetType)} {method} {value}"); - return result; - } - - private string EmitConvertFloat(ConvertFloatNode convertFloat) - { - var value = EmitExpression(convertFloat.Value); - - if (convertFloat.ValueType.Width == convertFloat.TargetType.Width) - { - return value; - } - - var method = convertFloat.ValueType.Width switch - { - 32 => "exts", - 64 => "truncd", - _ => throw new ArgumentOutOfRangeException() - }; - - var result = TmpName(); - _writer.Indented($"{result} {QBEAssign(convertFloat.TargetType)} {method} {value}"); - return result; - } - - private string EmitFloatToIntBuiltin(FloatToIntBuiltinNode floatToInt) - { - var value = EmitExpression(floatToInt.Value); - - var method = floatToInt.TargetType.Signed switch - { - true => floatToInt.ValueType.Width switch - { - 32 => "stosi", - 64 => "dtosi", - _ => throw new ArgumentOutOfRangeException() - }, - false => floatToInt.ValueType.Width switch - { - 32 => "stoui", - 64 => "dtoui", - _ => throw new ArgumentOutOfRangeException() - } - }; - - var result = TmpName(); - _writer.Indented($"{result} {QBEAssign(floatToInt.TargetType)} {method} {value}"); - return result; - } - - private string EmitFuncCall(FuncCallNode funcCall) - { - var funcPointer = EmitExpression(funcCall.Expression); - - var parameterStrings = new List(); - - foreach (var parameter in funcCall.Parameters) - { - var value = EmitExpression(parameter); - parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {value}"); - } - - if (funcCall.Type is NubVoidType) - { - _writer.Indented($"call {funcPointer}({string.Join(", ", parameterStrings)})"); - return string.Empty; - } - else - { - var outputName = TmpName(); - _writer.Indented($"{outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return outputName; - } - } - - private static int SizeOf(NubType type) - { - return type switch - { - NubIntType intType => intType.Width / 8, - NubFloatType fType => fType.Width / 8, - NubBoolType => 1, - NubPointerType => PTR_SIZE, - NubFuncType => PTR_SIZE, - NubStructType structType => CalculateStructSize(structType), - NubVoidType => throw new InvalidOperationException("Void type has no size"), - NubCStringType => PTR_SIZE, - NubStringType => PTR_SIZE, - NubArrayType => PTR_SIZE, - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}") - }; - } - - private static int CalculateStructSize(NubStructType structType) - { - var offset = 0; - - foreach (var field in structType.Fields) - { - var fieldAlignment = AlignmentOf(field.Type); - offset = AlignTo(offset, fieldAlignment); - offset += SizeOf(field.Type); - } - - var structAlignment = CalculateStructAlignment(structType); - return AlignTo(offset, structAlignment); - } - - private static int AlignmentOf(NubType type) - { - return type switch - { - NubIntType intType => intType.Width / 8, - NubFloatType fType => fType.Width / 8, - NubBoolType => 1, - NubPointerType => PTR_SIZE, - NubFuncType => PTR_SIZE, - NubStructType st => CalculateStructAlignment(st), - NubCStringType => PTR_SIZE, - NubStringType => PTR_SIZE, - NubArrayType => PTR_SIZE, - NubVoidType => throw new InvalidOperationException("Void has no alignment"), - _ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}") - }; - } - - - private static int CalculateStructAlignment(NubStructType structType) - { - var maxAlignment = 1; - - foreach (var field in structType.Fields) - { - var fieldAlignment = AlignmentOf(field.Type); - maxAlignment = Math.Max(maxAlignment, fieldAlignment); - } - - return maxAlignment; - } - - private static int AlignTo(int offset, int alignment) - { - return (offset + alignment - 1) & ~(alignment - 1); - } - - private static int OffsetOf(NubStructType type, string member) - { - var offset = 0; - - foreach (var field in type.Fields) - { - var fieldAlignment = AlignmentOf(field.Type); - offset = AlignTo(offset, fieldAlignment); - - if (field.Name == member) - { - return offset; - } - - offset += SizeOf(field.Type); - } - - throw new UnreachableException($"Member '{member}' not found in struct"); - } - - private NubStructType TypeOfStruct(StructNode definition) - { - var fieldTypes = definition.Fields - .Select(x => new NubStructFieldType(x.Name, x.Type, x.Value != null)) - .ToList(); - - var functionTypes = new List(); - foreach (var function in definition.Functions) - { - var parameters = function.Signature.Parameters.Select(x => x.Type).ToList(); - functionTypes.Add(new NubStructFuncType(function.Name, parameters, function.Signature.ReturnType)); - } - - return new NubStructType(definition.Module, definition.Name, fieldTypes, functionTypes); - } - - private string TmpName() - { - return $"%.t{++_tmpIndex}"; - } - - private string LabelName() - { - return $"@.l{++_labelIndex}"; - } - - private string CStringName() - { - return $"$.cstr{++_cStringLiteralIndex}"; - } - - private string StringName() - { - return $"$.str{++_stringLiteralIndex}"; - } - - private static string FuncName(string module, string name, string? externSymbol) - { - return $"${externSymbol ?? $".{module}.{name}"}"; - } - - private static string StructTypeName(string module, string name) - { - return $":{module}.{name}"; - } - - private static string StructCtorName(string module, string name) - { - return $"$.{module}.{name}.ctor"; - } - - private static string StructFuncName(string module, string structName, string funcName) - { - return $"$.{module}.{structName}.func.{funcName}"; - } -} - -public class Scope -{ - public readonly Stack Defers = []; -} - -public class StringLiteral(string value, string name) -{ - public string Value { get; } = value; - public string Name { get; } = name; -} - -public class CStringLiteral(string value, string name) -{ - public string Value { get; } = value; - public string Name { get; } = name; -} \ No newline at end of file diff --git a/compiler/NubLang/Generation/QBEWriter.cs b/compiler/NubLang/Generation/QBEWriter.cs deleted file mode 100644 index eb4fcf7..0000000 --- a/compiler/NubLang/Generation/QBEWriter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Text; - -namespace NubLang.Generation; - -internal class QBEWriter -{ - private readonly StringBuilder _builder = new(); - - public void Indented(string value) - { - _builder.Append('\t'); - _builder.AppendLine(value); - } - - 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(); - } - - public override string ToString() - { - return _builder.ToString(); - } -} \ No newline at end of file diff --git a/examples/raylib/makefile b/examples/raylib/makefile index 88c9aa2..5af76db 100644 --- a/examples/raylib/makefile +++ b/examples/raylib/makefile @@ -1,7 +1,7 @@ NUBC = ../../compiler/NubLang.CLI/bin/Debug/net9.0/nubc out: .build/out.o - gcc -nostartfiles -lm -o out x86_64.s .build/out.o raylib-5.5_linux_amd64/lib/libraylib.a + clang -nostartfiles -lm -O3 -o out x86_64.s .build/out.o raylib-5.5_linux_amd64/lib/libraylib.a .build/out.o: $(NUBC) main.nub raylib.nub $(NUBC) main.nub raylib.nub