From 4ec17a08d0c47f40dd7135259e873a6cf203e940 Mon Sep 17 00:00:00 2001 From: Raja R Harinath Date: Sat, 5 Apr 2008 18:50:25 +0000 Subject: [PATCH] Use the ResumableStatement infrastructure for MoveNext () * iterators.cs (Iterator.EmitMoveNext_NoResumePoints): New. (Iterator.EmitMoveNext): Use 'resume_points'. Get rid of 'old_resume_points'. Move dispatcher upfront. (Iterator.MarkYield): Mark the 'resume_point' of a Yield. * statement.cs (ExceptionStatement.DoEmit): Emit a dispatcher if in an enumerator. This encodes the main fix in this patch series -- we can only jump into the first instruction of a try from the outside, but we want to emit try/finally regions in iterators and resume in the middle of them. svn path=/trunk/mcs/; revision=99922 --- mcs/mcs/ChangeLog | 13 ++++ mcs/mcs/iterators.cs | 138 ++++++++++++++++++++++++------------------- mcs/mcs/statement.cs | 39 +++++++++--- 3 files changed, 120 insertions(+), 70 deletions(-) diff --git a/mcs/mcs/ChangeLog b/mcs/mcs/ChangeLog index 5b8d3bebeeb..e9ef0042583 100644 --- a/mcs/mcs/ChangeLog +++ b/mcs/mcs/ChangeLog @@ -1,3 +1,16 @@ +2008-04-06 Raja R Harinath + + Use the ResumableStatement infrastructure for MoveNext () + * iterators.cs (Iterator.EmitMoveNext_NoResumePoints): New. + (Iterator.EmitMoveNext): Use 'resume_points'. Get rid of + 'old_resume_points'. Move dispatcher upfront. + (Iterator.MarkYield): Mark the 'resume_point' of a Yield. + * statement.cs (ExceptionStatement.DoEmit): Emit a dispatcher if + in an enumerator. This encodes the main fix in this patch series + -- we can only jump into the first instruction of a try from the + outside, but we want to emit try/finally regions in iterators and + resume in the middle of them. + 2008-04-05 Raja R Harinath * statement.cs (ExceptionStatement.ResolveFinally): Move setting diff --git a/mcs/mcs/iterators.cs b/mcs/mcs/iterators.cs index 0c90a350cdf..0d80382049d 100644 --- a/mcs/mcs/iterators.cs +++ b/mcs/mcs/iterators.cs @@ -85,9 +85,7 @@ namespace Mono.CSharp { protected override void DoEmit (EmitContext ec) { - int pc = ec.CurrentIterator.MarkYield (ec, expr, unwind_protect); - if (pc != resume_pc) - throw new InternalErrorException (); + ec.CurrentIterator.MarkYield (ec, expr, resume_pc, unwind_protect, resume_point); } protected override void CloneTo (CloneContext clonectx, Statement t) @@ -346,11 +344,11 @@ namespace Mono.CSharp { protected override bool DoResolveInternal (EmitContext ec) { if (this is EnumeratorScopeInitializer) - state = Iterator.State.Running; + state = Iterator.State.Start; else if (Host.Iterator.IsEnumerable) state = Iterator.State.Uninitialized; else - state = Iterator.State.Running; + state = Iterator.State.Start; return base.DoResolveInternal (ec); } @@ -454,7 +452,7 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Ldarg_0); ig.Emit (OpCodes.Ldflda, host.PC.FieldBuilder); - ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Running); + ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Start); ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Uninitialized); ig.Emit (OpCodes.Call, ce); @@ -594,7 +592,7 @@ namespace Mono.CSharp { ig.Emit (OpCodes.Ldarg_0); ig.Emit (OpCodes.Ldfld, host.PC.FieldBuilder); - ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Running); + ig.Emit (OpCodes.Ldc_I4, (int) Iterator.State.Start); ig.Emit (OpCodes.Bgt, label_ok); ig.Emit (OpCodes.Newobj, TypeManager.invalid_operation_exception_ctor); @@ -632,16 +630,14 @@ namespace Mono.CSharp { get { return current_pc; } } - ArrayList old_resume_points = new ArrayList (); - int pc; - public readonly Type OriginalIteratorType; public readonly IteratorHost IteratorHost; public enum State { - Uninitialized = -2, - After, - Running + Running = -3, // Used only in CurrentPC, never stored into $PC + Uninitialized = -2, + After = -1, + Start = 0 } public void EmitYieldBreak (ILGenerator ig, bool unwind_protect) @@ -652,63 +648,95 @@ namespace Mono.CSharp { ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_error); } - internal void EmitMoveNext (EmitContext ec, Block original_block) + void EmitMoveNext_NoResumePoints (EmitContext ec, Block original_block) { ILGenerator ig = ec.ig; - move_next_ok = ig.DefineLabel (); - move_next_error = ig.DefineLabel (); - - LocalBuilder retval = ec.GetTemporaryLocal (TypeManager.int32_type); - skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type); - - ig.Emit (OpCodes.Ldc_I4_0); - ig.Emit (OpCodes.Stloc, skip_finally); - - ig.BeginExceptionBlock (); + ig.Emit (OpCodes.Ldarg_0); + ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder); - Label dispatcher = ig.DefineLabel (); - ig.Emit (OpCodes.Br, dispatcher); + ig.Emit (OpCodes.Ldarg_0); + IntConstant.EmitInt (ig, (int) State.After); + ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder); - OldResumePoint entry_point = new OldResumePoint (); - old_resume_points.Add (entry_point); - entry_point.Define (ig); + // We only care if the PC is zero (start executing) or non-zero (don't do anything) + ig.Emit (OpCodes.Brtrue, move_next_error); SymbolWriter.StartIteratorBody (ec.ig); - original_block.Emit (ec); - SymbolWriter.EndIteratorBody (ec.ig); - EmitYieldBreak (ig, false); + ig.MarkLabel (move_next_error); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Ret); + } - SymbolWriter.StartIteratorDispatcher (ec.ig); + internal void EmitMoveNext (EmitContext ec, Block original_block) + { + ILGenerator ig = ec.ig; - ig.MarkLabel (dispatcher); + move_next_ok = ig.DefineLabel (); + move_next_error = ig.DefineLabel (); - Label [] labels = new Label [old_resume_points.Count]; - for (int i = 0; i < labels.Length; i++) - labels [i] = ((OldResumePoint) old_resume_points [i]).Label; + if (resume_points == null) { + EmitMoveNext_NoResumePoints (ec, original_block); + return; + } + current_pc = ec.GetTemporaryLocal (TypeManager.uint32_type); ig.Emit (OpCodes.Ldarg_0); ig.Emit (OpCodes.Ldfld, IteratorHost.PC.FieldBuilder); + ig.Emit (OpCodes.Stloc, current_pc); + + Label [] labels = new Label [1 + resume_points.Count]; + labels [0] = ig.DefineLabel (); + + bool need_skip_finally = false; + for (int i = 0; i < resume_points.Count; ++i) { + ResumableStatement s = (ResumableStatement) resume_points [i]; + need_skip_finally |= s is ExceptionStatement; + labels [i+1] = s.PrepareForEmit (ec); + } + + if (need_skip_finally) { + skip_finally = ec.GetTemporaryLocal (TypeManager.bool_type); + ig.Emit (OpCodes.Ldc_I4_0); + ig.Emit (OpCodes.Stloc, skip_finally); + } + + ig.BeginExceptionBlock (); + + SymbolWriter.StartIteratorDispatcher (ec.ig); + ig.Emit (OpCodes.Ldloc, current_pc); ig.Emit (OpCodes.Switch, labels); + ig.Emit (OpCodes.Br, move_next_error); SymbolWriter.EndIteratorDispatcher (ec.ig); - Label end = ig.DefineLabel (); + ig.MarkLabel (labels [0]); + + SymbolWriter.StartIteratorBody (ec.ig); + original_block.Emit (ec); + SymbolWriter.EndIteratorBody (ec.ig); SymbolWriter.StartIteratorDispatcher (ec.ig); + ig.Emit (OpCodes.Ldarg_0); + IntConstant.EmitInt (ig, (int) State.After); + ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder); + + Label end = ig.DefineLabel (); + LocalBuilder retval = ec.GetTemporaryLocal (TypeManager.int32_type); + ig.MarkLabel (move_next_error); ig.Emit (OpCodes.Ldc_I4_0); ig.Emit (OpCodes.Stloc, retval); - ig.Emit (OpCodes.Leave, end); + ig.Emit (OpCodes.Leave_S, end); ig.MarkLabel (move_next_ok); ig.Emit (OpCodes.Ldc_I4_1); ig.Emit (OpCodes.Stloc, retval); - ig.Emit (OpCodes.Leave, end); + //ig.Emit (OpCodes.Leave_S, end); // SRE automatically emits a Leave on the BeginFaultBlock SymbolWriter.EndIteratorDispatcher (ec.ig); @@ -778,21 +806,10 @@ namespace Mono.CSharp { return resume_points.Count; } - protected class OldResumePoint - { - public Label Label; - - public void Define (ILGenerator ig) - { - Label = ig.DefineLabel (); - ig.MarkLabel (Label); - } - } - // // Called back from Yield // - public int MarkYield (EmitContext ec, Expression expr, bool unwind_protect) + public void MarkYield (EmitContext ec, Expression expr, int resume_pc, bool unwind_protect, Label resume_point) { ILGenerator ig = ec.ig; @@ -801,24 +818,21 @@ namespace Mono.CSharp { expr.Emit (ec); ig.Emit (OpCodes.Stfld, IteratorHost.CurrentField.FieldBuilder); - // increment pc - pc++; + // store resume program-counter ig.Emit (OpCodes.Ldarg_0); - IntConstant.EmitInt (ig, pc); + IntConstant.EmitInt (ig, resume_pc); ig.Emit (OpCodes.Stfld, IteratorHost.PC.FieldBuilder); // mark finally blocks as disabled - ig.Emit (OpCodes.Ldc_I4_1); - ig.Emit (OpCodes.Stloc, skip_finally); + if (unwind_protect && skip_finally != null) { + ig.Emit (OpCodes.Ldc_I4_1); + ig.Emit (OpCodes.Stloc, skip_finally); + } // Return ok ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, move_next_ok); - OldResumePoint point = new OldResumePoint (); - old_resume_points.Add (point); - point.Define (ig); - - return pc; + ig.MarkLabel (resume_point); } public override string ContainerType { diff --git a/mcs/mcs/statement.cs b/mcs/mcs/statement.cs index da42aecaf18..b8fd6ffa103 100644 --- a/mcs/mcs/statement.cs +++ b/mcs/mcs/statement.cs @@ -3789,14 +3789,15 @@ namespace Mono.CSharp { public abstract class ResumableStatement : Statement { bool prepared; - public Label ResumePoint; + protected Label resume_point; - public void PrepareForEmit (EmitContext ec) + public Label PrepareForEmit (EmitContext ec) { if (!prepared) { prepared = true; - ResumePoint = ec.ig.DefineLabel (); + resume_point = ec.ig.DefineLabel (); } + return resume_point; } public virtual Label PrepareForDispose (EmitContext ec, Label end) @@ -3825,21 +3826,43 @@ namespace Mono.CSharp { EmitPreTryBody (ec); + if (resume_points != null) { + IntConstant.EmitInt (ig, (int) Iterator.State.Running); + ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC); + } + if (emit_finally) ig.BeginExceptionBlock (); + if (resume_points != null) { + ig.MarkLabel (resume_point); + + // For normal control flow, we want to fall-through the Switch + // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above + ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC); + IntConstant.EmitInt (ig, first_resume_pc); + ig.Emit (OpCodes.Sub); + + Label [] labels = new Label [resume_points.Count]; + for (int i = 0; i < resume_points.Count; ++i) + labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec); + ig.Emit (OpCodes.Switch, labels); + } + EmitTryBody (ec); - Label end_finally = ec.ig.DefineLabel (); if (emit_finally) - ec.ig.BeginFinallyBlock (); + ig.BeginFinallyBlock (); + Label end_finally = ec.ig.DefineLabel (); if (resume_points != null) { - ec.ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally); - ec.ig.Emit (OpCodes.Brtrue, end_finally); + ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally); + ig.Emit (OpCodes.Brtrue, end_finally); + // should be: ig.Emit (OpCodes.Endfinally); } + EmitFinallyBody (ec); - ec.ig.MarkLabel (end_finally); + ig.MarkLabel (end_finally); if (emit_finally) ig.EndExceptionBlock (); -- 2.25.1