This commit is contained in:
nub31
2025-09-12 21:55:39 +02:00
parent 1eeeb67d88
commit 4c4d655f43
6 changed files with 246 additions and 191 deletions

View File

@@ -43,42 +43,97 @@ public class QBEGenerator
_stringLiteralIndex = 0;
_codeIsReachable = true;
_writer.Comment("========== Builtin functions ==========");
_writer.WriteLine("""
function l $.cstring_len(l %str) {
@start
%count =l copy 0
@loop
%address =l add %str, %count
%value =w loadub %address
jnz %value, @continue, @end
@continue
%count =l add %count, 1
jmp @loop
@end
ret %count
}
function $.memcpy(l %source, l %destination, l %length) {
@start
%count =l copy 0
@loop
%condition =w cultl %count, %length
jnz %condition, @continue, @end
@continue
%source_address =l add %source, %count
%destination_address =l add %destination, %count
%value =w loadub %source_address
storeb %value, %destination_address
%count =l add %count, 1
jmp @loop
@end
ret
}
function $.memset(l %destination, l %value, l %length) {
@start
%count =l copy 0
@loop
%condition =w cultl %count, %length
jnz %condition, @continue, @end
@continue
%destination_address =l add %destination, %count
storeb %value, %destination_address
%count =l add %count, 1
jmp @loop
@end
ret
}
function l $.array_size(l %array) {
@start
%size =l loadl %array
ret %size
}
""");
_writer.Comment("========== Referenced structs ==========");
foreach (var structType in _structTypes)
{
EmitStructType(structType);
}
_writer.NewLine();
_writer.Comment("========== Struct definitions ==========");
foreach (var structDef in _definitions.OfType<StructNode>())
{
EmitStructDefinition(structDef);
}
_writer.NewLine();
_writer.Comment("========== Function definitions ==========");
foreach (var funcDef in _definitions.OfType<FuncNode>())
{
EmitFuncDefinition(funcDef);
}
// foreach (var structDef in _definitions.OfType<StructNode>().Where(x => x.InterfaceImplementations.Count > 0))
// {
// _writer.Write($"data {StructVtableName(_module.Name, structDef.Name)} = {{ ");
//
// foreach (var interfaceImplementation in structDef.InterfaceImplementations)
// {
// var interfaceDef = _definitionTable.LookupInterface(interfaceImplementation.Name);
// foreach (var func in interfaceDef.Functions)
// {
// _writer.Write($"l {StructFuncName(_module.Name, structDef.Name, func.Name)}, ");
// }
// }
//
// _writer.WriteLine("}");
// }
_writer.NewLine();
_writer.Comment("========== cstring literals ==========");
foreach (var cStringLiteral in _cStringLiterals)
{
_writer.WriteLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}");
}
_writer.NewLine();
_writer.Comment("========== string literals ==========");
foreach (var stringLiteral in _stringLiterals)
{
var bytes = Encoding.UTF8.GetBytes(stringLiteral.Value).Select(b => $"b {b}");
@@ -163,67 +218,18 @@ public class QBEGenerator
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);
_writer.Indented($"call $.memset(l {destination}, l {value}, l {length})");
}
private void EmitMemcpy(string source, string destination, 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 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);
_writer.Indented($"call $.memcpy(l {source}, l {destination}, l {length})");
}
private string EmitArraySizeInBytes(ArrayTypeNode type, string array)
{
var size = TmpName();
_writer.Indented($"{size} =l loadl {array}");
_writer.Indented($"{size} =l call $.array_size(l {array})");
_writer.Indented($"{size} =l mul {size}, {SizeOf(type.ElementType)}");
_writer.Indented($"{size} =l add {size}, 8");
return size;
@@ -231,27 +237,10 @@ public class QBEGenerator
private string EmitCStringSizeInBytes(string cstring)
{
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;
var result = TmpName();
_writer.Indented($"{result} =l call $.cstring_len(l {cstring})");
_writer.Indented($"{result} =l add {result}, 1");
return result;
}
private string EmitStringSizeInBytes(string nubstring)
@@ -367,82 +356,6 @@ public class QBEGenerator
return "l";
}
private void EmitFuncDefinition(FuncNode funcDef)
{
if (funcDef.Body == null) return;
_labelIndex = 0;
_tmpIndex = 0;
_writer.Write(funcDef.ExternSymbol != null ? "export function " : "function ");
if (funcDef.Signature.ReturnType is not VoidTypeNode)
{
_writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' ');
}
_writer.Write(FuncName(funcDef.Module, funcDef.Name, funcDef.ExternSymbol));
_writer.Write("(");
foreach (var parameter in funcDef.Signature.Parameters)
{
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}");
}
_writer.WriteLine(") {");
_writer.WriteLine("@start");
EmitBlock(funcDef.Body);
// Implicit return for void functions if no explicit return has been set
if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode)
{
_writer.Indented("ret");
}
_writer.WriteLine("}");
}
private void EmitStructDefinition(StructNode structDef)
{
// todo(nub31): Find a way do run the initializers from other modules where the definition is not available.
// A constructor is probably the answer
foreach (var function in structDef.Functions)
{
_labelIndex = 0;
_tmpIndex = 0;
_writer.Write("export function ");
if (function.Signature.ReturnType is not VoidTypeNode)
{
_writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' ');
}
_writer.Write(StructFuncName(structDef.Module, structDef.Name, function.Name));
_writer.Write("(l %this, ");
foreach (var parameter in function.Signature.Parameters)
{
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}, ");
}
_writer.WriteLine(") {");
_writer.WriteLine("@start");
EmitBlock(function.Body);
// Implicit return for void functions if no explicit return has been set
if (function.Signature.ReturnType is VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode)
{
_writer.Indented("ret");
}
_writer.WriteLine("}");
}
}
private void EmitStructType(StructTypeNode structType)
{
_writer.Write($"type {StructTypeName(structType.Module, structType.Name)} = {{ ");
@@ -480,6 +393,99 @@ public class QBEGenerator
}
}
private void EmitStructDefinition(StructNode structDef)
{
_writer.Comment($" ===== {structDef.Module}::{structDef.Name} =====");
_writer.WriteLine($"export function {StructCtorName(structDef.Module, structDef.Name)}(l %struct) {{");
_writer.WriteLine("@start");
foreach (var field in structDef.Fields)
{
if (field.Value.TryGetValue(out var value))
{
var offset = OffsetOf(structDef.StructType, field.Name);
var destination = TmpName();
_writer.Indented($"{destination} =l add %struct, {offset}");
EmitCopyInto(value, destination);
}
}
_writer.Indented("ret");
_writer.WriteLine("}");
foreach (var function in structDef.Functions)
{
_writer.Comment($" ===== {structDef.Module}::{structDef.Name}.{function.Name} =====");
_labelIndex = 0;
_tmpIndex = 0;
_writer.NewLine();
_writer.Write("export function ");
if (function.Signature.ReturnType is not VoidTypeNode)
{
_writer.Write(FuncQBETypeName(function.Signature.ReturnType) + ' ');
}
_writer.Write(StructFuncName(structDef.Module, structDef.Name, function.Name));
_writer.Write("(l %this, ");
foreach (var parameter in function.Signature.Parameters)
{
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}, ");
}
_writer.WriteLine(") {");
_writer.WriteLine("@start");
EmitBlock(function.Body);
// Implicit return for void functions if no explicit return has been set
if (function.Signature.ReturnType is VoidTypeNode && function.Body.Statements.LastOrDefault() is not ReturnNode)
{
_writer.Indented("ret");
}
_writer.WriteLine("}");
}
}
private void EmitFuncDefinition(FuncNode funcDef)
{
if (funcDef.Body == null) return;
_labelIndex = 0;
_tmpIndex = 0;
_writer.Write(funcDef.ExternSymbol != null ? "export function " : "function ");
if (funcDef.Signature.ReturnType is not VoidTypeNode)
{
_writer.Write(FuncQBETypeName(funcDef.Signature.ReturnType) + ' ');
}
_writer.Write(FuncName(funcDef.Module, funcDef.Name, funcDef.ExternSymbol));
_writer.Write("(");
foreach (var parameter in funcDef.Signature.Parameters)
{
_writer.Write(FuncQBETypeName(parameter.Type) + $" %{parameter.Name}");
}
_writer.WriteLine(") {");
_writer.WriteLine("@start");
EmitBlock(funcDef.Body);
// Implicit return for void functions if no explicit return has been set
if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode)
{
_writer.Indented("ret");
}
_writer.WriteLine("}");
}
private void EmitBlock(BlockNode block)
{
foreach (var statement in block.Statements)
@@ -1011,6 +1017,7 @@ public class QBEGenerator
var destination = TmpName();
var size = SizeOf(structInitializer.StructType);
_writer.Indented($"{destination} =l alloc8 {size}");
_writer.Indented($"call {StructCtorName(structInitializer.StructType.Module, structInitializer.StructType.Name)}(l {destination})");
foreach (var (field, value) in structInitializer.Initializers)
{
@@ -1361,27 +1368,27 @@ public class QBEGenerator
private string TmpName()
{
return $"%t.{++_tmpIndex}";
return $"%.t{++_tmpIndex}";
}
private string LabelName()
{
return $"@l.{++_labelIndex}";
return $"@.l{++_labelIndex}";
}
private string CStringName()
{
return $"$cstr.{++_cStringLiteralIndex}";
return $"$.cstr{++_cStringLiteralIndex}";
}
private string StringName()
{
return $"$str.{++_stringLiteralIndex}";
return $"$.str{++_stringLiteralIndex}";
}
private static string FuncName(string module, string name, string? externSymbol)
{
return $"${externSymbol ?? $"{module}.{name}"}";
return $"${externSymbol ?? $".{module}.{name}"}";
}
private static string StructTypeName(string module, string name)
@@ -1389,9 +1396,14 @@ public class QBEGenerator
return $":{module}.{name}";
}
private static string StructCtorName(string module, string name)
{
return $"$.{module}.{name}.ctor";
}
private static string StructFuncName(string module, string structName, string funcName)
{
return $"${module}.{structName}.func.{funcName}";
return $"$.{module}.{structName}.func.{funcName}";
}
private static string StructVtableName(string module, string structName)

View File

@@ -12,8 +12,8 @@ public record StructFieldNode(string Name, TypeNode Type, Optional<ExpressionNod
public record StructFuncNode(string Name, FuncSignatureNode Signature, BlockNode Body) : Node;
public record StructNode(string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions, List<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode(Module, Name);
public record StructNode(StructTypeNode StructType, string Module, string Name, List<StructFieldNode> Fields, List<StructFuncNode> Functions, List<InterfaceTypeNode> InterfaceImplementations) : DefinitionNode(Module, Name);
public record InterfaceFuncNode(string Name, FuncSignatureNode Signature) : Node;
public record InterfaceNode(string Module, string Name, List<InterfaceFuncNode> Functions) : DefinitionNode(Module, Name);
public record InterfaceNode(InterfaceTypeNode InterfaceType, string Module, string Name, List<InterfaceFuncNode> Functions) : DefinitionNode(Module, Name);

View File

@@ -200,11 +200,10 @@ public class StructTypeNode(string module, string name, List<StructTypeField> fi
public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name);
}
public class InterfaceTypeFunc(string name, FuncTypeNode type, int index)
public class InterfaceTypeFunc(string name, FuncTypeNode type)
{
public string Name { get; } = name;
public FuncTypeNode Type { get; } = type;
public int Index { get; } = index;
}
public class InterfaceTypeNode(string module, string name, List<InterfaceTypeFunc> functions) : ComplexTypeNode

View File

@@ -74,7 +74,17 @@ public sealed class TypeChecker
.Select(function => new InterfaceFuncNode(function.Name, CheckFuncSignature(function.Signature)))
.ToList();
return new InterfaceNode(_syntaxTree.Metadata.ModuleName, node.Name, functions);
var functionTypes = new List<InterfaceTypeFunc>();
foreach (var function in node.Functions)
{
var parameters = function.Signature.Parameters.Select(x => ResolveType(x.Type)).ToList();
var funcType = new FuncTypeNode(parameters, ResolveType(function.Signature.ReturnType));
functionTypes.Add(new InterfaceTypeFunc(function.Name, funcType));
}
var type = new InterfaceTypeNode(_syntaxTree.Metadata.ModuleName, node.Name, functionTypes);
return new InterfaceNode(type, _syntaxTree.Metadata.ModuleName, node.Name, functions);
}
private StructNode CheckStructDefinition(StructSyntax node)
@@ -85,7 +95,7 @@ public sealed class TypeChecker
var value = Optional.Empty<ExpressionNode>();
if (field.Value.HasValue)
{
value = CheckExpression(field.Value.Value);
value = CheckExpression(field.Value.Value, ResolveType(field.Type));
}
fields.Add(new StructFieldNode(field.Name, ResolveType(field.Type), value));
@@ -110,8 +120,8 @@ public sealed class TypeChecker
var interfaceImplementations = new List<InterfaceTypeNode>();
foreach (var interfaceImplementation in node.InterfaceImplementations)
{
var type = ResolveType(interfaceImplementation);
if (type is not InterfaceTypeNode interfaceType)
var resolvedType = ResolveType(interfaceImplementation);
if (resolvedType is not InterfaceTypeNode interfaceType)
{
_diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build());
continue;
@@ -120,7 +130,34 @@ public sealed class TypeChecker
interfaceImplementations.Add(interfaceType);
}
return new StructNode(_syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations);
var fieldTypes = node.Fields
.Select(x => new StructTypeField(x.Name, ResolveType(x.Type), x.Value.HasValue))
.ToList();
var functionTypes = new List<StructTypeFunc>();
foreach (var function in node.Functions)
{
var parameters = function.Signature.Parameters.Select(x => ResolveType(x.Type)).ToList();
var funcType = new FuncTypeNode(parameters, ResolveType(function.Signature.ReturnType));
functionTypes.Add(new StructTypeFunc(function.Name, funcType));
}
var interfaceImplementationTypes = new List<InterfaceTypeNode>();
foreach (var interfaceImplementation in node.InterfaceImplementations)
{
var resolvedType = ResolveType(interfaceImplementation);
if (resolvedType is not InterfaceTypeNode interfaceType)
{
_diagnostics.Add(Diagnostic.Error($"Struct {node.Name} cannot implement non-struct type {interfaceImplementation}").At(interfaceImplementation).Build());
continue;
}
interfaceImplementationTypes.Add(interfaceType);
}
var type = new StructTypeNode(_syntaxTree.Metadata.ModuleName, node.Name, fieldTypes, functionTypes, interfaceImplementationTypes);
return new StructNode(type, _syntaxTree.Metadata.ModuleName, node.Name, fields, functions, interfaceImplementations);
}
private FuncNode CheckFuncDefinition(FuncSyntax node)
@@ -466,7 +503,18 @@ public sealed class TypeChecker
foreach (var initializer in expression.Initializers)
{
initializers.Add(initializer.Key, CheckExpression(initializer.Value));
var typeField = structType.Fields.FirstOrDefault(x => x.Name == initializer.Key);
if (typeField == null)
{
_diagnostics.AddRange(Diagnostic
.Error($"Struct {structType.Name} does not have a field named {initializer.Key}")
.At(initializer.Value)
.Build());
continue;
}
initializers.Add(initializer.Key, CheckExpression(initializer.Value, typeField.Type));
}
var missingFields = structType.Fields