This commit is contained in:
nub31
2025-09-10 16:32:59 +02:00
parent 16b86ea1c7
commit f25d003432
8 changed files with 168 additions and 182 deletions

9
README.md Normal file
View File

@@ -0,0 +1,9 @@
main.nm
missing:
- std::print
functions:
- main
std.nm
functions:
- print

View File

@@ -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

View File

@@ -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)
}
// func test(human: ^Human)
// {
// puts(human^.name.last)
// }

View File

@@ -13,11 +13,4 @@
<ProjectReference Include="..\NubLang\NubLang.csproj" />
</ItemGroup>
<ItemGroup>
<None Remove="assets\runtime.o" />
<EmbeddedResource Include="assets\runtime.o" />
<None Remove="assets\x64.o" />
<EmbeddedResource Include="assets\x64.o" />
</ItemGroup>
</Project>

View File

@@ -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<string>();
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;

View File

@@ -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}");
}