private void GenerateFunctionHeader(AstFuncExpr function, bool forceEmitCode)
        {
            // Don't emit global functions that aren't even used
            if (!forceEmitCode && dontEmitUnusedDeclarations && !function.IsUsed)
            {
                return;
            }

            if (function.IsMacroFunction)
            {
                return;
            }

            var name = "";

            if (function.ImplBlock != null)
            {
                name += function.ImplBlock.TargetType + ".";
                if (function.ImplBlock.Trait != null)
                {
                    name += function.ImplBlock.Trait + ".";
                }
            }

            name += function.Name;

            if (function.PolymorphicTypes != null && function.PolymorphicTypes.Count > 0)
            {
                name += "." + string.Join(".", function.PolymorphicTypes.Select(p => $"{p.Key}.{p.Value}"));
            }
            if (function.ConstParameters != null && function.ConstParameters.Count > 0)
            {
                name += "." + string.Join(".", function.ConstParameters.Select(p => $"{p.Key}.{p.Value.type}.{p.Value.value}"));
            }

            if (function.Body != null)
            {
                name += ".che";
            }

            var linkname = function.GetDirective("linkname");

            LLVMValueRef lfunc = new LLVMValueRef();

            if (linkname != null)
            {
                name = linkname.Arguments[0].Value as string;

                lfunc = module.GetNamedFunction(name);
            }

            LLVMTypeRef ltype = FuncTypeToLLVMType(function.FunctionType);

            if (lfunc.Pointer.ToInt64() == 0)
            {
                lfunc = module.AddFunction(name, ltype);
            }

            // :temporary
            if (function.Body != null)
            {
                lfunc.SetLinkage(LLVMLinkage.LLVMInternalLinkage);
            }

            if (function.HasDirective("extern"))
            {
                lfunc.SetLinkage(LLVMLinkage.LLVMExternalLinkage);
            }

            // TODO

            if (function.HasDirective("noinline"))
            {
                lfunc.AddFunctionAttribute(context, LLVMAttributeKind.NoInline);
            }
            lfunc.AddFunctionAttribute(context, LLVMAttributeKind.NoUnwind);

            var ccDir = function.GetDirective("stdcall");

            if (ccDir != null)
            {
                LLVM.SetFunctionCallConv(lfunc, (int)LLVMCallConv.LLVMX86StdcallCallConv);
            }

            valueMap[function] = lfunc;
        }
        private void GenerateFunctionImplementation(AstFuncExpr function, bool forceEmitCode)
        {
            // Don't emit global functions that aren't even used
            if (!forceEmitCode && dontEmitUnusedDeclarations && !function.IsUsed)
            {
                return;
            }

            if (function.Body == null || function.IsMacroFunction)
            {
                return;
            }

            currentFunction = function;

            keepTrackOfStackTrace = !function.HasDirective("nostacktrace") && enableStackTrace;

            var lfunc = valueMap[function];

            currentLLVMFunction = lfunc;

            // generate body
            {
                var builder = new IRBuilder();
                this.builder = builder;

                var bbParams = lfunc.AppendBasicBlock("locals");
                //var bbTemps = lfunc.AppendBasicBlock("temps");
                var bbBody = lfunc.AppendBasicBlock("body");

                // allocate space for parameters and return values on stack
                builder.PositionBuilderAtEnd(bbParams);

                PushStackTrace(function);

                for (int i = 0; i < function.Parameters.Count; i++)
                {
                    var param = function.Parameters[i];
                    var p     = lfunc.GetParam((uint)i);
                    var ptype = LLVM.TypeOf(p);
                    p = builder.CreateAlloca(ptype, $"p_{param.Name?.Name}");
                    valueMap[param] = p;
                }

                // @todo: do we still need this?
                //foreach (var c in function.ConstScope.Symbols)
                //{
                //    if (c.Value is ConstSymbol s && !s.Type.IsComptimeOnly)
                //    {
                //        var val = CheezValueToLLVMValue(s.Type, s.Value);
                //        var cnst = builder.CreateAlloca(CheezTypeToLLVMType(s.Type), $"c_");
                //        builder.CreateStore(val, cnst);
                //        valueMap[s] = cnst;
                //    }
                //}

                if (function.ReturnTypeExpr != null)
                {
                    var ptype = CheezTypeToLLVMType(function.ReturnTypeExpr.Type);
                    var p     = builder.CreateAlloca(ptype, $"ret_{function.ReturnTypeExpr.Name?.Name}");
                    valueMap[function.ReturnTypeExpr] = p;
                }

                // store params and rets in local variables
                for (int i = 0; i < function.Parameters.Count; i++)
                {
                    var param = function.Parameters[i];
                    var p     = lfunc.GetParam((uint)i);
                    builder.CreateStore(p, valueMap[param]);
                }

                // temp values
                //builder.PositionBuilderAtEnd(bbTemps);
                builder.CreateBr(bbBody);

                // body
                builder.PositionBuilderAtEnd(bbBody);
                GenerateExpression(function.Body, false);

                // ret if void
                if (function.ReturnTypeExpr == null && !function.Body.GetFlag(ExprFlags.Returns))
                {
                    PopStackTrace();
                    builder.CreateRetVoid();
                }
                builder.Dispose();
            }

            // remove empty basic blocks
            var bb = lfunc.GetFirstBasicBlock();

            while (bb.Pointer.ToInt64() != 0)
            {
                var first = bb.GetFirstInstruction();

                if (bb.GetBasicBlockTerminator().Pointer.ToInt64() == 0)
                {
                    var b = new IRBuilder();
                    b.PositionBuilderAtEnd(bb);
                    b.CreateUnreachable();
                    b.Dispose();
                }

                bb = bb.GetNextBasicBlock();
            }

            // TODO
            //if (lfunc.VerifyFunction(LLVMVerifierFailureAction.LLVMPrintMessageAction))
            //{
            //    Console.Error.WriteLine($"in function {lfunc}");
            //}

            currentFunction = null;
        }