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 CC = clang
NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc NUBC = ../src/compiler/NubLang.CLI/bin/Debug/net9.0/nubc
out: build/out.a .build/out: .build/out.a
$(CC) -o 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) src/main.nub
$(NUBC): $(NUBC):
dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj dotnet build ../src/compiler/NubLang.CLI/NubLang.CLI.csproj
run: out run: .build/out
./out ./.build/out
clean: clean:
@rm -r build 2>/dev/null || true @rm -r .build 2>/dev/null || true
@rm out 2>/dev/null || true @rm out 2>/dev/null || true

View File

@@ -23,12 +23,12 @@ func main(args: []cstring): i64
age = "23" age = "23"
} }
test(x&) puts(x.age)
return 0 return 0
} }
func test(human: ^Human) // func test(human: ^Human)
{ // {
puts(human^.name.last) // puts(human^.name.last)
} // }

View File

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

View File

@@ -1,5 +1,4 @@
using System.Reflection; using NubLang.CLI;
using NubLang.CLI;
using NubLang.Code; using NubLang.Code;
using NubLang.Diagnostics; using NubLang.Diagnostics;
using NubLang.Generation; using NubLang.Generation;
@@ -91,7 +90,7 @@ var objectFiles = new List<string>();
for (var i = 0; i < typedSyntaxTrees.Count; i++) for (var i = 0; i < typedSyntaxTrees.Count; i++)
{ {
var syntaxTree = typedSyntaxTrees[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); var outFileDir = Path.GetDirectoryName(outFileName);
if (!string.IsNullOrEmpty(outFileDir)) if (!string.IsNullOrEmpty(outFileDir))
@@ -124,39 +123,7 @@ for (var i = 0; i < typedSyntaxTrees.Count; i++)
objectFiles.Add(objFilePath); objectFiles.Add(objFilePath);
} }
var resources = Assembly.GetExecutingAssembly().GetManifestResourceNames(); var outPath = options.OutputPath ?? Path.Combine(".build", "out.a");
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); var outDir = Path.GetDirectoryName(outPath);
if (!string.IsNullOrEmpty(outDir)) if (!string.IsNullOrEmpty(outDir))
{ {
@@ -169,5 +136,4 @@ if (!archiveResult)
return 1; return 1;
} }
return 0; return 0;

View File

@@ -166,9 +166,63 @@ public class QBEGenerator
return into; 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) 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) private string EmitArraySizeInBytes(ArrayTypeNode type, string array)
@@ -182,10 +236,27 @@ public class QBEGenerator
private string EmitCStringSizeInBytes(string cstring) private string EmitCStringSizeInBytes(string cstring)
{ {
var size = TmpName(); var count = TmpName();
_writer.Indented($"{size} =l call $nub_cstring_length(l {cstring})"); _writer.Indented($"{count} =l copy 0");
_writer.Indented($"{size} =l add {size}, 1");
return size; 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) private string EmitStringSizeInBytes(string nubstring)
@@ -196,57 +267,26 @@ public class QBEGenerator
return size; return size;
} }
private bool EmitTryMoveInto(ExpressionNode source, string destinationLValue) private void EmitCopyInto(ExpressionNode source, string destination)
{ {
switch (source) // Simple types are passed in registers and can therefore just be stored
{ if (source.Type.IsSimpleType(out var simpleType, out var complexType))
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))
{ {
var value = EmitExpression(source);
EmitStore(simpleType, value, destination);
return; return;
} }
var value = EmitExpression(source); // Structs and interfaces has known sizes at compile time
if (source.Type.IsSimpleType(out var simpleType, out var complexType))
{
EmitStore(simpleType, value, destinationLValue);
}
else
{
if (complexType is StructTypeNode or InterfaceTypeNode) if (complexType is StructTypeNode or InterfaceTypeNode)
{ {
EmitMemcpy(value, destinationLValue, SizeOf(complexType).ToString()); var value = EmitExpression(source);
_writer.Indented($"blit {value}, {destination}, {SizeOf(complexType)}");
} }
// The rest of the complex types has unknown sizes
else else
{ {
var value = EmitExpression(source);
var size = complexType switch var size = complexType switch
{ {
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value), ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
@@ -258,12 +298,11 @@ public class QBEGenerator
var buffer = TmpName(); var buffer = TmpName();
_writer.Indented($"{buffer} =l alloc8 {size}"); _writer.Indented($"{buffer} =l alloc8 {size}");
EmitMemcpy(value, buffer, size); EmitMemcpy(value, buffer, size);
EmitStore(complexType, buffer, destinationLValue); EmitStore(complexType, buffer, destination);
}
} }
} }
private string EmitCreateCopy(ExpressionNode source) private string EmitCopy(ExpressionNode source)
{ {
// Allowlist for types which are safe to not copy // Allowlist for types which are safe to not copy
if (source is ArrayInitializerNode or StructInitializerNode or ConvertToInterfaceNode or LiteralNode) 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 // For the rest, we figure out the size of the type and shallow copy them
var value = EmitExpression(source); var value = EmitExpression(source);
var destination = TmpName();
// 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 var size = complexType switch
{ {
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value), ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
CStringTypeNode => EmitCStringSizeInBytes(value), CStringTypeNode => EmitCStringSizeInBytes(value),
StringTypeNode => EmitStringSizeInBytes(value), StringTypeNode => EmitStringSizeInBytes(value),
InterfaceTypeNode interfaceType => SizeOf(interfaceType).ToString(),
StructTypeNode structType => SizeOf(structType).ToString(),
_ => throw new ArgumentOutOfRangeException(nameof(source.Type)) _ => throw new ArgumentOutOfRangeException(nameof(source.Type))
}; };
var destination = TmpName();
_writer.Indented($"{destination} =l alloc8 {size}"); _writer.Indented($"{destination} =l alloc8 {size}");
EmitMemcpy(value, destination, size); EmitMemcpy(value, destination, size);
}
return destination; return destination;
} }
@@ -460,11 +510,11 @@ public class QBEGenerator
private void EmitStatement(StatementNode statement) private void EmitStatement(StatementNode statement)
{ {
var tokens = statement.Tokens.ToArray(); // var tokens = statement.Tokens.ToArray();
if (tokens.Length != 0) // if (tokens.Length != 0)
{ // {
_writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}"); // _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
} // }
switch (statement) switch (statement)
{ {
@@ -499,7 +549,7 @@ public class QBEGenerator
private void EmitAssignment(AssignmentNode assignment) private void EmitAssignment(AssignmentNode assignment)
{ {
EmitCopyIntoOrInitialize(assignment.Value, EmitAddressOfLValue(assignment.Target)); EmitCopyInto(assignment.Value, EmitAddressOfLValue(assignment.Target));
} }
private void EmitBreak() private void EmitBreak()
@@ -554,7 +604,7 @@ public class QBEGenerator
if (variableDeclaration.Assignment.HasValue) 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) private string EmitExpression(ExpressionNode expression)
{ {
var tokens = expression.Tokens.ToArray(); // var tokens = expression.Tokens.ToArray();
if (tokens.Length != 0) // if (tokens.Length != 0)
{ // {
_writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}"); // _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
} // }
return expression switch return expression switch
{ {
@@ -623,30 +673,6 @@ public class QBEGenerator
return EmitLoad(arrayIndexAccess.Type, address); 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) private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
{ {
var capacity = EmitExpression(arrayInitializer.Capacity); var capacity = EmitExpression(arrayInitializer.Capacity);
@@ -663,7 +689,7 @@ public class QBEGenerator
var dataPointer = TmpName(); var dataPointer = TmpName();
_writer.Indented($"{dataPointer} =l add {arrayPointer}, 8"); _writer.Indented($"{dataPointer} =l add {arrayPointer}, 8");
_writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); EmitMemset(dataPointer, 0, capacityInBytes);
return arrayPointer; return arrayPointer;
} }
@@ -700,8 +726,6 @@ public class QBEGenerator
var array = EmitExpression(arrayIndexAccess.Target); var array = EmitExpression(arrayIndexAccess.Target);
var index = EmitExpression(arrayIndexAccess.Index); var index = EmitExpression(arrayIndexAccess.Index);
EmitArrayBoundsCheck(array, index);
var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType; var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType;
var offset = TmpName(); var offset = TmpName();
@@ -1006,16 +1030,13 @@ public class QBEGenerator
throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}"); 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); var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name);
if (destination == null) var destination = TmpName();
{
destination = TmpName();
var size = SizeOf(structInitializer.StructType); var size = SizeOf(structInitializer.StructType);
_writer.Indented($"{destination} =l alloc8 {size}"); _writer.Indented($"{destination} =l alloc8 {size}");
}
foreach (var field in structDef.Fields) foreach (var field in structDef.Fields)
{ {
@@ -1031,7 +1052,7 @@ public class QBEGenerator
var offset = TmpName(); var offset = TmpName();
_writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}"); _writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}");
EmitCopyIntoOrInitialize(valueExpression, offset); EmitCopyInto(valueExpression, offset);
} }
return destination; return destination;
@@ -1106,7 +1127,7 @@ public class QBEGenerator
foreach (var parameter in structFuncCall.Parameters) foreach (var parameter in structFuncCall.Parameters)
{ {
var copy = EmitCreateCopy(parameter); var copy = EmitCopy(parameter);
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
} }
@@ -1148,7 +1169,7 @@ public class QBEGenerator
foreach (var parameter in interfaceFuncCall.Parameters) foreach (var parameter in interfaceFuncCall.Parameters)
{ {
var copy = EmitCreateCopy(parameter); var copy = EmitCopy(parameter);
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); 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); var implementation = EmitExpression(convertToInterface.Implementation);
@@ -1180,11 +1201,8 @@ public class QBEGenerator
vtableOffset += interfaceImplementation.Functions.Count * 8; vtableOffset += interfaceImplementation.Functions.Count * 8;
} }
if (destination == null) var destination = TmpName();
{
destination = TmpName();
_writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}"); _writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}");
}
var interfaceVtablePointer = TmpName(); var interfaceVtablePointer = TmpName();
_writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Name)}, {vtableOffset}"); _writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Name)}, {vtableOffset}");
@@ -1258,7 +1276,7 @@ public class QBEGenerator
foreach (var parameter in funcCall.Parameters) foreach (var parameter in funcCall.Parameters)
{ {
var copy = EmitCreateCopy(parameter); var copy = EmitCopy(parameter);
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}"); parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
} }