diff --git a/example/makefile b/example/makefile
index 9fc5f56..4932cb8 100644
--- a/example/makefile
+++ b/example/makefile
@@ -1,8 +1,8 @@
-CC = clang
+CC = gcc
NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc
.build/out: .build/out.a
- $(CC) -g -o .build/out .build/out.a
+ $(CC) -ffreestanding -g -o .build/out .build/out.a
.build/out.a: $(NUBC) src/main.nub
$(NUBC) src/main.nub
diff --git a/src/compiler/NubLang.CLI/NubLang.CLI.csproj b/src/compiler/NubLang.CLI/NubLang.CLI.csproj
index 0550d0f..2f25112 100644
--- a/src/compiler/NubLang.CLI/NubLang.CLI.csproj
+++ b/src/compiler/NubLang.CLI/NubLang.CLI.csproj
@@ -13,4 +13,11 @@
+
+
+
+
+
+
+
diff --git a/src/compiler/NubLang.CLI/Program.cs b/src/compiler/NubLang.CLI/Program.cs
index 98406b2..55dc9d0 100644
--- a/src/compiler/NubLang.CLI/Program.cs
+++ b/src/compiler/NubLang.CLI/Program.cs
@@ -1,4 +1,5 @@
-using NubLang.CLI;
+using System.Reflection;
+using NubLang.CLI;
using NubLang.Code;
using NubLang.Diagnostics;
using NubLang.Generation.QBE;
@@ -6,6 +7,7 @@ using NubLang.Parsing;
using NubLang.Parsing.Syntax;
using NubLang.Tokenization;
using NubLang.TypeChecking;
+using Module = NubLang.TypeChecking.Module;
var options = new Options();
@@ -120,6 +122,38 @@ for (var i = 0; i < typedModules.Count; i++)
objectFiles.Add(objFilePath);
}
+var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames();
+
+string[] runtimeObjects = ["runtime.o", "x64.o"];
+
+foreach (var runtimeObject in runtimeObjects)
+{
+ var runtime = resources.First(r => r.EndsWith(runtimeObject));
+
+ await using var reader = Assembly
+ .GetExecutingAssembly()
+ .GetManifestResourceStream(runtime);
+
+ if (reader == null)
+ {
+ Console.Error.WriteLine($"Cannot open read stream to '{runtimeObject}'");
+ return 1;
+ }
+
+ var runtimePath = Path.Combine(".build", "runtime", runtimeObject);
+ var runtimeDir = Path.GetDirectoryName(runtimePath);
+ if (!string.IsNullOrEmpty(runtimeDir))
+ {
+ Directory.CreateDirectory(runtimeDir);
+ }
+
+ await using var writer = new FileStream(runtimePath, FileMode.Create);
+
+ reader.CopyTo(writer);
+
+ objectFiles.Add(runtimePath);
+}
+
var outPath = options.OutputPath ?? Path.Combine(".build", "out.a");
var outDir = Path.GetDirectoryName(outPath);
if (!string.IsNullOrEmpty(outDir))
diff --git a/src/compiler/NubLang.CLI/assets/runtime.o b/src/compiler/NubLang.CLI/assets/runtime.o
new file mode 100644
index 0000000..3024a90
Binary files /dev/null and b/src/compiler/NubLang.CLI/assets/runtime.o differ
diff --git a/src/compiler/NubLang.CLI/assets/x64.o b/src/compiler/NubLang.CLI/assets/x64.o
new file mode 100644
index 0000000..6724856
Binary files /dev/null and b/src/compiler/NubLang.CLI/assets/x64.o differ
diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs
index a93754d..4f956e2 100644
--- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs
+++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs
@@ -11,7 +11,7 @@ public class QBEGenerator
{
private readonly QBEWriter _writer;
private readonly TypedModule _module;
- private readonly IReadOnlyList _moduleSignatures;
+ private readonly IReadOnlyDictionary _moduleSignatures;
private readonly List _cStringLiterals = [];
private readonly List _stringLiterals = [];
@@ -23,7 +23,7 @@ public class QBEGenerator
private int _stringLiteralIndex;
private bool _codeIsReachable = true;
- public QBEGenerator(TypedModule module, IReadOnlyList moduleSignatures)
+ public QBEGenerator(TypedModule module, IReadOnlyDictionary moduleSignatures)
{
_module = module;
_moduleSignatures = moduleSignatures;
@@ -42,11 +42,11 @@ public class QBEGenerator
_stringLiteralIndex = 0;
_codeIsReachable = true;
- foreach (var moduleSignature in _moduleSignatures)
+ foreach (var (module, signature) in _moduleSignatures)
{
- foreach (var structType in moduleSignature.Symbols.Values.OfType())
+ foreach (var structType in signature.StructTypes)
{
- EmitStructType(moduleSignature.Name, structType);
+ EmitStructType(module, structType);
_writer.NewLine();
}
}
@@ -384,7 +384,7 @@ public class QBEGenerator
_writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' ');
}
- _writer.Write(LocalFuncName(_module.Name, funcDef));
+ _writer.Write(LocalFuncName(_module.Name, funcDef.Name));
_writer.Write("(");
foreach (var parameter in funcDef.Signature.Parameters)
@@ -408,15 +408,17 @@ public class QBEGenerator
private void EmitStructDefinition(StructNode structDef)
{
- _writer.WriteLine($"export function {StructCtorName(_module.Name, structDef.Name)}() {{");
- _writer.WriteLine("@start");
- _writer.Indented($"%struct =l alloc8 {SizeOf(structDef.)}");
- _writer.Indented("ret %struct");
- _writer.WriteLine("}");
+ var type = TypeResolver.ResolveStructType(_module.Name, structDef.Name, _moduleSignatures);
- for (var i = 0; i < structDef.Functions.Count; i++)
+ // _writer.WriteLine($"export function {StructCtorName(_module.Name, structDef.Name)}() {{");
+ // _writer.WriteLine("@start");
+ // _writer.Indented($"%struct =l alloc8 {SizeOf(type)}");
+ // // todo(nub31): Finish constructor
+ // _writer.Indented("ret %struct");
+ // _writer.WriteLine("}");
+
+ foreach (var function in structDef.Functions)
{
- var function = structDef.Functions[i];
_labelIndex = 0;
_tmpIndex = 0;
@@ -457,7 +459,7 @@ public class QBEGenerator
foreach (var field in structType.Fields)
{
- _writer.Indented($"{StructDefQBEType(field)},");
+ _writer.Indented($"{StructDefQBEType(field.Type)},");
}
_writer.WriteLine("}");
@@ -479,9 +481,9 @@ public class QBEGenerator
};
}
- if (complexType is StructTypeNode structType)
+ if (complexType is StructTypeNode childStructType)
{
- return StructTypeName(structType.Module, structType.Name);
+ return StructTypeName(childStructType.Module, childStructType.Name);
}
return "l";
@@ -642,7 +644,9 @@ public class QBEGenerator
ConvertToInterfaceNode convertToInterface => EmitConvertToInterface(convertToInterface),
ConvertIntNode convertInt => EmitConvertInt(convertInt),
ConvertFloatNode convertFloat => EmitConvertFloat(convertFloat),
- VariableIdentifierNode identifier => EmitIdentifier(identifier),
+ VariableIdentifierNode identifier => EmitVariableIdentifier(identifier),
+ FuncIdentifierNode funcIdentifier => EmitFuncIdentifier(funcIdentifier),
+ FuncParameterIdentifierNode funcParameterIdentifier => EmitParameterFuncIdentifier(funcParameterIdentifier),
LiteralNode literal => EmitLiteral(literal),
UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression),
StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess),
@@ -652,9 +656,24 @@ public class QBEGenerator
};
}
- private string EmitIdentifier(VariableIdentifierNode variableIdentifier)
+ private string EmitFuncIdentifier(FuncIdentifierNode localFuncIdent)
{
- throw new NotImplementedException();
+ // todo(nub31): Support for extern funcs
+ return LocalFuncName(localFuncIdent.Module, localFuncIdent.Name);
+ }
+
+ private string EmitVariableIdentifier(VariableIdentifierNode variableIdent)
+ {
+ var address = EmitAddressOfVariableIdent(variableIdent);
+
+ return variableIdent.Type.IsSimpleType(out _, out _)
+ ? EmitLoad(variableIdent.Type, address)
+ : address;
+ }
+
+ private string EmitParameterFuncIdentifier(FuncParameterIdentifierNode funcParameterIdent)
+ {
+ return "%" + funcParameterIdent.Name;
}
private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess)
@@ -710,43 +729,43 @@ public class QBEGenerator
{
return addressOf switch
{
- // ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess),
- // StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess),
- // VariableIdentNode variableIdent => EmitAddressOfVariableIdent(variableIdent),
+ ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess),
+ StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess),
+ VariableIdentifierNode variableIdent => EmitAddressOfVariableIdent(variableIdent),
_ => throw new ArgumentOutOfRangeException(nameof(addressOf))
};
}
- // private string EmitAddressOfArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess)
- // {
- // var array = EmitExpression(arrayIndexAccess.Target);
- // var index = EmitExpression(arrayIndexAccess.Index);
- //
- // var elementType = ((ArrayTypeNode)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 target = EmitExpression(structFieldAccess.Target);
- //
- // var structDef = _definitionTable.LookupStruct(structFieldAccess.StructType.Name);
- // var offset = OffsetOf(structDef, structFieldAccess.Field);
- //
- // var address = TmpName();
- // _writer.Indented($"{address} =l add {target}, {offset}");
- // return address;
- // }
- //
- // private string EmitAddressOfVariableIdent(VariableIdentNode variableIdent)
- // {
- // return "%" + variableIdent.Name;
- // }
+ private string EmitAddressOfArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess)
+ {
+ var array = EmitExpression(arrayIndexAccess.Target);
+ var index = EmitExpression(arrayIndexAccess.Index);
+
+ var elementType = ((ArrayTypeNode)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 target = EmitExpression(structFieldAccess.Target);
+
+ var structType = TypeResolver.ResolveStructType(structFieldAccess.StructType.Module, structFieldAccess.StructType.Name, _moduleSignatures);
+ var offset = OffsetOf(structType, structFieldAccess.Field);
+
+ var address = TmpName();
+ _writer.Indented($"{address} =l add {target}, {offset}");
+ return address;
+ }
+
+ private string EmitAddressOfVariableIdent(VariableIdentifierNode variableIdent)
+ {
+ return "%" + variableIdent.Name;
+ }
private string EmitBinaryExpression(BinaryExpressionNode binaryExpression)
{
@@ -1006,21 +1025,11 @@ public class QBEGenerator
var size = SizeOf(structInitializer.StructType);
_writer.Indented($"{destination} =l alloc8 {size}");
- foreach (var field in structInitializer.StructType.Fields)
+ foreach (var (field, value) in structInitializer.Initializers)
{
- if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression))
- {
- valueExpression = field.Value.Value;
- }
-
- if (valueExpression == null)
- {
- throw new UnreachableException("Value of field in uninitialized. This should have been caught in the type checker");
- }
-
var offset = TmpName();
- _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}");
- EmitCopyInto(valueExpression, offset);
+ _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structInitializer.StructType, field)}");
+ EmitCopyInto(value, offset);
}
return destination;
@@ -1086,8 +1095,7 @@ public class QBEGenerator
private string EmitStructFuncCall(StructFuncCallNode structFuncCall)
{
- var structDef = _definitionTable.LookupStruct(structFuncCall.StructType.Name);
- var func = StructFuncName(structDef.Name, structFuncCall.Name);
+ var func = StructFuncName(structFuncCall.StructType.Module, structFuncCall.StructType.Name, structFuncCall.Name);
var thisParameter = EmitExpression(structFuncCall.StructExpression);
@@ -1116,8 +1124,7 @@ public class QBEGenerator
{
var target = EmitExpression(interfaceFuncCall.InterfaceExpression);
- var interfaceDef = _definitionTable.LookupInterface(interfaceFuncCall.InterfaceType.Name);
- var functionIndex = interfaceDef.Functions.ToList().FindIndex(x => x.Name == interfaceFuncCall.Name);
+ var functionIndex = interfaceFuncCall.InterfaceType.Functions.ToList().FindIndex(x => x.Name == interfaceFuncCall.Name);
var offset = functionIndex * 8;
var vtable = TmpName();
@@ -1173,7 +1180,7 @@ public class QBEGenerator
_writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}");
var interfaceVtablePointer = TmpName();
- _writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Name)}, {vtableOffset}");
+ _writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Module, convertToInterface.StructType.Name)}, {vtableOffset}");
_writer.Indented($"storel {interfaceVtablePointer}, {destination}");
var objectPointer = TmpName();
@@ -1287,11 +1294,13 @@ public class QBEGenerator
{
var offset = 0;
+ var fields = new List(structType.Fields.Count);
+
foreach (var field in structType.Fields)
{
- var fieldAlignment = AlignmentOf(field);
+ var fieldAlignment = AlignmentOf(field.Type);
offset = AlignTo(offset, fieldAlignment);
- offset += SizeOf(field);
+ offset += SizeOf(field.Type);
}
var structAlignment = CalculateStructAlignment(structType);
@@ -1331,7 +1340,7 @@ public class QBEGenerator
foreach (var field in structType.Fields)
{
- var fieldAlignment = AlignmentOf(field);
+ var fieldAlignment = AlignmentOf(field.Type);
maxAlignment = Math.Max(maxAlignment, fieldAlignment);
}
@@ -1343,7 +1352,7 @@ public class QBEGenerator
return (offset + alignment - 1) & ~(alignment - 1);
}
- private static int OffsetOf(StructNode structDef, string member)
+ private static int OffsetOf(StructTypeNode structDef, string member)
{
var offset = 0;
@@ -1385,9 +1394,9 @@ public class QBEGenerator
return $"$string{++_stringLiteralIndex}";
}
- private string LocalFuncName(string module, LocalFuncNode funcDef)
+ private string LocalFuncName(string module, string name)
{
- return $"${module}.{funcDef.Name}";
+ return $"${module}.{name}";
}
private string ExternFuncName(ExternFuncNode funcDef)
diff --git a/src/compiler/NubLang/TypeChecking/Module.cs b/src/compiler/NubLang/TypeChecking/Module.cs
index 598604e..e522ca7 100644
--- a/src/compiler/NubLang/TypeChecking/Module.cs
+++ b/src/compiler/NubLang/TypeChecking/Module.cs
@@ -107,12 +107,13 @@ public class ModuleSignature
}
case InterfaceSyntax interfaceDef:
{
- var functions = new Dictionary();
- foreach (var function in interfaceDef.Functions)
+ var functions = new List();
+ for (var i = 0; i < interfaceDef.Functions.Count; i++)
{
+ var function = interfaceDef.Functions[i];
var parameters = function.Signature.Parameters.Select(p => TypeResolver.ResolveType(p.Type, modules)).ToList();
var returnType = TypeResolver.ResolveType(function.Signature.ReturnType, modules);
- functions.Add(function.Name, new FuncTypeNode(parameters, returnType));
+ functions.Add(new InterfaceTypeFunc(function.Name, new FuncTypeNode(parameters, returnType), i));
}
var type = new InterfaceTypeNode(moduleName, interfaceDef.Name, functions);
@@ -121,14 +122,18 @@ public class ModuleSignature
}
case StructSyntax structDef:
{
- var fields = structDef.Fields.Select(x => new StructTypeField(x.Name, TypeResolver.ResolveType(x.Type, modules), x.Value.HasValue)).ToList();
+ var fields = new List();
+ foreach (var field in structDef.Fields)
+ {
+ fields.Add(new StructTypeField(field.Name, TypeResolver.ResolveType(field.Type, modules), field.Index, field.Value.HasValue));
+ }
- var functions = new Dictionary();
+ var functions = new List();
foreach (var function in structDef.Functions)
{
var parameters = function.Signature.Parameters.Select(p => TypeResolver.ResolveType(p.Type, modules)).ToList();
var returnType = TypeResolver.ResolveType(function.Signature.ReturnType, modules);
- functions.Add(function.Name, new FuncTypeNode(parameters, returnType));
+ functions.Add(new StructTypeFunc(function.Name, new FuncTypeNode(parameters, returnType)));
}
var interfaceImplementations = new List();
diff --git a/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs b/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs
index 2f5bbdd..55d29aa 100644
--- a/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs
+++ b/src/compiler/NubLang/TypeChecking/Node/TypeNode.cs
@@ -174,19 +174,26 @@ public class StringTypeNode : ComplexTypeNode
public override int GetHashCode() => HashCode.Combine(typeof(StringTypeNode));
}
-public class StructTypeField(string name, TypeNode type, bool hasDefaultValue)
+public class StructTypeField(string name, TypeNode type, int index, bool hasDefaultValue)
{
public string Name { get; } = name;
public TypeNode Type { get; } = type;
+ public int Index { get; } = index;
public bool HasDefaultValue { get; } = hasDefaultValue;
}
-public class StructTypeNode(string module, string name, IReadOnlyList fields, IReadOnlyDictionary functions, IReadOnlyList interfaceImplementations) : ComplexTypeNode
+public class StructTypeFunc(string name, FuncTypeNode type)
+{
+ public string Name { get; } = name;
+ public FuncTypeNode Type { get; } = type;
+}
+
+public class StructTypeNode(string module, string name, IReadOnlyList fields, IReadOnlyList functions, IReadOnlyList interfaceImplementations) : ComplexTypeNode
{
public string Module { get; } = module;
public string Name { get; } = name;
public IReadOnlyList Fields { get; set; } = fields;
- public IReadOnlyDictionary Functions { get; set; } = functions;
+ public IReadOnlyList Functions { get; set; } = functions;
public IReadOnlyList InterfaceImplementations { get; set; } = interfaceImplementations;
public override string ToString() => Name;
@@ -194,11 +201,18 @@ public class StructTypeNode(string module, string name, IReadOnlyList HashCode.Combine(typeof(StructTypeNode), Name);
}
-public class InterfaceTypeNode(string module, string name, IReadOnlyDictionary functions) : ComplexTypeNode
+public class InterfaceTypeFunc(string name, FuncTypeNode type, int index)
+{
+ public string Name { get; } = name;
+ public FuncTypeNode Type { get; } = type;
+ public int Index { get; } = index;
+}
+
+public class InterfaceTypeNode(string module, string name, IReadOnlyList functions) : ComplexTypeNode
{
public string Module { get; } = module;
public string Name { get; } = name;
- public IReadOnlyDictionary Functions { get; set; } = functions;
+ public IReadOnlyList Functions { get; set; } = functions;
public override string ToString() => Name;
public override bool Equals(TypeNode? other) => other is InterfaceTypeNode interfaceType && Name == interfaceType.Name && Module == interfaceType.Module;
diff --git a/src/compiler/NubLang/TypeResolver.cs b/src/compiler/NubLang/TypeResolver.cs
index 7e49649..972dec2 100644
--- a/src/compiler/NubLang/TypeResolver.cs
+++ b/src/compiler/NubLang/TypeResolver.cs
@@ -43,6 +43,38 @@ public static class TypeResolver
return interfaceType;
}
- throw new Exception("Type not found: " + typeName);
+ throw new Exception($"Type {typeName} not found in module {moduleName}");
+ }
+
+ public static StructTypeNode ResolveStructType(string moduleName, string structName, IReadOnlyDictionary modules)
+ {
+ if (!modules.TryGetValue(moduleName, out var module))
+ {
+ throw new Exception("Module not found: " + moduleName);
+ }
+
+ var structType = module.StructTypes.FirstOrDefault(x => x.Name == structName);
+ if (structType != null)
+ {
+ return structType;
+ }
+
+ throw new Exception($"Struct type {structName} not found in module {moduleName}");
+ }
+
+ public static InterfaceTypeNode ResolveInterfaceType(string moduleName, string structName, IReadOnlyDictionary modules)
+ {
+ if (!modules.TryGetValue(moduleName, out var module))
+ {
+ throw new Exception("Module not found: " + moduleName);
+ }
+
+ var structType = module.InterfaceTypes.FirstOrDefault(x => x.Name == structName);
+ if (structType != null)
+ {
+ return structType;
+ }
+
+ throw new Exception($"Interface type {structName} not found in module {moduleName}");
}
}
\ No newline at end of file