This commit is contained in:
nub31
2025-06-14 23:33:09 +02:00
parent 04fb47ef32
commit 6121334ab2
7 changed files with 156 additions and 199 deletions

View File

@@ -1,42 +1,18 @@
namespace main namespace main
struct Human { struct Human {
age: u64 name: string
print_age: func() = func() { // print_age: func() = func() {
c::puts("pwp") // c::puts("test3")
} // }
} }
export func main(args: []^string): i64 { export func main(args: [][]u8): i64 {
let x = 2 let x: []u8
let uwu = func() { x = args[0]
c::puts("uwu")
}
uwu() c::puts(x)
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
}
return 0 return 0
} }

View File

@@ -85,6 +85,9 @@ foreach (var compilationUnit in compilationUnits)
Directory.CreateDirectory(outputDirectory); Directory.CreateDirectory(outputDirectory);
var ssa = QBEGenerator.Generate(compilationUnit, definitionTable); var ssa = QBEGenerator.Generate(compilationUnit, definitionTable);
var ssaPath = Path.ChangeExtension(outputPath, "ssa");
File.WriteAllText(ssaPath, ssa);
var asm = await QBE.Invoke(ssa); var asm = await QBE.Invoke(ssa);
var asmPath = Path.ChangeExtension(outputPath, "s"); var asmPath = Path.ChangeExtension(outputPath, "s");

View File

@@ -4,12 +4,60 @@
.global _start .global _start
_start: _start:
mov rdi, rsp # On entry, the stack contains:
sub rsp, 8 # [rsp] = argc (argument count)
mov [rsp], rdi # [rsp+8] = argv[0] (program name)
mov rdi, rsp # [rsp+16] = argv[1] (first argument)
# func main(args: []^string): i64 # ...
# 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 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 syscall

View File

@@ -21,7 +21,7 @@ public static class QBEGenerator
private static Stack<string> _breakLabels = []; private static Stack<string> _breakLabels = [];
private static Stack<string> _continueLabels = []; private static Stack<string> _continueLabels = [];
private static Queue<(AnonymousFuncNode Func, string Name)> _anonymousFunctions = []; private static Queue<(AnonymousFuncNode Func, string Name)> _anonymousFunctions = [];
private static Stack<(string Name, string Pointer)> _variables = []; private static Stack<Variable> _variables = [];
private static Stack<int> _variableScopes = []; private static Stack<int> _variableScopes = [];
private static int _variableIndex; private static int _variableIndex;
private static int _labelIndex; private static int _labelIndex;
@@ -298,14 +298,14 @@ public static class QBEGenerator
throw new UnreachableException($"Member '{member}' not found in struct"); throw new UnreachableException($"Member '{member}' not found in struct");
} }
private static bool IsLargeType(NubType type) private static bool IsPointerType(NubType type)
{ {
return type switch return type switch
{ {
NubArrayType => false,
NubPointerType => false, NubPointerType => false,
NubPrimitiveType => false, NubPrimitiveType => false,
NubStructType => true, NubStructType => true,
NubArrayType => true,
NubFixedArrayType => true, NubFixedArrayType => true,
NubFuncType => false, NubFuncType => false,
_ => throw new ArgumentOutOfRangeException(nameof(type)) _ => throw new ArgumentOutOfRangeException(nameof(type))
@@ -387,7 +387,7 @@ public static class QBEGenerator
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{"); _builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
_builder.AppendLine("@start"); _builder.AppendLine("@start");
List<(string Name, string Pointer)> parameterVars = []; List<Variable> parameterVars = [];
foreach (var parameter in parameters) 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); 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<Variable>? variables = null)
{ {
_variableScopes.Push(_variables.Count); _variableScopes.Push(_variables.Count);
if (variables != null) if (variables != null)
@@ -654,29 +654,17 @@ public static class QBEGenerator
private static void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration) private static void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration)
{ {
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value!.Type; var generatedName = VarName();
var pointer = VarName(); _builder.AppendLine($" {generatedName} =l alloc8 {SizeOf(variableDeclaration.Type)}");
_builder.AppendLine($" {pointer} =l alloc8 {SizeOf(type)}"); _variables.Push(new Variable(variableDeclaration.Name, generatedName));
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));
} }
private static void GenerateVariableAssignment(VariableAssignmentNode variableAssignment) private static void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
{ {
var value = GenerateExpression(variableAssignment.Value); var value = GenerateExpression(variableAssignment.Value);
var variable = _variables.Single(x => x.Name == variableAssignment.Identifier.Name); 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) private static void GenerateWhile(WhileNode whileStatement)
@@ -706,7 +694,7 @@ public static class QBEGenerator
{ {
AddressOfNode addressOf => GenerateAddressOf(addressOf), AddressOfNode addressOf => GenerateAddressOf(addressOf),
AnonymousFuncNode anonymousFunc => GenerateAnonymousFunc(anonymousFunc), AnonymousFuncNode anonymousFunc => GenerateAnonymousFunc(anonymousFunc),
ArrayIndexAccessNode arrayIndex => GenerateArrayAccessIndex(arrayIndex), ArrayIndexAccessNode arrayIndex => GenerateArrayIndexAccessNode(arrayIndex),
ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer), ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer),
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression), BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
DereferenceNode dereference => GenerateDereference(dereference), DereferenceNode dereference => GenerateDereference(dereference),
@@ -725,10 +713,7 @@ public static class QBEGenerator
{ {
var name = $"$anon_func{++_anonymousFuncIndex}"; var name = $"$anon_func{++_anonymousFuncIndex}";
_anonymousFunctions.Enqueue((anonymousFunc, name)); _anonymousFunctions.Enqueue((anonymousFunc, name));
var pointer = VarName(); return name;
_builder.AppendLine($" {pointer} =l alloc8 8");
_builder.AppendLine($" storel {name}, {pointer}");
return pointer;
} }
private static string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess) private static string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess)
@@ -737,40 +722,27 @@ public static class QBEGenerator
var index = GenerateExpression(arrayIndexAccess.Index); var index = GenerateExpression(arrayIndexAccess.Index);
GenerateArrayBoundsCheck(array, index); GenerateArrayBoundsCheck(array, index);
switch (arrayIndexAccess.Array.Type) var elementType = arrayIndexAccess.Array.Type switch
{ {
case NubArrayType arrayType: NubArrayType arrayType => arrayType.ElementType,
{ NubFixedArrayType fixedArrayType => fixedArrayType.ElementType,
var firstItemPointerName = VarName(); _ => throw new ArgumentOutOfRangeException()
_builder.AppendLine($" {firstItemPointerName} =l add {array}, 8"); };
var offsetPointerName = VarName();
_builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(arrayType.ElementType)}"); var firstItemPointerName = VarName();
var resultPointerName = VarName(); _builder.AppendLine($" {firstItemPointerName} =l add {array}, 8");
_builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}"); var offsetPointerName = VarName();
return resultPointerName; _builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(elementType)}");
} var resultPointerName = VarName();
case NubFixedArrayType fixedArrayType: _builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}");
{ return resultPointerName;
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();
}
}
} }
private static string GenerateArrayAccessIndex(ArrayIndexAccessNode arrayIndexAccess) private static string GenerateArrayIndexAccessNode(ArrayIndexAccessNode arrayIndexAccess)
{ {
var pointerName = GenerateArrayIndexPointer(arrayIndexAccess); var pointerName = GenerateArrayIndexPointer(arrayIndexAccess);
if (IsLargeType(arrayIndexAccess.Type)) if (IsPointerType(arrayIndexAccess.Type))
{ {
return pointerName; return pointerName;
} }
@@ -784,26 +756,26 @@ public static class QBEGenerator
private static void GenerateArrayBoundsCheck(string array, string index) private static void GenerateArrayBoundsCheck(string array, string index)
{ {
var countName = VarName(); // var countName = VarName();
_builder.AppendLine($" {countName} =l loadl {array}"); // _builder.AppendLine($" {countName} =l loadl {array}");
//
var isNegativeName = VarName(); // var isNegativeName = VarName();
_builder.AppendLine($" {isNegativeName} =w csltl {index}, 0"); // _builder.AppendLine($" {isNegativeName} =w csltl {index}, 0");
//
var isOobName = VarName(); // var isOobName = VarName();
_builder.AppendLine($" {isOobName} =w csgel {index}, {countName}"); // _builder.AppendLine($" {isOobName} =w csgel {index}, {countName}");
//
var anyOobName = VarName(); // var anyOobName = VarName();
_builder.AppendLine($" {anyOobName} =w or {isNegativeName}, {isOobName}"); // _builder.AppendLine($" {anyOobName} =w or {isNegativeName}, {isOobName}");
//
var oobLabel = LabelName(); // var oobLabel = LabelName();
var notOobLabel = LabelName(); // var notOobLabel = LabelName();
_builder.AppendLine($" jnz {anyOobName}, {oobLabel}, {notOobLabel}"); // _builder.AppendLine($" jnz {anyOobName}, {oobLabel}, {notOobLabel}");
//
_builder.AppendLine(oobLabel); // _builder.AppendLine(oobLabel);
_builder.AppendLine($" call $nub_panic_array_oob()"); // _builder.AppendLine($" call $nub_panic_array_oob()");
//
_builder.AppendLine(notOobLabel); // _builder.AppendLine(notOobLabel);
} }
private static string GenerateArrayInitializer(ArrayInitializerNode arrayInitializer) 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"); 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: case MemberAccessNode memberAccess:
return GenerateMemberAccessPointer(memberAccess); return GenerateMemberAccessPointer(memberAccess);
default: default:
@@ -1085,16 +1057,22 @@ public static class QBEGenerator
{ {
if (_definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func)) if (_definitionTable.LookupFunc(identifier.Namespace.Or(_compilationUnit.Namespace), identifier.Name).TryGetValue(out var func))
{ {
var pointer = VarName(); return FuncName(func);
_builder.AppendLine($" {pointer} =l alloc8 8");
_builder.AppendLine($" storel {FuncName(func)}, {pointer}");
return pointer;
} }
if (!identifier.Namespace.HasValue) if (!identifier.Namespace.HasValue)
{ {
var variable = _variables.Single(v => v.Name == identifier.Name); 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(); throw new UnreachableException();
@@ -1257,7 +1235,7 @@ public static class QBEGenerator
{ {
var pointer = GenerateMemberAccessPointer(memberAccess); var pointer = GenerateMemberAccessPointer(memberAccess);
if (IsLargeType(memberAccess.Type)) if (IsPointerType(memberAccess.Type))
{ {
return pointer; return pointer;
} }
@@ -1324,35 +1302,24 @@ public static class QBEGenerator
parameterStrings.Add($"{qbeType} {result}"); parameterStrings.Add($"{qbeType} {result}");
} }
string funcTarget; var funcPointer = GenerateExpression(funcCall.Expression);
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;
}
if (funcType.ReturnType is not NubVoidType) if (funcType.ReturnType is not NubVoidType)
{ {
var outputName = VarName(); 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; return outputName;
} }
else else
{ {
_builder.AppendLine($" call {funcTarget}({string.Join(", ", parameterStrings)})"); _builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})");
return "fuck"; return "fuck";
} }
} }
private static void GenerateCopy(NubType type, string value, string destinationPointer) private static void GenerateCopy(NubType type, string value, string destinationPointer)
{ {
if (IsLargeType(type)) if (IsPointerType(type))
{ {
_builder.AppendLine($" blit {value}, {destinationPointer}, {SizeOf(type)}"); _builder.AppendLine($" blit {value}, {destinationPointer}, {SizeOf(type)}");
} }
@@ -1361,18 +1328,17 @@ public static class QBEGenerator
_builder.AppendLine($" {QBEStore(type)} {value}, {destinationPointer}"); _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)) Initialized = true;
{ return this;
return pointer;
}
else
{
var result = VarName();
_builder.AppendLine($" {result} {QBEAssign(type)} {QBELoad(type)} {pointer}");
return result;
}
} }
} }

View File

@@ -271,19 +271,10 @@ public static class Parser
{ {
ExpectSymbol(Symbol.Let); ExpectSymbol(Symbol.Let);
var name = ExpectIdentifier().Value; var name = ExpectIdentifier().Value;
var type = Optional<NubType>.Empty(); ExpectSymbol(Symbol.Colon);
if (TryExpectSymbol(Symbol.Colon)) var type = ParseType();
{
type = ParseType(); return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type);
}
var value = Optional<ExpressionNode>.Empty();
if (TryExpectSymbol(Symbol.Assign))
{
value = ParseExpression();
}
return new VariableDeclarationNode(GetTokensForNode(startIndex), name, type, value);
} }
private static StatementNode ParseBreak(int startIndex) private static StatementNode ParseBreak(int startIndex)
@@ -674,6 +665,11 @@ public static class Parser
return new NubVoidType(); return new NubVoidType();
} }
if (name.Value == "string")
{
return new NubArrayType(NubPrimitiveType.U8);
}
if (NubPrimitiveType.TryParse(name.Value, out var primitiveTypeKind)) if (NubPrimitiveType.TryParse(name.Value, out var primitiveTypeKind))
{ {
return new NubPrimitiveType(primitiveTypeKind.Value); return new NubPrimitiveType(primitiveTypeKind.Value);

View File

@@ -1,13 +1,10 @@
using Common; using Syntax.Tokenization;
using Syntax.Parsing.Expressions;
using Syntax.Tokenization;
using Syntax.Typing; using Syntax.Typing;
namespace Syntax.Parsing.Statements; namespace Syntax.Parsing.Statements;
public class VariableDeclarationNode(IReadOnlyList<Token> tokens, string name, Optional<NubType> explicitType, Optional<ExpressionNode> value) : StatementNode(tokens) public class VariableDeclarationNode(IReadOnlyList<Token> tokens, string name, NubType type) : StatementNode(tokens)
{ {
public string Name { get; } = name; public string Name { get; } = name;
public Optional<NubType> ExplicitType { get; } = explicitType; public NubType Type { get; } = type;
public Optional<ExpressionNode> Value { get; } = value;
} }

View File

@@ -177,42 +177,12 @@ public static class TypeChecker
private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration) private static void CheckVariableVariableDeclaration(VariableDeclarationNode variableDeclaration)
{ {
NubType? type = null;
if (_variables.TryGetValue(variableDeclaration.Name, out var variable)) if (_variables.TryGetValue(variableDeclaration.Name, out var variable))
{ {
ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration); ReportError($"Cannot redeclare variable '{variable}'", variableDeclaration);
} }
if (variableDeclaration.Value.HasValue) _variables[variableDeclaration.Name] = variableDeclaration.Type;
{
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;
} }
private static NubType? CheckDereference(DereferenceNode dereference) private static NubType? CheckDereference(DereferenceNode dereference)
@@ -246,6 +216,7 @@ public static class TypeChecker
if (funcCall.Parameters.Count != funcType.Parameters.Count) if (funcCall.Parameters.Count != funcType.Parameters.Count)
{ {
ReportError($"{funcType} expects {funcType.Parameters.Count} arguments, but was called with {funcType.Parameters.Count} arguments", funcCall); 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++) for (var i = 0; i < funcCall.Parameters.Count; i++)