This repository has been archived on 2025-10-24. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nub-lang-archive-2/src/lang/Nub.Lang/Frontend/Generation/QBEGenerator.cs
nub31 79ec5f9b0f ...
2025-06-10 17:49:44 +02:00

1364 lines
50 KiB
C#

using System.Diagnostics;
using System.Globalization;
using System.Text;
using Nub.Lang.Frontend.Lexing;
using Nub.Lang.Frontend.Parsing;
using Nub.Lang.Frontend.Parsing.Definitions;
using Nub.Lang.Frontend.Parsing.Expressions;
using Nub.Lang.Frontend.Parsing.Statements;
using Nub.Lang.Frontend.Typing;
namespace Nub.Lang.Frontend.Generation;
public class QBEGenerator
{
private const string OutOfBoundsMessage = "Index is out of bounds\\n";
private List<SourceFile> _sourceFiles = [];
private StringBuilder _builder = new();
private List<string> _strings = [];
private Stack<string> _breakLabels = [];
private Stack<string> _continueLabels = [];
private int _variableIndex;
private int _labelIndex;
private bool _codeIsReachable = true;
private Dictionary<AnonymousFuncNode, string> _anonymousFunctions = [];
private int _anonymousFuncIndex;
private SymbolTable _symbolTable = new([]);
public string Generate(List<SourceFile> sourceFiles)
{
_sourceFiles = sourceFiles;
_builder = new StringBuilder();
_strings = [];
_breakLabels = [];
_continueLabels = [];
_anonymousFunctions = [];
_variableIndex = 0;
_labelIndex = 0;
_anonymousFuncIndex = 0;
_codeIsReachable = true;
_symbolTable = new SymbolTable(_sourceFiles.SelectMany(f => f.Definitions.OfType<IFuncSignature>()));
foreach (var structDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<StructDefinitionNode>())
{
GenerateStructDefinition(structDef);
_builder.AppendLine();
}
foreach (var funcDef in _sourceFiles.SelectMany(f => f.Definitions).OfType<LocalFuncDefinitionNode>())
{
var symbol = _symbolTable.LookupFunc(funcDef.Namespace, funcDef.Name);
GenerateFuncDefinition(symbol.GeneratedName, funcDef.Parameters, funcDef.ReturnType, funcDef.Body, funcDef.Exported);
_builder.AppendLine();
}
foreach (var (func, name) in _anonymousFunctions)
{
GenerateFuncDefinition(name, func.Parameters, func.ReturnType, func.Body, false);
_builder.AppendLine();
}
_builder.AppendLine($"data $oob_message = {{ b \"{OutOfBoundsMessage}\" }}");
for (var i = 0; i < _strings.Count; i++)
{
var str = _strings[i];
_builder.AppendLine($"data $string{i + 1} = {{ b \"{str}\", b 0 }}");
}
return _builder.ToString();
}
private static string QBEStore(NubType type)
{
return $"store{type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "h",
PrimitiveTypeKind.I8 => "b",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "h",
PrimitiveTypeKind.U8 => "b",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new UnreachableException()
},
NubStructType => "l",
NubFixedArrayType => "l",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
}}";
}
private static string QBELoad(NubType type)
{
return $"load{type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new UnreachableException()
},
NubStructType => "l",
NubFixedArrayType => "l",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
}}";
}
private static string QBEAssign(NubType type)
{
return $"={type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "w",
PrimitiveTypeKind.I8 => "w",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "w",
PrimitiveTypeKind.U8 => "w",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new UnreachableException()
},
NubStructType => "l",
NubFixedArrayType => "l",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
}}";
}
private int AlignmentOf(NubType type)
{
switch (type)
{
case NubPrimitiveType primitiveType:
{
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U64:
case PrimitiveTypeKind.F64:
return 8;
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.U32:
case PrimitiveTypeKind.F32:
return 4;
case PrimitiveTypeKind.I16:
case PrimitiveTypeKind.U16:
return 2;
case PrimitiveTypeKind.I8:
case PrimitiveTypeKind.U8:
case PrimitiveTypeKind.Bool:
return 1;
default:
throw new ArgumentOutOfRangeException();
}
}
case NubStructType nubStructType:
{
var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name);
return definition.Fields.Max(f => AlignmentOf(f.Type));
}
case NubPointerType:
case NubArrayType:
case NubFuncType:
{
return 8;
}
case NubFixedArrayType nubFixedArrayType:
{
return AlignmentOf(nubFixedArrayType.ElementType);
}
default:
{
throw new UnreachableException();
}
}
}
private int AlignTo(int offset, int alignment)
{
return (offset + alignment - 1) & ~(alignment - 1);
}
private int SizeOf(NubType type)
{
switch (type)
{
case NubPrimitiveType primitiveType:
{
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.I64:
case PrimitiveTypeKind.U64:
return 8;
case PrimitiveTypeKind.I32:
case PrimitiveTypeKind.U32:
return 4;
case PrimitiveTypeKind.I16:
case PrimitiveTypeKind.U16:
return 2;
case PrimitiveTypeKind.I8:
case PrimitiveTypeKind.U8:
case PrimitiveTypeKind.Bool:
return 1;
case PrimitiveTypeKind.F64:
return 8;
case PrimitiveTypeKind.F32:
return 4;
default:
throw new ArgumentOutOfRangeException();
}
}
case NubStructType nubStructType:
{
var definition = LookupStructDefinition(nubStructType.Namespace, nubStructType.Name);
int size = 0;
int maxAlignment = 1;
foreach (var field in definition.Fields)
{
int fieldAlignment = AlignmentOf(field.Type);
maxAlignment = Math.Max(maxAlignment, fieldAlignment);
size = AlignTo(size, fieldAlignment);
size += SizeOf(field.Type);
}
size = AlignTo(size, maxAlignment);
return size;
}
case NubPointerType:
case NubArrayType:
case NubFuncType:
{
return 8;
}
case NubFixedArrayType nubFixedArrayType:
{
int elementSize = SizeOf(nubFixedArrayType.ElementType);
return elementSize * nubFixedArrayType.Capacity + 8;
}
default:
{
throw new UnreachableException();
}
}
}
private bool IsLargeType(NubType type)
{
return type switch
{
NubArrayType => false,
NubPointerType => false,
NubPrimitiveType => false,
NubStructType => true,
NubFixedArrayType => true,
NubFuncType => false,
_ => throw new ArgumentOutOfRangeException(nameof(type))
};
}
private void GenerateFuncDefinition(string name, List<FuncParameter> parameters, NubType returnType, BlockNode body, bool exported)
{
_symbolTable.Reset();
if (exported)
{
_builder.Append("export ");
}
_builder.Append("function ");
if (returnType is not NubVoidType)
{
_builder.Append(returnType switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new UnreachableException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type")
});
_builder.Append(' ');
}
_builder.Append(name);
var parameterStrings = parameters.Select(parameter => $"{parameter.Type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new UnreachableException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type")
}} %{parameter.Name}");
_builder.AppendLine($"({string.Join(", ", parameterStrings)}) {{");
_builder.AppendLine("@start");
foreach (var parameter in parameters)
{
var parameterName = "%" + parameter.Name;
if (parameter.Type is NubPrimitiveType primitiveType)
{
switch (primitiveType.Kind)
{
case PrimitiveTypeKind.I16:
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extsh %{parameter.Name}");
break;
case PrimitiveTypeKind.I8:
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extsb %{parameter.Name}");
break;
case PrimitiveTypeKind.U16:
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extuh %{parameter.Name}");
break;
case PrimitiveTypeKind.U8:
parameterName = GenVarName();
_builder.AppendLine($" {parameterName} =w extub %{parameter.Name}");
break;
}
}
_symbolTable.DeclareVariable(parameter.Name, parameterName);
}
GenerateBlock(body);
if (body.Statements.LastOrDefault() is not ReturnNode)
{
if (returnType is NubVoidType)
{
_builder.AppendLine(" ret");
}
}
_builder.AppendLine("}");
}
private void GenerateStructDefinition(StructDefinitionNode structDefinition)
{
_builder.Append($"type :{structDefinition.Namespace}_{structDefinition.Name} = {{ ");
foreach (var structDefinitionField in structDefinition.Fields)
{
var fieldQbeType = structDefinitionField.Type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType primitiveType => primitiveType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "h",
PrimitiveTypeKind.I8 => "b",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "h",
PrimitiveTypeKind.U8 => "b",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new UnreachableException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
};
_builder.Append(fieldQbeType + ", ");
}
_builder.AppendLine("}");
}
private void GenerateStatement(StatementNode statement)
{
switch (statement)
{
case ArrayIndexAssignmentNode arrayIndexAssignment:
GenerateArrayIndexAssignment(arrayIndexAssignment);
break;
case BreakNode:
GenerateBreak();
break;
case ContinueNode:
GenerateContinue();
break;
case DereferenceAssignmentNode dereferenceAssignment:
GenerateDereferenceAssignment(dereferenceAssignment);
break;
case IfNode ifStatement:
GenerateIf(ifStatement);
break;
case MemberAssignmentNode memberAssignment:
GenerateMemberAssignment(memberAssignment);
break;
case ReturnNode @return:
GenerateReturn(@return);
break;
case StatementExpressionNode statementExpression:
GenerateExpression(statementExpression.Expression);
break;
case VariableAssignmentNode variableAssignment:
GenerateVariableAssignment(variableAssignment);
break;
case VariableDeclarationNode variableDeclaration:
GenerateVariableDeclaration(variableDeclaration);
break;
case WhileNode whileStatement:
GenerateWhile(whileStatement);
break;
default:
throw new ArgumentOutOfRangeException(nameof(statement));
}
}
private void GenerateArrayIndexAssignment(ArrayIndexAssignmentNode arrayIndexAssignment)
{
var array = GenerateExpression(arrayIndexAssignment.ArrayIndexAccess.Array);
var index = GenerateExpression(arrayIndexAssignment.ArrayIndexAccess.Index);
GenerateArrayBoundsCheck(array, index);
var value = GenerateExpression(arrayIndexAssignment.Value);
switch (arrayIndexAssignment.ArrayIndexAccess.Array.Type)
{
case NubArrayType arrayType:
{
var startName = GenVarName();
_builder.AppendLine($" {startName} =l add {array}, 8");
var adjustedIndex = GenVarName();
_builder.AppendLine($" {adjustedIndex} =l mul {index}, {SizeOf(arrayType.ElementType)}");
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l add {startName}, {adjustedIndex}");
GenerateCopy(arrayType.ElementType, value, pointer);
break;
}
case NubFixedArrayType fixedArrayType:
{
var startName = GenVarName();
_builder.AppendLine($" {startName} =l add {array}, 8");
var adjustedIndex = GenVarName();
_builder.AppendLine($" {adjustedIndex} =l mul {index}, {SizeOf(fixedArrayType.ElementType)}");
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l add {startName}, {adjustedIndex}");
GenerateCopy(fixedArrayType.ElementType, value, pointer);
break;
}
default:
{
throw new UnreachableException();
}
}
}
private void GenerateBlock(BlockNode block)
{
_symbolTable.StartScope();
foreach (var statement in block.Statements.Where(_ => _codeIsReachable))
{
GenerateStatement(statement);
}
_symbolTable.EndScope();
_codeIsReachable = true;
}
private void GenerateBreak()
{
_builder.AppendLine($" jmp {_breakLabels.Peek()}");
_codeIsReachable = false;
}
private void GenerateContinue()
{
_builder.AppendLine($" jmp {_continueLabels.Peek()}");
_codeIsReachable = false;
}
private void GenerateDereferenceAssignment(DereferenceAssignmentNode dereferenceAssignment)
{
var pointer = GenerateExpression(dereferenceAssignment.Dereference.Expression);
var value = GenerateExpression(dereferenceAssignment.Value);
GenerateCopy(dereferenceAssignment.Value.Type, value, pointer);
}
private void GenerateIf(IfNode ifStatement)
{
var trueLabel = GenLabelName();
var falseLabel = GenLabelName();
var endLabel = GenLabelName();
var result = GenerateExpression(ifStatement.Condition);
_builder.AppendLine($" jnz {result}, {trueLabel}, {falseLabel}");
_builder.AppendLine(trueLabel);
GenerateBlock(ifStatement.Body);
_builder.AppendLine($" jmp {endLabel}");
_builder.AppendLine(falseLabel);
if (ifStatement.Else.HasValue)
{
ifStatement.Else.Value.Match
(
GenerateIf,
GenerateBlock
);
}
_builder.AppendLine(endLabel);
}
private void GenerateMemberAssignment(MemberAssignmentNode memberAssignment)
{
var structType = memberAssignment.MemberAccess.Expression.Type as NubStructType;
Debug.Assert(structType != null);
var structDefinition = LookupStructDefinition(structType.Namespace, structType.Name);
var offset = LookupMemberOffset(structDefinition, memberAssignment.MemberAccess.Member);
var item = GenerateExpression(memberAssignment.MemberAccess.Expression);
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l add {item}, {offset}");
var value = GenerateExpression(memberAssignment.Value);
GenerateCopy(memberAssignment.Value.Type, value, pointer);
}
private void GenerateReturn(ReturnNode @return)
{
if (@return.Value.HasValue)
{
var result = GenerateExpression(@return.Value.Value);
_builder.AppendLine($" ret {result}");
}
else
{
_builder.AppendLine(" ret");
}
}
private void GenerateVariableDeclaration(VariableDeclarationNode variableDeclaration)
{
var type = variableDeclaration.ExplicitType.Value ?? variableDeclaration.Value.Value!.Type;
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l alloc8 {SizeOf(type)}");
if (variableDeclaration.Value.HasValue)
{
var value = GenerateExpression(variableDeclaration.Value.Value);
GenerateCopy(variableDeclaration.Value.Value.Type, value, pointer);
}
else
{
var pointerName = GenVarName();
_symbolTable.DeclareVariable(variableDeclaration.Name, pointerName);
}
_symbolTable.DeclareVariable(variableDeclaration.Name, pointer);
}
private void GenerateVariableAssignment(VariableAssignmentNode variableAssignment)
{
var value = GenerateExpression(variableAssignment.Value);
var variable = _symbolTable.LookupVariable(variableAssignment.Identifier.Name);
GenerateCopy(variableAssignment.Value.Type, value, variable.Pointer);
}
private void GenerateWhile(WhileNode whileStatement)
{
var conditionLabel = GenLabelName();
var iterationLabel = GenLabelName();
var endLabel = GenLabelName();
_breakLabels.Push(endLabel);
_continueLabels.Push(conditionLabel);
_builder.AppendLine($" jmp {conditionLabel}");
_builder.AppendLine(iterationLabel);
GenerateBlock(whileStatement.Body);
_builder.AppendLine(conditionLabel);
var result = GenerateExpression(whileStatement.Condition);
_builder.AppendLine($" jnz {result}, {iterationLabel}, {endLabel}");
_builder.AppendLine(endLabel);
_continueLabels.Pop();
_breakLabels.Pop();
}
private string GenerateExpression(ExpressionNode expression)
{
return expression switch
{
AddressOfNode addressOf => GenerateAddressOf(addressOf),
AnonymousFuncNode anonymousFunc => GenerateAnonymousFunc(anonymousFunc),
ArrayIndexAccessNode arrayIndex => GenerateArrayAccessIndex(arrayIndex),
ArrayInitializerNode arrayInitializer => GenerateArrayInitializer(arrayInitializer),
BinaryExpressionNode binaryExpression => GenerateBinaryExpression(binaryExpression),
DereferenceNode dereference => GenerateDereference(dereference),
FixedArrayInitializerNode fixedArrayInitializer => GenerateFixedArrayInitializer(fixedArrayInitializer),
FuncCallNode funcCallExpression => GenerateFuncCall(funcCallExpression),
IdentifierNode identifier => GenerateIdentifier(identifier),
LiteralNode literal => GenerateLiteral(literal),
StructInitializerNode structInitializer => GenerateStructInitializer(structInitializer),
UnaryExpressionNode unaryExpression => GenerateUnaryExpression(unaryExpression),
MemberAccessNode memberAccess => GenerateMemberAccess(memberAccess),
_ => throw new ArgumentOutOfRangeException(nameof(expression))
};
}
private string GenerateAnonymousFunc(AnonymousFuncNode anonymousFunc)
{
var name = $"$anon_func{++_anonymousFuncIndex}";
_anonymousFunctions[anonymousFunc] = name;
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l alloc8 8");
_builder.AppendLine($" storel {name}, {pointer}");
return pointer;
}
private string GenerateArrayIndexPointer(ArrayIndexAccessNode arrayIndexAccess)
{
var array = GenerateExpression(arrayIndexAccess.Array);
var index = GenerateExpression(arrayIndexAccess.Index);
GenerateArrayBoundsCheck(array, index);
switch (arrayIndexAccess.Array.Type)
{
case NubArrayType arrayType:
{
var firstItemPointerName = GenVarName();
_builder.AppendLine($" {firstItemPointerName} =l add {array}, 8");
var offsetPointerName = GenVarName();
_builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(arrayType.ElementType)}");
var resultPointerName = GenVarName();
_builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}");
return resultPointerName;
}
case NubFixedArrayType fixedArrayType:
{
var firstItemPointerName = GenVarName();
_builder.AppendLine($" {firstItemPointerName} =l add {array}, 8");
var offsetPointerName = GenVarName();
_builder.AppendLine($" {offsetPointerName} =l mul {index}, {SizeOf(fixedArrayType.ElementType)}");
var resultPointerName = GenVarName();
_builder.AppendLine($" {resultPointerName} =l add {firstItemPointerName}, {offsetPointerName}");
return resultPointerName;
}
default:
{
throw new UnreachableException();
}
}
}
private string GenerateArrayAccessIndex(ArrayIndexAccessNode arrayIndexAccess)
{
var pointerName = GenerateArrayIndexPointer(arrayIndexAccess);
if (IsLargeType(arrayIndexAccess.Type))
{
return pointerName;
}
else
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(arrayIndexAccess.Type)} {QBELoad(arrayIndexAccess.Type)} {pointerName}");
return outputName;
}
}
private void GenerateArrayBoundsCheck(string array, string index)
{
var countName = GenVarName();
_builder.AppendLine($" {countName} =l loadl {array}");
var isNegativeName = GenVarName();
_builder.AppendLine($" {isNegativeName} =w csltl {index}, 0");
var isOobName = GenVarName();
_builder.AppendLine($" {isOobName} =w csgel {index}, {countName}");
var anyOobName = GenVarName();
_builder.AppendLine($" {anyOobName} =w or {isNegativeName}, {isOobName}");
var oobLabel = GenLabelName();
var notOobLabel = GenLabelName();
_builder.AppendLine($" jnz {anyOobName}, {oobLabel}, {notOobLabel}");
_builder.AppendLine(oobLabel);
_builder.AppendLine($" call $nub_panic(l $oob_message, l {OutOfBoundsMessage.Length})");
_builder.AppendLine(notOobLabel);
}
private string GenerateArrayInitializer(ArrayInitializerNode arrayInitializer)
{
var capacity = GenerateExpression(arrayInitializer.Capacity);
var elementSize = SizeOf(arrayInitializer.ElementType);
var capacityInBytes = GenVarName();
_builder.AppendLine($" {capacityInBytes} =l mul {capacity}, {elementSize}");
var totalArraySize = GenVarName();
_builder.AppendLine($" {totalArraySize} =l add {capacityInBytes}, 8");
var outputName = GenVarName();
_builder.AppendLine($" {outputName} =l alloc8 {totalArraySize}");
_builder.AppendLine($" storel {capacity}, {outputName}");
var dataPtr = GenVarName();
_builder.AppendLine($" {dataPtr} =l add {outputName}, 8");
_builder.AppendLine($" call $nub_memset(l {dataPtr}, w 0, l {capacityInBytes})");
return outputName;
}
private string GenerateDereference(DereferenceNode dereference)
{
var result = GenerateExpression(dereference.Expression);
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(dereference.Type)} {QBELoad(dereference.Type)} {result}");
return outputName;
}
private string GenerateAddressOf(AddressOfNode addressOf)
{
switch (addressOf.Expression)
{
case ArrayIndexAccessNode arrayIndexAccess:
return GenerateArrayIndexPointer(arrayIndexAccess);
case DereferenceNode dereference:
return GenerateExpression(dereference.Expression);
case IdentifierNode identifier:
if (identifier.Namespace != null)
{
throw new NotSupportedException("There is nothing to address in another namespace");
}
return _symbolTable.LookupVariable(identifier.Name).Pointer;
case MemberAccessNode memberAccess:
return GenerateMemberAccessPointer(memberAccess);
default:
throw new UnreachableException();
}
}
private string GenerateBinaryExpression(BinaryExpressionNode binaryExpression)
{
var left = GenerateExpression(binaryExpression.Left);
var right = GenerateExpression(binaryExpression.Right);
var outputName = GenVarName();
switch (binaryExpression.Operator)
{
case BinaryExpressionOperator.Equal:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =w ceql {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w ceqw {left}, {right}");
return outputName;
}
// if (binaryExpression.Left.Type.Equals(NubPrimitiveType.String))
// {
// _builder.AppendLine($" {outputName} =w call $nub_strcmp(l {left}, l {right})");
// return outputName;
// }
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool))
{
_builder.AppendLine($" {outputName} =w ceqw {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.NotEqual:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =w cnel {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w cnew {left}, {right}");
return outputName;
}
// if (binaryExpression.Left.Type.Equals(NubPrimitiveType.String))
// {
// _builder.AppendLine($" {outputName} =w call $nub_strcmp(l {left}, l {right})");
// _builder.AppendLine($" {outputName} =w xor {outputName}, 1");
// return outputName;
// }
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool))
{
_builder.AppendLine($" {outputName} =w cnew {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.GreaterThan:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =w csgtl {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w csgtw {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool))
{
_builder.AppendLine($" {outputName} =w csgtw {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.GreaterThanOrEqual:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =w csgel {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w csgew {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool))
{
_builder.AppendLine($" {outputName} =w csgew {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.LessThan:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =w csltl {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w csltw {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool))
{
_builder.AppendLine($" {outputName} =w csltw {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.LessThanOrEqual:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =w cslel {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w cslew {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.Bool))
{
_builder.AppendLine($" {outputName} =w cslew {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.Plus:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =l add {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w add {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.Minus:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =l sub {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w sub {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.Multiply:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =l mul {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w mul {left}, {right}");
return outputName;
}
break;
}
case BinaryExpressionOperator.Divide:
{
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I64))
{
_builder.AppendLine($" {outputName} =l div {left}, {right}");
return outputName;
}
if (binaryExpression.Left.Type.Equals(NubPrimitiveType.I32))
{
_builder.AppendLine($" {outputName} =w div {left}, {right}");
return outputName;
}
break;
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
throw new NotSupportedException($"Binary operator {binaryExpression.Operator} for types {binaryExpression.Left.Type} and {binaryExpression.Right.Type} not supported");
}
private string GenerateIdentifier(IdentifierNode identifier)
{
var symbol = _symbolTable.Lookup(identifier.Namespace, identifier.Name);
switch (symbol)
{
case SymbolTable.Func func:
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l alloc8 8");
_builder.AppendLine($" storel {func.GeneratedName}, {pointer}");
return pointer;
case SymbolTable.Variable variable:
return GenerateDereference(identifier.Type, variable.Pointer);
default:
throw new ArgumentOutOfRangeException(nameof(symbol));
}
}
private string GenerateLiteral(LiteralNode literal)
{
if (literal.Type.IsInteger)
{
switch (literal.Kind)
{
case LiteralKind.Integer:
return literal.Literal;
case LiteralKind.Float:
return literal.Literal.Split(".").First();
default:
throw new ArgumentOutOfRangeException();
}
}
if (literal.Type.IsFloat64)
{
var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture);
var bits = BitConverter.DoubleToInt64Bits(value);
return bits.ToString();
}
if (literal.Type.IsFloat32)
{
var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture);
var bits = BitConverter.SingleToInt32Bits(value);
return bits.ToString();
}
switch (literal.Kind)
{
case LiteralKind.String:
_strings.Add(literal.Literal);
return $"$string{_strings.Count}";
case LiteralKind.Bool:
return bool.Parse(literal.Literal) ? "1" : "0";
default:
throw new ArgumentOutOfRangeException();
}
}
private string GenerateStructInitializer(StructInitializerNode structInitializer)
{
var structDefinition = LookupStructDefinition(structInitializer.StructType.Namespace, structInitializer.StructType.Name);
var structVar = GenVarName();
var size = SizeOf(structInitializer.StructType);
_builder.AppendLine($" {structVar} =l alloc8 {size}");
foreach (var field in structDefinition.Fields)
{
var offset = LookupMemberOffset(structDefinition, field.Name);
if (structInitializer.Initializers.TryGetValue(field.Name, out var fieldValue))
{
var value = GenerateExpression(fieldValue);
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l add {structVar}, {offset}");
GenerateCopy(field.Type, value, pointer);
}
else if (field.Value.HasValue)
{
var value = GenerateExpression(field.Value.Value);
var pointer = GenVarName();
_builder.AppendLine($" {pointer} =l add {structVar}, {offset}");
GenerateCopy(field.Type, value, pointer);
}
else
{
Debug.Assert(false);
}
}
return structVar;
}
private string GenerateUnaryExpression(UnaryExpressionNode unaryExpression)
{
var operand = GenerateExpression(unaryExpression.Operand);
var outputName = GenVarName();
switch (unaryExpression.Operator)
{
case UnaryExpressionOperator.Negate:
{
switch (unaryExpression.Operand.Type)
{
case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }:
_builder.AppendLine($" {outputName} =l neg {operand}");
return outputName;
case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }:
_builder.AppendLine($" {outputName} =w neg {operand}");
return outputName;
case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }:
_builder.AppendLine($" {outputName} =d neg {operand}");
return outputName;
case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }:
_builder.AppendLine($" {outputName} =s neg {operand}");
return outputName;
}
break;
}
case UnaryExpressionOperator.Invert:
{
switch (unaryExpression.Operand.Type)
{
case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }:
_builder.AppendLine($" {outputName} =w xor {operand}, 1");
return outputName;
}
break;
}
default:
{
throw new ArgumentOutOfRangeException();
}
}
throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported");
}
private string GenerateMemberAccessPointer(MemberAccessNode memberAccess)
{
var item = GenerateExpression(memberAccess.Expression);
switch (memberAccess.Expression.Type)
{
case NubArrayType:
{
if (memberAccess.Member == "count")
{
return item;
}
throw new UnreachableException(nameof(memberAccess.Member));
}
case NubStructType structType:
{
var structDefinition = LookupStructDefinition(structType.Namespace, structType.Name);
var offset = LookupMemberOffset(structDefinition, memberAccess.Member);
var offsetName = GenVarName();
_builder.AppendLine($" {offsetName} =l add {item}, {offset}");
return offsetName;
}
default:
{
throw new UnreachableException(nameof(memberAccess.Expression.Type));
}
}
}
private string GenerateMemberAccess(MemberAccessNode memberAccess)
{
var pointer = GenerateMemberAccessPointer(memberAccess);
if (IsLargeType(memberAccess.Type))
{
return pointer;
}
else
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} {pointer}");
return outputName;
}
}
private string GenerateFixedArrayInitializer(FixedArrayInitializerNode fixedArrayInitializer)
{
var totalSize = SizeOf(fixedArrayInitializer.Type);
var outputName = GenVarName();
_builder.AppendLine($" {outputName} =l alloc8 {totalSize}");
_builder.AppendLine($" storel {fixedArrayInitializer.Capacity}, {outputName}");
var dataPtr = GenVarName();
_builder.AppendLine($" {dataPtr} =l add {outputName}, 8");
var dataSize = totalSize - 8;
_builder.AppendLine($" call $nub_memset(l {dataPtr}, w 0, l {dataSize})");
return outputName;
}
private string GenerateFuncCall(FuncCallNode funcCall)
{
var funcType = (NubFuncType)funcCall.Expression.Type;
var parameterStrings = new List<string>();
for (var i = 0; i < funcCall.Parameters.Count; i++)
{
var parameter = funcCall.Parameters[i];
var result = GenerateExpression(parameter);
var qbeParameterType = parameter.Type switch
{
NubArrayType => "l",
NubPointerType => "l",
NubPrimitiveType pointerType => pointerType.Kind switch
{
PrimitiveTypeKind.I64 => "l",
PrimitiveTypeKind.I32 => "w",
PrimitiveTypeKind.I16 => "sh",
PrimitiveTypeKind.I8 => "sb",
PrimitiveTypeKind.U64 => "l",
PrimitiveTypeKind.U32 => "w",
PrimitiveTypeKind.U16 => "uh",
PrimitiveTypeKind.U8 => "ub",
PrimitiveTypeKind.F64 => "d",
PrimitiveTypeKind.F32 => "s",
PrimitiveTypeKind.Bool => "w",
_ => throw new ArgumentOutOfRangeException()
},
NubStructType structType => $":{structType.Namespace}_{structType.Name}",
NubFixedArrayType => "l",
NubFuncType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
};
parameterStrings.Add($"{qbeParameterType} {result}");
}
// var funcPointer = GenerateExpression(funcCall.Expression);
var funcPointerPointer = GenerateExpression(funcCall.Expression);
var funcPointer = GenVarName();
_builder.AppendLine($" {funcPointer} =l loadl {funcPointerPointer}");
if (funcType.ReturnType is not NubVoidType)
{
var outputName = GenVarName();
_builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})");
return outputName;
}
else
{
_builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})");
return "fuck";
}
}
private void GenerateCopy(NubType type, string value, string destinationPointer)
{
if (IsLargeType(type))
{
_builder.AppendLine($" blit {value}, {destinationPointer}, {SizeOf(type)}");
}
else
{
_builder.AppendLine($" {QBEStore(type)} {value}, {destinationPointer}");
}
}
private string GenerateDereference(NubType type, string pointer)
{
if (IsLargeType(type))
{
return pointer;
}
else
{
var result = GenVarName();
_builder.AppendLine($" {result} {QBEAssign(type)} {QBELoad(type)} {pointer}");
return result;
}
}
private string GenVarName()
{
return $"%v{++_variableIndex}";
}
private string GenLabelName()
{
return $"@l{++_labelIndex}";
}
private StructDefinitionNode LookupStructDefinition(string @namespace, string name)
{
return _sourceFiles
.Where(f => f.Namespace == @namespace)
.SelectMany(f => f.Definitions)
.OfType<StructDefinitionNode>()
.Single(s => s.Name == name);
}
private int LookupMemberOffset(StructDefinitionNode structDefinition, string member)
{
var offset = 0;
foreach (var field in structDefinition.Fields)
{
if (field.Name == member)
{
return offset;
}
var fieldAlignment = AlignmentOf(field.Type);
offset = AlignTo(offset, fieldAlignment);
offset += SizeOf(field.Type);
}
throw new ArgumentException($"Member '{member}' not found in struct");
}
}