This commit is contained in:
nub31
2025-07-02 23:25:23 +02:00
parent 2b331e432a
commit 291e882a4b
3 changed files with 172 additions and 91 deletions

View File

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

View File

@@ -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<Variable>? 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<string>();
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,
}

View File

@@ -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<NubType> parameters) : NubType
{
public override bool ValueIsPointer => false;
public NubType ReturnType { get; } = returnType;
public List<NubType> Parameters { get; } = parameters;
@@ -103,6 +111,8 @@ public class NubFuncType(NubType returnType, List<NubType> 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);