Compound assignments over reference type array cannot use ldelema
authorMarek Safar <marek.safar@gmail.com>
Mon, 11 Oct 2010 16:24:24 +0000 (17:24 +0100)
committerMarek Safar <marek.safar@gmail.com>
Mon, 11 Oct 2010 16:25:51 +0000 (17:25 +0100)
mcs/mcs/argument.cs
mcs/mcs/expression.cs
mcs/tests/test-580.cs
mcs/tests/ver-il-dmcs.xml
mcs/tests/ver-il-gmcs.xml

index 32207781595e7577723d064c703f7d2bfe289186..cde08b886f649648695a4a2f02bfec85351b9087 100644 (file)
@@ -345,21 +345,23 @@ namespace Mono.CSharp
                // 
                public void Emit (EmitContext ec)
                {
-                       Emit (ec, false, null);
+                       Emit (ec, false);
                }
 
                //
                // if `dup_args' is true, a copy of the arguments will be left
-               // on the stack. If `dup_args' is true, you can specify `this_arg'
-               // which will be duplicated before any other args. Only EmitCall
-               // should be using this interface.
+               // on the stack and return value will contain an array of access
+               // expressions
+               // NOTE: It's caller responsibility is to release temporary variables
                //
-               public void Emit (EmitContext ec, bool dup_args, LocalTemporary this_arg)
+               public Expression[] Emit (EmitContext ec, bool dup_args)
                {
-                       LocalTemporary[] temps = null;
+                       Expression[] temps;
 
                        if (dup_args && Count != 0)
-                               temps = new LocalTemporary [Count];
+                               temps = new Expression [Count];
+                       else
+                               temps = null;
 
                        if (reordered != null && Count > 1) {
                                foreach (NamedArgument na in reordered)
@@ -367,23 +369,27 @@ namespace Mono.CSharp
                        }
 
                        int i = 0;
+                       LocalTemporary lt;
                        foreach (Argument a in args) {
                                a.Emit (ec);
-                               if (dup_args) {
+                               if (!dup_args)
+                                       continue;
+
+                               if (a.Expr is Constant) {
+                                       //
+                                       // No need to create a temporary variable for constants
+                                       //
+                                       temps[i] = a.Expr;
+                               } else {
                                        ec.Emit (OpCodes.Dup);
-                                       (temps [i++] = new LocalTemporary (a.Type)).Store (ec);
+                                       temps[i] = lt = new LocalTemporary (a.Type);
+                                       lt.Store (ec);
                                }
-                       }
-
-                       if (dup_args) {
-                               if (this_arg != null)
-                                       this_arg.Emit (ec);
 
-                               for (i = 0; i < temps.Length; i++) {
-                                       temps[i].Emit (ec);
-                                       temps[i].Release (ec);
-                               }
+                               ++i;
                        }
+
+                       return temps;
                }
 
                public List<Argument>.Enumerator GetEnumerator ()
index 4549bd824865ae24d4bc53d6bb1fb7556d05733d..c17803b9e4c19442d01af5a45aa76f17e039b158 100644 (file)
@@ -5286,8 +5286,19 @@ namespace Mono.CSharp {
                                }
                        }
 
-                       if (!omit_args && Arguments != null)
-                               Arguments.Emit (ec, dup_args, this_arg);
+                       if (!omit_args && Arguments != null) {
+                               var dup_arg_exprs = Arguments.Emit (ec, dup_args);
+                               if (dup_args) {
+                                       this_arg.Emit (ec);
+                                       LocalTemporary lt;
+                                       foreach (var dup in dup_arg_exprs) {
+                                               dup.Emit (ec);
+                                               lt = dup as LocalTemporary;
+                                               if (lt != null)
+                                                       lt.Release (ec);
+                                       }
+                               }
+                       }
 
                        if (call_op == OpCodes.Callvirt && (iexpr_type.IsGenericParameter || iexpr_type.IsStruct)) {
                                ec.Emit (OpCodes.Constrained, iexpr_type);
@@ -7960,8 +7971,8 @@ namespace Mono.CSharp {
                //
                ElementAccess ea;
 
-               LocalTemporary temp, prepared_address;
-
+               LocalTemporary temp, expr_copy;
+               Expression[] prepared_arguments;
                bool prepared;
                
                public ArrayAccess (ElementAccess ea_data, Location l)
@@ -8022,10 +8033,7 @@ namespace Mono.CSharp {
                void LoadArrayAndArguments (EmitContext ec)
                {
                        ea.Expr.Emit (ec);
-
-                       for (int i = 0; i < ea.Arguments.Count; ++i) {
-                               ea.Arguments [i].Emit (ec);
-                       }
+                       ea.Arguments.Emit (ec);
                }
 
                public void Emit (EmitContext ec, bool leave_copy)
@@ -8033,12 +8041,21 @@ namespace Mono.CSharp {
                        var ac = ea.Expr.Type as ArrayContainer;
 
                        if (prepared) {
-                               if (prepared_address != null)
-                                       prepared_address.Emit (ec);
-
                                ec.EmitLoadFromPtr (type);
                        } else {
-                               LoadArrayAndArguments (ec);
+                               if (prepared_arguments == null) {
+                                       LoadArrayAndArguments (ec);
+                               } else {
+                                       expr_copy.Emit (ec);
+                                       LocalTemporary lt;
+                                       foreach (var expr in prepared_arguments) {
+                                               expr.Emit (ec);
+                                               lt = expr as LocalTemporary;
+                                               if (lt != null)
+                                                       lt.Release (ec);
+                                       }
+                               }
+
                                ec.EmitArrayLoad (ac);
                        }       
 
@@ -8058,40 +8075,38 @@ namespace Mono.CSharp {
                {
                        var ac = (ArrayContainer) ea.Expr.Type;
                        TypeSpec t = source.Type;
-                       prepared = prepare_for_load;
 
-                       LoadArrayAndArguments (ec);
-
-                       if (prepared) {
+                       //
+                       // When we are dealing with a struct, get the address of it to avoid value copy
+                       // Same cannot be done for reference type because array covariance and the
+                       // check in ldelema requires to specify the type of array element stored at the index
+                       //
+                       if (t.IsStruct && (prepare_for_load || !TypeManager.IsPrimitiveType (t))) {
+                               LoadArrayAndArguments (ec);
                                ec.EmitArrayAddress (ac);
-                               if (source is DynamicExpressionStatement) {
-                                       // Store prepared address in a variable to keep stack
-                                       // consistent for compound dynamic operation which is
-                                       // emitted as a method call
-                                       prepared_address = new LocalTemporary (ReferenceContainer.MakeType (t));
-                                       prepared_address.Store (ec);
-                                       prepared_address.Emit (ec);
-                               } else {
+
+                               if (prepare_for_load) {
                                        ec.Emit (OpCodes.Dup);
                                }
-                       } else {
-                               //
-                               // If we are dealing with a struct, get the
-                               // address of it, so we can store it.
-                               //
-                               // The stobj opcode used by value types will need
-                               // an address on the stack, not really an array/array
-                               // pair
-                               //
-                               if (ac.Rank == 1 && TypeManager.IsStruct (t) &&
-                                       (!TypeManager.IsBuiltinOrEnum (t) ||
-                                        t == TypeManager.decimal_type)) {
 
-                                       ec.Emit (OpCodes.Ldelema, t);
-                               }
+                               prepared = true;
+                       } else if (prepare_for_load) {
+                               ea.Expr.Emit (ec);
+                               ec.Emit (OpCodes.Dup);
+
+                               expr_copy = new LocalTemporary (ea.Expr.Type);
+                               expr_copy.Store (ec);
+                               prepared_arguments = ea.Arguments.Emit (ec, true);
+                       } else {
+                               LoadArrayAndArguments (ec);
                        }
 
                        source.Emit (ec);
+
+                       if (expr_copy != null) {
+                               expr_copy.Release (ec);
+                       }
+
                        if (leave_copy) {
                                ec.Emit (OpCodes.Dup);
                                temp = new LocalTemporary (this.type);
index e0c7dba68ad15edc3c2b72eb8c65d51e68b5c9da..e1712837a7d43ac433de56564adb43b0daf8f8e6 100644 (file)
@@ -55,6 +55,35 @@ public class Bla {
                label[idx++, idx - 1] += s + s + s + s;
        }
        
+       static bool Test_Object ()
+       {
+               int a = 0;
+               object[] o_a = new string[] { "A" };
+               o_a [a++] += "Z";
+               if ((string) o_a [0] != "AZ")
+                       return false;
+               
+               a = 0;
+               object[,] o_a2 = new string[,] { { "X" } };
+               o_a2[a++, 0] += "Z";
+               if ((string) o_a2 [0, 0] != "XZ")
+                       return false;
+               
+               return true;
+       }
+       
+       static bool Test_Decimal ()
+       {
+               decimal[,] da = new decimal[,] { { 5, 6 } };
+               da[0,0] = 6.7m;
+               da[0,0] += 1.2m;
+               
+               if (da [0,0] != 7.9m)
+                       return false;
+               
+               return true;
+       }
+       
        public static int Main ()
        {
                String str = "test";
@@ -97,6 +126,12 @@ public class Bla {
                if (sa2 [0,0] != "aaaa")
                        return 7;
                
+               if (!Test_Object ())
+                       return 8;
+
+               if (!Test_Decimal ())
+                       return 9;
+
                return 0;
        }
 }
index 3f0f05520280b9f4c0c3c6500db2ce8f026ce523..4f1848f19243a07e1145c3f2e64abfdd08806445 100644 (file)
         <size>74</size>
       </method>
       <method name="Int32 Main()">
-        <size>844</size>
+        <size>842</size>
       </method>
       <method name="Void .ctor()">
         <size>18</size>
       </method>
     </type>
   </test>
+  <test name="dtest-cls-01.cs">
+    <type name="A">
+      <method name="Void Main()">
+        <size>1</size>
+      </method>
+      <method name="Void CLSCompliantMethod(System.Object[])">
+        <size>1</size>
+      </method>
+      <method name="Void CLSCompliantMethod(IEnumerable`1)">
+        <size>1</size>
+      </method>
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="dtest-collectioninit-01.cs">
     <type name="Test">
       <method name="Int32 Main()">
         <size>7</size>
       </method>
       <method name="Void A(System.Collections.Specialized.NameValueCollection, MyClass, System.Object)">
-        <size>65</size>
+        <size>69</size>
       </method>
       <method name="Int32 Main()">
         <size>2</size>
         <size>1</size>
       </method>
       <method name="Boolean Test()">
-        <size>110</size>
+        <size>108</size>
       </method>
     </type>
     <type name="Driver">
         <size>18</size>
       </method>
       <method name="Int32 i_pre_increment(X)">
-        <size>27</size>
+        <size>26</size>
       </method>
       <method name="Int32 i_post_increment(X)">
-        <size>27</size>
+        <size>26</size>
       </method>
       <method name="Z overload_increment(Z)">
         <size>10</size>
       </method>
     </type>
   </test>
+  <test name="test-539.cs">
+    <type name="Test">
+      <method name="Int32 Main()">
+        <size>84</size>
+      </method>
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="&lt;PrivateImplementationDetails&gt;">
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="test-54.cs">
     <type name="X">
       <method name="Void .ctor()">
         <size>43</size>
       </method>
       <method name="Void BuildNode(System.String[] ByRef)">
-        <size>56</size>
+        <size>58</size>
       </method>
       <method name="Void BuildNode_B(System.Object ByRef)">
         <size>18</size>
       </method>
       <method name="System.String BuildNode_C(System.String ByRef)">
-        <size>76</size>
+        <size>80</size>
       </method>
       <method name="System.String BuildNode_D()">
-        <size>156</size>
+        <size>160</size>
       </method>
       <method name="Void BuildNode_E(System.String[,] ByRef)">
-        <size>68</size>
+        <size>83</size>
       </method>
       <method name="Int32 Main()">
-        <size>265</size>
+        <size>290</size>
+      </method>
+      <method name="Boolean Test_Object()">
+        <size>160</size>
+      </method>
+      <method name="Boolean Test_Decimal()">
+        <size>128</size>
       </method>
     </type>
   </test>
         <size>40</size>
       </method>
       <method name="Void TestMethod()">
-        <size>71</size>
+        <size>73</size>
       </method>
     </type>
     <type name="M">
         <size>8</size>
       </method>
       <method name="Int32 Do(System.String, System.String, System.String)">
-        <size>334</size>
+        <size>332</size>
       </method>
     </type>
   </test>
         <size>8</size>
       </method>
       <method name="Int32 Main()">
-        <size>77</size>
+        <size>69</size>
       </method>
     </type>
   </test>
     </type>
     <type name="Tester">
       <method name="Int32 Main()">
-        <size>80</size>
+        <size>74</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>9</size>
       </method>
       <method name="Int32 Main()">
-        <size>53</size>
+        <size>52</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
index 280d61b90538e9ac5d85b423a06d089a1eef90bc..8a18cad5fecfe289f7491f1ca2b217da1b89ce47 100644 (file)
         <size>7</size>
       </method>
       <method name="Void A(System.Collections.Specialized.NameValueCollection, MyClass, System.Object)">
-        <size>65</size>
+        <size>69</size>
       </method>
       <method name="Int32 Main()">
         <size>2</size>
         <size>1</size>
       </method>
       <method name="Boolean Test()">
-        <size>110</size>
+        <size>108</size>
       </method>
     </type>
     <type name="Driver">
         <size>18</size>
       </method>
       <method name="Int32 i_pre_increment(X)">
-        <size>27</size>
+        <size>26</size>
       </method>
       <method name="Int32 i_post_increment(X)">
-        <size>27</size>
+        <size>26</size>
       </method>
       <method name="Z overload_increment(Z)">
         <size>10</size>
       </method>
     </type>
   </test>
+  <test name="test-539.cs">
+    <type name="Test">
+      <method name="Int32 Main()">
+        <size>84</size>
+      </method>
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+    <type name="&lt;PrivateImplementationDetails&gt;">
+      <method name="Void .ctor()">
+        <size>7</size>
+      </method>
+    </type>
+  </test>
   <test name="test-54.cs">
     <type name="X">
       <method name="Void .ctor()">
         <size>43</size>
       </method>
       <method name="Void BuildNode(System.String[] ByRef)">
-        <size>56</size>
+        <size>58</size>
       </method>
       <method name="Void BuildNode_B(System.Object ByRef)">
         <size>18</size>
       </method>
       <method name="System.String BuildNode_C(System.String ByRef)">
-        <size>76</size>
+        <size>80</size>
       </method>
       <method name="System.String BuildNode_D()">
-        <size>156</size>
+        <size>160</size>
       </method>
       <method name="Void BuildNode_E(System.String[,] ByRef)">
-        <size>68</size>
+        <size>83</size>
       </method>
       <method name="Int32 Main()">
-        <size>265</size>
+        <size>290</size>
+      </method>
+      <method name="Boolean Test_Object()">
+        <size>160</size>
+      </method>
+      <method name="Boolean Test_Decimal()">
+        <size>128</size>
       </method>
     </type>
   </test>
         <size>40</size>
       </method>
       <method name="Void TestMethod()">
-        <size>71</size>
+        <size>73</size>
       </method>
     </type>
     <type name="M">
         <size>8</size>
       </method>
       <method name="Int32 Do(System.String, System.String, System.String)">
-        <size>334</size>
+        <size>332</size>
       </method>
     </type>
   </test>
         <size>8</size>
       </method>
       <method name="Int32 Main()">
-        <size>77</size>
+        <size>69</size>
       </method>
     </type>
   </test>
     </type>
     <type name="Tester">
       <method name="Int32 Main()">
-        <size>80</size>
+        <size>74</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>9</size>
       </method>
       <method name="Int32 Main()">
-        <size>53</size>
+        <size>52</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>