...
This commit is contained in:
@@ -8,6 +8,8 @@ namespace NubLang.Generation.QBE;
|
|||||||
|
|
||||||
public class QBEGenerator
|
public class QBEGenerator
|
||||||
{
|
{
|
||||||
|
private const int PTR_SIZE = 8;
|
||||||
|
|
||||||
private readonly QBEWriter _writer;
|
private readonly QBEWriter _writer;
|
||||||
private readonly List<DefinitionNode> _definitions;
|
private readonly List<DefinitionNode> _definitions;
|
||||||
private readonly HashSet<StructTypeNode> _structTypes;
|
private readonly HashSet<StructTypeNode> _structTypes;
|
||||||
@@ -20,7 +22,6 @@ public class QBEGenerator
|
|||||||
private int _labelIndex;
|
private int _labelIndex;
|
||||||
private int _cStringLiteralIndex;
|
private int _cStringLiteralIndex;
|
||||||
private int _stringLiteralIndex;
|
private int _stringLiteralIndex;
|
||||||
private bool _codeIsReachable = true;
|
|
||||||
|
|
||||||
public QBEGenerator(List<DefinitionNode> definitions, HashSet<StructTypeNode> structTypes)
|
public QBEGenerator(List<DefinitionNode> definitions, HashSet<StructTypeNode> structTypes)
|
||||||
{
|
{
|
||||||
@@ -39,7 +40,6 @@ public class QBEGenerator
|
|||||||
_labelIndex = 0;
|
_labelIndex = 0;
|
||||||
_cStringLiteralIndex = 0;
|
_cStringLiteralIndex = 0;
|
||||||
_stringLiteralIndex = 0;
|
_stringLiteralIndex = 0;
|
||||||
_codeIsReachable = true;
|
|
||||||
|
|
||||||
_writer.Comment("========== Builtin functions ==========");
|
_writer.Comment("========== Builtin functions ==========");
|
||||||
|
|
||||||
@@ -142,77 +142,125 @@ public class QBEGenerator
|
|||||||
|
|
||||||
private static string QBEAssign(TypeNode type)
|
private static string QBEAssign(TypeNode type)
|
||||||
{
|
{
|
||||||
if (type.IsSimpleType(out var simpleType, out _))
|
return type switch
|
||||||
{
|
{
|
||||||
return simpleType.StorageSize switch
|
IntTypeNode { Width: <= 32 } => "=w",
|
||||||
{
|
IntTypeNode { Width: 64 } => "=l",
|
||||||
StorageSize.I8 or StorageSize.U8 or StorageSize.I16 or StorageSize.U16 or StorageSize.I32 or StorageSize.U32 => "=w",
|
FloatTypeNode { Width: 32 } => "=s",
|
||||||
StorageSize.I64 or StorageSize.U64 => "=l",
|
FloatTypeNode { Width: 64 } => "=d",
|
||||||
StorageSize.F32 => "=s",
|
BoolTypeNode => "=w",
|
||||||
StorageSize.F64 => "=d",
|
PointerTypeNode => "=l",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize))
|
FuncTypeNode => "=l",
|
||||||
};
|
CStringTypeNode => "=l",
|
||||||
}
|
StringTypeNode => "=l",
|
||||||
|
ArrayTypeNode => "=l",
|
||||||
return "=l";
|
StructTypeNode => throw new InvalidOperationException("Structs are not loaded/stored directly"),
|
||||||
|
VoidTypeNode => throw new InvalidOperationException("Void has no assignment"),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}")
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmitStore(TypeNode type, string value, string destination)
|
private void EmitStore(TypeNode type, string value, string destination)
|
||||||
{
|
{
|
||||||
string store;
|
var store = type switch
|
||||||
|
|
||||||
if (type.IsSimpleType(out var simpleType, out _))
|
|
||||||
{
|
{
|
||||||
store = simpleType.StorageSize switch
|
BoolTypeNode => "storeb",
|
||||||
{
|
IntTypeNode { Width: 8 } => "storeb",
|
||||||
StorageSize.I8 or StorageSize.U8 => "storeb",
|
IntTypeNode { Width: 16 } => "storeh",
|
||||||
StorageSize.I16 or StorageSize.U16 => "storeh",
|
IntTypeNode { Width: 32 } => "storew",
|
||||||
StorageSize.I32 or StorageSize.U32 => "storew",
|
IntTypeNode { Width: 64 } => "storel",
|
||||||
StorageSize.I64 or StorageSize.U64 => "storel",
|
FloatTypeNode { Width: 32 } => "stores",
|
||||||
StorageSize.F32 => "stores",
|
FloatTypeNode { Width: 64 } => "stored",
|
||||||
StorageSize.F64 => "stored",
|
PointerTypeNode => "storel",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize))
|
FuncTypeNode => "storel",
|
||||||
};
|
CStringTypeNode => "storel",
|
||||||
}
|
StringTypeNode => "storel",
|
||||||
else
|
ArrayTypeNode => "storel",
|
||||||
{
|
StructTypeNode => throw new InvalidOperationException("Struct stores must use blit/memcpy"),
|
||||||
store = "storel";
|
VoidTypeNode => throw new InvalidOperationException("Cannot store void"),
|
||||||
}
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}")
|
||||||
|
};
|
||||||
|
|
||||||
_writer.Indented($"{store} {value}, {destination}");
|
_writer.Indented($"{store} {value}, {destination}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitLoad(TypeNode type, string from)
|
private string EmitLoad(TypeNode type, string from)
|
||||||
{
|
{
|
||||||
string load;
|
string load = type switch
|
||||||
|
|
||||||
if (type.IsSimpleType(out var simpleType, out _))
|
|
||||||
{
|
{
|
||||||
load = simpleType.StorageSize switch
|
BoolTypeNode => "loadub",
|
||||||
{
|
IntTypeNode { Signed: true, Width: 8 } => "loadsb",
|
||||||
StorageSize.I64 or StorageSize.U64 => "loadl",
|
IntTypeNode { Signed: true, Width: 16 } => "loadsh",
|
||||||
StorageSize.I32 or StorageSize.U32 => "loadw",
|
IntTypeNode { Signed: true, Width: 32 } => "loadw",
|
||||||
StorageSize.I16 => "loadsh",
|
IntTypeNode { Signed: true, Width: 64 } => "loadl",
|
||||||
StorageSize.I8 => "loadsb",
|
IntTypeNode { Signed: false, Width: 8 } => "loadsb",
|
||||||
StorageSize.U16 => "loaduh",
|
IntTypeNode { Signed: false, Width: 16 } => "loadsh",
|
||||||
StorageSize.U8 => "loadub",
|
IntTypeNode { Signed: false, Width: 32 } => "loadw",
|
||||||
StorageSize.F64 => "loadd",
|
IntTypeNode { Signed: false, Width: 64 } => "loadl",
|
||||||
StorageSize.F32 => "loads",
|
FloatTypeNode { Width: 32 } => "loads",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize))
|
FloatTypeNode { Width: 64 } => "loadd",
|
||||||
};
|
PointerTypeNode => "loadl",
|
||||||
}
|
FuncTypeNode => "loadl",
|
||||||
else
|
CStringTypeNode => "loadl",
|
||||||
{
|
StringTypeNode => "loadl",
|
||||||
load = "loadl";
|
ArrayTypeNode => "loadl",
|
||||||
}
|
StructTypeNode => throw new InvalidOperationException("Struct loads must use blit/memcpy"),
|
||||||
|
VoidTypeNode => throw new InvalidOperationException("Cannot load void"),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}")
|
||||||
|
};
|
||||||
|
|
||||||
var into = TmpName();
|
var into = TmpName();
|
||||||
|
|
||||||
_writer.Indented($"{into} {QBEAssign(type)} {load} {from}");
|
_writer.Indented($"{into} {QBEAssign(type)} {load} {from}");
|
||||||
|
|
||||||
return into;
|
return into;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static string StructDefQBEType(TypeNode type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
BoolTypeNode => "b",
|
||||||
|
IntTypeNode { Width: 8 } => "b",
|
||||||
|
IntTypeNode { Width: 16 } => "h",
|
||||||
|
IntTypeNode { Width: 32 } => "w",
|
||||||
|
IntTypeNode { Width: 64 } => "l",
|
||||||
|
FloatTypeNode { Width: 32 } => "s",
|
||||||
|
FloatTypeNode { Width: 64 } => "d",
|
||||||
|
PointerTypeNode => "l",
|
||||||
|
FuncTypeNode => "l",
|
||||||
|
CStringTypeNode => "l",
|
||||||
|
StringTypeNode => "l",
|
||||||
|
ArrayTypeNode => "l",
|
||||||
|
StructTypeNode st => StructTypeName(st.Module, st.Name),
|
||||||
|
VoidTypeNode => throw new InvalidOperationException("Void has no QBE type"),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FuncQBETypeName(TypeNode type)
|
||||||
|
{
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
BoolTypeNode => "ub",
|
||||||
|
IntTypeNode { Signed: true, Width: 8 } => "sb",
|
||||||
|
IntTypeNode { Signed: true, Width: 16 } => "sh",
|
||||||
|
IntTypeNode { Signed: false, Width: 8 } => "ub",
|
||||||
|
IntTypeNode { Signed: false, Width: 16 } => "uh",
|
||||||
|
IntTypeNode { Width: 32 } => "w",
|
||||||
|
IntTypeNode { Width: 64 } => "l",
|
||||||
|
FloatTypeNode { Width: 32 } => "s",
|
||||||
|
FloatTypeNode { Width: 64 } => "d",
|
||||||
|
PointerTypeNode => "l",
|
||||||
|
FuncTypeNode => "l",
|
||||||
|
CStringTypeNode => "l",
|
||||||
|
StringTypeNode => "l",
|
||||||
|
ArrayTypeNode => "l",
|
||||||
|
StructTypeNode st => StructTypeName(st.Module, st.Name),
|
||||||
|
VoidTypeNode => throw new InvalidOperationException("Void has no QBE type"),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void EmitMemset(string destination, int value, string length)
|
private void EmitMemset(string destination, int value, string length)
|
||||||
{
|
{
|
||||||
_writer.Indented($"call $.memset(l {destination}, l {value}, l {length})");
|
_writer.Indented($"call $.memset(l {destination}, l {value}, l {length})");
|
||||||
@@ -250,109 +298,100 @@ public class QBEGenerator
|
|||||||
|
|
||||||
private void EmitCopyInto(ExpressionNode source, string destination)
|
private void EmitCopyInto(ExpressionNode source, string destination)
|
||||||
{
|
{
|
||||||
// Simple types are passed in registers and can therefore just be stored
|
if (source.Type.IsScalar)
|
||||||
if (source.Type.IsSimpleType(out var simpleType, out var complexType))
|
|
||||||
{
|
{
|
||||||
var value = EmitExpression(source);
|
EmitStore(source.Type, EmitExpression(source), destination);
|
||||||
EmitStore(simpleType, value, destination);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Structs has known sizes at compile time
|
if (source.Type.IsValueType)
|
||||||
if (complexType is StructTypeNode)
|
|
||||||
{
|
{
|
||||||
var value = EmitExpression(source);
|
var value = EmitExpression(source);
|
||||||
_writer.Indented($"blit {value}, {destination}, {SizeOf(complexType)}");
|
_writer.Indented($"blit {value}, {destination}, {SizeOf(source.Type)}");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
// The rest of the complex types has unknown sizes
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var value = EmitExpression(source);
|
|
||||||
var size = complexType switch
|
|
||||||
{
|
|
||||||
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
|
|
||||||
CStringTypeNode => EmitCStringSizeInBytes(value),
|
|
||||||
StringTypeNode => EmitStringSizeInBytes(value),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
|
|
||||||
};
|
|
||||||
|
|
||||||
var buffer = TmpName();
|
switch (source.Type)
|
||||||
_writer.Indented($"{buffer} =l alloc8 {size}");
|
{
|
||||||
EmitMemcpy(value, buffer, size);
|
case ArrayTypeNode arrayType:
|
||||||
EmitStore(complexType, buffer, destination);
|
{
|
||||||
|
var value = EmitExpression(source);
|
||||||
|
var size = EmitArraySizeInBytes(arrayType, value);
|
||||||
|
var buffer = TmpName();
|
||||||
|
_writer.Indented($"{buffer} =l alloc8 {size}");
|
||||||
|
EmitMemcpy(value, buffer, size);
|
||||||
|
EmitStore(arrayType, buffer, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case CStringTypeNode:
|
||||||
|
{
|
||||||
|
var value = EmitExpression(source);
|
||||||
|
var size = EmitCStringSizeInBytes(value);
|
||||||
|
var buffer = TmpName();
|
||||||
|
_writer.Indented($"{buffer} =l alloc8 {size}");
|
||||||
|
EmitMemcpy(value, buffer, size);
|
||||||
|
EmitStore(source.Type, buffer, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case StringTypeNode:
|
||||||
|
{
|
||||||
|
var value = EmitExpression(source);
|
||||||
|
var size = EmitStringSizeInBytes(value);
|
||||||
|
var buffer = TmpName();
|
||||||
|
_writer.Indented($"{buffer} =l alloc8 {size}");
|
||||||
|
EmitMemcpy(value, buffer, size);
|
||||||
|
EmitStore(source.Type, buffer, destination);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case VoidTypeNode:
|
||||||
|
throw new InvalidOperationException("Cannot copy void");
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException(nameof(source.Type), $"Unknown type {source.Type}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitCopy(ExpressionNode source)
|
private string EmitCopy(ExpressionNode source)
|
||||||
{
|
{
|
||||||
// Allowlist for types which are safe to not copy
|
if (source is RValueExpressionNode || source.Type.IsScalar)
|
||||||
if (source is ArrayInitializerNode or StructInitializerNode or LiteralNode)
|
|
||||||
{
|
{
|
||||||
return EmitExpression(source);
|
return EmitExpression(source);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Simple types are passed in registers and therefore always copied
|
|
||||||
if (source.Type.IsSimpleType(out _, out var complexType))
|
|
||||||
{
|
|
||||||
return EmitExpression(source);
|
|
||||||
}
|
|
||||||
|
|
||||||
// For the rest, we figure out the size of the type and shallow copy them
|
|
||||||
var value = EmitExpression(source);
|
var value = EmitExpression(source);
|
||||||
var destination = TmpName();
|
var destination = TmpName();
|
||||||
|
|
||||||
// Structs has known sizes at compile time
|
if (source.Type.IsValueType)
|
||||||
if (complexType is StructTypeNode)
|
|
||||||
{
|
{
|
||||||
var size = SizeOf(complexType);
|
var size = SizeOf(source.Type);
|
||||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||||
_writer.Indented($"blit {value}, {destination}, {size}");
|
_writer.Indented($"blit {value}, {destination}, {size}");
|
||||||
|
return destination;
|
||||||
}
|
}
|
||||||
// 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}");
|
switch (source.Type)
|
||||||
EmitMemcpy(value, destination, size);
|
{
|
||||||
|
case ArrayTypeNode arrayType:
|
||||||
|
var arraySize = EmitArraySizeInBytes(arrayType, value);
|
||||||
|
_writer.Indented($"{destination} =l alloc8 {arraySize}");
|
||||||
|
EmitMemcpy(value, destination, arraySize);
|
||||||
|
break;
|
||||||
|
case CStringTypeNode:
|
||||||
|
var cstrSize = EmitCStringSizeInBytes(value);
|
||||||
|
_writer.Indented($"{destination} =l alloc8 {cstrSize}");
|
||||||
|
EmitMemcpy(value, destination, cstrSize);
|
||||||
|
break;
|
||||||
|
case StringTypeNode:
|
||||||
|
var strSize = EmitStringSizeInBytes(value);
|
||||||
|
_writer.Indented($"{destination} =l alloc8 {strSize}");
|
||||||
|
EmitMemcpy(value, destination, strSize);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new InvalidOperationException($"Cannot copy type {source.Type}");
|
||||||
}
|
}
|
||||||
|
|
||||||
return destination;
|
return destination;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility to create QBE type names for function parameters and return types
|
|
||||||
private string FuncQBETypeName(TypeNode type)
|
|
||||||
{
|
|
||||||
if (type.IsSimpleType(out var simpleType, out var complexType))
|
|
||||||
{
|
|
||||||
return simpleType.StorageSize switch
|
|
||||||
{
|
|
||||||
StorageSize.I64 or StorageSize.U64 => "l",
|
|
||||||
StorageSize.I32 or StorageSize.U32 => "w",
|
|
||||||
StorageSize.I16 => "sh",
|
|
||||||
StorageSize.I8 => "sb",
|
|
||||||
StorageSize.U16 => "uh",
|
|
||||||
StorageSize.U8 => "ub",
|
|
||||||
StorageSize.F64 => "d",
|
|
||||||
StorageSize.F32 => "s",
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complexType is StructTypeNode structType)
|
|
||||||
{
|
|
||||||
return StructTypeName(structType.Module, structType.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "l";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitStructType(StructTypeNode structType)
|
private void EmitStructType(StructTypeNode structType)
|
||||||
{
|
{
|
||||||
// todo(nub31): qbe expects structs to be declared in order. We must Check the dependencies of the struct to see if a type need to be declared before this one
|
// todo(nub31): qbe expects structs to be declared in order. We must Check the dependencies of the struct to see if a type need to be declared before this one
|
||||||
@@ -365,31 +404,6 @@ public class QBEGenerator
|
|||||||
}
|
}
|
||||||
|
|
||||||
_writer.WriteLine(" }");
|
_writer.WriteLine(" }");
|
||||||
return;
|
|
||||||
|
|
||||||
string StructDefQBEType(TypeNode type)
|
|
||||||
{
|
|
||||||
if (type.IsSimpleType(out var simpleType, out var complexType))
|
|
||||||
{
|
|
||||||
return simpleType.StorageSize switch
|
|
||||||
{
|
|
||||||
StorageSize.I64 or StorageSize.U64 => "l",
|
|
||||||
StorageSize.I32 or StorageSize.U32 => "w",
|
|
||||||
StorageSize.I16 or StorageSize.U16 => "h",
|
|
||||||
StorageSize.I8 or StorageSize.U8 => "b",
|
|
||||||
StorageSize.F64 => "d",
|
|
||||||
StorageSize.F32 => "s",
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (complexType is StructTypeNode childStructType)
|
|
||||||
{
|
|
||||||
return StructTypeName(childStructType.Module, childStructType.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
return "l";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmitStructDefinition(StructNode structDef)
|
private void EmitStructDefinition(StructNode structDef)
|
||||||
@@ -477,12 +491,6 @@ public class QBEGenerator
|
|||||||
|
|
||||||
EmitBlock(funcDef.Body);
|
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("}");
|
_writer.WriteLine("}");
|
||||||
_writer.NewLine();
|
_writer.NewLine();
|
||||||
}
|
}
|
||||||
@@ -491,13 +499,8 @@ public class QBEGenerator
|
|||||||
{
|
{
|
||||||
foreach (var statement in block.Statements)
|
foreach (var statement in block.Statements)
|
||||||
{
|
{
|
||||||
if (_codeIsReachable)
|
EmitStatement(statement);
|
||||||
{
|
|
||||||
EmitStatement(statement);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_codeIsReachable = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmitStatement(StatementNode statement)
|
private void EmitStatement(StatementNode statement)
|
||||||
@@ -505,13 +508,13 @@ public class QBEGenerator
|
|||||||
switch (statement)
|
switch (statement)
|
||||||
{
|
{
|
||||||
case AssignmentNode assignment:
|
case AssignmentNode assignment:
|
||||||
EmitAssignment(assignment);
|
EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target));
|
||||||
break;
|
break;
|
||||||
case BreakNode:
|
case BreakNode:
|
||||||
EmitBreak();
|
_writer.Indented($"jmp {_breakLabels.Peek()}");
|
||||||
break;
|
break;
|
||||||
case ContinueNode:
|
case ContinueNode:
|
||||||
EmitContinue();
|
_writer.Indented($"jmp {_continueLabels.Peek()}");
|
||||||
break;
|
break;
|
||||||
case IfNode ifStatement:
|
case IfNode ifStatement:
|
||||||
EmitIf(ifStatement);
|
EmitIf(ifStatement);
|
||||||
@@ -536,23 +539,6 @@ public class QBEGenerator
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EmitAssignment(AssignmentNode assignment)
|
|
||||||
{
|
|
||||||
EmitCopyInto(assignment.Value, EmitAddressOfLValue(assignment.Target));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitBreak()
|
|
||||||
{
|
|
||||||
_writer.Indented($"jmp {_breakLabels.Peek()}");
|
|
||||||
_codeIsReachable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitContinue()
|
|
||||||
{
|
|
||||||
_writer.Indented($"jmp {_continueLabels.Peek()}");
|
|
||||||
_codeIsReachable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void EmitIf(IfNode ifStatement)
|
private void EmitIf(IfNode ifStatement)
|
||||||
{
|
{
|
||||||
var trueLabel = LabelName();
|
var trueLabel = LabelName();
|
||||||
@@ -666,60 +652,47 @@ public class QBEGenerator
|
|||||||
_writer.WriteLine(endLabel);
|
_writer.WriteLine(endLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitExpression(ExpressionNode expression)
|
private string EmitExpression(ExpressionNode expr)
|
||||||
{
|
{
|
||||||
return expression switch
|
return expr switch
|
||||||
{
|
{
|
||||||
ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer),
|
RValueExpressionNode rValue => EmitRValue(rValue),
|
||||||
StructInitializerNode structInitializer => EmitStructInitializer(structInitializer),
|
LValueExpressionNode lValue => EmitLValue(lValue),
|
||||||
AddressOfNode addressOf => EmitAddressOf(addressOf),
|
_ => throw new ArgumentOutOfRangeException(nameof(expr))
|
||||||
DereferenceNode dereference => EmitDereference(dereference),
|
|
||||||
BinaryExpressionNode binary => EmitBinaryExpression(binary),
|
|
||||||
FuncCallNode funcCall => EmitFuncCall(funcCall),
|
|
||||||
ConvertIntNode convertInt => EmitConvertInt(convertInt),
|
|
||||||
ConvertFloatNode convertFloat => EmitConvertFloat(convertFloat),
|
|
||||||
VariableIdentifierNode identifier => EmitVariableIdentifier(identifier),
|
|
||||||
FuncIdentifierNode funcIdentifier => EmitFuncIdentifier(funcIdentifier),
|
|
||||||
FuncParameterIdentifierNode funcParameterIdentifier => EmitParameterFuncIdentifier(funcParameterIdentifier),
|
|
||||||
LiteralNode literal => EmitLiteral(literal),
|
|
||||||
UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression),
|
|
||||||
StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess),
|
|
||||||
StructFuncCallNode structFuncCall => EmitStructFuncCall(structFuncCall),
|
|
||||||
ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(expression))
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitFuncIdentifier(FuncIdentifierNode funcIdent)
|
private string EmitRValue(RValueExpressionNode rValue)
|
||||||
{
|
{
|
||||||
return FuncName(funcIdent.Module, funcIdent.Name, funcIdent.ExternSymbol);
|
return rValue switch
|
||||||
|
{
|
||||||
|
AddressOfNode expr => EmitAddressOf(expr.LValue),
|
||||||
|
ArrayInitializerNode expr => EmitArrayInitializer(expr),
|
||||||
|
BinaryExpressionNode expr => EmitBinaryExpression(expr),
|
||||||
|
ConvertFloatNode expr => EmitConvertFloat(expr),
|
||||||
|
ConvertIntNode expr => EmitConvertInt(expr),
|
||||||
|
DereferenceNode expr => EmitLoad(expr.Type, EmitExpression(expr.Expression)),
|
||||||
|
FuncCallNode expr => EmitFuncCall(expr),
|
||||||
|
FuncIdentifierNode expr => FuncName(expr.Module, expr.Name, expr.ExternSymbol),
|
||||||
|
FuncParameterIdentifierNode expr => $"%{expr.Name}",
|
||||||
|
LiteralNode expr => EmitLiteral(expr),
|
||||||
|
StructFuncCallNode expr => EmitStructFuncCall(expr),
|
||||||
|
StructInitializerNode expr => EmitStructInitializer(expr),
|
||||||
|
UnaryExpressionNode expr => EmitUnaryExpression(expr),
|
||||||
|
_ => throw new ArgumentOutOfRangeException(nameof(rValue))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitVariableIdentifier(VariableIdentifierNode variableIdent)
|
private string EmitLValue(LValueExpressionNode lValue)
|
||||||
{
|
{
|
||||||
var address = EmitAddressOfVariableIdent(variableIdent);
|
var address = EmitAddressOf(lValue);
|
||||||
if (variableIdent.Type is StructTypeNode)
|
|
||||||
|
if (lValue.Type is { IsValueType: true, IsScalar: false })
|
||||||
{
|
{
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
return EmitLoad(variableIdent.Type, address);
|
return EmitLoad(lValue.Type, address);
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitParameterFuncIdentifier(FuncParameterIdentifierNode funcParameterIdent)
|
|
||||||
{
|
|
||||||
return "%" + funcParameterIdent.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess)
|
|
||||||
{
|
|
||||||
var address = EmitAddressOfArrayIndexAccess(arrayIndexAccess);
|
|
||||||
if (arrayIndexAccess.Type is StructTypeNode)
|
|
||||||
{
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmitLoad(arrayIndexAccess.Type, address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
|
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
|
||||||
@@ -743,30 +716,14 @@ public class QBEGenerator
|
|||||||
return arrayPointer;
|
return arrayPointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitDereference(DereferenceNode dereference)
|
private string EmitAddressOf(LValueExpressionNode lval)
|
||||||
{
|
{
|
||||||
var address = EmitExpression(dereference.Expression);
|
return lval switch
|
||||||
if (dereference.Type is StructTypeNode)
|
|
||||||
{
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmitLoad(dereference.Type, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitAddressOf(AddressOfNode addressOf)
|
|
||||||
{
|
|
||||||
return EmitAddressOfLValue(addressOf.LValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitAddressOfLValue(LValueExpressionNode addressOf)
|
|
||||||
{
|
|
||||||
return addressOf switch
|
|
||||||
{
|
{
|
||||||
ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess),
|
ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess),
|
||||||
StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess),
|
StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess),
|
||||||
VariableIdentifierNode variableIdent => EmitAddressOfVariableIdent(variableIdent),
|
VariableIdentifierNode variableIdent => $"%{variableIdent.Name}",
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(addressOf))
|
_ => throw new ArgumentOutOfRangeException(nameof(lval))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -794,11 +751,6 @@ public class QBEGenerator
|
|||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitAddressOfVariableIdent(VariableIdentifierNode variableIdent)
|
|
||||||
{
|
|
||||||
return "%" + variableIdent.Name;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitBinaryExpression(BinaryExpressionNode binaryExpression)
|
private string EmitBinaryExpression(BinaryExpressionNode binaryExpression)
|
||||||
{
|
{
|
||||||
var left = EmitExpression(binaryExpression.Left);
|
var left = EmitExpression(binaryExpression.Left);
|
||||||
@@ -1116,17 +1068,6 @@ public class QBEGenerator
|
|||||||
throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported");
|
throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported");
|
||||||
}
|
}
|
||||||
|
|
||||||
private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccess)
|
|
||||||
{
|
|
||||||
var address = EmitAddressOfStructFieldAccess(structFieldAccess);
|
|
||||||
if (structFieldAccess.Type is StructTypeNode)
|
|
||||||
{
|
|
||||||
return address;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EmitLoad(structFieldAccess.Type, address);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string EmitStructFuncCall(StructFuncCallNode structFuncCall)
|
private string EmitStructFuncCall(StructFuncCallNode structFuncCall)
|
||||||
{
|
{
|
||||||
var func = StructFuncName(structFuncCall.StructType.Module, structFuncCall.StructType.Name, structFuncCall.Name);
|
var func = StructFuncName(structFuncCall.StructType.Module, structFuncCall.StructType.Name, structFuncCall.Name);
|
||||||
@@ -1236,19 +1177,16 @@ public class QBEGenerator
|
|||||||
{
|
{
|
||||||
return type switch
|
return type switch
|
||||||
{
|
{
|
||||||
SimpleTypeNode simple => simple.StorageSize switch
|
IntTypeNode intType => intType.Width / 8,
|
||||||
{
|
FloatTypeNode fType => fType.Width / 8,
|
||||||
StorageSize.Void => 0,
|
BoolTypeNode => 1,
|
||||||
StorageSize.I8 or StorageSize.U8 => 1,
|
PointerTypeNode => PTR_SIZE,
|
||||||
StorageSize.I16 or StorageSize.U16 => 2,
|
FuncTypeNode => PTR_SIZE,
|
||||||
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
|
|
||||||
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}")
|
|
||||||
},
|
|
||||||
CStringTypeNode => 8,
|
|
||||||
StringTypeNode => 8,
|
|
||||||
ArrayTypeNode => 8,
|
|
||||||
StructTypeNode structType => CalculateStructSize(structType),
|
StructTypeNode structType => CalculateStructSize(structType),
|
||||||
|
VoidTypeNode => throw new InvalidOperationException("Void type has no size"),
|
||||||
|
CStringTypeNode => PTR_SIZE,
|
||||||
|
StringTypeNode => PTR_SIZE,
|
||||||
|
ArrayTypeNode => PTR_SIZE,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1272,23 +1210,21 @@ public class QBEGenerator
|
|||||||
{
|
{
|
||||||
return type switch
|
return type switch
|
||||||
{
|
{
|
||||||
SimpleTypeNode simple => simple.StorageSize switch
|
IntTypeNode intType => intType.Width / 8,
|
||||||
{
|
FloatTypeNode fType => fType.Width / 8,
|
||||||
StorageSize.Void => 1,
|
BoolTypeNode => 1,
|
||||||
StorageSize.I8 or StorageSize.U8 => 1,
|
PointerTypeNode => PTR_SIZE,
|
||||||
StorageSize.I16 or StorageSize.U16 => 2,
|
FuncTypeNode => PTR_SIZE,
|
||||||
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
|
StructTypeNode st => CalculateStructAlignment(st),
|
||||||
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
|
CStringTypeNode => PTR_SIZE,
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}")
|
StringTypeNode => PTR_SIZE,
|
||||||
},
|
ArrayTypeNode => PTR_SIZE,
|
||||||
CStringTypeNode => 8,
|
VoidTypeNode => throw new InvalidOperationException("Void has no alignment"),
|
||||||
StringTypeNode => 8,
|
|
||||||
ArrayTypeNode => 8,
|
|
||||||
StructTypeNode structType => CalculateStructAlignment(structType),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
|
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static int CalculateStructAlignment(StructTypeNode structType)
|
private static int CalculateStructAlignment(StructTypeNode structType)
|
||||||
{
|
{
|
||||||
var maxAlignment = 1;
|
var maxAlignment = 1;
|
||||||
@@ -1307,26 +1243,27 @@ public class QBEGenerator
|
|||||||
return (offset + alignment - 1) & ~(alignment - 1);
|
return (offset + alignment - 1) & ~(alignment - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int OffsetOf(StructTypeNode structDef, string member)
|
private static int OffsetOf(StructTypeNode type, string member)
|
||||||
{
|
{
|
||||||
var offset = 0;
|
var offset = 0;
|
||||||
|
|
||||||
foreach (var field in structDef.Fields)
|
foreach (var field in type.Fields)
|
||||||
{
|
{
|
||||||
|
var fieldAlignment = AlignmentOf(field.Type);
|
||||||
|
offset = AlignTo(offset, fieldAlignment);
|
||||||
|
|
||||||
if (field.Name == member)
|
if (field.Name == member)
|
||||||
{
|
{
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fieldAlignment = AlignmentOf(field.Type);
|
|
||||||
|
|
||||||
offset = AlignTo(offset, fieldAlignment);
|
|
||||||
offset += SizeOf(field.Type);
|
offset += SizeOf(field.Type);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new UnreachableException($"Member '{member}' not found in struct");
|
throw new UnreachableException($"Member '{member}' not found in struct");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private string TmpName()
|
private string TmpName()
|
||||||
{
|
{
|
||||||
return $"%.t{++_tmpIndex}";
|
return $"%.t{++_tmpIndex}";
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public enum BinaryOperator
|
|||||||
|
|
||||||
public abstract record ExpressionNode(TypeNode Type) : Node;
|
public abstract record ExpressionNode(TypeNode Type) : Node;
|
||||||
|
|
||||||
public abstract record LValueExpressionNode(TypeNode Type) : RValueExpressionNode(Type);
|
public abstract record LValueExpressionNode(TypeNode Type) : ExpressionNode(Type);
|
||||||
|
|
||||||
public abstract record RValueExpressionNode(TypeNode Type) : ExpressionNode(Type);
|
public abstract record RValueExpressionNode(TypeNode Type) : ExpressionNode(Type);
|
||||||
|
|
||||||
|
|||||||
@@ -3,5 +3,3 @@
|
|||||||
public abstract record Node;
|
public abstract record Node;
|
||||||
|
|
||||||
public record BlockNode(List<StatementNode> Statements) : Node;
|
public record BlockNode(List<StatementNode> Statements) : Node;
|
||||||
|
|
||||||
public record TypedSyntaxTree(List<DefinitionNode> Definitions);
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace NubLang.TypeChecking.Node;
|
namespace NubLang.TypeChecking.Node;
|
||||||
|
|
||||||
public record StatementNode : Node;
|
public abstract record StatementNode : Node;
|
||||||
|
|
||||||
public record StatementExpressionNode(ExpressionNode Expression) : StatementNode;
|
public record StatementExpressionNode(ExpressionNode Expression) : StatementNode;
|
||||||
|
|
||||||
|
|||||||
@@ -1,31 +1,13 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
namespace NubLang.TypeChecking.Node;
|
||||||
|
|
||||||
namespace NubLang.TypeChecking.Node;
|
|
||||||
|
|
||||||
public abstract class TypeNode : IEquatable<TypeNode>
|
public abstract class TypeNode : IEquatable<TypeNode>
|
||||||
{
|
{
|
||||||
public bool IsSimpleType([NotNullWhen(true)] out SimpleTypeNode? simpleType, [NotNullWhen(false)] out ComplexTypeNode? complexType)
|
public abstract bool IsValueType { get; }
|
||||||
{
|
public abstract bool IsScalar { get; }
|
||||||
if (this is SimpleTypeNode st)
|
|
||||||
{
|
|
||||||
complexType = null;
|
|
||||||
simpleType = st;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this is ComplexTypeNode ct)
|
|
||||||
{
|
|
||||||
complexType = ct;
|
|
||||||
simpleType = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new ArgumentException($"Type {this} is not a simple type nor a complex type");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj) => obj is TypeNode other && Equals(other);
|
public override bool Equals(object? obj) => obj is TypeNode other && Equals(other);
|
||||||
|
|
||||||
public abstract bool Equals(TypeNode? other);
|
public abstract bool Equals(TypeNode? other);
|
||||||
|
|
||||||
public abstract override int GetHashCode();
|
public abstract override int GetHashCode();
|
||||||
public abstract override string ToString();
|
public abstract override string ToString();
|
||||||
|
|
||||||
@@ -33,90 +15,71 @@ public abstract class TypeNode : IEquatable<TypeNode>
|
|||||||
public static bool operator !=(TypeNode? left, TypeNode? right) => !Equals(left, right);
|
public static bool operator !=(TypeNode? left, TypeNode? right) => !Equals(left, right);
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum StorageSize
|
public class VoidTypeNode : TypeNode
|
||||||
{
|
{
|
||||||
Void,
|
public override bool IsValueType => false;
|
||||||
I8,
|
public override bool IsScalar => false;
|
||||||
I16,
|
|
||||||
I32,
|
public override string ToString() => "void";
|
||||||
I64,
|
public override bool Equals(TypeNode? other) => other is VoidTypeNode;
|
||||||
U8,
|
public override int GetHashCode() => HashCode.Combine(typeof(VoidTypeNode));
|
||||||
U16,
|
|
||||||
U32,
|
|
||||||
U64,
|
|
||||||
F32,
|
|
||||||
F64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class SimpleTypeNode : TypeNode
|
public sealed class IntTypeNode(bool signed, int width) : TypeNode
|
||||||
{
|
{
|
||||||
public abstract StorageSize StorageSize { get; }
|
public override bool IsValueType => true;
|
||||||
}
|
public override bool IsScalar => true;
|
||||||
|
|
||||||
#region Simple types
|
|
||||||
|
|
||||||
public class IntTypeNode(bool signed, int width) : SimpleTypeNode
|
|
||||||
{
|
|
||||||
public bool Signed { get; } = signed;
|
public bool Signed { get; } = signed;
|
||||||
public int Width { get; } = width;
|
public int Width { get; } = width;
|
||||||
|
|
||||||
public override StorageSize StorageSize => Signed switch
|
|
||||||
{
|
|
||||||
true => Width switch
|
|
||||||
{
|
|
||||||
8 => StorageSize.I8,
|
|
||||||
16 => StorageSize.I16,
|
|
||||||
32 => StorageSize.I32,
|
|
||||||
64 => StorageSize.I64,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(Width))
|
|
||||||
},
|
|
||||||
false => Width switch
|
|
||||||
{
|
|
||||||
8 => StorageSize.U8,
|
|
||||||
16 => StorageSize.U16,
|
|
||||||
32 => StorageSize.U32,
|
|
||||||
64 => StorageSize.U64,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(Width))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public override string ToString() => $"{(Signed ? "i" : "u")}{Width}";
|
public override string ToString() => $"{(Signed ? "i" : "u")}{Width}";
|
||||||
public override bool Equals(TypeNode? other) => other is IntTypeNode @int && @int.Width == Width && @int.Signed == Signed;
|
public override bool Equals(TypeNode? other) => other is IntTypeNode @int && @int.Width == Width && @int.Signed == Signed;
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(IntTypeNode), Signed, Width);
|
public override int GetHashCode() => HashCode.Combine(typeof(IntTypeNode), Signed, Width);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FloatTypeNode(int width) : SimpleTypeNode
|
public sealed class FloatTypeNode(int width) : TypeNode
|
||||||
{
|
{
|
||||||
|
public override bool IsValueType => true;
|
||||||
|
public override bool IsScalar => true;
|
||||||
|
|
||||||
public int Width { get; } = width;
|
public int Width { get; } = width;
|
||||||
|
|
||||||
public override StorageSize StorageSize => Width switch
|
|
||||||
{
|
|
||||||
32 => StorageSize.F32,
|
|
||||||
64 => StorageSize.F64,
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(Width))
|
|
||||||
};
|
|
||||||
|
|
||||||
public override string ToString() => $"f{Width}";
|
public override string ToString() => $"f{Width}";
|
||||||
public override bool Equals(TypeNode? other) => other is FloatTypeNode @int && @int.Width == Width;
|
public override bool Equals(TypeNode? other) => other is FloatTypeNode @float && @float.Width == Width;
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(FloatTypeNode), Width);
|
public override int GetHashCode() => HashCode.Combine(typeof(FloatTypeNode), Width);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BoolTypeNode : SimpleTypeNode
|
public class BoolTypeNode : TypeNode
|
||||||
{
|
{
|
||||||
public override StorageSize StorageSize => StorageSize.U8;
|
public override bool IsValueType => true;
|
||||||
|
public override bool IsScalar => true;
|
||||||
|
|
||||||
public override string ToString() => "bool";
|
public override string ToString() => "bool";
|
||||||
public override bool Equals(TypeNode? other) => other is BoolTypeNode;
|
public override bool Equals(TypeNode? other) => other is BoolTypeNode;
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(BoolTypeNode));
|
public override int GetHashCode() => HashCode.Combine(typeof(BoolTypeNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class FuncTypeNode(List<TypeNode> parameters, TypeNode returnType) : SimpleTypeNode
|
public sealed class PointerTypeNode(TypeNode baseType) : TypeNode
|
||||||
{
|
{
|
||||||
|
public override bool IsValueType => true;
|
||||||
|
public override bool IsScalar => true;
|
||||||
|
|
||||||
|
public TypeNode BaseType { get; } = baseType;
|
||||||
|
|
||||||
|
public override string ToString() => "^" + BaseType;
|
||||||
|
public override bool Equals(TypeNode? other) => other is PointerTypeNode pointer && BaseType.Equals(pointer.BaseType);
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(PointerTypeNode), BaseType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FuncTypeNode(List<TypeNode> parameters, TypeNode returnType) : TypeNode
|
||||||
|
{
|
||||||
|
public override bool IsValueType => true;
|
||||||
|
public override bool IsScalar => true;
|
||||||
|
|
||||||
public List<TypeNode> Parameters { get; } = parameters;
|
public List<TypeNode> Parameters { get; } = parameters;
|
||||||
public TypeNode ReturnType { get; } = returnType;
|
public TypeNode ReturnType { get; } = returnType;
|
||||||
|
|
||||||
public override StorageSize StorageSize => StorageSize.U64;
|
|
||||||
|
|
||||||
public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}";
|
public override string ToString() => $"func({string.Join(", ", Parameters)}): {ReturnType}";
|
||||||
public override bool Equals(TypeNode? other) => other is FuncTypeNode func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
public override bool Equals(TypeNode? other) => other is FuncTypeNode func && ReturnType.Equals(func.ReturnType) && Parameters.SequenceEqual(func.Parameters);
|
||||||
|
|
||||||
@@ -134,44 +97,19 @@ public class FuncTypeNode(List<TypeNode> parameters, TypeNode returnType) : Simp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PointerTypeNode(TypeNode baseType) : SimpleTypeNode
|
public class StructTypeNode(string module, string name, List<StructTypeField> fields, List<StructTypeFunc> functions) : TypeNode
|
||||||
{
|
{
|
||||||
public TypeNode BaseType { get; } = baseType;
|
public override bool IsValueType => true;
|
||||||
|
public override bool IsScalar => false;
|
||||||
|
|
||||||
public override StorageSize StorageSize => StorageSize.U64;
|
public string Module { get; } = module;
|
||||||
|
public string Name { get; } = name;
|
||||||
|
public List<StructTypeField> Fields { get; set; } = fields;
|
||||||
|
public List<StructTypeFunc> Functions { get; set; } = functions;
|
||||||
|
|
||||||
public override string ToString() => "^" + BaseType;
|
public override string ToString() => Name;
|
||||||
public override bool Equals(TypeNode? other) => other is PointerTypeNode pointer && BaseType.Equals(pointer.BaseType);
|
public override bool Equals(TypeNode? other) => other is StructTypeNode structType && Name == structType.Name && Module == structType.Module;
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(PointerTypeNode), BaseType);
|
public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name);
|
||||||
}
|
|
||||||
|
|
||||||
public class VoidTypeNode : SimpleTypeNode
|
|
||||||
{
|
|
||||||
public override StorageSize StorageSize => StorageSize.Void;
|
|
||||||
|
|
||||||
public override string ToString() => "void";
|
|
||||||
public override bool Equals(TypeNode? other) => other is VoidTypeNode;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(VoidTypeNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
public abstract class ComplexTypeNode : TypeNode;
|
|
||||||
|
|
||||||
#region Complex types
|
|
||||||
|
|
||||||
public class CStringTypeNode : ComplexTypeNode
|
|
||||||
{
|
|
||||||
public override string ToString() => "cstring";
|
|
||||||
public override bool Equals(TypeNode? other) => other is CStringTypeNode;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(CStringTypeNode));
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringTypeNode : ComplexTypeNode
|
|
||||||
{
|
|
||||||
public override string ToString() => "string";
|
|
||||||
public override bool Equals(TypeNode? other) => other is StringTypeNode;
|
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(StringTypeNode));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StructTypeField(string name, TypeNode type, bool hasDefaultValue)
|
public class StructTypeField(string name, TypeNode type, bool hasDefaultValue)
|
||||||
@@ -187,26 +125,34 @@ public class StructTypeFunc(string name, FuncTypeNode type)
|
|||||||
public FuncTypeNode Type { get; } = type;
|
public FuncTypeNode Type { get; } = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class StructTypeNode(string module, string name, List<StructTypeField> fields, List<StructTypeFunc> functions) : ComplexTypeNode
|
public class CStringTypeNode : TypeNode
|
||||||
{
|
{
|
||||||
public string Module { get; } = module;
|
public override bool IsValueType => false;
|
||||||
public string Name { get; } = name;
|
public override bool IsScalar => false;
|
||||||
public List<StructTypeField> Fields { get; set; } = fields;
|
|
||||||
public List<StructTypeFunc> Functions { get; set; } = functions;
|
|
||||||
|
|
||||||
public override string ToString() => Name;
|
public override string ToString() => "cstring";
|
||||||
public override bool Equals(TypeNode? other) => other is StructTypeNode structType && Name == structType.Name && Module == structType.Module;
|
public override bool Equals(TypeNode? other) => other is CStringTypeNode;
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(StructTypeNode), Name);
|
public override int GetHashCode() => HashCode.Combine(typeof(CStringTypeNode));
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ArrayTypeNode(TypeNode elementType) : ComplexTypeNode
|
public class StringTypeNode : TypeNode
|
||||||
{
|
{
|
||||||
|
public override bool IsValueType => false;
|
||||||
|
public override bool IsScalar => false;
|
||||||
|
|
||||||
|
public override string ToString() => "string";
|
||||||
|
public override bool Equals(TypeNode? other) => other is StringTypeNode;
|
||||||
|
public override int GetHashCode() => HashCode.Combine(typeof(StringTypeNode));
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ArrayTypeNode(TypeNode elementType) : TypeNode
|
||||||
|
{
|
||||||
|
public override bool IsValueType => false;
|
||||||
|
public override bool IsScalar => false;
|
||||||
|
|
||||||
public TypeNode ElementType { get; } = elementType;
|
public TypeNode ElementType { get; } = elementType;
|
||||||
|
|
||||||
public override string ToString() => "[]" + ElementType;
|
public override string ToString() => "[]" + ElementType;
|
||||||
|
|
||||||
public override bool Equals(TypeNode? other) => other is ArrayTypeNode array && ElementType.Equals(array.ElementType);
|
public override bool Equals(TypeNode? other) => other is ArrayTypeNode array && ElementType.Equals(array.ElementType);
|
||||||
public override int GetHashCode() => HashCode.Combine(typeof(ArrayTypeNode), ElementType);
|
public override int GetHashCode() => HashCode.Combine(typeof(ArrayTypeNode), ElementType);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
@@ -117,15 +117,25 @@ public sealed class TypeChecker
|
|||||||
scope.Declare(new Identifier(parameter.Name, ResolveType(parameter.Type), IdentifierKind.FunctionParameter));
|
scope.Declare(new Identifier(parameter.Name, ResolveType(parameter.Type), IdentifierKind.FunctionParameter));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var signature = CheckFuncSignature(node.Signature);
|
||||||
|
|
||||||
BlockNode? body = null;
|
BlockNode? body = null;
|
||||||
if (node.Body != null)
|
if (node.Body != null)
|
||||||
{
|
{
|
||||||
_funcReturnTypes.Push(ResolveType(node.Signature.ReturnType));
|
_funcReturnTypes.Push(signature.ReturnType);
|
||||||
|
|
||||||
body = CheckBlock(node.Body, scope);
|
body = CheckBlock(node.Body, scope);
|
||||||
|
|
||||||
|
// Insert implicit return for void functions
|
||||||
|
if (signature.ReturnType is VoidTypeNode && body.Statements.LastOrDefault() is not ReturnNode)
|
||||||
|
{
|
||||||
|
body.Statements.Add(new ReturnNode(Optional<ExpressionNode>.Empty()));
|
||||||
|
}
|
||||||
|
|
||||||
_funcReturnTypes.Pop();
|
_funcReturnTypes.Pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FuncNode(_syntaxTree.Metadata.ModuleName, node.Name, node.ExternSymbol, CheckFuncSignature(node.Signature), body);
|
return new FuncNode(_syntaxTree.Metadata.ModuleName, node.Name, node.ExternSymbol, signature, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
private StatementNode CheckStatement(StatementSyntax node)
|
private StatementNode CheckStatement(StatementSyntax node)
|
||||||
@@ -715,9 +725,29 @@ public sealed class TypeChecker
|
|||||||
|
|
||||||
_scopes.Push(scope ?? Scope.SubScope());
|
_scopes.Push(scope ?? Scope.SubScope());
|
||||||
|
|
||||||
|
var reachable = true;
|
||||||
|
var warnedUnreachable = false;
|
||||||
|
|
||||||
foreach (var statement in node.Statements)
|
foreach (var statement in node.Statements)
|
||||||
{
|
{
|
||||||
statements.Add(CheckStatement(statement));
|
var checkedStatement = CheckStatement(statement);
|
||||||
|
|
||||||
|
if (reachable)
|
||||||
|
{
|
||||||
|
statements.Add(checkedStatement);
|
||||||
|
if (checkedStatement is ReturnNode or BreakNode or ContinueNode)
|
||||||
|
{
|
||||||
|
reachable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!warnedUnreachable)
|
||||||
|
{
|
||||||
|
Diagnostics.Add(Diagnostic.Warning("Statement is unreachable").At(statement).Build());
|
||||||
|
warnedUnreachable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_scopes.Pop();
|
_scopes.Pop();
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ module "main"
|
|||||||
|
|
||||||
extern "puts" func puts(text: cstring)
|
extern "puts" func puts(text: cstring)
|
||||||
|
|
||||||
|
struct Human
|
||||||
|
{
|
||||||
|
name: cstring
|
||||||
|
}
|
||||||
|
|
||||||
extern "main" func main(args: []cstring): i64
|
extern "main" func main(args: []cstring): i64
|
||||||
{
|
{
|
||||||
let x = [2]cstring
|
let x = [2]cstring
|
||||||
@@ -14,5 +19,16 @@ extern "main" func main(args: []cstring): i64
|
|||||||
puts(u)
|
puts(u)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let me: Human = {
|
||||||
|
name = "test"
|
||||||
|
}
|
||||||
|
|
||||||
|
puts(me.name)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func test()
|
||||||
|
{
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user