/// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        public static void WriteCatchBegin(this LlvmWriter llvmWriter, CatchOfFinallyClause exceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            var isFinally = exceptionHandlingClause.Flags.HasFlag(ExceptionHandlingClauseOptions.Finally);
            if (isFinally)
            {
                writer.WriteLine("; Begin of Finally");
            }
            else
            {
                writer.WriteLine("; Begin of Catch");
            }

            var catchType = exceptionHandlingClause.Catch;

            var opCodeNone = OpCodePart.CreateNop;
            var bytePointerType = llvmWriter.ResolveType("System.Byte").ToPointerType();
            var errorObjectOfCatchResultNumber = llvmWriter.WriteSetResultNumber(opCodeNone, bytePointerType);
            writer.WriteLine("load i8** %.error_object");
            var beginCatchResultNumber = llvmWriter.WriteSetResultNumber(opCodeNone, bytePointerType);
            writer.WriteLine("call i8* @__cxa_begin_catch(i8* {0})", errorObjectOfCatchResultNumber);
            if (catchType != null)
            {
                llvmWriter.WriteBitcast(opCodeNone, beginCatchResultNumber, catchType);
                writer.WriteLine(string.Empty);

                exceptionHandlingClause.ExceptionResult = opCodeNone.Result;
            }

            if (isFinally)
            {
                // set default error handler jump to carry on try/catch execution
                writer.WriteLine("store i32 0, i32* %.finally_jump{0}", exceptionHandlingClause.Offset);
                writer.WriteLine("br label %.finally_no_error_entry{0}", exceptionHandlingClause.Offset);
                writer.Indent--;
                writer.WriteLine(".finally_no_error_entry{0}:", exceptionHandlingClause.Offset);
                writer.Indent++;
            }

            if (isFinally)
            {
                writer.WriteLine("; Begin of Finally Handler Body");
            }
            else
            {
                writer.WriteLine("; Begin of Catch Handler Body");
            }
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="opCode">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        /// <returns>
        /// </returns>
        private static IType WriteThrowInvoke(LlvmWriter llvmWriter, OpCodePart opCode, CatchOfFinallyClause exceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            var errorAllocationResultNumber = llvmWriter.WriteAllocateException(opCode);

            var exceptionPointerType = opCode.OpCodeOperands[0].Result.Type;
            writer.Write("invoke void @__cxa_throw(i8* {0}, i8* bitcast (", errorAllocationResultNumber);
            exceptionPointerType.WriteRttiPointerClassInfoDeclaration(writer);
            writer.WriteLine("* @\"{0}\" to i8*), i8* null)", exceptionPointerType.GetRttiPointerInfoName());
            writer.Indent++;
            if (exceptionHandlingClause != null)
            {
                writer.Write("to label %.unreachable unwind label %.catch{0}", exceptionHandlingClause.Offset);
            }
            else
            {
                writer.Write("to label %.unreachable unwind label %.unwind_exception");
                llvmWriter.needToWriteUnwindException = true;
            }

            llvmWriter.WriteDbgLine(opCode);
            writer.WriteLine(string.Empty);

            writer.Indent--;
            llvmWriter.needToWriteUnreachable = true;

            return exceptionPointerType;
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="opCode">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        public static void WriteThrow(this LlvmWriter llvmWriter, OpCodePart opCode, CatchOfFinallyClause exceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            writer.WriteLine("; Throw");

            var exceptionPointerType = exceptionHandlingClause != null
                                           ? WriteThrowInvoke(llvmWriter, opCode, exceptionHandlingClause)
                                           : WriteThrowCall(llvmWriter, opCode);

            llvmWriter.typeRttiPointerDeclRequired.Add(exceptionPointerType);
            llvmWriter.CheckIfExternalDeclarationIsRequired(exceptionPointerType);
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        private static void WriteRethrowInvoke(LlvmWriter llvmWriter, CatchOfFinallyClause exceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            writer.WriteLine("invoke void @__cxa_rethrow()");
            if (exceptionHandlingClause != null)
            {
                writer.Indent++;
                writer.WriteLine(
                    "to label %.unreachable unwind label %.catch_with_cleanup_{0}_{1}",
                    exceptionHandlingClause.Offset,
                    exceptionHandlingClause.Offset + exceptionHandlingClause.Length);
                writer.Indent--;
                exceptionHandlingClause.RethrowCatchWithCleanUpRequired = true;
            }
            else
            {
                writer.Indent++;
                writer.WriteLine("to label %.unreachable unwind label %.unwind_exception");
                writer.Indent--;
                llvmWriter.needToWriteUnwindException = true;
            }

            llvmWriter.needToWriteUnreachable = true;
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="opCode">
        /// </param>
        /// <param name="options">
        /// </param>
        /// <param name="finallyOrFaultClause">
        /// </param>
        /// <param name="catch">
        /// </param>
        /// <param name="filter">
        /// </param>
        /// <param name="exceptionAllocationResultNumber">
        /// </param>
        public static void WriteLandingPad(
            this LlvmWriter llvmWriter,
            OpCodePart opCode,
            LandingPadOptions options,
            CatchOfFinallyClause finallyOrFaultClause,
            IType[] @catch = null,
            int[] filter = null,
            int? exceptionAllocationResultNumber = null)
        {
            var writer = llvmWriter.Output;

            llvmWriter.WriteLandingPadVariables();

            var landingPadResult = llvmWriter.WriteSetResultNumber(opCode, llvmWriter.ResolveType("System.Byte").ToPointerType());

            writer.WriteLine("landingpad { i8*, i32 } personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)");
            if (options.HasFlag(LandingPadOptions.Cleanup))
            {
                writer.Indent++;
                writer.WriteLine("cleanup");
                writer.Indent--;
            }

            if (options.HasFlag(LandingPadOptions.EmptyFilter))
            {
                writer.Indent++;
                writer.WriteLine("filter [0 x i8*] zeroinitializer");
                writer.Indent--;
            }

            if (@catch != null && @catch.Any())
            {
                foreach (var catchType in @catch)
                {
                    writer.Indent++;

                    if (catchType != null)
                    {
                        writer.Write("catch i8* bitcast (");
                        catchType.WriteRttiPointerClassInfoDeclaration(writer);
                        writer.WriteLine("* @\"{0}\" to i8*)", catchType.GetRttiPointerInfoName());

                        llvmWriter.typeRttiPointerDeclRequired.Add(catchType);
                        llvmWriter.CheckIfExternalDeclarationIsRequired(catchType);
                    }
                    else
                    {
                        writer.Write("catch i8* null");
                    }

                    writer.Indent--;
                }
            }
            else if (finallyOrFaultClause != null)
            {
                // default catch with rethrowing it
                writer.Indent++;
                writer.WriteLine("catch i8* null");
                writer.Indent--;

                finallyOrFaultClause.EmptyFinallyRethrowRequired = true;
            }

            var getErrorObjectResultNumber = llvmWriter.WriteSetResultNumber(opCode, llvmWriter.ResolveType("System.Byte").ToPointerType());
            writer.WriteLine("extractvalue {1} {0}, 0", landingPadResult, "{ i8*, i32 }");
            writer.WriteLine("store i8* {0}, i8** %.error_object", getErrorObjectResultNumber);
            var getErrorTypeIdResultNumber = llvmWriter.WriteSetResultNumber(opCode, llvmWriter.ResolveType("System.Int32"));
            writer.WriteLine("extractvalue {1} {0}, 1", landingPadResult, "{ i8*, i32 }");
            writer.Write("store i32 {0}, i32* %.error_typeid", getErrorTypeIdResultNumber);

            if (exceptionAllocationResultNumber.HasValue)
            {
                writer.WriteLine(string.Empty);
                writer.Write("call void @__cxa_free_exception(i8* {0})", exceptionAllocationResultNumber.Value);
            }

            opCode.Result = landingPadResult;
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="opCode">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        /// <param name="upperLevelExceptionHandlingClause">
        /// </param>
        public static void WriteRethrow(
            this LlvmWriter llvmWriter, OpCodePart opCode, CatchOfFinallyClause exceptionHandlingClause, CatchOfFinallyClause upperLevelExceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            writer.WriteLine("; Rethrow");
            WriteRethrowInvoke(llvmWriter, exceptionHandlingClause);
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="finallyClause">
        /// </param>
        public static void WriteFinallyVariables(this LlvmWriter llvmWriter, CatchOfFinallyClause finallyClause)
        {
            if (finallyClause.FinallyVariablesAreWritten)
            {
                return;
            }

            finallyClause.FinallyVariablesAreWritten = true;

            var writer = llvmWriter.Output;

            writer.Write("%.finally_jump{0} = ", finallyClause.Offset);
            writer.Write("alloca i32, align " + LlvmWriter.PointerSize);
            writer.WriteLine(string.Empty);
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="finallyClause">
        /// </param>
        public static void WriteFinallyLeave(this LlvmWriter llvmWriter, CatchOfFinallyClause finallyClause)
        {
            var writer = llvmWriter.Output;

            llvmWriter.WriteFinallyVariables(finallyClause);

            writer.WriteLine("store i32 {0}, i32* %.finally_jump{1}", finallyClause.FinallyJumps.Count, finallyClause.Offset);
            writer.WriteLine("br label %.finally_no_error_entry{0}", finallyClause.Offset);
        }
 /// <summary>
 /// </summary>
 /// <param name="llvmWriter">
 /// </param>
 /// <param name="finallyClause">
 /// </param>
 public static void WriteEndFinally(this LlvmWriter llvmWriter, CatchOfFinallyClause finallyClause)
 {
     llvmWriter.WriteFinallyVariables(finallyClause);
 }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        /// <param name="nextExceptionHandlingClause">
        /// </param>
        public static void WriteCatchTest(
            this LlvmWriter llvmWriter, CatchOfFinallyClause exceptionHandlingClause, CatchOfFinallyClause nextExceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            writer.WriteLine("br label %.exception_switch{0}", exceptionHandlingClause.Offset);

            writer.Indent--;
            writer.WriteLine(".exception_switch{0}:", exceptionHandlingClause.Offset);
            writer.Indent++;

            writer.WriteLine("; Test Exception type");

            var catchType = exceptionHandlingClause.Catch;

            var opCodeNone = OpCodePart.CreateNop;
            var errorTypeIdOfCatchResultNumber = llvmWriter.WriteSetResultNumber(opCodeNone, llvmWriter.ResolveType("System.Int32"));
            writer.WriteLine("load i32* %.error_typeid");
            var errorTypeIdOfExceptionResultNumber = llvmWriter.WriteSetResultNumber(opCodeNone, llvmWriter.ResolveType("System.Byte").ToPointerType());
            writer.Write("call i32 @llvm.eh.typeid.for(i8* bitcast (");
            catchType.WriteRttiPointerClassInfoDeclaration(writer);
            writer.WriteLine("* @\"{0}\" to i8*))", catchType.GetRttiPointerInfoName());
            var compareResultResultNumber = llvmWriter.WriteSetResultNumber(opCodeNone, llvmWriter.ResolveType("System.Boolean"));
            writer.WriteLine("icmp eq i32 {0}, {1}", errorTypeIdOfCatchResultNumber, errorTypeIdOfExceptionResultNumber);
            writer.WriteLine(
                "br i1 {0}, label %.exception_handler{1}, label %.{2}",
                compareResultResultNumber,
                exceptionHandlingClause.Offset,
                nextExceptionHandlingClause != null ? string.Concat("exception_switch", nextExceptionHandlingClause.Offset) : "resume");

            writer.Indent--;
            writer.WriteLine(".exception_handler{0}:", exceptionHandlingClause.Offset);
            writer.Indent++;
        }
        /// <summary>
        /// </summary>
        /// <param name="llvmWriter">
        /// </param>
        /// <param name="opCode">
        /// </param>
        /// <param name="exceptionHandlingClause">
        /// </param>
        /// <param name="upperLevelExceptionHandlingClause">
        /// </param>
        public static void WriteCatchEnd(
            this LlvmWriter llvmWriter, OpCodePart opCode, CatchOfFinallyClause exceptionHandlingClause, CatchOfFinallyClause upperLevelExceptionHandlingClause)
        {
            var writer = llvmWriter.Output;

            var isFinally = exceptionHandlingClause.Flags.HasFlag(ExceptionHandlingClauseOptions.Finally);
            if (isFinally)
            {
                writer.WriteLine("; End of Finally Handler Body");
            }
            else
            {
                writer.WriteLine("; End of Catch Handler Body");
            }

            if (isFinally)
            {
                // process Leave jumps
                var index = 0;
                var opCodeNope = OpCodePart.CreateNop;

                var fullyDefinedRef = new FullyDefinedReference(
                    string.Concat("%.finally_jump", exceptionHandlingClause.Offset), llvmWriter.ResolveType("System.Int32"));

                llvmWriter.WriteLlvmLoad(opCodeNope, llvmWriter.ResolveType("System.Int32"), fullyDefinedRef);
                writer.WriteLine(string.Empty);
                writer.WriteLine("switch i32 {1}, label %.finally_exit{0} [", exceptionHandlingClause.Offset, opCodeNope.Result);
                writer.Indent++;
                writer.WriteLine("i32 {0}, label %.finally_exit{1}", index++, exceptionHandlingClause.Offset);
                foreach (var leave in exceptionHandlingClause.FinallyJumps)
                {
                    writer.WriteLine("i32 {0}, label %{1}", index++, leave);
                }

                writer.Indent--;
                writer.WriteLine("]");

                llvmWriter.WriteLabel(writer, string.Concat(".finally_exit", exceptionHandlingClause.Offset));

                if (exceptionHandlingClause.EmptyFinallyRethrowRequired)
                {
                    // rethrow exception in empty finally block
                    var opCodeNop = OpCodePart.CreateNop;
                    llvmWriter.WriteRethrow(
                        opCodeNop,
                        llvmWriter.catchScopes.Count > 0 ? llvmWriter.catchScopes.Peek() : null,
                        llvmWriter.tryScopes.Count > 0 ? llvmWriter.tryScopes.Peek().Catches.First() : null);
                }
            }

            var startOfHandlerAddress = exceptionHandlingClause.Offset;
            var endOfHandlerAddress = exceptionHandlingClause.Offset + exceptionHandlingClause.Length;

            if (exceptionHandlingClause.RethrowCatchWithCleanUpRequired)
            {
                llvmWriter.WriteLabel(writer, string.Format(".catch_with_cleanup_{0}_{1}", startOfHandlerAddress, endOfHandlerAddress));

                var opCodeNop = OpCodePart.CreateNop;
                llvmWriter.WriteLandingPad(
                    opCodeNop,
                    LandingPadOptions.Cleanup,
                    null,
                    new[] { upperLevelExceptionHandlingClause != null ? upperLevelExceptionHandlingClause.Catch : llvmWriter.ResolveType("System.Exception") });
                writer.WriteLine(string.Empty);
            }
            else
            {
                writer.WriteLine("store i32 0, i32* %.error_typeid");
            }

            writer.WriteLine("call void @__cxa_end_catch()");

            if (!exceptionHandlingClause.RethrowCatchWithCleanUpRequired || upperLevelExceptionHandlingClause == null)
            {
                var isLeave = opCode.Any(Code.Leave, Code.Leave_S);
                var nextOp = opCode.Next;
                if (!isLeave &&
                    (nextOp == null || nextOp.JumpDestination == null || !nextOp.JumpDestination.Any() || nextOp.GroupAddressStart != endOfHandlerAddress))
                {
                    if (nextOp != null && nextOp.CatchOrFinallyBegin == null || opCode.Any(Code.Leave, Code.Leave_S)
                        || llvmWriter.OpsByAddressStart.Values.Any(op => op.ToCode() == Code.Ret))
                    {
                        writer.WriteLine("br label %.exit{0}", endOfHandlerAddress);
                        llvmWriter.WriteLabel(writer, string.Concat(".exit", endOfHandlerAddress));
                        writer.WriteLine(string.Empty);
                    }
                    else
                    {
                        writer.WriteLine("unreachable");
                    }
                }
                else
                {
                    if (isLeave)
                    {
                        writer.WriteLine("br label %.a{0}", opCode.JumpAddress());
                    }
                    else
                    {
                        writer.WriteLine("br label %.a{0}", nextOp.GroupAddressStart);
                    }
                }
            }
            else
            {
                if (!upperLevelExceptionHandlingClause.Flags.HasFlag(ExceptionHandlingClauseOptions.Finally))
                {
                    writer.WriteLine("br label %.exception_switch{0}", upperLevelExceptionHandlingClause.Offset);
                }
                else
                {
                    writer.WriteLine("br label %.finally_no_error_entry{0}", upperLevelExceptionHandlingClause.Offset);
                }
            }

            if (isFinally)
            {
                writer.WriteLine("; End of Finally");
            }
            else
            {
                writer.WriteLine("; End of Catch");
            }
        }