From 4c4d655f43b5f41cb516436887f045e89ecad86b Mon Sep 17 00:00:00 2001 From: nub31 Date: Fri, 12 Sep 2025 21:55:39 +0200 Subject: [PATCH] ... --- .../NubLang/Generation/QBE/QBEGenerator.cs | 352 +++++++++--------- .../TypeChecking/Node/DefinitionNode.cs | 4 +- .../NubLang/TypeChecking/Node/TypeNode.cs | 3 +- compiler/NubLang/TypeChecking/TypeChecker.cs | 60 ++- example/src/main.nub | 11 +- example/src/test.nub | 7 - 6 files changed, 246 insertions(+), 191 deletions(-) delete mode 100644 example/src/test.nub diff --git a/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/compiler/NubLang/Generation/QBE/QBEGenerator.cs index 277bff3..4e7aca7 100644 --- a/compiler/NubLang/Generation/QBE/QBEGenerator.cs +++ b/compiler/NubLang/Generation/QBE/QBEGenerator.cs @@ -43,42 +43,97 @@ public class QBEGenerator _stringLiteralIndex = 0; _codeIsReachable = true; + _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.NewLine(); + _writer.Comment("========== Function definitions =========="); + foreach (var funcDef in _definitions.OfType()) { EmitFuncDefinition(funcDef); } - // foreach (var structDef in _definitions.OfType().Where(x => x.InterfaceImplementations.Count > 0)) - // { - // _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ "); - // - // foreach (var interfaceImplementation in structDef.InterfaceImplementations) - // { - // var interfaceDef = _definitionTable.LookupInterface(interfaceImplementation.Name); - // foreach (var func in interfaceDef.Functions) - // { - // _writer.Write($"l {StructFuncName(_module.Name, structDef.Name, func.Name)}, "); - // } - // } - // - // _writer.WriteLine("}"); - // } + _writer.NewLine(); + _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}"); @@ -163,67 +218,18 @@ public class QBEGenerator private void EmitMemset(string destination, int value, string length) { - var count = TmpName(); - _writer.Indented($"{count} =l copy 0"); - - var loopLabel = LabelName(); - _writer.WriteLine(loopLabel); - - var continueLabel = LabelName(); - var doneLabel = LabelName(); - var condition = TmpName(); - _writer.Indented($"{condition} =w cultl {count}, {length}"); - _writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}"); - - _writer.WriteLine(continueLabel); - - var destinationAddress = TmpName(); - _writer.Indented($"{destinationAddress} =l add {destination}, {count}"); - - _writer.Indented($"storeb {value}, {destinationAddress}"); - - _writer.Indented($"{count} =l add {count}, 1"); - _writer.Indented($"jmp {loopLabel}"); - - _writer.WriteLine(doneLabel); + _writer.Indented($"call $.memset(l {destination}, l {value}, l {length})"); } private void EmitMemcpy(string source, string destination, string length) { - var count = TmpName(); - _writer.Indented($"{count} =l copy 0"); - - var loopLabel = LabelName(); - _writer.WriteLine(loopLabel); - - var continueLabel = LabelName(); - var doneLabel = LabelName(); - var condition = TmpName(); - _writer.Indented($"{condition} =w cultl {count}, {length}"); - _writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}"); - - _writer.WriteLine(continueLabel); - - var sourceAddress = TmpName(); - _writer.Indented($"{sourceAddress} =l add {source}, {count}"); - - var destinationAddress = TmpName(); - _writer.Indented($"{destinationAddress} =l add {destination}, {count}"); - - var value = TmpName(); - _writer.Indented($"{value} =w loadub {sourceAddress}"); - _writer.Indented($"storeb {value}, {destinationAddress}"); - - _writer.Indented($"{count} =l add {count}, 1"); - _writer.Indented($"jmp {loopLabel}"); - - _writer.WriteLine(doneLabel); + _writer.Indented($"call $.memcpy(l {source}, l {destination}, l {length})"); } private string EmitArraySizeInBytes(ArrayTypeNode type, string array) { var size = TmpName(); - _writer.Indented($"{size} =l loadl {array}"); + _writer.Indented($"{size} =l call $.array_size(l {array})"); _writer.Indented($"{size} =l mul {size}, {SizeOf(type.ElementType)}"); _writer.Indented($"{size} =l add {size}, 8"); return size; @@ -231,27 +237,10 @@ public class QBEGenerator private string EmitCStringSizeInBytes(string cstring) { - var count = TmpName(); - _writer.Indented($"{count} =l copy 0"); - - var loopLabel = LabelName(); - _writer.WriteLine(loopLabel); - - var address = TmpName(); - _writer.Indented($"{address} =l add {cstring}, {count}"); - - var value = TmpName(); - _writer.Indented($"{value} =w loadub {address}"); - - var notZeroLabel = LabelName(); - var zeroLabel = LabelName(); - _writer.Indented($"jnz {value}, {notZeroLabel}, {zeroLabel}"); - _writer.WriteLine(notZeroLabel); - _writer.Indented($"{count} =l add {count}, 1"); - _writer.Indented($"jmp {loopLabel}"); - _writer.WriteLine(zeroLabel); - - return count; + 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) @@ -367,82 +356,6 @@ public class QBEGenerator return "l"; } - 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 VoidTypeNode) - { - _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); - - // Implicit return for void functions if no explicit return has been set - if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode) - { - _writer.Indented("ret"); - } - - _writer.WriteLine("}"); - } - - private void EmitStructDefinition(StructNode structDef) - { - // todo(nub31): Find a way do run the initializers from other modules where the definition is not available. - // A constructor is probably the answer - - foreach (var function in structDef.Functions) - { - _labelIndex = 0; - _tmpIndex = 0; - - _writer.Write("export function "); - - if (function.Signature.ReturnType is not VoidTypeNode) - { - _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 VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode) - { - _writer.Indented("ret"); - } - - _writer.WriteLine("}"); - } - } - private void EmitStructType(StructTypeNode structType) { _writer.Write($"type {StructTypeName(structType.Module, structType.Name)} = {{ "); @@ -480,6 +393,99 @@ public class QBEGenerator } } + 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.TryGetValue(out var value)) + { + var offset = OffsetOf(structDef.StructType, field.Name); + var destination = TmpName(); + _writer.Indented($"{destination} =l add %struct, {offset}"); + EmitCopyInto(value, destination); + } + } + + _writer.Indented("ret"); + _writer.WriteLine("}"); + + foreach (var function in structDef.Functions) + { + _writer.Comment($" ===== {structDef.Module}::{structDef.Name}.{function.Name} ====="); + _labelIndex = 0; + _tmpIndex = 0; + + _writer.NewLine(); + _writer.Write("export function "); + + if (function.Signature.ReturnType is not VoidTypeNode) + { + _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 VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode) + { + _writer.Indented("ret"); + } + + _writer.WriteLine("}"); + } + } + + 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 VoidTypeNode) + { + _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); + + // Implicit return for void functions if no explicit return has been set + if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode) + { + _writer.Indented("ret"); + } + + _writer.WriteLine("}"); + } + private void EmitBlock(BlockNode block) { foreach (var statement in block.Statements) @@ -1011,6 +1017,7 @@ public class QBEGenerator var destination = TmpName(); var size = SizeOf(structInitializer.StructType); _writer.Indented($"{destination} =l alloc8 {size}"); + _writer.Indented($"call {StructCtorName(structInitializer.StructType.Module, structInitializer.StructType.Name)}(l {destination})"); foreach (var (field, value) in structInitializer.Initializers) { @@ -1361,27 +1368,27 @@ public class QBEGenerator private string TmpName() { - return $"%t.{++_tmpIndex}"; + return $"%.t{++_tmpIndex}"; } private string LabelName() { - return $"@l.{++_labelIndex}"; + return $"@.l{++_labelIndex}"; } private string CStringName() { - return $"$cstr.{++_cStringLiteralIndex}"; + return $"$.cstr{++_cStringLiteralIndex}"; } private string StringName() { - return $"$str.{++_stringLiteralIndex}"; + return $"$.str{++_stringLiteralIndex}"; } private static string FuncName(string module, string name, string? externSymbol) { - return $"${externSymbol ?? $"{module}.{name}"}"; + return $"${externSymbol ?? $".{module}.{name}"}"; } private static string StructTypeName(string module, string name) @@ -1389,9 +1396,14 @@ public class QBEGenerator 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}"; + return $"$.{module}.{structName}.func.{funcName}"; } private static string StructVtableName(string module, string structName) diff --git a/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs b/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs index a67b19a..46c8afb 100644 --- a/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs +++ b/compiler/NubLang/TypeChecking/Node/DefinitionNode.cs @@ -12,8 +12,8 @@ public record StructFieldNode(string Name, TypeNode Type, Optional Fields, List Functions, List InterfaceImplementations) : DefinitionNode(Module, Name); +public record StructNode(StructTypeNode StructType, string Module, string Name, List Fields, List Functions, List InterfaceImplementations) : DefinitionNode(Module, Name); public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node; -public record InterfaceNode(string Module, string Name, List Functions) : DefinitionNode(Module, Name); \ No newline at end of file +public record InterfaceNode(InterfaceTypeNode InterfaceType, string Module, string Name, List Functions) : DefinitionNode(Module, Name); \ No newline at end of file diff --git a/compiler/NubLang/TypeChecking/Node/TypeNode.cs b/compiler/NubLang/TypeChecking/Node/TypeNode.cs index 92bba75..e3bd930 100644 --- a/compiler/NubLang/TypeChecking/Node/TypeNode.cs +++ b/compiler/NubLang/TypeChecking/Node/TypeNode.cs @@ -200,11 +200,10 @@ public class StructTypeNode(string module, string name, List fi public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name); } -public class InterfaceTypeFunc(string name, FuncTypeNode type, int index) +public class InterfaceTypeFunc(string name, FuncTypeNode type) { public string Name { get; } = name; public FuncTypeNode Type { get; } = type; - public int Index { get; } = index; } public class InterfaceTypeNode(string module, string name, List functions) : ComplexTypeNode diff --git a/compiler/NubLang/TypeChecking/TypeChecker.cs b/compiler/NubLang/TypeChecking/TypeChecker.cs index d36107f..428c54c 100644 --- a/compiler/NubLang/TypeChecking/TypeChecker.cs +++ b/compiler/NubLang/TypeChecking/TypeChecker.cs @@ -74,7 +74,17 @@ public sealed class TypeChecker .Select(function => new InterfaceFuncNode(function.Name, CheckFuncSignature(function.Signature))) .ToList(); - return new InterfaceNode(_syntaxTree.Metadata.ModuleName, node.Name, functions); + var functionTypes = new List(); + foreach (var function in node.Functions) + { + var parameters = function.Signature.Parameters.Select(x => ResolveType(x.Type)).ToList(); + var funcType = new FuncTypeNode(parameters, ResolveType(function.Signature.ReturnType)); + functionTypes.Add(new InterfaceTypeFunc(function.Name, funcType)); + } + + var type = new InterfaceTypeNode(_syntaxTree.Metadata.ModuleName, node.Name, functionTypes); + + return new InterfaceNode(type, _syntaxTree.Metadata.ModuleName, node.Name, functions); } private StructNode CheckStructDefinition(StructSyntax node) @@ -85,7 +95,7 @@ public sealed class TypeChecker var value = Optional.Empty(); if (field.Value.HasValue) { - value = CheckExpression(field.Value.Value); + value = CheckExpression(field.Value.Value, ResolveType(field.Type)); } fields.Add(new StructFieldNode(field.Name, ResolveType(field.Type), value)); @@ -110,8 +120,8 @@ public sealed class TypeChecker var interfaceImplementations = new List(); foreach (var interfaceImplementation in node.InterfaceImplementations) { - var type = ResolveType(interfaceImplementation); - if (type is not InterfaceTypeNode interfaceType) + var resolvedType = ResolveType(interfaceImplementation); + if (resolvedType is not InterfaceTypeNode interfaceType) { _diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build()); continue; @@ -120,7 +130,34 @@ public sealed class TypeChecker interfaceImplementations.Add(interfaceType); } - return new StructNode(_syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations); + var fieldTypes = node.Fields + .Select(x => new StructTypeField(x.Name, ResolveType(x.Type), x.Value.HasValue)) + .ToList(); + + var functionTypes = new List(); + foreach (var function in node.Functions) + { + var parameters = function.Signature.Parameters.Select(x => ResolveType(x.Type)).ToList(); + var funcType = new FuncTypeNode(parameters, ResolveType(function.Signature.ReturnType)); + functionTypes.Add(new StructTypeFunc(function.Name, funcType)); + } + + var interfaceImplementationTypes = new List(); + foreach (var interfaceImplementation in node.InterfaceImplementations) + { + var resolvedType = ResolveType(interfaceImplementation); + if (resolvedType is not InterfaceTypeNode interfaceType) + { + _diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build()); + continue; + } + + interfaceImplementationTypes.Add(interfaceType); + } + + var type = new StructTypeNode(_syntaxTree.Metadata.ModuleName, node.Name, fieldTypes, functionTypes, interfaceImplementationTypes); + + return new StructNode(type, _syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations); } private FuncNode CheckFuncDefinition(FuncSyntax node) @@ -466,7 +503,18 @@ public sealed class TypeChecker foreach (var initializer in expression.Initializers) { - initializers.Add(initializer.Key, CheckExpression(initializer.Value)); + var typeField = structType.Fields.FirstOrDefault(x => x.Name == initializer.Key); + if (typeField == null) + { + _diagnostics.AddRange(Diagnostic + .Error($"Struct {structType.Name} does not have a field named {initializer.Key}") + .At(initializer.Value) + .Build()); + + continue; + } + + initializers.Add(initializer.Key, CheckExpression(initializer.Value, typeField.Type)); } var missingFields = structType.Fields diff --git a/example/src/main.nub b/example/src/main.nub index 38a9726..648ee60 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -1,15 +1,18 @@ module "main" -import "test" export extern "puts" func puts(text: cstring) -export struct Test1 +export struct Human { - test2: ^test::Test2 + name: cstring } extern "main" func main(args: []cstring): i64 { - puts("test") + let x: Human = { + name = "test" + } + + puts(x.name) return 0 } diff --git a/example/src/test.nub b/example/src/test.nub deleted file mode 100644 index ae15028..0000000 --- a/example/src/test.nub +++ /dev/null @@ -1,7 +0,0 @@ -module "test" -import "main" - -export struct Test2 -{ - test1: ^main::Test1 -} \ No newline at end of file