예제 #1
0
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            if (Callee is DelegateBlock)
            {
                var deleg = (DelegateBlock)Callee;

                var targetAndBlock = EmitTarget(BasicBlock, deleg.Target);
                BasicBlock = targetAndBlock.BasicBlock;

                var argsAndBlock = EmitArguments(BasicBlock, targetAndBlock.Value, Arguments);
                BasicBlock = argsAndBlock.Item2;

                var calleeAndBlock = EmitCallee(BasicBlock, targetAndBlock.Value, deleg.Callee, deleg.Op);
                BasicBlock = calleeAndBlock.BasicBlock;

                return(EmitCall(BasicBlock, calleeAndBlock.Value, argsAndBlock.Item1));
            }
            else if (Callee is IntrinsicBlock)
            {
                var argsAndBlock = EmitArguments(BasicBlock, new LLVMValueRef(IntPtr.Zero), Arguments);
                BasicBlock = argsAndBlock.Item2;

                var intrinsicAndBlock = Callee.Emit(BasicBlock);
                BasicBlock = intrinsicAndBlock.BasicBlock;

                return(EmitCall(BasicBlock, intrinsicAndBlock.Value, argsAndBlock.Item1));
            }
            else
            {
                // To invoke a delegate, we first need to figure out if it includes
                // a context or not. If so, then we should prepend a context value
                // to the argument list.
                //
                // The code'll look more or less like this:
                //
                //     var delegate = ...;
                //     var args... = ...;
                //     void* fptr = delegate->fptr;
                //     if (delegate->has_context)
                //     {
                //         void* context = delegate->context;
                //         result1 = ((TRet(void*, TArgs...)*)fptr)(context, args...);
                //     }
                //     else
                //     {
                //         result2 = ((TRet(TArgs...)*)fptr)(args...);
                //     }
                //     result = phi(result1, result2);

                var delegateAndBlock = Callee.Emit(BasicBlock);
                BasicBlock = delegateAndBlock.BasicBlock;

                var argsAndBlock = EmitArguments(BasicBlock, default(LLVMValueRef), Arguments);
                BasicBlock = argsAndBlock.Item2;

                var contextfulCallBlock  = BasicBlock.CreateChildBlock("contextful_call");
                var contextlessCallBlock = BasicBlock.CreateChildBlock("contextless_call");
                var postCallBlock        = BasicBlock.CreateChildBlock("post_call");

                var funcPrototype = BasicBlock.FunctionBody.Module.DeclarePrototype(
                    methodSignature);

                var contextFuncPrototype =
                    methodSignature.IsStatic
                    ? FunctionType(
                        funcPrototype.GetReturnType(),
                        new LLVMTypeRef[] { PointerType(Int8Type(), 0) }
                        .Concat <LLVMTypeRef>(funcPrototype.GetParamTypes())
                        .ToArray <LLVMTypeRef>(),
                        funcPrototype.IsFunctionVarArg)
                    : funcPrototype;

                var contextlessFuncPrototype =
                    methodSignature.IsStatic
                    ? funcPrototype
                    : FunctionType(
                        funcPrototype.GetReturnType(),
                        funcPrototype.GetParamTypes()
                        .Skip <LLVMTypeRef>(1)
                        .ToArray <LLVMTypeRef>(),
                        funcPrototype.IsFunctionVarArg);

                var delegatePtr = BuildBitCast(
                    BasicBlock.Builder,
                    delegateAndBlock.Value,
                    PointerType(DelegateBlock.MethodTypeLayout, 0),
                    "delegate_ptr");

                var funcPtr = DelegateBlock.BuildLoadFunctionPointer(
                    BasicBlock.Builder, delegatePtr);

                var hasContext = DelegateBlock.BuildLoadHasContext(
                    BasicBlock.Builder, delegatePtr);

                BuildCondBr(
                    BasicBlock.Builder,
                    hasContext,
                    contextfulCallBlock.Block,
                    contextlessCallBlock.Block);

                // Write the contextful call block.
                var contextPtr = DelegateBlock.BuildLoadContextObject(
                    contextfulCallBlock.Builder, delegatePtr);

                var contextfulCallAndBlock = EmitCall(
                    contextfulCallBlock,
                    BuildBitCast(
                        contextfulCallBlock.Builder,
                        funcPtr,
                        PointerType(contextFuncPrototype, 0),
                        "contextful_fptr"),
                    new LLVMValueRef[] { contextPtr }
                    .Concat <LLVMValueRef>(argsAndBlock.Item1)
                    .ToArray <LLVMValueRef>());
                contextfulCallBlock = contextfulCallAndBlock.BasicBlock;

                BuildBr(contextfulCallBlock.Builder, postCallBlock.Block);

                // Write the contextless call block.
                var contextlessCallAndBlock = EmitCall(
                    contextlessCallBlock,
                    BuildBitCast(
                        contextlessCallBlock.Builder,
                        funcPtr,
                        PointerType(contextlessFuncPrototype, 0),
                        "contextless_fptr"),
                    argsAndBlock.Item1);
                contextlessCallBlock = contextlessCallAndBlock.BasicBlock;

                BuildBr(contextlessCallBlock.Builder, postCallBlock.Block);

                // Write a phi if the return type is non-void.
                if (contextfulCallAndBlock.HasValue)
                {
                    var resultPhi = BuildPhi(
                        postCallBlock.Builder,
                        contextfulCallAndBlock.Value.TypeOf(),
                        "result_phi");
                    resultPhi.AddIncoming(
                        new LLVMValueRef[]
                    {
                        contextfulCallAndBlock.Value,
                        contextlessCallAndBlock.Value
                    },
                        new LLVMBasicBlockRef[]
                    {
                        contextfulCallAndBlock.BasicBlock.Block,
                        contextlessCallAndBlock.BasicBlock.Block
                    },
                        2);
                    return(new BlockCodegen(postCallBlock, resultPhi));
                }
                else
                {
                    return(new BlockCodegen(postCallBlock));
                }
            }
        }