Beispiel #1
0
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            if (CatchClauses.Count == 0)
            {
                return(TryBody.Emit(BasicBlock));
            }

            var exceptionDataType = StructType(new[] { PointerType(Int8Type(), 0), Int32Type() }, false);

            var catchBlock           = BasicBlock.CreateChildBlock("catch");
            var catchLandingPadBlock = BasicBlock.CreateChildBlock("catch_landingpad");
            var leaveBlock           = BasicBlock.CreateChildBlock("leave");

            // The try block is a regular block that jumps to the 'leave' block.
            //
            // try:
            //     <try body>
            //     br label %leave

            var tryCodegen = TryBody.Emit(BasicBlock.WithUnwindTarget(catchLandingPadBlock, catchBlock));

            BuildBr(tryCodegen.BasicBlock.Builder, leaveBlock.Block);

            PopulateCatchBlock(catchBlock, leaveBlock);
            ItaniumCxxFinallyBlock.PopulateThunkLandingPadBlock(catchLandingPadBlock, catchBlock.Block, false);

            return(new BlockCodegen(leaveBlock, tryCodegen.Value));
        }
Beispiel #2
0
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            var continueBlock = BasicBlock.CreateChildBlock("continue_block");
            var breakBlock    = BasicBlock.CreateChildBlock("break_block");

            BasicBlock.FunctionBody.TagFlowBlock(Tag, breakBlock.Block, continueBlock.Block);

            BuildBr(BasicBlock.Builder, continueBlock.Block);
            var bodyResult = Body.Emit(continueBlock);

            BuildBr(bodyResult.BasicBlock.Builder, breakBlock.Block);
            return(new BlockCodegen(breakBlock, bodyResult.Value));
        }
Beispiel #3
0
        private BlockCodegen EmitCall(
            BasicBlockBuilder BasicBlock,
            LLVMValueRef Callee,
            LLVMValueRef[] Arguments)
        {
            bool hasVoidRetType = retType == PrimitiveTypes.Void;

            LLVMValueRef callRef;

            if (CanThrow && BasicBlock.HasUnwindTarget)
            {
                var successBlock = BasicBlock.CreateChildBlock("success");
                callRef = BuildInvoke(
                    BasicBlock.Builder,
                    Callee,
                    Arguments,
                    successBlock.Block,
                    BasicBlock.UnwindTarget,
                    hasVoidRetType ? "" : "call_tmp");
                BasicBlock = successBlock;
            }
            else
            {
                callRef = BuildCall(
                    BasicBlock.Builder,
                    Callee,
                    Arguments,
                    hasVoidRetType ? "" : "call_tmp");
            }

            return(hasVoidRetType
                ? new BlockCodegen(BasicBlock)
                : new BlockCodegen(BasicBlock, callRef));
        }
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            var exceptionDataType    = StructType(new[] { PointerType(Int8Type(), 0), Int32Type() }, false);
            var isPropagatingStorage = BasicBlock.FunctionBody.CreateEntryPointAlloca(
                Int1Type(),
                "exception_value_alloca");

            var finallyBlock           = BasicBlock.CreateChildBlock("finally");
            var finallyLandingPadBlock = BasicBlock.CreateChildBlock("finally_landingpad");
            var finallyExceptionBlock  = BasicBlock.CreateChildBlock("finally_exception");
            var leaveBlock             = BasicBlock.CreateChildBlock("leave");

            // The try block is a regular block that jumps to the finally block.
            //
            // try:
            //     store i1 false, i1* %is_propagating_exception_alloca
            //     <try body>
            //     br label %finally

            BuildStore(
                BasicBlock.Builder,
                ConstInt(Int1Type(), 0, false),
                isPropagatingStorage);
            var tryCodegen = TryBody.Emit(BasicBlock.WithUnwindTarget(finallyLandingPadBlock, finallyExceptionBlock));

            BuildBr(tryCodegen.BasicBlock.Builder, finallyBlock.Block);

            PopulateFinallyBlock(finallyBlock, leaveBlock, isPropagatingStorage);
            PopulateThunkLandingPadBlock(finallyLandingPadBlock, finallyExceptionBlock.Block, true);

            // The 'finally_exception' block is entered if an exception is propagating from
            // the 'try' block. It sets the 'is propagating' flag to 'true' and branches
            // to the finally block.
            //
            // finally_exception:
            //     store i1 true, i1* %is_propagating_exception_alloca
            //     br label %finally

            BuildStore(
                finallyExceptionBlock.Builder,
                ConstInt(Int1Type(), 1, false),
                isPropagatingStorage);
            BuildBr(finallyExceptionBlock.Builder, finallyBlock.Block);

            return(new BlockCodegen(leaveBlock, tryCodegen.Value));
        }
Beispiel #5
0
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            var targetBlock = IsBreak
                ? BasicBlock.FunctionBody.GetBreakBlock(Tag)
                : BasicBlock.FunctionBody.GetContinueBlock(Tag);

            BuildBr(BasicBlock.Builder, targetBlock);
            return(new BlockCodegen(BasicBlock.CreateChildBlock()));
        }
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            BuildUnreachable(BasicBlock.Builder);
            var newBlock = BasicBlock.CreateChildBlock("post_unreachable");

            return(new BlockCodegen(
                       newBlock,
                       PrimitiveTypes.Void == Type
                    ? default(LLVMValueRef)
                    : GetUndef(BasicBlock.FunctionBody.Module.Declare(resultType))));
        }
Beispiel #7
0
 private BlockCodegen EmitClause(
     BasicBlockBuilder MergeBlock,
     CodeBlock Body,
     string Name,
     out BasicBlockBuilder ClauseBlock)
 {
     if (Body != null)
     {
         ClauseBlock = MergeBlock.CreateChildBlock(Name);
         var clauseResult = Body.Emit(ClauseBlock);
         BuildBr(clauseResult.BasicBlock.Builder, MergeBlock.Block);
         return(clauseResult);
     }
     else
     {
         ClauseBlock = MergeBlock;
         return(new BlockCodegen(MergeBlock));
     }
 }
Beispiel #8
0
        /// <inheritdoc/>
        public override BlockCodegen Emit(BasicBlockBuilder BasicBlock)
        {
            var conditionResult = Condition.Emit(BasicBlock);

            BasicBlock = conditionResult.BasicBlock;
            var mergeBlock = BasicBlock.CreateChildBlock("if_else_merge");

            BasicBlockBuilder ifBlock;
            BasicBlockBuilder elseBlock;
            var ifResult   = EmitClause(mergeBlock, IfClause, "if_clause", out ifBlock);
            var elseResult = EmitClause(mergeBlock, ElseClause, "else_clause", out elseBlock);

            BuildCondBr(
                BasicBlock.Builder,
                conditionResult.Value,
                ifBlock.Block,
                elseBlock.Block);

            if (ifResult.HasValue)
            {
                var phiVal = BuildPhi(mergeBlock.Builder, ifResult.Value.TypeOf(), "if_else_result");
                AddIncoming(
                    phiVal,
                    new LLVMValueRef[] { ifResult.Value, elseResult.Value },
                    new LLVMBasicBlockRef[]
                {
                    ifResult.BasicBlock.Block,
                    elseResult.BasicBlock.Block
                },
                    2);
                return(new BlockCodegen(mergeBlock, phiVal));
            }
            else
            {
                return(new BlockCodegen(mergeBlock));
            }
        }
        /// <summary>
        /// Gets the manual unwind target for the given block, if any.
        /// If there is no manual unwind target, then a block is created
        /// that resumes the exception.
        /// </summary>
        /// <param name="BasicBlock">The basic block to unwind from.</param>
        /// <returns>An unwind target block.</returns>
        public static LLVMBasicBlockRef GetManualUnwindTarget(BasicBlockBuilder BasicBlock)
        {
            // The idea is to create a block that, if jumped to, does the following:
            //
            //     static if (is_top_level_try)
            //         resume exception_data;
            //     else
            //         goto next_unwind_target;

            if (BasicBlock.HasUnwindTarget)
            {
                return(BasicBlock.ManualUnwindTarget);
            }
            else
            {
                var resumeBlock    = BasicBlock.CreateChildBlock("resume");
                var exceptionTuple = BuildLoad(
                    resumeBlock.Builder,
                    resumeBlock.FunctionBody.ExceptionDataStorage.Value,
                    "exception_tuple");
                BuildResume(resumeBlock.Builder, exceptionTuple);
                return(resumeBlock.Block);
            }
        }
Beispiel #10
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));
                }
            }
        }
Beispiel #11
0
        /// <summary>
        /// Populates the given 'catch' block.
        /// </summary>
        /// <param name="CatchBlock">The 'catch' block to populate.</param>
        /// <param name="LeaveBlock">The 'leave' block to jump to when the 'catch' is done.</param>
        private void PopulateCatchBlock(BasicBlockBuilder CatchBlock, BasicBlockBuilder LeaveBlock)
        {
            var catchBlockEntry = CatchBlock;

            // Before we even get started on the catch block's body, we should first
            // take the time to ensure that the '__cxa_begin_catch' environment set up
            // by the catch block is properly terminated by a '__cxa_end_catch' call,
            // even if an exception is thrown from the catch block itself. We can do
            // so by creating a 'catch_end' block and a thunk landing pad.
            var catchEndBlock       = CatchBlock.CreateChildBlock("catch_end");
            var catchExceptionBlock = CatchBlock.CreateChildBlock("catch_exception");

            var catchEndLandingPadBlock = CatchBlock.CreateChildBlock("catch_end_landingpad");

            ItaniumCxxFinallyBlock.PopulateThunkLandingPadBlock(
                catchEndLandingPadBlock,
                catchExceptionBlock.Block,
                true);

            CatchBlock = CatchBlock.WithUnwindTarget(catchEndLandingPadBlock, catchEndBlock);

            // The catch block starts like this:
            //
            // catch:
            //     %exception_data = load { i8*, i32 }* %exception_data_alloca
            //     %exception_obj = extractvalue { i8*, i32 } %exception_data, 0
            //     %exception_ptr_opaque = call i8* @__cxa_begin_catch(i8* %exception_obj)
            //     %exception_ptr = bitcast i8* %exception_ptr_opaque to i8**
            //     %exception = load i8*, i8** %exception_ptr
            //     %exception_vtable_ptr_ptr = bitcast i8* %exception to i8**
            //     %exception_vtable_ptr = load i8*, i8** %exception_vtable_ptr_ptr
            //     %exception_typeid = <typeid> i64, i8* %exception_vtable_ptr

            var exceptionData = BuildLoad(
                CatchBlock.Builder,
                CatchBlock.FunctionBody.ExceptionDataStorage.Value,
                "exception_data");

            var exceptionObj = BuildExtractValue(
                CatchBlock.Builder,
                exceptionData,
                0,
                "exception_obj");

            var exceptionPtrOpaque = BuildCall(
                CatchBlock.Builder,
                CatchBlock.FunctionBody.Module.Declare(IntrinsicValue.CxaBeginCatch),
                new LLVMValueRef[] { exceptionObj },
                "exception_ptr_opaque");

            var exceptionPtr = BuildBitCast(
                CatchBlock.Builder,
                exceptionPtrOpaque,
                PointerType(PointerType(Int8Type(), 0), 0),
                "exception_ptr");

            var exception = BuildLoad(CatchBlock.Builder, exceptionPtr, "exception");

            var exceptionVtablePtrPtr = BuildBitCast(
                CatchBlock.Builder,
                exception,
                PointerType(PointerType(Int8Type(), 0), 0),
                "exception_vtable_ptr_ptr");

            var exceptionVtablePtr = AtAddressEmitVariable.BuildConstantLoad(
                CatchBlock.Builder,
                exceptionVtablePtrPtr,
                "exception_vtable_ptr");

            var exceptionTypeid = TypeIdBlock.BuildTypeid(
                CatchBlock.Builder,
                exceptionVtablePtr);

            // Next, we need to figure out if we have a catch block that can handle the
            // exception we've thrown. We do so by iterating over all catch blocks and
            // testing if they're a match for the exception's type.
            var fallthroughBlock = CatchBlock.CreateChildBlock("catch_test");

            for (int i = 0; i < CatchClauses.Count; i++)
            {
                var clause = CatchClauses[i];

                var catchBodyBlock     = CatchBlock.CreateChildBlock("catch_body");
                var catchBodyBlockTail = catchBodyBlock;

                // First, emit the catch body. This is just a regular block that
                // clears the exception value variable when it gets started and
                // jumps to the 'catch_end' when it's done.
                //
                // catch_body:
                //     %exception_val = bitcast i8* %exception to <clause_type>
                //     store <clause_type> %exception_val, <clause_type>* %clause_exception_variable_alloca
                //     <catch clause body>
                //     br catch_end

                var ehVarAddressAndBlock = clause.LLVMHeader
                                           .AtAddressExceptionVariable.Address.Emit(catchBodyBlockTail);
                catchBodyBlockTail = ehVarAddressAndBlock.BasicBlock;

                BuildStore(
                    catchBodyBlockTail.Builder,
                    BuildBitCast(
                        catchBodyBlockTail.Builder,
                        exception,
                        catchBodyBlockTail.FunctionBody.Module.Declare(clause.ExceptionType),
                        "exception_val"),
                    ehVarAddressAndBlock.Value);

                catchBodyBlockTail = clause.Body.Emit(catchBodyBlockTail).BasicBlock;

                BuildBr(catchBodyBlockTail.Builder, catchEndBlock.Block);

                // Each clause is implemented as:
                //
                // catch_clause:
                //     %clause_typeid = <typeid> i64, clause-exception-type
                //     %typeid_rem = urem i64 %exception_typeid_tmp, %clause_typeid
                //     %is_subtype = cmp eq i64 %typeid_rem, 0
                //     br i1 %is_subtype, label %catch_body, label %fallthrough
                //
                var clauseTypeid = CatchBlock.FunctionBody.Module.GetTypeId((LLVMType)clause.ExceptionType);

                var typeIdRem = BuildURem(
                    CatchBlock.Builder,
                    exceptionTypeid,
                    ConstInt(exceptionTypeid.TypeOf(), clauseTypeid, false),
                    "typeid_rem");

                var isSubtype = BuildICmp(
                    CatchBlock.Builder,
                    LLVMIntPredicate.LLVMIntEQ,
                    typeIdRem,
                    ConstInt(exceptionTypeid.TypeOf(), 0, false),
                    "is_subtype");

                BuildCondBr(CatchBlock.Builder, isSubtype, catchBodyBlock.Block, fallthroughBlock.Block);

                CatchBlock       = fallthroughBlock;
                fallthroughBlock = CatchBlock.CreateChildBlock("catch_test");
            }

            // If we didn't match any catch clauses, then we'll rethrow the exception and
            // unwind to the end-catch landing pad.
            //
            // no_matching_clause:
            //     invoke void @__cxa_rethrow() to label %unreachable unwind label %catch_end_landingpad
            //
            // unreachable:
            //     unreachable
            BuildInvoke(
                CatchBlock.Builder,
                CatchBlock.FunctionBody.Module.Declare(IntrinsicValue.CxaRethrow),
                new LLVMValueRef[] { },
                fallthroughBlock.Block,
                catchEndLandingPadBlock.Block,
                "");
            BuildUnreachable(fallthroughBlock.Builder);

            // The catch end block simply calls '__cxa_end_catch' and jumps to the 'leave'
            // block. Like so:
            //
            // catch_end:
            //     call void @__cxa_end_catch()
            //     br label %leave

            BuildCall(
                catchEndBlock.Builder,
                CatchBlock.FunctionBody.Module.Declare(IntrinsicValue.CxaEndCatch),
                new LLVMValueRef[] { },
                "");
            BuildBr(catchEndBlock.Builder, LeaveBlock.Block);

            // The 'catch exception' block is entered when an exception is thrown from
            // the 'catch' block, or if the catch block is ill-equipped to handle the
            // exception. Its responsibility is to end the catch block and jump to
            // the manual unwind target.
            //
            // catch_exception:
            //     call void @__cxa_end_catch()
            //     br label %manual_unwind_target

            BuildCall(
                catchExceptionBlock.Builder,
                catchExceptionBlock.FunctionBody.Module.Declare(IntrinsicValue.CxaEndCatch),
                new LLVMValueRef[] { },
                "");
            BuildBr(catchExceptionBlock.Builder, ItaniumCxxFinallyBlock.GetManualUnwindTarget(catchBlockEntry));
        }