|
|
|
|
@@ -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}";
|
|
|
|
|
}
|
|
|
|
|
}
|