From 16768beea63465b2fa5ced1cab3145cfa7611011 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sat, 14 Jun 2025 23:33:09 +0200 Subject: [PATCH] ... --- example/main.nub | 40 +--- src/CLI/Program.cs | 3 + src/CLI/Runtime/entry.s | 62 +++++- src/Generation/QBE/QBEGenerator.cs | 186 +++++++----------- src/Syntax/Parsing/Parser.cs | 22 +-- .../Statements/VariableDeclarationNode.cs | 9 +- src/Syntax/Typing/TypeChecker.cs | 33 +--- 7 files changed, 156 insertions(+), 199 deletions(-) diff --git a/example/main.nub b/example/main.nub index 331e130..14f2d64 100644 --- a/example/main.nub +++ b/example/main.nub @@ -1,42 +1,18 @@ namespace main struct Human { - age: u64 - print_age: func() = func() { - c::puts("pwp") - } + name: string + // print_age: func() = func() { + // c::puts("test3") + // } } -export func main(args: []^string): i64 { - let x = 2 +export func main(args: [][]u8): i64 { + let x: []u8 - let uwu = func() { - c::puts("uwu") - } + x = args[0] - uwu() - - func() { - c::puts("owo") - }() - - let me = alloc Human { - age = 23 - } - - me.print_age() - - if true { - // do something - } - - c::puts("test") - - let i = 1 - while i <= 10 { - c::puts("test") - i = i + 1 - } + c::puts(x) return 0 } diff --git a/src/CLI/Program.cs b/src/CLI/Program.cs index 3094ef4..899e733 100644 --- a/src/CLI/Program.cs +++ b/src/CLI/Program.cs @@ -85,6 +85,9 @@ foreach (var compilationUnit in compilationUnits) Directory.CreateDirectory(outputDirectory); var ssa = QBEGenerator.Generate(compilationUnit, definitionTable); + var ssaPath = Path.ChangeExtension(outputPath, "ssa"); + File.WriteAllText(ssaPath, ssa); + var asm = await QBE.Invoke(ssa); var asmPath = Path.ChangeExtension(outputPath, "s"); diff --git a/src/CLI/Runtime/entry.s b/src/CLI/Runtime/entry.s index 2da9aa7..9c66ed7 100644 --- a/src/CLI/Runtime/entry.s +++ b/src/CLI/Runtime/entry.s @@ -4,12 +4,60 @@ .global _start _start: - mov rdi, rsp - sub rsp, 8 - mov [rsp], rdi - mov rdi, rsp - # func main(args: []^string): i64 + # On entry, the stack contains: + # [rsp] = argc (argument count) + # [rsp+8] = argv[0] (program name) + # [rsp+16] = argv[1] (first argument) + # ... + + # Get argc from stack + mov rdi, [rsp] # rdi = argc + + # Calculate space needed for our array structure + # We need: 8 bytes (length) + argc * 8 bytes (pointers) + mov rax, rdi # rax = argc + shl rax, 3 # rax = argc * 8 (each pointer is 8 bytes) + add rax, 8 # rax = 8 + argc * 8 (add space for length) + + # Allocate space on stack (align to 16 bytes) + add rax, 15 # Round up to nearest 16 + and rax, -16 # Align to 16 bytes + sub rsp, rax # Allocate space + + # Store array length at beginning + mov [rsp], rdi # Store argc as array length + + # Copy argv pointers to our array + lea rsi, [rsp + 8] # rsi points to start of argv in stack + lea rdi, [rsp + 8] # rdi points to our array data (after length) + mov rcx, [rsp] # rcx = argc (loop counter) + +copy_loop: + test rcx, rcx # Check if we're done + jz done_copying + + mov rax, [rsi] # Load argv[i] pointer + mov [rdi], rax # Store in our array + add rsi, 8 # Move to next argv entry + add rdi, 8 # Move to next array slot + dec rcx # Decrement counter + jmp copy_loop + +done_copying: + # Now rsp points to our array: [length][ptr0][ptr1]...[ptrN-1] + mov rdi, rsp # Pass array pointer to main call main - mov rdi, rax - mov rax, 60 + + # Clean up stack (restore original rsp) + # Calculate how much we allocated + mov rdi, [rsp] # Get argc back + shl rdi, 3 # argc * 8 + add rdi, 8 # + 8 for length + add rdi, 15 # Round up + and rdi, -16 # Align + add rsp, rdi # Restore stack + + # Exit with main's return value + mov rdi, rax # rax contains main's return value + mov rax, 60 # sys_exit syscall diff --git a/src/Generation/QBE/QBEGenerator.cs b/src/Generation/QBE/QBEGenerator.cs index e5c5edf..2489c3e 100644 --- a/src/Generation/QBE/QBEGenerator.cs +++ b/src/Generation/QBE/QBEGenerator.cs @@ -21,7 +21,7 @@ public static class QBEGenerator private static Stack _breakLabels = []; private static Stack _continueLabels = []; private static Queue<(AnonymousFuncNode Func, string Name)> _anonymousFunctions = []; - private static Stack<(string Name, string Pointer)> _variables = []; + private static Stack _variables = []; private static Stack _variableScopes = []; private static int _variableIndex; private static int _labelIndex; @@ -298,14 +298,14 @@ public static class QBEGenerator throw new UnreachableException($"Member '{member}' not found in struct"); } - private static bool IsLargeType(NubType type) + private static bool IsPointerType(NubType type) { return type switch { - NubArrayType => false, NubPointerType => false, NubPrimitiveType => false, NubStructType => true, + NubArrayType => true, NubFixedArrayType => true, NubFuncType => false, _ => throw new ArgumentOutOfRangeException(nameof(type)) @@ -387,7 +387,7 @@ public static class QBEGenerator _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine("@start"); - List<(string Name, string Pointer)> parameterVars = []; + List parameterVars = []; foreach (var parameter in parameters) { @@ -416,7 +416,7 @@ public static class QBEGenerator } } - parameterVars.Add((parameter.Name, parameterName)); + parameterVars.Add(new Variable(parameter.Name, parameterName).Assign()); } GenerateBlock(body, parameterVars); @@ -553,7 +553,7 @@ public static class QBEGenerator } } - private static void GenerateBlock(BlockNode block, List<(string Name, string Pointer)>? variables = null) + private static void GenerateBlock(BlockNode block, List? variables = null) { _variableScopes.Push(_variables.Count); if (variables != null) @@ -654,29 +654,17 @@ public static class QBEGenerator private static void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration) { - var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value!.Type; - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l alloc8 {SizeOf(type)}"); - - if (variableDeclaration.Value.HasValue) - { - var value = GenerateExpression(variableDeclaration.Value.Value); - GenerateCopy(variableDeclaration.Value.Value.Type, value, pointer); - } - else - { - var pointerName = VarName(); - _variables.Push((variableDeclaration.Name, pointerName)); - } - - _variables.Push((variableDeclaration.Name, pointer)); + var generatedName = VarName(); + _builder.AppendLine($" {generatedName} =l alloc8 {SizeOf(variableDeclaration.Type)}"); + _variables.Push(new Variable(variableDeclaration.Name, generatedName)); } private static void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) { var value = GenerateExpression(variableAssignment.Value); var variable = _variables.Single(x => x.Name == variableAssignment.Identifier.Name); - GenerateCopy(variableAssignment.Value.Type, value, variable.Pointer); + GenerateCopy(variableAssignment.Value.Type, value, variable.GeneratedName); + variable.Assign(); } private static void GenerateWhile(WhileNode whileStatement) @@ -706,7 +694,7 @@ public static class QBEGenerator { AddressOfNode addressOf => GenerateAddressOf(addressOf), AnonymousFuncNode anonymousFunc => GenerateAnonymousFunc(anonymousFunc), - ArrayIndexAccessNode arrayIndex => GenerateArrayAccessIndex(arrayIndex), + ArrayIndexAccessNode arrayIndex => GenerateArrayIndexAccessNode(arrayIndex), ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer), BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression), DereferenceNode dereference => GenerateDereference(dereference), @@ -725,10 +713,7 @@ public static class QBEGenerator { var name = $"$anon_func{++_anonymousFuncIndex}"; _anonymousFunctions.Enqueue((anonymousFunc, name)); - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l alloc8 8"); - _builder.AppendLine($" storel {name}, {pointer}"); - return pointer; + return name; } private static string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess) @@ -737,40 +722,27 @@ public static class QBEGenerator var index = GenerateExpression(arrayIndexAccess.Index); GenerateArrayBoundsCheck(array, index); - switch (arrayIndexAccess.Array.Type) + var elementType = arrayIndexAccess.Array.Type switch { - case NubArrayType arrayType: - { - var firstItemPointerName = VarName(); - _builder.AppendLine($" {firstItemPointerName} =l add {array}, 8"); - var offsetPointerName = VarName(); - _builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(arrayType.ElementType)}"); - var resultPointerName = VarName(); - _builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}"); - return resultPointerName; - } - case NubFixedArrayType fixedArrayType: - { - var firstItemPointerName = VarName(); - _builder.AppendLine($" {firstItemPointerName} =l add {array}, 8"); - var offsetPointerName = VarName(); - _builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(fixedArrayType.ElementType)}"); - var resultPointerName = VarName(); - _builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}"); - return resultPointerName; - } - default: - { - throw new ArgumentOutOfRangeException(); - } - } + NubArrayType arrayType => arrayType.ElementType, + NubFixedArrayType fixedArrayType => fixedArrayType.ElementType, + _ => throw new ArgumentOutOfRangeException() + }; + + var firstItemPointerName = VarName(); + _builder.AppendLine($" {firstItemPointerName} =l add {array}, 8"); + var offsetPointerName = VarName(); + _builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(elementType)}"); + var resultPointerName = VarName(); + _builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}"); + return resultPointerName; } - private static string GenerateArrayAccessIndex(ArrayIndexAccessNode arrayIndexAccess) + private static string GenerateArrayIndexAccessNode(ArrayIndexAccessNode arrayIndexAccess) { var pointerName = GenerateArrayIndexPointer(arrayIndexAccess); - if (IsLargeType(arrayIndexAccess.Type)) + if (IsPointerType(arrayIndexAccess.Type)) { return pointerName; } @@ -784,26 +756,26 @@ public static class QBEGenerator private static void GenerateArrayBoundsCheck(string array, string index) { - var countName = VarName(); - _builder.AppendLine($" {countName} =l loadl {array}"); - - var isNegativeName = VarName(); - _builder.AppendLine($" {isNegativeName} =w csltl {index}, 0"); - - var isOobName = VarName(); - _builder.AppendLine($" {isOobName} =w csgel {index}, {countName}"); - - var anyOobName = VarName(); - _builder.AppendLine($" {anyOobName} =w or {isNegativeName}, {isOobName}"); - - var oobLabel = LabelName(); - var notOobLabel = LabelName(); - _builder.AppendLine($" jnz {anyOobName}, {oobLabel}, {notOobLabel}"); - - _builder.AppendLine(oobLabel); - _builder.AppendLine($" call $nub_panic_array_oob()"); - - _builder.AppendLine(notOobLabel); + // var countName = VarName(); + // _builder.AppendLine($" {countName} =l loadl {array}"); + // + // var isNegativeName = VarName(); + // _builder.AppendLine($" {isNegativeName} =w csltl {index}, 0"); + // + // var isOobName = VarName(); + // _builder.AppendLine($" {isOobName} =w csgel {index}, {countName}"); + // + // var anyOobName = VarName(); + // _builder.AppendLine($" {anyOobName} =w or {isNegativeName}, {isOobName}"); + // + // var oobLabel = LabelName(); + // var notOobLabel = LabelName(); + // _builder.AppendLine($" jnz {anyOobName}, {oobLabel}, {notOobLabel}"); + // + // _builder.AppendLine(oobLabel); + // _builder.AppendLine($" call $nub_panic_array_oob()"); + // + // _builder.AppendLine(notOobLabel); } private static string GenerateArrayInitializer(ArrayInitializerNode arrayInitializer) @@ -847,7 +819,7 @@ public static class QBEGenerator throw new NotSupportedException("There is nothing to address in another namespace"); } - return _variables.Single(x => x.Name == identifier.Name).Pointer; + return _variables.Single(x => x.Name == identifier.Name).GeneratedName; case MemberAccessNode memberAccess: return GenerateMemberAccessPointer(memberAccess); default: @@ -1085,16 +1057,22 @@ public static class QBEGenerator { if (_definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func)) { - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l alloc8 8"); - _builder.AppendLine($" storel {FuncName(func)}, {pointer}"); - return pointer; + return FuncName(func); } if (!identifier.Namespace.HasValue) { var variable = _variables.Single(v => v.Name == identifier.Name); - return GenerateDereference(identifier.Type, variable.Pointer); + if (IsPointerType(identifier.Type)) + { + return variable.GeneratedName; + } + else + { + var output = VarName(); + _builder.AppendLine($" {output} {QBEAssign(identifier.Type)} {QBELoad(identifier.Type)} {variable.GeneratedName}"); + return output; + } } throw new UnreachableException(); @@ -1257,7 +1235,7 @@ public static class QBEGenerator { var pointer = GenerateMemberAccessPointer(memberAccess); - if (IsLargeType(memberAccess.Type)) + if (IsPointerType(memberAccess.Type)) { return pointer; } @@ -1324,35 +1302,24 @@ public static class QBEGenerator parameterStrings.Add($"{qbeType} {result}"); } - string funcTarget; - if (funcCall.Expression is IdentifierNode identifier && _definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func)) - { - funcTarget = FuncName(func); - } - else - { - var funcPointerPointer = GenerateExpression(funcCall.Expression); - var funcPointer = VarName(); - _builder.AppendLine($" {funcPointer} =l loadl {funcPointerPointer}"); - funcTarget = funcPointer; - } + var funcPointer = GenerateExpression(funcCall.Expression); if (funcType.ReturnType is not NubVoidType) { var outputName = VarName(); - _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcTarget}({string.Join(", ", parameterStrings)})"); + _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); return outputName; } else { - _builder.AppendLine($" call {funcTarget}({string.Join(", ", parameterStrings)})"); + _builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})"); return "fuck"; } } private static void GenerateCopy(NubType type, string value, string destinationPointer) { - if (IsLargeType(type)) + if (IsPointerType(type)) { _builder.AppendLine($" blit {value}, {destinationPointer}, {SizeOf(type)}"); } @@ -1361,18 +1328,17 @@ public static class QBEGenerator _builder.AppendLine($" {QBEStore(type)} {value}, {destinationPointer}"); } } +} - private static string GenerateDereference(NubType type, string pointer) +internal class Variable(string name, string generatedName) +{ + public string Name { get; init; } = name; + public string GeneratedName { get; init; } = generatedName; + public bool Initialized { get; private set; } + + public Variable Assign() { - if (IsLargeType(type)) - { - return pointer; - } - else - { - var result = VarName(); - _builder.AppendLine($" {result} {QBEAssign(type)} {QBELoad(type)} {pointer}"); - return result; - } + Initialized = true; + return this; } -} \ No newline at end of file +} diff --git a/src/Syntax/Parsing/Parser.cs b/src/Syntax/Parsing/Parser.cs index 1bd2c17..6329ea8 100644 --- a/src/Syntax/Parsing/Parser.cs +++ b/src/Syntax/Parsing/Parser.cs @@ -271,19 +271,10 @@ public static class Parser { ExpectSymbol(Symbol.Let); var name = ExpectIdentifier().Value; - var type = Optional.Empty(); - if (TryExpectSymbol(Symbol.Colon)) - { - type = ParseType(); - } - - var value = Optional.Empty(); - if (TryExpectSymbol(Symbol.Assign)) - { - value = ParseExpression(); - } - - return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type, value); + ExpectSymbol(Symbol.Colon); + var type = ParseType(); + + return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type); } private static StatementNode ParseBreak(int startIndex) @@ -674,6 +665,11 @@ public static class Parser return new NubVoidType(); } + if (name.Value == "string") + { + return new NubArrayType(NubPrimitiveType.U8); + } + if (NubPrimitiveType.TryParse(name.Value, out var primitiveTypeKind)) { return new NubPrimitiveType(primitiveTypeKind.Value); diff --git a/src/Syntax/Parsing/Statements/VariableDeclarationNode.cs b/src/Syntax/Parsing/Statements/VariableDeclarationNode.cs index a4546f1..64fc58d 100644 --- a/src/Syntax/Parsing/Statements/VariableDeclarationNode.cs +++ b/src/Syntax/Parsing/Statements/VariableDeclarationNode.cs @@ -1,13 +1,10 @@ -using Common; -using Syntax.Parsing.Expressions; -using Syntax.Tokenization; +using Syntax.Tokenization; using Syntax.Typing; namespace Syntax.Parsing.Statements; -public class VariableDeclarationNode(IReadOnlyList tokens, string name, Optional explicitType, Optional value) : StatementNode(tokens) +public class VariableDeclarationNode(IReadOnlyList tokens, string name, NubType type) : StatementNode(tokens) { public string Name { get; } = name; - public Optional ExplicitType { get; } = explicitType; - public Optional Value { get; } = value; + public NubType Type { get; } = type; } \ No newline at end of file diff --git a/src/Syntax/Typing/TypeChecker.cs b/src/Syntax/Typing/TypeChecker.cs index 09426f0..e8bd75c 100644 --- a/src/Syntax/Typing/TypeChecker.cs +++ b/src/Syntax/Typing/TypeChecker.cs @@ -177,42 +177,12 @@ public static class TypeChecker private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) { - NubType? type = null; - if (_variables.TryGetValue(variableDeclaration.Name, out var variable)) { ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration); } - if (variableDeclaration.Value.HasValue) - { - var valueType = CheckExpression(variableDeclaration.Value.Value, variableDeclaration.ExplicitType.Value); - if (valueType == null) return; - type = valueType; - } - - if (variableDeclaration.ExplicitType.HasValue) - { - type = variableDeclaration.ExplicitType.Value; - } - - if (variableDeclaration.ExplicitType.HasValue && variableDeclaration.Value.HasValue) - { - if (!NubType.IsCompatibleWith(variableDeclaration.ExplicitType.Value, variableDeclaration.Value.Value.Type)) - { - ReportError( - $"Cannot assign expression of type '{variableDeclaration.Value.Value.Type}' to variable '{variableDeclaration.Name}' with type '{variableDeclaration.ExplicitType.Value}'", - variableDeclaration); - } - } - - if (type == null) - { - ReportError($"Cannot implicitly get type of variable '{variableDeclaration.Name}'", variableDeclaration); - return; - } - - _variables[variableDeclaration.Name] = type; + _variables[variableDeclaration.Name] = variableDeclaration.Type; } private static NubType? CheckDereference(DereferenceNode dereference) @@ -246,6 +216,7 @@ public static class TypeChecker if (funcCall.Parameters.Count != funcType.Parameters.Count) { ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall); + return null; } for (var i = 0; i < funcCall.Parameters.Count; i++)