diff --git a/example/src/main.nub b/example/src/main.nub index fb9c2e3..85d54d5 100644 --- a/example/src/main.nub +++ b/example/src/main.nub @@ -6,20 +6,16 @@ struct Human age: i64 } -func test() { - -} - export func main(args: []cstring): i64 { - let x: []cstring + let x: Human - x = [2]cstring + x = alloc Human { + name = "John" + age = 32 + } - x[0] = "test1" - x[1] = "test2" - - c::puts(x[1]) + c::puts(x.name) return 0 } diff --git a/src/compiler/Generation/QBE/QBEGenerator.cs b/src/compiler/Generation/QBE/QBEGenerator.cs index 2ffd4a3..793c46e 100644 --- a/src/compiler/Generation/QBE/QBEGenerator.cs +++ b/src/compiler/Generation/QBE/QBEGenerator.cs @@ -118,9 +118,9 @@ public static class QBEGenerator return $":{structDef.Namespace}_{structDef.Name}"; } - private static string QBEStore(NubType type) + private static void EmitStore(NubType type, string value, string destination) { - return type switch + var store = type switch { NubArrayType => "storel", NubPointerType => "storel", @@ -145,11 +145,14 @@ public static class QBEGenerator NubStringType => "storel", _ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions") }; + + _builder.AppendLine($" {store} {value}, {destination}"); } - private static string QBELoad(NubType type) + private static Val EmitLoad(NubType type, string from) { - return type switch + var into = VarName(); + var load = type switch { NubArrayType => "loadl", NubPointerType => "loadl", @@ -174,6 +177,92 @@ public static class QBEGenerator NubStringType => "loadl", _ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions") }; + + _builder.AppendLine($" {into} {QBEAssign(type)} {load} {from}"); + + return new Val(into, type, ValKind.Direct); + } + + private static void EmitMemcpy(string source, string destination, string length) + { + _builder.AppendLine($" call $nub_memcpy(l {source}, l {destination}, l {length})"); + } + + private static string EmitArraySizeInBytes(NubArrayType type, string array) + { + var size = VarName(); + _builder.AppendLine($" {size} =l loadl {array}"); + _builder.AppendLine($" {size} =l mul {size}, {SizeOf(type.ElementType)}"); + _builder.AppendLine($" {size} =l add {size}, 8"); + return size; + } + + private static string EmitCStringSizeInBytes(string cstring) + { + var size = VarName(); + _builder.AppendLine($" {size} =l call $nub_cstring_length(l {cstring})"); + _builder.AppendLine($" {size} =l add {size}, 1"); + return size; + } + + private static string EmitStringSizeInBytes(string nubstring) + { + var size = VarName(); + _builder.AppendLine($" {size} =l loadl {nubstring}"); + _builder.AppendLine($" {size} =l add {size}, 8"); + return size; + } + + private static void EmitCopyInto(NubType type, string source, string destination) + { + switch (type) + { + case NubArrayType nubArrayType: + { + var size = EmitArraySizeInBytes(nubArrayType, source); + var buffer = VarName(); + _builder.AppendLine($" {buffer} =l alloc8 {size}"); + EmitMemcpy(source, buffer, size); + EmitStore(type, buffer, destination); + break; + } + case NubCStringType: + { + var size = EmitCStringSizeInBytes(source); + var buffer = VarName(); + _builder.AppendLine($" {buffer} =l alloc8 {size}"); + EmitMemcpy(source, buffer, size); + EmitStore(type, buffer, destination); + break; + } + case NubStringType: + { + var size = EmitStringSizeInBytes(source); + var buffer = VarName(); + _builder.AppendLine($" {buffer} =l alloc8 {size}"); + EmitMemcpy(source, buffer, size); + EmitStore(type, buffer, destination); + break; + } + case NubStructType nubStructType: + { + var size = SizeOf(nubStructType); + EmitMemcpy(source, destination, size.ToString()); + break; + } + case NubPointerType: + case NubPrimitiveType: + { + EmitStore(type, source, destination); + break; + } + case NubVoidType: + case NubFuncType: + case NubAnyType: + throw new NotSupportedException($"Cannot copy type '{type}'"); + default: + throw new ArgumentOutOfRangeException(nameof(type)); + } } private static string QBEAssign(NubType type) @@ -421,7 +510,7 @@ public static class QBEGenerator } } - parameterVars.Add(new Variable(parameter.Name, new Val(parameterName, parameter.Type, ValKind.Value))); + parameterVars.Add(new Variable(parameter.Name, new Val(parameterName, parameter.Type, ValKind.Direct))); } EmitBlock(body, parameterVars); @@ -508,10 +597,11 @@ public static class QBEGenerator private static void EmitAssignment(BoundAssignmentNode assignment) { - var expression = EmitExpression(assignment.Expression); - Debug.Assert(expression.Kind == ValKind.LValue); - var value = EmitUnwrap(EmitExpression(assignment.Value)); - EmitCopy(assignment.Value.Type, value, expression.Name); + var destination = EmitExpression(assignment.Expression); + Debug.Assert(destination.Kind == ValKind.Pointer); + + var source = EmitUnwrap(EmitExpression(assignment.Value)); + EmitCopyInto(assignment.Value.Type, source, destination.Name); } private static void EmitBlock(BoundBlockNode block, List? variables = null) @@ -592,7 +682,7 @@ public static class QBEGenerator { var tmp = VarName(); _builder.AppendLine($" {tmp} =l alloc8 {SizeOf(variableDeclaration.Type)}"); - _variables.Push(new Variable(variableDeclaration.Name, new Val(tmp, variableDeclaration.Type, ValKind.LValue))); + _variables.Push(new Variable(variableDeclaration.Name, new Val(tmp, variableDeclaration.Type, ValKind.Pointer))); } private static void EmitWhile(BoundWhileNode whileStatement) @@ -640,7 +730,7 @@ public static class QBEGenerator { var name = $"$anon_func{++_anonymousFuncIndex}"; _anonymousFunctions.Enqueue((anonymousFunc, name)); - return new Val(name, anonymousFunc.Type, ValKind.Func); + return new Val(name, anonymousFunc.Type, ValKind.Direct); } private static Val EmitArrayIndexAccess(BoundArrayIndexAccessNode arrayIndexAccess) @@ -654,7 +744,7 @@ public static class QBEGenerator _builder.AppendLine($" {pointer} =l mul {index}, {SizeOf(elementType)}"); _builder.AppendLine($" {pointer} =l add {pointer}, 8"); _builder.AppendLine($" {pointer} =l add {array}, {pointer}"); - return new Val(pointer, arrayIndexAccess.Type, ValKind.LValue); + return new Val(pointer, arrayIndexAccess.Type, ValKind.Pointer); } private static void EmitArrayBoundsCheck(string array, string index) @@ -699,22 +789,18 @@ public static class QBEGenerator _builder.AppendLine($" {dataPointer} =l add {arrayPointer}, 8"); _builder.AppendLine($" call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); - return new Val(arrayPointer, arrayInitializer.Type, ValKind.Value); + return new Val(arrayPointer, arrayInitializer.Type, ValKind.Direct); } private static Val EmitDereference(BoundDereferenceNode dereference) { var result = EmitUnwrap(EmitExpression(dereference.Expression)); - var outputName = VarName(); - _builder.AppendLine($" {outputName} {QBEAssign(dereference.Type)} {QBELoad(dereference.Type)} {result}"); - return new Val(outputName, dereference.Type, ValKind.Value); + return EmitLoad(dereference.Type, result); } private static Val EmitAddressOf(BoundAddressOfNode addressOf) { - var value = EmitExpression(addressOf.Expression); - Debug.Assert(value.Kind == ValKind.LValue); - return value; + throw new NotImplementedException(); } private static Val EmitBinaryExpression(BoundBinaryExpressionNode binaryExpression) @@ -722,7 +808,7 @@ public static class QBEGenerator var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); var outputName = VarName(); - var output = new Val(outputName, binaryExpression.Type, ValKind.Value); + var output = new Val(outputName, binaryExpression.Type, ValKind.Direct); switch (binaryExpression.Operator) { @@ -935,7 +1021,7 @@ public static class QBEGenerator { if (_definitionTable.LookupFunc(identifier.Namespace.Or(_syntaxTree.Namespace), identifier.Name).TryGetValue(out var func)) { - return new Val(FuncName(func), identifier.Type, ValKind.Func); + return new Val(FuncName(func), identifier.Type, ValKind.Direct); } if (!identifier.Namespace.HasValue) @@ -956,19 +1042,19 @@ public static class QBEGenerator { var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture); var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); } if (literal.Type.IsFloat64) { var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture); var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); } if (literal.Type.IsInteger) { - return new Val(literal.Literal, literal.Type, ValKind.Value); + return new Val(literal.Literal, literal.Type, ValKind.Direct); } break; @@ -977,21 +1063,21 @@ public static class QBEGenerator { if (literal.Type.IsInteger) { - return new Val(literal.Literal.Split(".").First(), literal.Type, ValKind.Value); + return new Val(literal.Literal.Split(".").First(), literal.Type, ValKind.Direct); } if (literal.Type.IsFloat32) { var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture); var bits = BitConverter.SingleToInt32Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); } if (literal.Type.IsFloat64) { var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture); var bits = BitConverter.DoubleToInt64Bits(value); - return new Val(bits.ToString(), literal.Type, ValKind.Value); + return new Val(bits.ToString(), literal.Type, ValKind.Direct); } break; @@ -1002,14 +1088,14 @@ public static class QBEGenerator { var stringLiteral = new StringLiteral(literal.Literal, StringName()); _stringLiterals.Add(stringLiteral); - return new Val(stringLiteral.Name, literal.Type, ValKind.Value); + return new Val(stringLiteral.Name, literal.Type, ValKind.Direct); } if (literal.Type.IsCString) { var cStringLiteral = new CStringLiteral(literal.Literal, CStringName()); _cStringLiterals.Add(cStringLiteral); - return new Val(cStringLiteral.Name, literal.Type, ValKind.Value); + return new Val(cStringLiteral.Name, literal.Type, ValKind.Direct); } break; @@ -1018,7 +1104,7 @@ public static class QBEGenerator { if (literal.Type.IsBool) { - return new Val(bool.Parse(literal.Literal) ? "1" : "0", literal.Type, ValKind.Value); + return new Val(bool.Parse(literal.Literal) ? "1" : "0", literal.Type, ValKind.Direct); } break; @@ -1040,27 +1126,20 @@ public static class QBEGenerator { var offset = OffsetOf(structDefinition, field.Name); - if (structInitializer.Initializers.TryGetValue(field.Name, out var fieldValue)) + if (!structInitializer.Initializers.TryGetValue(field.Name, out var valueExpression)) { - var value = EmitUnwrap(EmitExpression(fieldValue)); - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l add {output}, {offset}"); - EmitCopy(field.Type, value, pointer); - } - else if (field.Value.HasValue) - { - var value = EmitUnwrap(EmitExpression(field.Value.Value)); - var pointer = VarName(); - _builder.AppendLine($" {pointer} =l add {output}, {offset}"); - EmitCopy(field.Type, value, pointer); - } - else - { - Debug.Assert(false); + valueExpression = field.Value.Value; } + + Debug.Assert(valueExpression != null); + + var destination = VarName(); + _builder.AppendLine($" {destination} =l add {output}, {offset}"); + var source = EmitUnwrap(EmitExpression(valueExpression)); + EmitCopyInto(valueExpression.Type, source, destination); } - return new Val(output, structInitializer.StructType, ValKind.Value); + return new Val(output, structInitializer.StructType, ValKind.Direct); } private static Val EmitUnaryExpression(BoundUnaryExpressionNode unaryExpression) @@ -1076,16 +1155,16 @@ public static class QBEGenerator { case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: _builder.AppendLine($" {outputName} =l neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Value); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }: _builder.AppendLine($" {outputName} =w neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Value); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: _builder.AppendLine($" {outputName} =d neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Value); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: _builder.AppendLine($" {outputName} =s neg {operand}"); - return new Val(outputName, unaryExpression.Type, ValKind.Value); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); } break; @@ -1096,7 +1175,7 @@ public static class QBEGenerator { case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }: _builder.AppendLine($" {outputName} =w xor {operand}, 1"); - return new Val(outputName, unaryExpression.Type, ValKind.Value); + return new Val(outputName, unaryExpression.Type, ValKind.Direct); } break; @@ -1122,7 +1201,7 @@ public static class QBEGenerator if (memberAccess.Member == "count") { _builder.AppendLine($" {output} =l loadl {item}"); - return new Val(output, memberAccess.Type, ValKind.Value); + return new Val(output, memberAccess.Type, ValKind.Direct); } break; @@ -1132,8 +1211,9 @@ public static class QBEGenerator if (memberAccess.Member == "count") { _builder.AppendLine($" {output} =l call $nub_string_length(l {item})"); - return new Val(output, memberAccess.Type, ValKind.Value); + return new Val(output, memberAccess.Type, ValKind.Direct); } + break; } case NubCStringType: @@ -1141,8 +1221,9 @@ public static class QBEGenerator if (memberAccess.Member == "count") { _builder.AppendLine($" {output} =l call $nub_cstring_length(l {item})"); - return new Val(output, memberAccess.Type, ValKind.Value); + return new Val(output, memberAccess.Type, ValKind.Direct); } + break; } case NubStructType structType: @@ -1151,7 +1232,7 @@ public static class QBEGenerator var offset = OffsetOf(structDefinition, memberAccess.Member); _builder.AppendLine($" {output} =l add {item}, {offset}"); - return new Val(output, memberAccess.Type, ValKind.LValue); + return new Val(output, memberAccess.Type, ValKind.Pointer); } } @@ -1164,9 +1245,8 @@ public static class QBEGenerator var parameterStrings = new List(); - for (var i = 0; i < funcCall.Parameters.Count; i++) + foreach (var parameter in funcCall.Parameters) { - var parameter = funcCall.Parameters[i]; var result = EmitUnwrap(EmitExpression(parameter)); var qbeType = parameter.Type switch @@ -1202,25 +1282,14 @@ public static class QBEGenerator if (funcType.ReturnType is not NubVoidType) { var outputName = VarName(); + _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(outputName, funcCall.Type, ValKind.Value); + return new Val(outputName, funcCall.Type, ValKind.Direct); } else { _builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})"); - return new Val(string.Empty, funcCall.Type, ValKind.Value); - } - } - - private static void EmitCopy(NubType type, string sourcePtr, string destinationPtr) - { - if (SizeOf(type) > 8) - { - _builder.AppendLine($" blit {sourcePtr}, {destinationPtr}, {SizeOf(type)}"); - } - else - { - _builder.AppendLine($" {QBEStore(type)} {sourcePtr}, {destinationPtr}"); + return new Val(string.Empty, funcCall.Type, ValKind.Direct); } } @@ -1233,21 +1302,18 @@ public static class QBEGenerator switch (val.Kind) { - case ValKind.Func: - case ValKind.Value: + case ValKind.Direct: { return val.Name; } - case ValKind.LValue: + case ValKind.Pointer: { if (val.Type is NubStructType) { return val.Name; } - var result = VarName(); - _builder.AppendLine($" {result} {QBEAssign(val.Type)} {QBELoad(val.Type)} {val.Name}"); - return result; + return EmitLoad(val.Type, val.Name).Name; } default: { @@ -1289,7 +1355,6 @@ internal class Val(string name, NubType type, ValKind kind) internal enum ValKind { - Func, - LValue, - Value + Pointer, + Direct, } \ No newline at end of file diff --git a/src/compiler/Syntax/Typing/NubType.cs b/src/compiler/Syntax/Typing/NubType.cs index 7b54de9..48a5e9c 100644 --- a/src/compiler/Syntax/Typing/NubType.cs +++ b/src/compiler/Syntax/Typing/NubType.cs @@ -4,6 +4,8 @@ namespace Syntax.Typing; public abstract class NubType { + public abstract bool ValueIsPointer { get; } + public bool IsInteger => this is NubPrimitiveType { Kind: PrimitiveTypeKind.I8 @@ -46,6 +48,8 @@ public abstract class NubType public class NubCStringType : NubType { + public override bool ValueIsPointer => true; + public override bool Equals(object? obj) { return obj is NubCStringType; @@ -64,6 +68,8 @@ public class NubCStringType : NubType public class NubStringType : NubType { + public override bool ValueIsPointer => true; + public override bool Equals(object? obj) { return obj is NubStringType; @@ -82,6 +88,8 @@ public class NubStringType : NubType public class NubFuncType(NubType returnType, List parameters) : NubType { + public override bool ValueIsPointer => false; + public NubType ReturnType { get; } = returnType; public List Parameters { get; } = parameters; @@ -103,6 +111,8 @@ public class NubFuncType(NubType returnType, List parameters) : NubType public class NubStructType(string @namespace, string name) : NubType { + public override bool ValueIsPointer => true; + public string Namespace { get; } = @namespace; public string Name { get; } = name; @@ -124,6 +134,8 @@ public class NubStructType(string @namespace, string name) : NubType public class NubPointerType(NubType baseType) : NubType { + public override bool ValueIsPointer => false; + public NubType BaseType { get; } = baseType; public override bool Equals(object? obj) @@ -144,6 +156,8 @@ public class NubPointerType(NubType baseType) : NubType public class NubArrayType(NubType elementType) : NubType { + public override bool ValueIsPointer => true; + public NubType ElementType { get; } = elementType; public override bool Equals(object? obj) @@ -168,6 +182,8 @@ public class NubArrayType(NubType elementType) : NubType public class NubAnyType : NubType { + public override bool ValueIsPointer => false; + public override string ToString() => "any"; public override bool Equals(object? obj) @@ -183,6 +199,8 @@ public class NubAnyType : NubType public class NubVoidType : NubType { + public override bool ValueIsPointer => false; + public override string ToString() => "void"; public override bool Equals(object? obj) @@ -198,6 +216,8 @@ public class NubVoidType : NubType public class NubPrimitiveType(PrimitiveTypeKind kind) : NubType { + public override bool ValueIsPointer => false; + public PrimitiveTypeKind Kind { get; } = kind; public static NubPrimitiveType I64 => new(PrimitiveTypeKind.I64);