...
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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
781
compiler/NubLang/Generation/LlvmSharpGenerator.cs
Normal file
781
compiler/NubLang/Generation/LlvmSharpGenerator.cs
Normal 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}";
|
||||
}
|
||||
}
|
||||
@@ -7,4 +7,8 @@
|
||||
<IsAotCompatible>true</IsAotCompatible>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="LLVMSharp" Version="20.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user