This commit is contained in:
nub31
2025-11-05 15:20:45 +01:00
parent 36622755a9
commit d3822bc9b4
9 changed files with 802 additions and 1110 deletions

View File

@@ -9,7 +9,7 @@ var syntaxTrees = new List<SyntaxTree>();
var tokenizer = new Tokenizer();
var parser = new Parser();
var generator = new LlvmGenerator();
var generator = new LlvmSharpGenerator();
foreach (var file in args)
{
@@ -83,7 +83,7 @@ for (var i = 0; i < args.Length; i++)
}
var path = Path.Combine(".build", Path.ChangeExtension(file, "ll"));
File.WriteAllText(path, generator.Emit(compilationUnit, moduleRepository));
generator.Emit(compilationUnit, moduleRepository, file, path);
}
return 0;

View File

@@ -596,6 +596,8 @@ public class CastNode(List<Token> tokens, NubType type, ExpressionNode value, Ca
ConstArrayToArray,
ConstArrayToSlice,
StringToCString
}
public ExpressionNode Value { get; } = value;
@@ -607,7 +609,7 @@ public class CastNode(List<Token> tokens, NubType type, ExpressionNode value, Ca
}
}
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : RValue(tokens, type)
public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<IdentifierToken, ExpressionNode> initializers) : LValue(tokens, type)
{
public Dictionary<IdentifierToken, ExpressionNode> Initializers { get; } = initializers;
@@ -620,7 +622,7 @@ public class StructInitializerNode(List<Token> tokens, NubType type, Dictionary<
}
}
public class ConstArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : RValue(tokens, type)
public class ConstArrayInitializerNode(List<Token> tokens, NubType type, List<ExpressionNode> values) : LValue(tokens, type)
{
public List<ExpressionNode> Values { get; } = values;

View File

@@ -389,6 +389,11 @@ public sealed class TypeChecker
conversion = CastNode.Conversion.ConstArrayToSlice;
return true;
}
case NubStringType when to is NubPointerType { BaseType: NubIntType { Signed: true, Width: 8 } }:
{
conversion = CastNode.Conversion.StringToCString;
return true;
}
}
if (!strict)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,781 @@
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<string, LLVMTypeRef> _structTypes = new();
private readonly Dictionary<string, LLVMValueRef> _functions = new();
private readonly Dictionary<string, LLVMValueRef> _locals = new();
private readonly Stack<(LLVMBasicBlockRef breakBlock, LLVMBasicBlockRef continueBlock)> _loopStack = new();
public void Emit(List<TopLevelNode> topLevelNodes, ModuleRepository repository, string sourceFileName, string outputPath)
{
_module = topLevelNodes.OfType<ModuleNode>().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<StructNode>())
{
EmitStructConstructor(structNode);
}
foreach (var funcNode in topLevelNodes.OfType<FuncNode>())
{
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}";
}
}

View File

@@ -7,4 +7,8 @@
<IsAotCompatible>true</IsAotCompatible>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="LLVMSharp" Version="20.1.2" />
</ItemGroup>
</Project>

View File

@@ -2,5 +2,5 @@
set -euo pipefail
nubc main.nub test.nub
clang .build/main.ll .build/test.ll -o .build/out
nubc main.nub
clang .build/main.ll -o .build/out

View File

@@ -2,20 +2,13 @@ module main
extern "puts" func puts(text: ^i8)
struct Test
{
field: u32
struct Test {
test: ^i8 = "test1"
}
extern "main" func main(argc: i64, argv: [?]^i8)
{
let x: ^i8 = "test"
// test
^x^ = "uwu"
puts(x)
}
let x = "test"
func test(test: Test): Test
{
return test
puts(x)
}

View File

@@ -1,8 +0,0 @@
module test
extern "puts" func puts(text: ^i8)
func test()
{
puts("uwu")
}