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