...
This commit is contained in:
9
README.md
Normal file
9
README.md
Normal file
@@ -0,0 +1,9 @@
|
||||
main.nm
|
||||
missing:
|
||||
- std::print
|
||||
functions:
|
||||
- main
|
||||
|
||||
std.nm
|
||||
functions:
|
||||
- print
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
// }
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
Binary file not shown.
Binary file not shown.
@@ -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}");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user