using System.Text; using LLVMSharp.Interop; using NubLang.Ast; using NubLang.Modules; using NubLang.Types; namespace NubLang.Generation; public class LlvmSharpGenerator { private string _module = string.Empty; private LLVMContextRef _context; private LLVMModuleRef _llvmModule; private LLVMBuilderRef _builder; private readonly Dictionary _structTypes = new(); private readonly Dictionary _functions = new(); private readonly Dictionary _locals = new(); private readonly Stack<(LLVMBasicBlockRef breakBlock, LLVMBasicBlockRef continueBlock)> _loopStack = new(); public void Emit(List topLevelNodes, ModuleRepository repository, string sourceFileName, string outputPath) { _module = topLevelNodes.OfType().First().NameToken.Value; _context = LLVMContextRef.Global; _llvmModule = _context.CreateModuleWithName(sourceFileName); _llvmModule.Target = "x86_64-pc-linux-gnu"; _llvmModule.DataLayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"; _builder = _context.CreateBuilder(); _structTypes.Clear(); _functions.Clear(); _locals.Clear(); _loopStack.Clear(); var stringType = _context.CreateNamedStruct("nub.string"); stringType.StructSetBody([LLVMTypeRef.Int64, LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0)], false); _structTypes["nub.string"] = stringType; foreach (var module in repository.GetAll()) { foreach (var structType in module.StructTypes) { var structName = StructName(structType.Module, structType.Name); var llvmStructType = _context.CreateNamedStruct(structName); _structTypes[structName] = llvmStructType; } } foreach (var module in repository.GetAll()) { foreach (var structType in module.StructTypes) { var structName = StructName(structType.Module, structType.Name); var llvmStructType = _structTypes[structName]; var fieldTypes = structType.Fields.Select(f => MapType(f.Type)).ToArray(); llvmStructType.StructSetBody(fieldTypes, false); } } foreach (var module in repository.GetAll()) { foreach (var prototype in module.FunctionPrototypes) { CreateFunctionDeclaration(prototype, module.Name); } } foreach (var structNode in topLevelNodes.OfType()) { EmitStructConstructor(structNode); } foreach (var funcNode in topLevelNodes.OfType()) { if (funcNode.Body != null) { EmitFunction(funcNode); } } if (!_llvmModule.TryVerify(LLVMVerifierFailureAction.LLVMPrintMessageAction, out var error)) { // throw new Exception($"LLVM module verification failed: {error}"); } _llvmModule.PrintToFile(outputPath); _builder.Dispose(); } private void CreateFunctionDeclaration(FuncPrototypeNode prototype, string moduleName) { var funcName = FuncName(moduleName, prototype.NameToken.Value, prototype.ExternSymbolToken?.Value); var paramTypes = prototype.Parameters.Select(p => MapType(p.Type)).ToArray(); var returnType = MapType(prototype.ReturnType); var funcType = LLVMTypeRef.CreateFunction(returnType, paramTypes); var func = _llvmModule.AddFunction(funcName, funcType); func.FunctionCallConv = (uint)LLVMCallConv.LLVMCCallConv; for (var i = 0; i < prototype.Parameters.Count; i++) { func.GetParam((uint)i).Name = prototype.Parameters[i].NameToken.Value; } _functions[funcName] = func; } private void EmitStructConstructor(StructNode structNode) { var structType = _structTypes[StructName(_module, structNode.NameToken.Value)]; var ptrType = LLVMTypeRef.CreatePointer(structType, 0); var funcType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, [ptrType]); var funcName = StructConstructorName(_module, structNode.NameToken.Value); var func = _llvmModule.AddFunction(funcName, funcType); func.FunctionCallConv = (uint)LLVMCallConv.LLVMCCallConv; var entryBlock = func.AppendBasicBlock("entry"); _builder.PositionAtEnd(entryBlock); var selfParam = func.GetParam(0); selfParam.Name = "self"; _locals.Clear(); foreach (var field in structNode.Fields) { if (field.Value != null) { var index = structNode.StructType.GetFieldIndex(field.NameToken.Value); var fieldPtr = _builder.BuildStructGEP2(structType, selfParam, (uint)index); EmitExpressionInto(field.Value, fieldPtr); } } _builder.BuildRetVoid(); _functions[funcName] = func; } private void EmitFunction(FuncNode funcNode) { var funcName = FuncName(_module, funcNode.Prototype.NameToken.Value, funcNode.Prototype.ExternSymbolToken?.Value); var func = _functions[funcName]; var entryBlock = func.AppendBasicBlock("entry"); _builder.PositionAtEnd(entryBlock); _locals.Clear(); for (uint i = 0; i < funcNode.Prototype.Parameters.Count; i++) { var param = func.GetParam(i); var paramNode = funcNode.Prototype.Parameters[(int)i]; var alloca = _builder.BuildAlloca(MapType(paramNode.Type), paramNode.NameToken.Value); _builder.BuildStore(param, alloca); _locals[paramNode.NameToken.Value] = alloca; } EmitBlock(funcNode.Body!); if (funcNode.Prototype.ReturnType is NubVoidType) { if (_builder.InsertBlock.Terminator.Handle == IntPtr.Zero) { _builder.BuildRetVoid(); } } } private void EmitBlock(BlockNode blockNode) { foreach (var statement in blockNode.Statements) { EmitStatement(statement); } } private void EmitStatement(StatementNode statement) { switch (statement) { case AssignmentNode assignment: EmitAssignment(assignment); break; case BlockNode block: EmitBlock(block); break; case BreakNode: EmitBreak(); break; case ContinueNode: EmitContinue(); break; case IfNode ifNode: EmitIf(ifNode); break; case ReturnNode returnNode: EmitReturn(returnNode); break; case StatementFuncCallNode funcCall: EmitExpression(funcCall.FuncCall); break; case VariableDeclarationNode varDecl: EmitVariableDeclaration(varDecl); break; case WhileNode whileNode: EmitWhile(whileNode); break; default: throw new NotImplementedException($"Statement type {statement.GetType()} not implemented"); } } private void EmitAssignment(AssignmentNode assignment) { var targetPtr = EmitExpression(assignment.Target, asLValue: true); var value = EmitExpression(assignment.Value); _builder.BuildStore(value, targetPtr); } private void EmitBreak() { var (breakBlock, _) = _loopStack.Peek(); _builder.BuildBr(breakBlock); } private void EmitContinue() { var (_, continueBlock) = _loopStack.Peek(); _builder.BuildBr(continueBlock); } private void EmitIf(IfNode ifNode) { var condition = EmitExpression(ifNode.Condition); var func = _builder.InsertBlock.Parent; var thenBlock = func.AppendBasicBlock("if.then"); var elseBlock = ifNode.Else.HasValue ? func.AppendBasicBlock("if.else") : default; var endBlock = func.AppendBasicBlock("if.end"); _builder.BuildCondBr(condition, thenBlock, ifNode.Else.HasValue ? elseBlock : endBlock); _builder.PositionAtEnd(thenBlock); EmitBlock(ifNode.Body); if (_builder.InsertBlock.Terminator.Handle == IntPtr.Zero) { _builder.BuildBr(endBlock); } if (ifNode.Else.HasValue) { _builder.PositionAtEnd(elseBlock); ifNode.Else.Value.Match(EmitIf, EmitBlock); if (_builder.InsertBlock.Terminator.Handle == IntPtr.Zero) { _builder.BuildBr(endBlock); } } _builder.PositionAtEnd(endBlock); } private void EmitReturn(ReturnNode returnNode) { if (returnNode.Value != null) { var value = EmitExpression(returnNode.Value); _builder.BuildRet(value); } else { _builder.BuildRetVoid(); } } private void EmitVariableDeclaration(VariableDeclarationNode varDecl) { var alloca = _builder.BuildAlloca(MapType(varDecl.Type), varDecl.NameToken.Value); _locals[varDecl.NameToken.Value] = alloca; if (varDecl.Assignment != null) { EmitExpressionInto(varDecl.Assignment, alloca); } } private void EmitWhile(WhileNode whileNode) { var func = _builder.InsertBlock.Parent; var condBlock = func.AppendBasicBlock("while.cond"); var bodyBlock = func.AppendBasicBlock("while.body"); var endBlock = func.AppendBasicBlock("while.end"); _loopStack.Push((endBlock, condBlock)); _builder.BuildBr(condBlock); _builder.PositionAtEnd(condBlock); var condition = EmitExpression(whileNode.Condition); _builder.BuildCondBr(condition, bodyBlock, endBlock); _builder.PositionAtEnd(bodyBlock); EmitBlock(whileNode.Body); if (_builder.InsertBlock.Terminator.Handle == IntPtr.Zero) { _builder.BuildBr(condBlock); } _loopStack.Pop(); _builder.PositionAtEnd(endBlock); } private LLVMValueRef EmitExpression(ExpressionNode expr, bool asLValue = false) { var result = expr switch { StringLiteralNode stringLiteralNode => EmitStringLiteral(stringLiteralNode), CStringLiteralNode cStringLiteralNode => EmitCStringLiteral(cStringLiteralNode), BoolLiteralNode b => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int1, b.Value ? 1UL : 0UL), I8LiteralNode i => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int8, (ulong)i.Value, true), I16LiteralNode i => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int16, (ulong)i.Value, true), I32LiteralNode i => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)i.Value, true), I64LiteralNode i => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, (ulong)i.Value, true), U8LiteralNode u => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int8, u.Value), U16LiteralNode u => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int16, u.Value), U32LiteralNode u => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, u.Value), U64LiteralNode u => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, u.Value), Float32LiteralNode f => LLVMValueRef.CreateConstReal(LLVMTypeRef.Float, f.Value), Float64LiteralNode f => LLVMValueRef.CreateConstReal(LLVMTypeRef.Double, f.Value), VariableIdentifierNode v => EmitVariableIdentifier(v), LocalFuncIdentifierNode localFuncIdentifierNode => EmitLocalFuncIdentifier(localFuncIdentifierNode), ModuleFuncIdentifierNode moduleFuncIdentifierNode => EmitModuleFuncIdentifier(moduleFuncIdentifierNode), BinaryExpressionNode bin => EmitBinaryExpression(bin), UnaryExpressionNode unary => EmitUnaryExpression(unary), StructFieldAccessNode field => EmitStructFieldAccess(field), ConstArrayIndexAccessNode arr => EmitConstArrayIndexAccess(arr), SliceIndexAccessNode sliceIndexAccessNode => EmitSliceIndexAccess(sliceIndexAccessNode), ArrayIndexAccessNode arrayIndexAccessNode => EmitArrayIndexAccess(arrayIndexAccessNode), ConstArrayInitializerNode constArrayInitializerNode => EmitConstArrayInitializer(constArrayInitializerNode), StructInitializerNode structInitializerNode => EmitStructInitializer(structInitializerNode), AddressOfNode addr => EmitAddressOf(addr), DereferenceNode deref => EmitDereference(deref), FuncCallNode funcCall => EmitFuncCall(funcCall), CastNode cast => EmitCast(cast), SizeNode size => LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, size.TargetType.GetSize()), _ => throw new ArgumentOutOfRangeException(nameof(expr), expr, null) }; if (expr is LValue) { if (asLValue) { return result; } return _builder.BuildLoad2(MapType(expr.Type), result); } if (asLValue) { throw new InvalidOperationException($"Expression of type {expr.GetType().Name} is not an lvalue and cannot be used where an address is required"); } return result; } private void EmitExpressionInto(ExpressionNode expr, LLVMValueRef destPtr) { switch (expr) { case StructInitializerNode structInit: EmitStructInitializer(structInit, destPtr); return; case ConstArrayInitializerNode arrayInit: EmitConstArrayInitializer(arrayInit, destPtr); return; default: { var result = EmitExpression(expr); _builder.BuildStore(result, destPtr); break; } } } private LLVMValueRef EmitStringLiteral(StringLiteralNode stringLiteralNode) { var strValue = stringLiteralNode.Value; var length = (ulong)Encoding.UTF8.GetByteCount(strValue); var globalStr = _builder.BuildGlobalStringPtr(strValue); var llvmStringType = MapType(stringLiteralNode.Type); var strAlloca = _builder.BuildAlloca(llvmStringType); var lengthPtr = _builder.BuildStructGEP2(llvmStringType, strAlloca, 0); var lengthConst = LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, length); _builder.BuildStore(lengthConst, lengthPtr); var dataPtr = _builder.BuildStructGEP2(llvmStringType, strAlloca, 1); _builder.BuildStore(globalStr, dataPtr); return _builder.BuildLoad2(llvmStringType, strAlloca); } private LLVMValueRef EmitCStringLiteral(CStringLiteralNode cStringLiteralNode) { return _builder.BuildGlobalStringPtr(cStringLiteralNode.Value); } private LLVMValueRef EmitVariableIdentifier(VariableIdentifierNode v) { return _locals[v.NameToken.Value]; } private LLVMValueRef EmitLocalFuncIdentifier(LocalFuncIdentifierNode localFuncIdentifierNode) { return _functions[FuncName(_module, localFuncIdentifierNode.NameToken.Value, localFuncIdentifierNode.ExternSymbolToken?.Value)]; } private LLVMValueRef EmitModuleFuncIdentifier(ModuleFuncIdentifierNode moduleFuncIdentifierNode) { return _functions[FuncName(moduleFuncIdentifierNode.ModuleToken.Value, moduleFuncIdentifierNode.NameToken.Value, moduleFuncIdentifierNode.ExternSymbolToken?.Value)]; } private LLVMValueRef EmitBinaryExpression(BinaryExpressionNode bin) { var left = EmitExpression(bin.Left); var right = EmitExpression(bin.Right); var leftType = bin.Left.Type; var result = bin.Operator switch { BinaryOperator.Plus when leftType is NubIntType => _builder.BuildAdd(left, right), BinaryOperator.Plus when leftType is NubFloatType => _builder.BuildFAdd(left, right), BinaryOperator.Minus when leftType is NubIntType => _builder.BuildSub(left, right), BinaryOperator.Minus when leftType is NubFloatType => _builder.BuildFSub(left, right), BinaryOperator.Multiply when leftType is NubIntType => _builder.BuildMul(left, right), BinaryOperator.Multiply when leftType is NubFloatType => _builder.BuildFMul(left, right), BinaryOperator.Divide when leftType is NubIntType intType => intType.Signed ? _builder.BuildSDiv(left, right) : _builder.BuildUDiv(left, right), BinaryOperator.Divide when leftType is NubFloatType => _builder.BuildFDiv(left, right), BinaryOperator.Modulo when leftType is NubIntType intType => intType.Signed ? _builder.BuildSRem(left, right) : _builder.BuildURem(left, right), BinaryOperator.Modulo when leftType is NubFloatType => _builder.BuildFRem(left, right), BinaryOperator.LogicalAnd => _builder.BuildAnd(left, right), BinaryOperator.LogicalOr => _builder.BuildOr(left, right), BinaryOperator.Equal when leftType is NubIntType or NubBoolType or NubPointerType => _builder.BuildICmp(LLVMIntPredicate.LLVMIntEQ, left, right), BinaryOperator.Equal when leftType is NubFloatType => _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOEQ, left, right), BinaryOperator.NotEqual when leftType is NubIntType or NubBoolType or NubPointerType => _builder.BuildICmp(LLVMIntPredicate.LLVMIntNE, left, right), BinaryOperator.NotEqual when leftType is NubFloatType => _builder.BuildFCmp(LLVMRealPredicate.LLVMRealONE, left, right), BinaryOperator.GreaterThan when leftType is NubIntType intType => _builder.BuildICmp(intType.Signed ? LLVMIntPredicate.LLVMIntSGT : LLVMIntPredicate.LLVMIntUGT, left, right), BinaryOperator.GreaterThan when leftType is NubFloatType => _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOGT, left, right), BinaryOperator.GreaterThanOrEqual when leftType is NubIntType intType => _builder.BuildICmp(intType.Signed ? LLVMIntPredicate.LLVMIntSGE : LLVMIntPredicate.LLVMIntUGE, left, right), BinaryOperator.GreaterThanOrEqual when leftType is NubFloatType => _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOGE, left, right), BinaryOperator.LessThan when leftType is NubIntType intType => _builder.BuildICmp(intType.Signed ? LLVMIntPredicate.LLVMIntSLT : LLVMIntPredicate.LLVMIntULT, left, right), BinaryOperator.LessThan when leftType is NubFloatType => _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOLT, left, right), BinaryOperator.LessThanOrEqual when leftType is NubIntType intType => _builder.BuildICmp(intType.Signed ? LLVMIntPredicate.LLVMIntSLE : LLVMIntPredicate.LLVMIntULE, left, right), BinaryOperator.LessThanOrEqual when leftType is NubFloatType => _builder.BuildFCmp(LLVMRealPredicate.LLVMRealOLE, left, right), BinaryOperator.LeftShift => _builder.BuildShl(left, right), BinaryOperator.RightShift when leftType is NubIntType intType => intType.Signed ? _builder.BuildAShr(left, right) : _builder.BuildLShr(left, right), BinaryOperator.BitwiseAnd => _builder.BuildAnd(left, right), BinaryOperator.BitwiseXor => _builder.BuildXor(left, right), BinaryOperator.BitwiseOr => _builder.BuildOr(left, right), _ => throw new ArgumentOutOfRangeException(nameof(bin.Operator)) }; return result; } private LLVMValueRef EmitUnaryExpression(UnaryExpressionNode unary) { var operand = EmitExpression(unary.Operand); var result = unary.Operator switch { UnaryOperator.Negate when unary.Operand.Type is NubIntType => _builder.BuildNeg(operand), UnaryOperator.Negate when unary.Operand.Type is NubFloatType => _builder.BuildFNeg(operand), UnaryOperator.Invert => _builder.BuildXor(operand, LLVMValueRef.CreateConstInt(LLVMTypeRef.Int1, 1)), _ => throw new ArgumentOutOfRangeException(nameof(unary.Operator)) }; return result; } private LLVMValueRef EmitFuncCall(FuncCallNode funcCall) { var funcPtr = EmitExpression(funcCall.Expression); var args = funcCall.Parameters.Select(x => EmitExpression(x)).ToArray(); return _builder.BuildCall2(MapType(funcCall.Expression.Type), funcPtr, args, funcCall.Type is NubVoidType ? "" : "call"); } private LLVMValueRef EmitStructFieldAccess(StructFieldAccessNode field) { var target = EmitExpression(field.Target, asLValue: true); var structType = (NubStructType)field.Target.Type; var index = structType.GetFieldIndex(field.FieldToken.Value); var llvmStructType = _structTypes[StructName(structType.Module, structType.Name)]; return _builder.BuildStructGEP2(llvmStructType, target, (uint)index); } private LLVMValueRef EmitConstArrayIndexAccess(ConstArrayIndexAccessNode constArrayIndexAccessNode) { var arrayPtr = EmitExpression(constArrayIndexAccessNode.Target, asLValue: true); var index = EmitExpression(constArrayIndexAccessNode.Index); var indices = new[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0), index }; return _builder.BuildInBoundsGEP2(MapType(constArrayIndexAccessNode.Target.Type), arrayPtr, indices); } private LLVMValueRef EmitSliceIndexAccess(SliceIndexAccessNode sliceIndexAccessNode) { var slicePtr = EmitExpression(sliceIndexAccessNode.Target, asLValue: true); var index = EmitExpression(sliceIndexAccessNode.Index); var sliceType = (NubSliceType)sliceIndexAccessNode.Target.Type; var llvmSliceType = MapType(sliceType); var elementType = MapType(sliceType.ElementType); var dataPtrPtr = _builder.BuildStructGEP2(llvmSliceType, slicePtr, 1); var dataPtr = _builder.BuildLoad2(LLVMTypeRef.CreatePointer(elementType, 0), dataPtrPtr); return _builder.BuildInBoundsGEP2(elementType, dataPtr, [index]); } private LLVMValueRef EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccessNode) { var arrayPtr = EmitExpression(arrayIndexAccessNode.Target); var index = EmitExpression(arrayIndexAccessNode.Index); return _builder.BuildGEP2(MapType(arrayIndexAccessNode.Target.Type), arrayPtr, [index]); } private LLVMValueRef EmitConstArrayInitializer(ConstArrayInitializerNode constArrayInitializerNode, LLVMValueRef? destination = null) { var arrayType = (NubConstArrayType)constArrayInitializerNode.Type; var llvmType = MapType(arrayType); destination ??= _builder.BuildAlloca(llvmType); for (var i = 0; i < constArrayInitializerNode.Values.Count; i++) { var indices = new[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0), LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, (ulong)i) }; var elementPtr = _builder.BuildInBoundsGEP2(llvmType, destination.Value, indices); EmitExpressionInto(constArrayInitializerNode.Values[i], elementPtr); } return destination.Value; } private LLVMValueRef EmitStructInitializer(StructInitializerNode structInitializerNode, LLVMValueRef? destination = null) { var type = (NubStructType)structInitializerNode.Type; var llvmType = MapType(type); destination ??= _builder.BuildAlloca(llvmType); var constructorType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Void, [LLVMTypeRef.CreatePointer(_structTypes[StructName(type.Module, type.Name)], 0)]); var constructor = _functions[StructConstructorName(type.Module, type.Name)]; _builder.BuildCall2(constructorType, constructor, [destination.Value]); foreach (var (name, value) in structInitializerNode.Initializers) { var fieldIndex = type.GetFieldIndex(name.Value); var fieldPtr = _builder.BuildStructGEP2(llvmType, destination.Value, (uint)fieldIndex); EmitExpressionInto(value, fieldPtr); } return destination.Value; } private LLVMValueRef EmitAddressOf(AddressOfNode addr) { return EmitExpression(addr.Target, asLValue: true); } private LLVMValueRef EmitDereference(DereferenceNode deref) { return EmitExpression(deref.Target, asLValue: false); } private LLVMValueRef EmitCast(CastNode castNode) { return castNode.ConversionType switch { CastNode.Conversion.IntToInt => EmitIntToIntCast(castNode), CastNode.Conversion.FloatToFloat => EmitFloatToFloatCast(castNode), CastNode.Conversion.IntToFloat => EmitIntToFloatCast(castNode), CastNode.Conversion.FloatToInt => EmitFloatToIntCast(castNode), CastNode.Conversion.PointerToPointer or CastNode.Conversion.PointerToUInt64 or CastNode.Conversion.UInt64ToPointer => _builder.BuildIntToPtr(EmitExpression(castNode.Value), MapType(castNode.Type)), CastNode.Conversion.ConstArrayToSlice => EmitConstArrayToSliceCast(castNode), CastNode.Conversion.ConstArrayToArray => EmitConstArrayToArrayCast(castNode), CastNode.Conversion.StringToCString => EmitStringToCStringCast(castNode), _ => throw new ArgumentOutOfRangeException(nameof(castNode.ConversionType)) }; } private LLVMValueRef EmitIntToIntCast(CastNode castNode) { var sourceInt = (NubIntType)castNode.Value.Type; var targetInt = (NubIntType)castNode.Type; var source = EmitExpression(castNode.Value); if (sourceInt.Width < targetInt.Width) { return sourceInt.Signed ? _builder.BuildSExt(source, MapType(targetInt)) : _builder.BuildZExt(source, MapType(targetInt)); } if (sourceInt.Width > targetInt.Width) { return _builder.BuildTrunc(source, MapType(targetInt)); } return _builder.BuildBitCast(source, MapType(targetInt)); } private LLVMValueRef EmitFloatToFloatCast(CastNode castNode) { var sourceFloat = (NubFloatType)castNode.Value.Type; var targetFloat = (NubFloatType)castNode.Type; var source = EmitExpression(castNode.Value); return sourceFloat.Width < targetFloat.Width ? _builder.BuildFPExt(source, MapType(castNode.Type)) : _builder.BuildFPTrunc(source, MapType(castNode.Type)); } private LLVMValueRef EmitIntToFloatCast(CastNode castNode) { var sourceInt = (NubIntType)castNode.Value.Type; var source = EmitExpression(castNode.Value); return sourceInt.Signed ? _builder.BuildSIToFP(source, MapType(castNode.Type)) : _builder.BuildUIToFP(source, MapType(castNode.Type)); } private LLVMValueRef EmitFloatToIntCast(CastNode castNode) { var targetInt = (NubIntType)castNode.Type; var source = EmitExpression(castNode.Value); return targetInt.Signed ? _builder.BuildFPToSI(source, MapType(targetInt)) : _builder.BuildFPToUI(source, MapType(targetInt)); } private LLVMValueRef EmitConstArrayToSliceCast(CastNode castNode) { var sourceArrayType = (NubConstArrayType)castNode.Value.Type; var targetSliceType = (NubSliceType)castNode.Type; var source = EmitExpression(castNode.Value, asLValue: true); var llvmArrayType = MapType(sourceArrayType); var llvmSliceType = MapType(targetSliceType); var indices = new[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0), LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0) }; var firstElementPtr = _builder.BuildInBoundsGEP2(llvmArrayType, source, indices); var slicePtr = _builder.BuildAlloca(llvmSliceType); var lengthPtr = _builder.BuildStructGEP2(llvmSliceType, slicePtr, 0); var length = LLVMValueRef.CreateConstInt(LLVMTypeRef.Int64, sourceArrayType.Size); _builder.BuildStore(length, lengthPtr); var dataPtrPtr = _builder.BuildStructGEP2(llvmSliceType, slicePtr, 1); _builder.BuildStore(firstElementPtr, dataPtrPtr); return _builder.BuildLoad2(llvmSliceType, slicePtr); } private LLVMValueRef EmitConstArrayToArrayCast(CastNode castNode) { var sourceArrayType = (NubConstArrayType)castNode.Value.Type; var source = EmitExpression(castNode.Value, asLValue: true); var indices = new[] { LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0), LLVMValueRef.CreateConstInt(LLVMTypeRef.Int32, 0) }; return _builder.BuildInBoundsGEP2(MapType(sourceArrayType), source, indices); } private LLVMValueRef EmitStringToCStringCast(CastNode castNode) { var source = EmitExpression(castNode.Value, asLValue: true); var dataPtrPtr = _builder.BuildStructGEP2(MapType(castNode.Value.Type), source, 1); return _builder.BuildLoad2(LLVMTypeRef.CreatePointer(LLVMTypeRef.Int8, 0), dataPtrPtr); } private LLVMTypeRef MapType(NubType type) { return type switch { NubBoolType => LLVMTypeRef.Int1, NubIntType intType => LLVMTypeRef.CreateInt((uint)intType.Width), NubFloatType floatType => floatType.Width == 32 ? LLVMTypeRef.Float : LLVMTypeRef.Double, NubFuncType funcType => LLVMTypeRef.CreateFunction(MapType(funcType.ReturnType), funcType.Parameters.Select(MapType).ToArray()), NubPointerType ptrType => LLVMTypeRef.CreatePointer(MapType(ptrType.BaseType), 0), NubSliceType nubSliceType => MapSliceType(nubSliceType), NubStringType => _structTypes["nub.string"], NubArrayType arrType => LLVMTypeRef.CreatePointer(MapType(arrType.ElementType), 0), NubConstArrayType constArr => LLVMTypeRef.CreateArray(MapType(constArr.ElementType), (uint)constArr.Size), NubStructType structType => _structTypes[StructName(structType.Module, structType.Name)], NubVoidType => LLVMTypeRef.Void, _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; } private LLVMTypeRef MapSliceType(NubSliceType nubSliceType) { var mangledName = NameMangler.Mangle(nubSliceType.ElementType); var name = $"nub.slice.{mangledName}"; if (!_structTypes.TryGetValue(name, out var type)) { type = _context.CreateNamedStruct(name); type.StructSetBody([LLVMTypeRef.Int64, LLVMTypeRef.CreatePointer(MapType(nubSliceType.ElementType), 0)], false); _structTypes[name] = type; } return type; } private static string StructName(string module, string name) { return $"struct.{module}.{name}"; } private static string StructConstructorName(string module, string name) { return $"{StructName(module, name)}.new"; } private static string FuncName(string module, string name, string? externSymbol) { if (externSymbol != null) { return externSymbol; } return $"{module}.{name}"; } }