diff --git a/README.md b/README.md
new file mode 100644
index 0000000..f02afbd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,9 @@
+main.nm
+ missing:
+ - std::print
+ functions:
+ - main
+
+std.nm
+ functions:
+ - print
\ No newline at end of file
diff --git a/example/makefile b/example/makefile
index 57fcdcc..9fc5f56 100644
--- a/example/makefile
+++ b/example/makefile
@@ -1,18 +1,18 @@
CC = clang
NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc
-out: build/out.a
- $(CC) -o out build/out.a
+.build/out: .build/out.a
+ $(CC) -g -o .build/out .build/out.a
-build/out.a: $(NUBC) src/main.nub
+.build/out.a: $(NUBC) src/main.nub
$(NUBC) src/main.nub
$(NUBC):
dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj
-run: out
- ./out
+run: .build/out
+ ./.build/out
clean:
- @rm -r build 2>/dev/null || true
+ @rm -r .build 2>/dev/null || true
@rm out 2>/dev/null || true
diff --git a/example/src/main.nub b/example/src/main.nub
index e6f514b..1195e4f 100644
--- a/example/src/main.nub
+++ b/example/src/main.nub
@@ -23,12 +23,12 @@ func main(args: []cstring): i64
age = "23"
}
- test(x&)
+ puts(x.age)
return 0
}
-func test(human: ^Human)
-{
- puts(human^.name.last)
-}
\ No newline at end of file
+// func test(human: ^Human)
+// {
+// puts(human^.name.last)
+// }
\ No newline at end of file
diff --git a/src/compiler/NubLang.CLI/NubLang.CLI.csproj b/src/compiler/NubLang.CLI/NubLang.CLI.csproj
index 2f25112..0550d0f 100644
--- a/src/compiler/NubLang.CLI/NubLang.CLI.csproj
+++ b/src/compiler/NubLang.CLI/NubLang.CLI.csproj
@@ -13,11 +13,4 @@
-
-
-
-
-
-
-
diff --git a/src/compiler/NubLang.CLI/Program.cs b/src/compiler/NubLang.CLI/Program.cs
index 86fea89..13a1fdc 100644
--- a/src/compiler/NubLang.CLI/Program.cs
+++ b/src/compiler/NubLang.CLI/Program.cs
@@ -1,5 +1,4 @@
-using System.Reflection;
-using NubLang.CLI;
+using NubLang.CLI;
using NubLang.Code;
using NubLang.Diagnostics;
using NubLang.Generation;
@@ -91,7 +90,7 @@ var objectFiles = new List();
for (var i = 0; i < typedSyntaxTrees.Count; i++)
{
var syntaxTree = typedSyntaxTrees[i];
- var outFileName = Path.Combine("build", "code", Path.ChangeExtension(options.Files[i].Path, null));
+ var outFileName = Path.Combine(".build", "code", Path.ChangeExtension(options.Files[i].Path, null));
var outFileDir = Path.GetDirectoryName(outFileName);
if (!string.IsNullOrEmpty(outFileDir))
@@ -124,39 +123,7 @@ for (var i = 0; i < typedSyntaxTrees.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 outPath = options.OutputPath ?? Path.Combine(".build", "out.a");
var outDir = Path.GetDirectoryName(outPath);
if (!string.IsNullOrEmpty(outDir))
{
@@ -169,5 +136,4 @@ if (!archiveResult)
return 1;
}
-
return 0;
\ No newline at end of file
diff --git a/src/compiler/NubLang.CLI/assets/runtime.o b/src/compiler/NubLang.CLI/assets/runtime.o
deleted file mode 100644
index 26e4678..0000000
Binary files a/src/compiler/NubLang.CLI/assets/runtime.o and /dev/null differ
diff --git a/src/compiler/NubLang.CLI/assets/x64.o b/src/compiler/NubLang.CLI/assets/x64.o
deleted file mode 100644
index f4f53cd..0000000
Binary files a/src/compiler/NubLang.CLI/assets/x64.o and /dev/null differ
diff --git a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs
index f640f4e..3f1f580 100644
--- a/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs
+++ b/src/compiler/NubLang/Generation/QBE/QBEGenerator.cs
@@ -166,9 +166,63 @@ public class QBEGenerator
return into;
}
+ 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);
+ }
+
private void EmitMemcpy(string source, string destination, string length)
{
- _writer.Indented($"call $nub_memcpy(l {source}, l {destination}, l {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);
}
private string EmitArraySizeInBytes(ArrayTypeNode type, string array)
@@ -182,10 +236,27 @@ public class QBEGenerator
private string EmitCStringSizeInBytes(string cstring)
{
- var size = TmpName();
- _writer.Indented($"{size} =l call $nub_cstring_length(l {cstring})");
- _writer.Indented($"{size} =l add {size}, 1");
- return size;
+ 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;
}
private string EmitStringSizeInBytes(string nubstring)
@@ -196,74 +267,42 @@ public class QBEGenerator
return size;
}
- private bool EmitTryMoveInto(ExpressionNode source, string destinationLValue)
+ private void EmitCopyInto(ExpressionNode source, string destination)
{
- switch (source)
- {
- case ArrayInitializerNode arrayInitializer:
- {
- EmitStore(source.Type, EmitArrayInitializer(arrayInitializer), destinationLValue);
- return true;
- }
- case StructInitializerNode structInitializer:
- {
- EmitStructInitializer(structInitializer, destinationLValue);
- return true;
- }
- case ConvertToInterfaceNode convertToInterface:
- {
- EmitConvertToInterface(convertToInterface, destinationLValue);
- return true;
- }
- case LiteralNode { Kind: LiteralKind.String } literal:
- {
- EmitStore(source.Type, EmitLiteral(literal), destinationLValue);
- return true;
- }
- }
-
- return false;
- }
-
- private void EmitCopyIntoOrInitialize(ExpressionNode source, string destinationLValue)
- {
- // If the source is a value which is not used yet such as an array/struct initializer or literal, we can skip copying
- if (EmitTryMoveInto(source, destinationLValue))
+ // Simple types are passed in registers and can therefore just be stored
+ if (source.Type.IsSimpleType(out var simpleType, out var complexType))
{
+ var value = EmitExpression(source);
+ EmitStore(simpleType, value, destination);
return;
}
- var value = EmitExpression(source);
-
- if (source.Type.IsSimpleType(out var simpleType, out var complexType))
+ // Structs and interfaces has known sizes at compile time
+ if (complexType is StructTypeNode or InterfaceTypeNode)
{
- EmitStore(simpleType, value, destinationLValue);
+ var value = EmitExpression(source);
+ _writer.Indented($"blit {value}, {destination}, {SizeOf(complexType)}");
}
+ // The rest of the complex types has unknown sizes
else
{
- if (complexType is StructTypeNode or InterfaceTypeNode)
+ var value = EmitExpression(source);
+ var size = complexType switch
{
- EmitMemcpy(value, destinationLValue, SizeOf(complexType).ToString());
- }
- else
- {
- var size = complexType switch
- {
- ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
- CStringTypeNode => EmitCStringSizeInBytes(value),
- StringTypeNode => EmitStringSizeInBytes(value),
- _ => throw new ArgumentOutOfRangeException(nameof(source.Type))
- };
+ ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
+ CStringTypeNode => EmitCStringSizeInBytes(value),
+ StringTypeNode => EmitStringSizeInBytes(value),
+ _ => throw new ArgumentOutOfRangeException(nameof(source.Type))
+ };
- var buffer = TmpName();
- _writer.Indented($"{buffer} =l alloc8 {size}");
- EmitMemcpy(value, buffer, size);
- EmitStore(complexType, buffer, destinationLValue);
- }
+ var buffer = TmpName();
+ _writer.Indented($"{buffer} =l alloc8 {size}");
+ EmitMemcpy(value, buffer, size);
+ EmitStore(complexType, buffer, destination);
}
}
- private string EmitCreateCopy(ExpressionNode source)
+ private string EmitCopy(ExpressionNode source)
{
// Allowlist for types which are safe to not copy
if (source is ArrayInitializerNode or StructInitializerNode or ConvertToInterfaceNode or LiteralNode)
@@ -279,19 +318,30 @@ public class QBEGenerator
// For the rest, we figure out the size of the type and shallow copy them
var value = EmitExpression(source);
- var size = complexType switch
- {
- ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
- CStringTypeNode => EmitCStringSizeInBytes(value),
- StringTypeNode => EmitStringSizeInBytes(value),
- InterfaceTypeNode interfaceType => SizeOf(interfaceType).ToString(),
- StructTypeNode structType => SizeOf(structType).ToString(),
- _ => throw new ArgumentOutOfRangeException(nameof(source.Type))
- };
-
var destination = TmpName();
- _writer.Indented($"{destination} =l alloc8 {size}");
- EmitMemcpy(value, destination, size);
+
+ // Structs and interfaces has known sizes at compile time
+ if (complexType is StructTypeNode or InterfaceTypeNode)
+ {
+ var size = SizeOf(complexType);
+ _writer.Indented($"{destination} =l alloc8 {size}");
+ _writer.Indented($"blit {value}, {destination}, {size}");
+ }
+ // The rest of the complex types has unknown sizes
+ else
+ {
+ var size = complexType switch
+ {
+ ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
+ CStringTypeNode => EmitCStringSizeInBytes(value),
+ StringTypeNode => EmitStringSizeInBytes(value),
+ _ => throw new ArgumentOutOfRangeException(nameof(source.Type))
+ };
+
+ _writer.Indented($"{destination} =l alloc8 {size}");
+ EmitMemcpy(value, destination, size);
+ }
+
return destination;
}
@@ -460,11 +510,11 @@ public class QBEGenerator
private void EmitStatement(StatementNode statement)
{
- var tokens = statement.Tokens.ToArray();
- if (tokens.Length != 0)
- {
- _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
- }
+ // var tokens = statement.Tokens.ToArray();
+ // if (tokens.Length != 0)
+ // {
+ // _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
+ // }
switch (statement)
{
@@ -499,7 +549,7 @@ public class QBEGenerator
private void EmitAssignment(AssignmentNode assignment)
{
- EmitCopyIntoOrInitialize(assignment.Value, EmitAddressOfLValue(assignment.Target));
+ EmitCopyInto(assignment.Value, EmitAddressOfLValue(assignment.Target));
}
private void EmitBreak()
@@ -554,7 +604,7 @@ public class QBEGenerator
if (variableDeclaration.Assignment.HasValue)
{
- EmitCopyIntoOrInitialize(variableDeclaration.Assignment.Value, name);
+ EmitCopyInto(variableDeclaration.Assignment.Value, name);
}
}
@@ -581,11 +631,11 @@ public class QBEGenerator
private string EmitExpression(ExpressionNode expression)
{
- var tokens = expression.Tokens.ToArray();
- if (tokens.Length != 0)
- {
- _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
- }
+ // var tokens = expression.Tokens.ToArray();
+ // if (tokens.Length != 0)
+ // {
+ // _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
+ // }
return expression switch
{
@@ -623,30 +673,6 @@ public class QBEGenerator
return EmitLoad(arrayIndexAccess.Type, address);
}
- private void EmitArrayBoundsCheck(string array, string index)
- {
- var count = TmpName();
- _writer.Indented($"{count} =l loadl {array}");
-
- var isNegative = TmpName();
- _writer.Indented($"{isNegative} =w csltl {index}, 0");
-
- var isOob = TmpName();
- _writer.Indented($"{isOob} =w csgel {index}, {count}");
-
- var anyOob = TmpName();
- _writer.Indented($"{anyOob} =w or {isNegative}, {isOob}");
-
- var oobLabel = LabelName();
- var notOobLabel = LabelName();
- _writer.Indented($"jnz {anyOob}, {oobLabel}, {notOobLabel}");
-
- _writer.Indented(oobLabel);
- _writer.Indented($"call $nub_panic_array_oob()");
-
- _writer.Indented(notOobLabel);
- }
-
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
{
var capacity = EmitExpression(arrayInitializer.Capacity);
@@ -663,7 +689,7 @@ public class QBEGenerator
var dataPointer = TmpName();
_writer.Indented($"{dataPointer} =l add {arrayPointer}, 8");
- _writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})");
+ EmitMemset(dataPointer, 0, capacityInBytes);
return arrayPointer;
}
@@ -700,8 +726,6 @@ public class QBEGenerator
var array = EmitExpression(arrayIndexAccess.Target);
var index = EmitExpression(arrayIndexAccess.Index);
- EmitArrayBoundsCheck(array, index);
-
var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType;
var offset = TmpName();
@@ -1006,16 +1030,13 @@ public class QBEGenerator
throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}");
}
- private string EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null)
+ private string EmitStructInitializer(StructInitializerNode structInitializer)
{
var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name);
- if (destination == null)
- {
- destination = TmpName();
- var size = SizeOf(structInitializer.StructType);
- _writer.Indented($"{destination} =l alloc8 {size}");
- }
+ var destination = TmpName();
+ var size = SizeOf(structInitializer.StructType);
+ _writer.Indented($"{destination} =l alloc8 {size}");
foreach (var field in structDef.Fields)
{
@@ -1031,7 +1052,7 @@ public class QBEGenerator
var offset = TmpName();
_writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}");
- EmitCopyIntoOrInitialize(valueExpression, offset);
+ EmitCopyInto(valueExpression, offset);
}
return destination;
@@ -1106,7 +1127,7 @@ public class QBEGenerator
foreach (var parameter in structFuncCall.Parameters)
{
- var copy = EmitCreateCopy(parameter);
+ var copy = EmitCopy(parameter);
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
}
@@ -1148,7 +1169,7 @@ public class QBEGenerator
foreach (var parameter in interfaceFuncCall.Parameters)
{
- var copy = EmitCreateCopy(parameter);
+ var copy = EmitCopy(parameter);
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
}
@@ -1165,7 +1186,7 @@ public class QBEGenerator
}
}
- private string EmitConvertToInterface(ConvertToInterfaceNode convertToInterface, string? destination = null)
+ private string EmitConvertToInterface(ConvertToInterfaceNode convertToInterface)
{
var implementation = EmitExpression(convertToInterface.Implementation);
@@ -1180,11 +1201,8 @@ public class QBEGenerator
vtableOffset += interfaceImplementation.Functions.Count * 8;
}
- if (destination == null)
- {
- destination = TmpName();
- _writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}");
- }
+ var destination = TmpName();
+ _writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}");
var interfaceVtablePointer = TmpName();
_writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Name)}, {vtableOffset}");
@@ -1258,7 +1276,7 @@ public class QBEGenerator
foreach (var parameter in funcCall.Parameters)
{
- var copy = EmitCreateCopy(parameter);
+ var copy = EmitCopy(parameter);
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
}