Generate better code for small switch statements
authorMarek Safar <marek.safar@gmail.com>
Wed, 14 Sep 2011 14:45:08 +0000 (15:45 +0100)
committerMarek Safar <marek.safar@gmail.com>
Thu, 15 Sep 2011 12:28:09 +0000 (13:28 +0100)
mcs/mcs/statement.cs
mcs/tests/test-49.cs
mcs/tests/ver-il-net_4_0.xml

index 77f836ce565720f554e6fdf734ea39dccf598bcf..7b68fa9e5d345b39fbfaf331a591468c2bf1a357 100644 (file)
@@ -1,10 +1,10 @@
 //
 // statement.cs: Statement representation for the IL tree.
 //
-// Author:
+// Authors:
 //   Miguel de Icaza (miguel@ximian.com)
 //   Martin Baulig (martin@ximian.com)
-//   Marek Safar (marek.safar@seznam.cz)
+//   Marek Safar (marek.safar@gmail.com)
 //
 // Copyright 2001, 2002, 2003 Ximian, Inc.
 // Copyright 2003, 2004 Novell, Inc.
@@ -1807,7 +1807,8 @@ namespace Mono.CSharp {
                        HasCapturedThis = 1 << 7,
                        IsExpressionTree = 1 << 8,
                        CompilerGenerated = 1 << 9,
-                       IsAsync = 1 << 10
+                       IsAsync = 1 << 10,
+                       Resolved = 1 << 11
                }
 
                public Block Parent;
@@ -1996,6 +1997,9 @@ namespace Mono.CSharp {
 
                public override bool Resolve (BlockContext ec)
                {
+                       if ((flags & Flags.Resolved) != 0)
+                               return true;
+
                        Block prev_block = ec.CurrentBlock;
                        bool ok = true;
 
@@ -2080,6 +2084,7 @@ namespace Mono.CSharp {
                        if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
                                ok = false;
 
+                       flags |= Flags.Resolved;
                        return ok;
                }
 
@@ -3258,6 +3263,32 @@ namespace Mono.CSharp {
                        }
                }
 
+               sealed class LabelMarker : Statement
+               {
+                       readonly Switch s;
+                       readonly List<SwitchLabel> labels;
+
+                       public LabelMarker (Switch s, List<SwitchLabel> labels)
+                       {
+                               this.s = s;
+                               this.labels = labels;
+                       }
+
+                       protected override void CloneTo (CloneContext clonectx, Statement target)
+                       {
+                       }
+
+                       protected override void DoEmit (EmitContext ec)
+                       {
+                               foreach (var l in labels) {
+                                       if (l.IsDefault)
+                                               ec.MarkLabel (s.DefaultLabel);
+                                       else
+                                               ec.MarkLabel (l.GetILLabel (ec));
+                               }
+                       }
+               }
+
                public List<SwitchSection> Sections;
                public Expression Expr;
 
@@ -3284,6 +3315,8 @@ namespace Mono.CSharp {
                SwitchSection default_section;
                SwitchLabel null_section;
 
+               Statement simple_stmt;
+               VariableReference value;
                ExpressionStatement string_dictionary;
                FieldExpr switch_cache_field;
                static int unique_counter;
@@ -3701,6 +3734,13 @@ namespace Mono.CSharp {
 
                                if (constant_section == null)
                                        constant_section = default_section;
+                       } else {
+                               //
+                               // Store switch expression for comparission purposes
+                               //
+                               value = new_expr as VariableReference;
+                               if (value == null)
+                                       value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
                        }
 
                        bool first = true;
@@ -3727,8 +3767,7 @@ namespace Mono.CSharp {
                        }
 
                        if (default_section == null)
-                               ec.CurrentBranching.CreateSibling (
-                                       null, FlowBranching.SiblingType.SwitchSection);
+                               ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
 
                        ec.EndFlowBranching ();
                        ec.Switch = old_switch;
@@ -3736,9 +3775,15 @@ namespace Mono.CSharp {
                        if (!ok)
                                return false;
 
-                       if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
-                               // TODO: Optimize single case, and single+default case
-                               ResolveStringSwitchMap (ec);
+                       if (!is_constant) {
+                               if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
+                                       if (string_labels.Count < 7)
+                                               ResolveSimpleSwitch (ec);
+                                       else
+                                               ResolveStringSwitchMap (ec);
+                               } else if (labels.Count < 3 && !IsNullable) {
+                                       ResolveSimpleSwitch (ec);
+                               }
                        }
 
                        return true;
@@ -3755,6 +3800,45 @@ namespace Mono.CSharp {
                        return sl;
                }
 
+               //
+               // Prepares switch using simple if/else comparison for small label count (4 + optional default)
+               //
+               void ResolveSimpleSwitch (BlockContext bc)
+               {
+                       simple_stmt = default_section != null ? default_section.Block : null;
+
+                       for (int i = Sections.Count - 1; i >= 0; --i) {
+                               var s = Sections[i];
+
+                               if (s == default_section) {
+                                       s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
+                                       continue;
+                               }
+
+                               s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
+
+                               Expression cond = null;
+                               for (int ci = 0; ci < s.Labels.Count; ++ci) {
+                                       var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
+
+                                       if (ci > 0) {
+                                               cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
+                                       } else {
+                                               cond = e;
+                                       }
+                               }
+
+                               simple_stmt = new If (cond, s.Block, simple_stmt, loc);
+                       }
+
+                       // It's null for empty switch
+                       if (simple_stmt != null)
+                               simple_stmt.Resolve (bc);
+               }
+
+               //
+               // Converts string switch into string hashtable
+               //
                void ResolveStringSwitchMap (ResolveContext ec)
                {
                        FullNamedExpression string_dictionary_type;
@@ -3819,7 +3903,7 @@ namespace Mono.CSharp {
                        string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
                }
 
-               void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
+               void DoEmitStringSwitch (EmitContext ec)
                {
                        Label l_initialized = ec.DefineLabel ();
 
@@ -3873,7 +3957,7 @@ namespace Mono.CSharp {
                        EmitTableSwitch (ec, string_switch_variable);
                        string_switch_variable.Release (ec);
                }
-               
+
                protected override void DoEmit (EmitContext ec)
                {
                        //
@@ -3885,21 +3969,13 @@ namespace Mono.CSharp {
                        default_target = ec.DefineLabel ();
                        null_target = ec.DefineLabel ();
 
-                       // Store variable for comparission purposes
-                       // TODO: Don't duplicate non-captured VariableReference
-                       LocalTemporary value;
                        if (IsNullable) {
-                               value = new LocalTemporary (SwitchType);
                                unwrap.EmitCheck (ec);
                                ec.Emit (OpCodes.Brfalse, null_target);
-                               new_expr.Emit (ec);
-                               value.Store (ec);
-                       } else if (!is_constant) {
-                               value = new LocalTemporary (SwitchType);
-                               new_expr.Emit (ec);
-                               value.Store (ec);
-                       } else
-                               value = null;
+                               value.EmitAssign (ec, new_expr, false, false);
+                       } else if (new_expr != value && !is_constant) {
+                               value.EmitAssign (ec, new_expr, false, false);
+                       }
 
                        //
                        // Setup the codegen context
@@ -3915,14 +3991,13 @@ namespace Mono.CSharp {
                                if (constant_section != null)
                                        constant_section.Block.Emit (ec);
                        } else if (string_dictionary != null) {
-                               DoEmitStringSwitch (value, ec);
+                               DoEmitStringSwitch (ec);
+                       } else if (simple_stmt != null) {
+                               simple_stmt.Emit (ec);
                        } else {
                                EmitTableSwitch (ec, value);
                        }
 
-                       if (value != null)
-                               value.Release (ec);
-
                        // Restore context state. 
                        ec.MarkLabel (ec.LoopEnd);
 
index b950db27c30226952e7641f0c7bba46e03e0fc96..ad047cab8f1c6e8a6e721575d73a6ce38a052fdb 100644 (file)
@@ -326,6 +326,19 @@ class X {
 
                return 1;
        }
+       
+       static int tests2 (string s)
+       {
+               switch (s){
+               case "one":
+                       goto case null;
+               case "two":
+                       goto default;
+               case null:
+               default:
+                       return 3;
+               }
+       }
 
        static int testn (string s)
        {
@@ -693,6 +706,13 @@ class X {
                if (!bug_78860 ())
                        return 60;
                
+               if (tests2 ("a") != 3)
+                       return 71;
+               if (tests2 ("one") != 3)
+                       return 72;
+               if (tests2 ("two") != 3)
+                       return 73;
+
                Console.WriteLine ("All tests pass");
                return 0;
        }
index 00d0fddff4e37af34f019a86c9e832db92dc64c3..aa323fe77fa6c96300c093cf73007f4a1fb2c7f6 100644 (file)
         <size>7</size>
       </method>
       <method name="Int32 Main()">
-        <size>101</size>
+        <size>86</size>
       </method>
     </type>
   </test>
         <size>23</size>
       </method>
       <method name="Int64 test11(Int32)">
-        <size>44</size>
+        <size>47</size>
       </method>
       <method name="Void test12(Single ByRef)">
         <size>20</size>
         <size>23</size>
       </method>
       <method name="Int64 test14(Int32, Single ByRef)">
-        <size>66</size>
+        <size>64</size>
       </method>
       <method name="Int32 test15(Int32, Single ByRef)">
         <size>33</size>
         <size>29</size>
       </method>
       <method name="Int64 test24(Int32)">
-        <size>67</size>
+        <size>60</size>
       </method>
       <method name="Int64 test25(Int32)">
         <size>34</size>
         <size>31</size>
       </method>
       <method name="System.String test31(Int32)">
-        <size>78</size>
+        <size>76</size>
       </method>
       <method name="Void test32()">
         <size>11</size>
         <size>20</size>
       </method>
       <method name="Void test35(Int32, Boolean)">
-        <size>28</size>
+        <size>26</size>
       </method>
       <method name="Void test36()">
         <size>41</size>
       </method>
       <method name="Void test37()">
-        <size>42</size>
+        <size>28</size>
       </method>
       <method name="Int32 test38()">
         <size>2</size>
         <size>7</size>
       </method>
       <method name="Int32 Test(Int32)">
-        <size>88</size>
+        <size>80</size>
       </method>
       <method name="Int32 Main()">
         <size>7</size>
         <size>7</size>
       </method>
       <method name="Void Main()">
-        <size>701</size>
+        <size>699</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Void Main(System.String[])">
-        <size>40</size>
+        <size>33</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Void Main()">
-        <size>27</size>
+        <size>20</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Int32 ParseType(System.String)">
-        <size>168</size>
+        <size>92</size>
       </method>
       <method name="Int32 Main()">
         <size>54</size>
         <size>7</size>
       </method>
       <method name="Int32 Main()">
-        <size>40</size>
+        <size>38</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Void Main(System.String[])">
-        <size>40</size>
+        <size>33</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Int32 s(Byte)">
-        <size>2182</size>
+        <size>2180</size>
       </method>
       <method name="Int32 test(Int32)">
-        <size>70</size>
+        <size>68</size>
       </method>
       <method name="Int32 tests(System.String)">
-        <size>152</size>
+        <size>108</size>
       </method>
       <method name="Int32 testn(System.String)">
-        <size>76</size>
+        <size>20</size>
       </method>
       <method name="Int32 testm(System.String)">
-        <size>77</size>
+        <size>21</size>
       </method>
       <method name="Int32 testo(System.String)">
-        <size>79</size>
+        <size>40</size>
       </method>
       <method name="Int32 testp(System.String)">
-        <size>109</size>
+        <size>71</size>
       </method>
       <method name="Int32 test_def(System.String)">
-        <size>119</size>
+        <size>59</size>
       </method>
       <method name="Int32 test_coverage(Int32)">
-        <size>18</size>
+        <size>11</size>
       </method>
       <method name="Int32 test_goto(Int32)">
-        <size>39</size>
+        <size>37</size>
       </method>
       <method name="Int32 test_memberaccess(System.String)">
-        <size>76</size>
+        <size>20</size>
       </method>
       <method name="Int32 test_string_multiple_targets(System.String)">
-        <size>128</size>
+        <size>82</size>
       </method>
       <method name="Int32 test_casts(Int32)">
-        <size>17</size>
+        <size>10</size>
       </method>
       <method name="Int32 testSwitchEnumLong(TestEnum)">
-        <size>56</size>
+        <size>54</size>
       </method>
       <method name="Int32 test_long_enum_switch()">
         <size>66</size>
       </method>
       <method name="Int32 tests_default(System.String)">
-        <size>107</size>
+        <size>20</size>
       </method>
       <method name="Int32 tests_default_2(System.String)">
-        <size>124</size>
+        <size>79</size>
       </method>
       <method name="Void test_76590(System.String)">
-        <size>107</size>
+        <size>70</size>
       </method>
       <method name="Void test_77964()">
-        <size>37</size>
+        <size>35</size>
       </method>
       <method name="Boolean bug_78860()">
-        <size>114</size>
+        <size>72</size>
       </method>
       <method name="Int32 Main()">
-        <size>1037</size>
+        <size>1094</size>
+      </method>
+      <method name="Int32 tests2(System.String)">
+        <size>44</size>
       </method>
     </type>
   </test>
   <test name="test-499.cs">
     <type name="A">
       <method name="Int32 switch1(UInt64)">
-        <size>96</size>
+        <size>94</size>
       </method>
       <method name="Int32 switch2(SByte)">
         <size>26</size>
         <size>27</size>
       </method>
       <method name="Int32 switch4(UInt64)">
-        <size>39</size>
+        <size>37</size>
       </method>
       <method name="Int32 switch5(UInt64)">
         <size>22</size>
         <size>7</size>
       </method>
       <method name="Void test39(Int32 ByRef)">
-        <size>36</size>
+        <size>34</size>
       </method>
       <method name="Void Main()">
         <size>28</size>
         <size>7</size>
       </method>
       <method name="Void Main()">
-        <size>32</size>
+        <size>24</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Void Foo(System.String)">
-        <size>49</size>
+        <size>14</size>
       </method>
     </type>
     <type name="C2">
         <size>33</size>
       </method>
       <method name="Void foo(Int32)">
-        <size>38</size>
+        <size>31</size>
       </method>
       <method name="Void XXXA()">
         <size>6</size>
         <size>7</size>
       </method>
       <method name="Boolean Test(System.String)">
-        <size>148</size>
+        <size>146</size>
       </method>
       <method name="Int32 Main()">
         <size>49</size>
         <size>2</size>
       </method>
       <method name="Void Foo(ItemSlot)">
-        <size>27</size>
+        <size>20</size>
       </method>
       <method name="Int32 Main()">
         <size>2</size>
     </type>
     <type name="B">
       <method name="Int32 Main()">
-        <size>237</size>
+        <size>184</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>6</size>
       </method>
       <method name="Int32 Main()">
-        <size>265</size>
+        <size>212</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>53</size>
       </method>
       <method name="Void &lt;Main&gt;m__7(E)">
-        <size>36</size>
+        <size>39</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>35</size>
       </method>
       <method name="Void &lt;Main&gt;m__0()">
-        <size>137</size>
+        <size>79</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>112</size>
       </method>
       <method name="Int32 &lt;Main&gt;m__0(Int32)">
-        <size>40</size>
+        <size>43</size>
       </method>
       <method name="Int32 &lt;Main&gt;m__1(Int32)">
-        <size>35</size>
+        <size>28</size>
       </method>
       <method name="Int32 &lt;Main&gt;m__2(Int32)">
-        <size>26</size>
+        <size>19</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>3</size>
       </method>
       <method name="Program+D Get(Int32)">
-        <size>128</size>
+        <size>126</size>
       </method>
       <method name="Int32 Run(Int32)">
         <size>12</size>
         <size>179</size>
       </method>
       <method name="Boolean &lt;Main&gt;m__0(System.Reflection.MethodInfo)">
-        <size>12</size>
+        <size>37</size>
       </method>
       <method name="System.String &lt;Main&gt;m__1(System.Reflection.MethodInfo)">
         <size>7</size>
     </type>
     <type name="Tester+&lt;SwitchTest_1&gt;c__async0">
       <method name="Void MoveNext()">
-        <size>387</size>
+        <size>409</size>
       </method>
       <method name="System.String &lt;&gt;m__4()">
         <size>6</size>
         <size>36</size>
       </method>
     </type>
-    <type name="Tester+&lt;Using_1&gt;c__async1+&lt;Using_1&gt;c__AnonStorey3">
+    <type name="Tester+&lt;SwitchTest_1&gt;c__async0+&lt;SwitchTest_1&gt;c__AnonStorey3">
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="Tester+&lt;Using_1&gt;c__async1+&lt;Using_1&gt;c__AnonStorey4">
       <method name="Void .ctor()">
         <size>7</size>
       </method>
     </type>
-    <type name="Tester+&lt;Foreach_1&gt;c__async2+&lt;Foreach_1&gt;c__AnonStorey4">
+    <type name="Tester+&lt;Foreach_1&gt;c__async2+&lt;Foreach_1&gt;c__AnonStorey5">
       <method name="Void .ctor()">
         <size>7</size>
       </method>