/// <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)); }
/// <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)); }
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)); }
/// <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)))); }
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)); } }
/// <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); } }
/// <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)); } } }
/// <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)); }