Use the ResumableStatement infrastructure for MoveNext ()
authorRaja R Harinath <harinath@hurrynot.org>
Sat, 5 Apr 2008 18:50:25 +0000 (18:50 -0000)
committerRaja R Harinath <harinath@hurrynot.org>
Sat, 5 Apr 2008 18:50:25 +0000 (18:50 -0000)
* 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
mcs/mcs/iterators.cs
mcs/mcs/statement.cs

index 5b8d3bebeeb69d39d04aa7ca623a999f14b53a89..e9ef00425832779568d2bb7bf1665211d7b1b816 100644 (file)
@@ -1,3 +1,16 @@
+2008-04-06  Raja R Harinath  <harinath@hurrynot.org>
+
+       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  <harinath@hurrynot.org>
 
        * statement.cs (ExceptionStatement.ResolveFinally): Move setting
index 0c90a350cdf74a5435f1e267369d7c28b404834d..0d80382049d2d7f4d3fa2ac00ca187445ebe5201 100644 (file)
@@ -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 {
index da42aecaf18bad34d5e519fb21827ceed7afeae3..b8fd6ffa103cbd366770b91b3410966d13cd74fd 100644 (file)
@@ -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 ();