using System.Text; using NubLang.Ast; namespace NubLang.Generation; public class LlvmGenerator { private string _module = string.Empty; private int _tmpIndex; private int _labelIndex; private List<(string Name, int Size, string Text)> _stringLiterals = []; private Stack<(string breakLabel, string continueLabel)> _loopStack = []; public string Emit(List topLevelNodes) { _stringLiterals = []; _loopStack = []; var writer = new IndentedTextWriter(); _module = topLevelNodes.OfType().First().NameToken.Value; writer.WriteLine($"; Module {_module}"); writer.WriteLine(); writer.WriteLine("declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)"); writer.WriteLine(); foreach (var structNode in topLevelNodes.OfType()) { var types = structNode.Fields.Select(x => MapType(x.Type)); writer.WriteLine($"%{StructName(structNode)} = type {{ {string.Join(", ", types)} }}"); writer.WriteLine(); _tmpIndex = 0; _labelIndex = 0; writer.WriteLine($"define void @{StructName(structNode)}.new(ptr %self) {{"); using (writer.Indent()) { foreach (var field in structNode.Fields) { if (field.Value != null) { var index = structNode.StructType.GetFieldIndex(field.NameToken.Value); var fieldTmp = NewTmp($"struct.field.{field.NameToken.Value}"); writer.WriteLine($"{fieldTmp} = getelementptr %{StructName(structNode)}, ptr %self, i32 0, i32 {index}"); EmitExpressionInto(writer, field.Value, fieldTmp); } } writer.WriteLine("ret void"); } writer.WriteLine("}"); writer.WriteLine(); } foreach (var funcNode in topLevelNodes.OfType()) { if (funcNode.Body != null) continue; var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}"); writer.WriteLine($"declare {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)})"); writer.WriteLine(); } foreach (var funcNode in topLevelNodes.OfType()) { if (funcNode.Body == null) continue; _tmpIndex = 0; _labelIndex = 0; var parameters = funcNode.Prototype.Parameters.Select(x => $"{MapType(x.Type)} %{x.NameToken.Value}"); writer.WriteLine($"define {MapType(funcNode.Prototype.ReturnType)} @{FuncName(funcNode.Prototype)}({string.Join(", ", parameters)}) {{"); using (writer.Indent()) { EmitBlock(writer, funcNode.Body); // note(nub31): Implicit return for void functions if (funcNode.Prototype.ReturnType is NubVoidType) { writer.WriteLine("ret void"); } } writer.WriteLine("}"); writer.WriteLine(); } foreach (var stringLiteral in _stringLiterals) { writer.WriteLine($"{stringLiteral.Name} = private unnamed_addr constant [{stringLiteral.Size} x i8] c\"{stringLiteral.Text}\\00\", align 1"); } return writer.ToString(); } private void EmitStatement(IndentedTextWriter writer, StatementNode statementNode) { switch (statementNode) { case AssignmentNode assignmentNode: EmitAssignment(writer, assignmentNode); break; case BlockNode blockNode: EmitBlock(writer, blockNode); break; case BreakNode breakNode: EmitBreak(writer, breakNode); break; case ContinueNode continueNode: EmitContinue(writer, continueNode); break; case DeferNode deferNode: EmitDefer(writer, deferNode); break; case ForConstArrayNode forConstArrayNode: EmitForConstArray(writer, forConstArrayNode); break; case ForSliceNode forSliceNode: EmitForSlice(writer, forSliceNode); break; case IfNode ifNode: EmitIf(writer, ifNode); break; case ReturnNode returnNode: EmitReturn(writer, returnNode); break; case StatementFuncCallNode statementFuncCallNode: EmitStatementFuncCall(writer, statementFuncCallNode); break; case VariableDeclarationNode variableDeclarationNode: EmitVariableDeclaration(writer, variableDeclarationNode); break; case WhileNode whileNode: EmitWhile(writer, whileNode); break; default: { throw new NotImplementedException(); } } } private void EmitAssignment(IndentedTextWriter writer, AssignmentNode assignmentNode) { var target = EmitExpression(writer, assignmentNode.Target); var value = Unwrap(writer, EmitExpression(writer, assignmentNode.Value)); writer.WriteLine($"store {MapType(assignmentNode.Value.Type)} {value}, ptr {target.Ident}"); } private void EmitBlock(IndentedTextWriter writer, BlockNode blockNode) { foreach (var statementNode in blockNode.Statements) { EmitStatement(writer, statementNode); } } private void EmitBreak(IndentedTextWriter writer, BreakNode breakNode) { var (breakLabel, _) = _loopStack.Peek(); writer.WriteLine($"br label %{breakLabel}"); } private void EmitContinue(IndentedTextWriter writer, ContinueNode continueNode) { var (_, continueLabel) = _loopStack.Peek(); writer.WriteLine($"br label %{continueLabel}"); } private void EmitDefer(IndentedTextWriter writer, DeferNode deferNode) { throw new NotImplementedException(); } private void EmitForConstArray(IndentedTextWriter writer, ForConstArrayNode forConstArrayNode) { throw new NotImplementedException(); } private void EmitForSlice(IndentedTextWriter writer, ForSliceNode forSliceNode) { throw new NotImplementedException(); } private void EmitIf(IndentedTextWriter writer, IfNode ifNode) { var endLabel = NewLabel("if.end"); EmitIf(writer, ifNode, endLabel); writer.WriteLine($"{endLabel}:"); } private void EmitIf(IndentedTextWriter writer, IfNode ifNode, string endLabel) { var condition = Unwrap(writer, EmitExpression(writer, ifNode.Condition)); var thenLabel = NewLabel("if.then"); var elseLabel = ifNode.Else.HasValue ? NewLabel("if.else") : endLabel; writer.WriteLine($"br i1 {condition}, label %{thenLabel}, label %{elseLabel}"); writer.WriteLine($"{thenLabel}:"); using (writer.Indent()) { EmitBlock(writer, ifNode.Body); writer.WriteLine($"br label %{endLabel}"); } if (!ifNode.Else.HasValue) return; writer.WriteLine($"{elseLabel}:"); using (writer.Indent()) { ifNode.Else.Value.Match( nestedElseIf => EmitIf(writer, nestedElseIf, endLabel), finalElse => { EmitBlock(writer, finalElse); writer.WriteLine($"br label %{endLabel}"); } ); } } private void EmitReturn(IndentedTextWriter writer, ReturnNode returnNode) { if (returnNode.Value != null) { var returnValue = Unwrap(writer, EmitExpression(writer, returnNode.Value)); writer.WriteLine($"ret {MapType(returnNode.Value.Type)} {returnValue}"); } else { writer.WriteLine("ret void"); } } private void EmitStatementFuncCall(IndentedTextWriter writer, StatementFuncCallNode statementFuncCallNode) { EmitFuncCall(writer, statementFuncCallNode.FuncCall); } private void EmitVariableDeclaration(IndentedTextWriter writer, VariableDeclarationNode variableDeclarationNode) { writer.WriteLine($"%{variableDeclarationNode.NameToken.Value} = alloca {MapType(variableDeclarationNode.Type)}"); if (variableDeclarationNode.Assignment != null) { EmitExpressionInto(writer, variableDeclarationNode.Assignment, $"%{variableDeclarationNode.NameToken.Value}"); } } private void EmitWhile(IndentedTextWriter writer, WhileNode whileNode) { var conditionLabel = NewLabel("while.condition"); var bodyLabel = NewLabel("while.body"); var endLabel = NewLabel("while.end"); _loopStack.Push((endLabel, conditionLabel)); writer.WriteLine($"br label %{conditionLabel}"); writer.WriteLine($"{conditionLabel}:"); using (writer.Indent()) { var condition = Unwrap(writer, EmitExpression(writer, whileNode.Condition)); writer.WriteLine($"br i1 {condition}, label %{bodyLabel}, label %{endLabel}"); } writer.WriteLine($"{bodyLabel}:"); using (writer.Indent()) { EmitBlock(writer, whileNode.Body); writer.WriteLine($"br label %{conditionLabel}"); } _loopStack.Pop(); writer.WriteLine($"{endLabel}:"); } private Tmp EmitExpression(IndentedTextWriter writer, ExpressionNode expressionNode) { return expressionNode switch { RValue rValue => EmitRValue(writer, rValue), LValue lValue => EmitLValue(writer, lValue), _ => throw new ArgumentOutOfRangeException(nameof(expressionNode)) }; } private void EmitExpressionInto(IndentedTextWriter writer, ExpressionNode expressionNode, string destination) { switch (expressionNode) { case StructInitializerNode structInitializerNode: { EmitStructInitializer(writer, structInitializerNode, destination); return; } case ConstArrayInitializerNode constArrayInitializerNode: { EmitConstArrayInitializer(writer, constArrayInitializerNode, destination); return; } } var value = Unwrap(writer, EmitExpression(writer, expressionNode)); if (expressionNode.Type.IsAggregate()) { // note(nub31): Fall back to slow method creating a copy writer.WriteLine("; Slow rvalue copy instead of direct memory write"); writer.WriteLine($"call void @llvm.memcpy.p0.p0.i64(ptr {destination}, ptr {value}, i64 {expressionNode.Type.GetSize()}, i1 false)"); } else { writer.WriteLine($"store {MapType(expressionNode.Type)} {value}, ptr {destination}"); } } private Tmp EmitRValue(IndentedTextWriter writer, RValue rValue) { return rValue switch { AddressOfNode addressOfNode => EmitAddressOf(writer, addressOfNode), BinaryExpressionNode binaryExpressionNode => EmitBinaryExpression(writer, binaryExpressionNode), BoolLiteralNode boolLiteralNode => EmitBoolLiteral(writer, boolLiteralNode), CastNode castNode => EmitCast(writer, castNode), ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(writer, constArrayInitializerNode), CStringLiteralNode cStringLiteralNode => EmitCStringLiteral(writer, cStringLiteralNode), Float32LiteralNode float32LiteralNode => EmitFloat32Literal(writer, float32LiteralNode), Float64LiteralNode float64LiteralNode => EmitFloat64Literal(writer, float64LiteralNode), FuncCallNode funcCallNode => EmitFuncCall(writer, funcCallNode), FuncIdentifierNode funcIdentifierNode => EmitFuncIdentifier(writer, funcIdentifierNode), I16LiteralNode i16LiteralNode => EmitI16Literal(writer, i16LiteralNode), I32LiteralNode i32LiteralNode => EmitI32Literal(writer, i32LiteralNode), I64LiteralNode i64LiteralNode => EmitI64Literal(writer, i64LiteralNode), I8LiteralNode i8LiteralNode => EmitI8Literal(writer, i8LiteralNode), SizeNode sizeNode => EmitSize(writer, sizeNode), StringLiteralNode stringLiteralNode => EmitStringLiteral(writer, stringLiteralNode), StructInitializerNode structInitializerNode => EmitStructInitializer(writer, structInitializerNode), U16LiteralNode u16LiteralNode => EmitU16Literal(writer, u16LiteralNode), U32LiteralNode u32LiteralNode => EmitU32Literal(writer, u32LiteralNode), U64LiteralNode u64LiteralNode => EmitU64Literal(writer, u64LiteralNode), U8LiteralNode u8LiteralNode => EmitU8Literal(writer, u8LiteralNode), UnaryExpressionNode unaryExpressionNode => EmitUnaryExpression(writer, unaryExpressionNode), _ => throw new ArgumentOutOfRangeException(nameof(rValue), rValue, null) }; } private Tmp EmitLValue(IndentedTextWriter writer, LValue lValue) { return lValue switch { ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(writer, arrayIndexAccessNode), ConstArrayIndexAccessNode constArrayIndexAccessNode => EmitConstArrayIndexAccess(writer, constArrayIndexAccessNode), DereferenceNode dereferenceNode => EmitDereference(writer, dereferenceNode), SliceIndexAccessNode sliceIndexAccessNode => EmitSliceIndexAccess(writer, sliceIndexAccessNode), StructFieldAccessNode structFieldAccessNode => EmitStructFieldAccess(writer, structFieldAccessNode), VariableIdentifierNode variableIdentifierNode => EmitVariableIdentifier(writer, variableIdentifierNode), _ => throw new ArgumentOutOfRangeException(nameof(lValue), lValue, null) }; } private Tmp EmitAddressOf(IndentedTextWriter writer, AddressOfNode addressOfNode) { var target = EmitExpression(writer, addressOfNode.Target); return new Tmp(target.Ident, addressOfNode.Type, false); } private Tmp EmitArrayIndexAccess(IndentedTextWriter writer, ArrayIndexAccessNode arrayIndexAccessNode) { var arrayPtr = Unwrap(writer, EmitExpression(writer, arrayIndexAccessNode.Target)); var index = Unwrap(writer, EmitExpression(writer, arrayIndexAccessNode.Index)); var elementType = ((NubArrayType)arrayIndexAccessNode.Target.Type).ElementType; var ptrTmp = NewTmp("array.element"); writer.WriteLine($"{ptrTmp} = getelementptr {MapType(elementType)}, ptr {arrayPtr}, {MapType(arrayIndexAccessNode.Index.Type)} {index}"); return new Tmp(ptrTmp, arrayIndexAccessNode.Type, true); } private Tmp EmitBinaryExpression(IndentedTextWriter writer, BinaryExpressionNode binaryExpressionNode) { var left = Unwrap(writer, EmitExpression(writer, binaryExpressionNode.Left)); var right = Unwrap(writer, EmitExpression(writer, binaryExpressionNode.Right)); var result = NewTmp("binop"); var leftType = binaryExpressionNode.Left.Type; var op = binaryExpressionNode.Operator; switch (op) { case BinaryOperator.Equal: case BinaryOperator.NotEqual: case BinaryOperator.GreaterThan: case BinaryOperator.GreaterThanOrEqual: case BinaryOperator.LessThan: case BinaryOperator.LessThanOrEqual: { var cmpOp = leftType switch { NubIntType intType => GenerateIntComparison(op, intType.Signed), NubFloatType => GenerateFloatComparison(op), NubBoolType => GenerateBoolComparison(op), NubPointerType => GeneratePointerComparison(op), _ => throw new InvalidOperationException($"Unexpected type for comparison: {leftType}") }; writer.WriteLine($"{result} = {cmpOp} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.LogicalAnd: { writer.WriteLine($"{result} = and i1 {left}, {right}"); break; } case BinaryOperator.LogicalOr: { writer.WriteLine($"{result} = or i1 {left}, {right}"); break; } case BinaryOperator.Plus: { var instruction = leftType switch { NubIntType => "add", NubFloatType => "fadd", _ => throw new InvalidOperationException($"Unexpected type for plus: {leftType}") }; writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.Minus: { var instruction = leftType switch { NubIntType => "sub", NubFloatType => "fsub", _ => throw new InvalidOperationException($"Unexpected type for minus: {leftType}") }; writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.Multiply: { var instruction = leftType switch { NubIntType => "mul", NubFloatType => "fmul", _ => throw new InvalidOperationException($"Unexpected type for multiply: {leftType}") }; writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.Divide: { var instruction = leftType switch { NubIntType intType => intType.Signed ? "sdiv" : "udiv", NubFloatType => "fdiv", _ => throw new InvalidOperationException($"Unexpected type for divide: {leftType}") }; writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.Modulo: { var instruction = leftType switch { NubIntType intType => intType.Signed ? "srem" : "urem", NubFloatType => "frem", _ => throw new InvalidOperationException($"Unexpected type for modulo: {leftType}") }; writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.LeftShift: { writer.WriteLine($"{result} = shl {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.RightShift: { var intType = (NubIntType)leftType; var instruction = intType.Signed ? "ashr" : "lshr"; writer.WriteLine($"{result} = {instruction} {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.BitwiseAnd: { writer.WriteLine($"{result} = and {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.BitwiseXor: { writer.WriteLine($"{result} = xor {MapType(leftType)} {left}, {right}"); break; } case BinaryOperator.BitwiseOr: { writer.WriteLine($"{result} = or {MapType(leftType)} {left}, {right}"); break; } default: throw new ArgumentOutOfRangeException(nameof(op), op, null); } return new Tmp(result, binaryExpressionNode.Type, false); } private string GenerateIntComparison(BinaryOperator op, bool signed) { return op switch { BinaryOperator.Equal => "icmp eq", BinaryOperator.NotEqual => "icmp ne", BinaryOperator.GreaterThan => signed ? "icmp sgt" : "icmp ugt", BinaryOperator.GreaterThanOrEqual => signed ? "icmp sge" : "icmp uge", BinaryOperator.LessThan => signed ? "icmp slt" : "icmp ult", BinaryOperator.LessThanOrEqual => signed ? "icmp sle" : "icmp ule", _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) }; } private string GenerateFloatComparison(BinaryOperator op) { return op switch { BinaryOperator.Equal => "fcmp oeq", BinaryOperator.NotEqual => "fcmp one", BinaryOperator.GreaterThan => "fcmp ogt", BinaryOperator.GreaterThanOrEqual => "fcmp oge", BinaryOperator.LessThan => "fcmp olt", BinaryOperator.LessThanOrEqual => "fcmp ole", _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) }; } private string GenerateBoolComparison(BinaryOperator op) { return op switch { BinaryOperator.Equal => "icmp eq", BinaryOperator.NotEqual => "icmp ne", _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) }; } private string GeneratePointerComparison(BinaryOperator op) { return op switch { BinaryOperator.Equal => "icmp eq", BinaryOperator.NotEqual => "icmp ne", BinaryOperator.GreaterThan => "icmp ugt", BinaryOperator.GreaterThanOrEqual => "icmp uge", BinaryOperator.LessThan => "icmp ult", BinaryOperator.LessThanOrEqual => "icmp ule", _ => throw new ArgumentOutOfRangeException(nameof(op), op, null) }; } private Tmp EmitBoolLiteral(IndentedTextWriter writer, BoolLiteralNode boolLiteralNode) { return new Tmp(boolLiteralNode.Value ? "1" : "0", boolLiteralNode.Type, false); } private Tmp EmitCast(IndentedTextWriter writer, CastNode castNode) { var source = Unwrap(writer, EmitExpression(writer, castNode.Value)); var sourceType = castNode.Value.Type; var targetType = castNode.Type; var result = NewTmp("cast"); switch (sourceType, targetType) { case (NubIntType sourceInt, NubIntType targetInt): { if (sourceInt.Width < targetInt.Width) { var op = sourceInt.Signed ? "sext" : "zext"; writer.WriteLine($"{result} = {op} {MapType(sourceType)} {source} to {MapType(targetType)}"); } else if (sourceInt.Width > targetInt.Width) { writer.WriteLine($"{result} = trunc {MapType(sourceType)} {source} to {MapType(targetType)}"); } else { writer.WriteLine($"{result} = bitcast {MapType(sourceType)} {source} to {MapType(targetType)}"); } break; } case (NubFloatType sourceFloat, NubFloatType targetFloat): { if (sourceFloat.Width < targetFloat.Width) { writer.WriteLine($"{result} = fpext {MapType(sourceType)} {source} to {MapType(targetType)}"); } else { writer.WriteLine($"{result} = fptrunc {MapType(sourceType)} {source} to {MapType(targetType)}"); } break; } case (NubIntType intType, NubFloatType): { var intToFloatOp = intType.Signed ? "sitofp" : "uitofp"; writer.WriteLine($"{result} = {intToFloatOp} {MapType(sourceType)} {source} to {MapType(targetType)}"); break; } case (NubFloatType, NubIntType targetInt): { var floatToIntOp = targetInt.Signed ? "fptosi" : "fptoui"; writer.WriteLine($"{result} = {floatToIntOp} {MapType(sourceType)} {source} to {MapType(targetType)}"); break; } case (NubPointerType, NubPointerType): case (NubPointerType, NubIntType): case (NubIntType, NubPointerType): { writer.WriteLine($"{result} = inttoptr {MapType(sourceType)} {source} to {MapType(targetType)}"); break; } default: { throw new NotImplementedException($"Cast from {sourceType} to {targetType} not implemented"); } } return new Tmp(result, castNode.Type, false); } private Tmp EmitConstArrayIndexAccess(IndentedTextWriter writer, ConstArrayIndexAccessNode constArrayIndexAccessNode) { var arrayPtr = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Target)); var index = Unwrap(writer, EmitExpression(writer, constArrayIndexAccessNode.Index)); var elementType = ((NubConstArrayType)constArrayIndexAccessNode.Target.Type).ElementType; var ptrTmp = NewTmp("array.element"); writer.WriteLine($"{ptrTmp} = getelementptr {MapType(elementType)}, ptr {arrayPtr}, {MapType(constArrayIndexAccessNode.Index.Type)} {index}"); return new Tmp(ptrTmp, constArrayIndexAccessNode.Type, true); } private Tmp EmitConstArrayInitializer(IndentedTextWriter writer, ConstArrayInitializerNode constArrayInitializerNode, string? destination = null) { var arrayType = (NubConstArrayType)constArrayInitializerNode.Type; if (destination == null) { destination = NewTmp("array"); writer.WriteLine($"{destination} = alloca {MapType(arrayType)}"); } for (var i = 0; i < constArrayInitializerNode.Values.Count; i++) { var value = constArrayInitializerNode.Values[i]; var indexTmp = NewTmp("array.element"); writer.WriteLine($"{indexTmp} = getelementptr {MapType(arrayType)}, ptr {destination}, i32 0, i32 {i}"); EmitExpressionInto(writer, value, indexTmp); } return new Tmp(destination, constArrayInitializerNode.Type, false); } private Tmp EmitCStringLiteral(IndentedTextWriter writer, CStringLiteralNode cStringLiteralNode) { var escaped = EscapeStringForLLVM(cStringLiteralNode.Value); var stringWithNull = cStringLiteralNode.Value + "\0"; var length = stringWithNull.Length; var globalName = $"@.str.{_stringLiterals.Count}"; _stringLiterals.Add((globalName, length, escaped)); var gepTmp = NewTmp("str.ptr"); writer.WriteLine($"{gepTmp} = getelementptr [{length} x i8], ptr {globalName}, i32 0, i32 0"); return new Tmp(gepTmp, cStringLiteralNode.Type, false); } private string EscapeStringForLLVM(string input) { var result = new StringBuilder(); foreach (var c in input) { if (c == '\0') result.Append("\\00"); else if (c == '\n') result.Append("\\0A"); else if (c == '\r') result.Append("\\0D"); else if (c == '\t') result.Append("\\09"); else if (c == '\\') result.Append("\\\\"); else if (c == '"') result.Append("\\22"); else if (c < 32 || c > 126) result.Append($"\\{(int)c:X2}"); else result.Append(c); } return result.ToString(); } private Tmp EmitDereference(IndentedTextWriter writer, DereferenceNode dereferenceNode) { throw new NotImplementedException(); } private Tmp EmitFloat32Literal(IndentedTextWriter writer, Float32LiteralNode float32LiteralNode) { var literal = ((double)float32LiteralNode.Value).ToString("R", System.Globalization.CultureInfo.InvariantCulture); if (!literal.Contains('.')) { literal += ".0"; } return new Tmp(literal, float32LiteralNode.Type, false); } private Tmp EmitFloat64Literal(IndentedTextWriter writer, Float64LiteralNode float64LiteralNode) { var literal = float64LiteralNode.Value.ToString("R", System.Globalization.CultureInfo.InvariantCulture); if (!literal.Contains('.')) { literal += ".0"; } return new Tmp(literal, float64LiteralNode.Type, false); } private Tmp EmitFuncCall(IndentedTextWriter writer, FuncCallNode funcCallNode) { var result = NewTmp(); var parameterStrings = new List(); foreach (var parameter in funcCallNode.Parameters) { var value = Unwrap(writer, EmitExpression(writer, parameter)); parameterStrings.Add($"{MapType(parameter.Type)} {value}"); } var functionPtr = Unwrap(writer, EmitExpression(writer, funcCallNode.Expression)); if (funcCallNode.Type is NubVoidType) { writer.WriteLine($"call {MapType(funcCallNode.Type)} {functionPtr}({string.Join(", ", parameterStrings)})"); } else { writer.WriteLine($"{result} = call {MapType(funcCallNode.Type)} {functionPtr}({string.Join(", ", parameterStrings)})"); } return new Tmp(result, funcCallNode.Type, false); } private Tmp EmitFuncIdentifier(IndentedTextWriter writer, FuncIdentifierNode funcIdentifierNode) { var name = FuncName(funcIdentifierNode.ModuleToken.Value, funcIdentifierNode.NameToken.Value, funcIdentifierNode.ExternSymbolToken?.Value); return new Tmp($"@{name}", funcIdentifierNode.Type, false); } private Tmp EmitI16Literal(IndentedTextWriter writer, I16LiteralNode i16LiteralNode) { return new Tmp(i16LiteralNode.Value.ToString(), i16LiteralNode.Type, false); } private Tmp EmitI32Literal(IndentedTextWriter writer, I32LiteralNode i32LiteralNode) { return new Tmp(i32LiteralNode.Value.ToString(), i32LiteralNode.Type, false); } private Tmp EmitI64Literal(IndentedTextWriter writer, I64LiteralNode i64LiteralNode) { return new Tmp(i64LiteralNode.Value.ToString(), i64LiteralNode.Type, false); } private Tmp EmitI8Literal(IndentedTextWriter writer, I8LiteralNode i8LiteralNode) { return new Tmp(i8LiteralNode.Value.ToString(), i8LiteralNode.Type, false); } private Tmp EmitSize(IndentedTextWriter writer, SizeNode sizeNode) { return new Tmp(sizeNode.TargetType.GetSize().ToString(), sizeNode.Type, false); } private Tmp EmitSliceIndexAccess(IndentedTextWriter writer, SliceIndexAccessNode sliceIndexAccessNode) { throw new NotImplementedException(); } private Tmp EmitStringLiteral(IndentedTextWriter writer, StringLiteralNode stringLiteralNode) { throw new NotImplementedException(); } private Tmp EmitStructFieldAccess(IndentedTextWriter writer, StructFieldAccessNode structFieldAccessNode) { var target = Unwrap(writer, EmitExpression(writer, structFieldAccessNode.Target)); var structType = (NubStructType)structFieldAccessNode.Target.Type; var index = structType.GetFieldIndex(structFieldAccessNode.FieldToken.Value); var ptrTmp = NewTmp($"struct.field.{structFieldAccessNode.FieldToken.Value}"); writer.WriteLine($"{ptrTmp} = getelementptr %{StructName(structType.Module, structType.Name)}, ptr {target}, i32 0, i32 {index}"); return new Tmp(ptrTmp, structFieldAccessNode.Type, true); } private Tmp EmitStructInitializer(IndentedTextWriter writer, StructInitializerNode structInitializerNode, string? destination = null) { if (destination == null) { destination = NewTmp("struct"); writer.WriteLine($"{destination} = alloca {MapType(structInitializerNode.Type)}"); } var structType = (NubStructType)structInitializerNode.Type; writer.WriteLine($"call void @{StructName(structType.Module, structType.Name)}.new(ptr {destination})"); foreach (var (name, value) in structInitializerNode.Initializers) { var index = structType.GetFieldIndex(name.Value); var fieldTmp = NewTmp($"struct.field.{name}"); writer.WriteLine($"{fieldTmp} = getelementptr %{StructName(structType.Module, structType.Name)}, ptr {destination}, i32 0, i32 {index}"); EmitExpressionInto(writer, value, fieldTmp); } return new Tmp(destination, structInitializerNode.Type, false); } private Tmp EmitU16Literal(IndentedTextWriter writer, U16LiteralNode u16LiteralNode) { return new Tmp(u16LiteralNode.Value.ToString(), u16LiteralNode.Type, false); } private Tmp EmitU32Literal(IndentedTextWriter writer, U32LiteralNode u32LiteralNode) { return new Tmp(u32LiteralNode.Value.ToString(), u32LiteralNode.Type, false); } private Tmp EmitU64Literal(IndentedTextWriter writer, U64LiteralNode u64LiteralNode) { return new Tmp(u64LiteralNode.Value.ToString(), u64LiteralNode.Type, false); } private Tmp EmitU8Literal(IndentedTextWriter writer, U8LiteralNode u8LiteralNode) { return new Tmp(u8LiteralNode.Value.ToString(), u8LiteralNode.Type, false); } private Tmp EmitUnaryExpression(IndentedTextWriter writer, UnaryExpressionNode unaryExpressionNode) { var operand = Unwrap(writer, EmitExpression(writer, unaryExpressionNode.Operand)); var result = NewTmp("unary"); switch (unaryExpressionNode.Operator) { case UnaryOperator.Negate: switch (unaryExpressionNode.Operand.Type) { case NubIntType intType: writer.WriteLine($"{result} = sub {MapType(intType)} 0, {operand}"); break; case NubFloatType floatType: writer.WriteLine($"{result} = fneg {MapType(floatType)} {operand}"); break; default: throw new ArgumentOutOfRangeException(); } break; case UnaryOperator.Invert: writer.WriteLine($"{result} = xor i1 {operand}, true"); break; default: throw new ArgumentOutOfRangeException(); } return new Tmp(result, unaryExpressionNode.Type, false); } private Tmp EmitVariableIdentifier(IndentedTextWriter writer, VariableIdentifierNode variableIdentifierNode) { return new Tmp($"%{variableIdentifierNode.NameToken.Value}", variableIdentifierNode.Type, true); } private string StructName(StructNode structNode) { return StructName(_module, structNode.NameToken.Value); } private string StructName(string module, string name) { return $"struct.{module}.{name}"; } private string FuncName(FuncPrototypeNode funcNodePrototype) { return FuncName(_module, funcNodePrototype.NameToken.Value, funcNodePrototype.ExternSymbolToken?.Value); } private string FuncName(string module, string name, string? externSymbol) { if (externSymbol != null) { return externSymbol; } return $"{module}.{name}"; } private string MapType(NubType type) { return type switch { NubArrayType arrayType => $"{MapType(arrayType.ElementType)}*", NubBoolType => "i1", NubConstArrayType constArrayType => $"[{constArrayType.Size} x {MapType(constArrayType.ElementType)}]", NubFloatType floatType => floatType.Width == 32 ? "float" : "double", NubFuncType funcType => MapFuncType(funcType), NubIntType intType => $"i{intType.Width}", NubPointerType pointerType => $"{MapType(pointerType.BaseType)}*", NubSliceType sliceType => throw new NotImplementedException(), NubStringType stringType => throw new NotImplementedException(), NubStructType structType => $"%{StructName(structType.Module, structType.Name)}", NubVoidType => "void", _ => throw new ArgumentOutOfRangeException(nameof(type)) }; } private string MapFuncType(NubFuncType funcType) { var paramTypes = string.Join(", ", funcType.Parameters.Select(MapType)); var returnType = MapType(funcType.ReturnType); return $"{returnType} ({paramTypes})*"; } private record Tmp(string Ident, NubType Type, bool LValue); private string Unwrap(IndentedTextWriter writer, Tmp tmp) { if (tmp.LValue && !tmp.Type.IsAggregate()) { var newTmp = NewTmp("deref"); writer.WriteLine($"{newTmp} = load {MapType(tmp.Type)}, ptr {tmp.Ident}"); return newTmp; } return tmp.Ident; } private string NewTmp(string name = "t") { return $"%{name}.{++_tmpIndex}"; } private string NewLabel(string name = "l") { return $"{name}.{++_labelIndex}"; } }