[mcs] null operator on address locations
authorMarek Safar <marek.safar@gmail.com>
Wed, 13 Aug 2014 18:20:44 +0000 (20:20 +0200)
committerMarek Safar <marek.safar@gmail.com>
Wed, 13 Aug 2014 18:21:10 +0000 (20:21 +0200)
mcs/mcs/codegen.cs
mcs/tests/test-null-operator-01.cs
mcs/tests/test-null-operator-08.cs [new file with mode: 0644]
mcs/tests/ver-il-net_4_5.xml

index a499e31ba5fe3c43c52c134813e70527d534461f..55a061cf034c4191e9aef92ed25b9228a8e3582b 100644 (file)
@@ -1208,17 +1208,50 @@ namespace Mono.CSharp
                                unwrap = null;
                        }
 
+                       IMemoryLocation instance_address = null;
+                       bool conditional_access_dup = false;
+
                        if (unwrap != null) {
                                unwrap.Store (ec);
                                unwrap.EmitCheck (ec);
                                ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
                        } else {
-                               EmitLoad (ec);
+                               if (conditionalAccess && addressRequired) {
+                                       //
+                                       // Don't allocate temp variable when instance load is cheap and load and load-address
+                                       // operate on same memory
+                                       //
+                                       instance_address = instance as VariableReference;
+                                       if (instance_address == null)
+                                               instance_address = instance as LocalTemporary;
+
+                                       if (instance_address == null) {
+                                               EmitLoad (ec);
+                                               ec.Emit (OpCodes.Dup);
+                                               ec.EmitLoadFromPtr (instance.Type);
+
+                                               conditional_access_dup = true;
+                                       } else {
+                                               instance.Emit (ec);
+                                       }
+
+                                       if (instance.Type.Kind == MemberKind.TypeParameter)
+                                               ec.Emit (OpCodes.Box, instance.Type);
+                               } else {
+                                       EmitLoad (ec);
+
+                                       if (conditionalAccess) {
+                                               conditional_access_dup = !IsInexpensiveLoad ();
+                                               if (conditional_access_dup)
+                                                       ec.Emit (OpCodes.Dup);
+                                       }
+                               }
 
                                if (conditionalAccess) {
-                                       ec.Emit (OpCodes.Dup);
                                        ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
-                                       ec.Emit (OpCodes.Pop);
+
+                                       if (conditional_access_dup)
+                                               ec.Emit (OpCodes.Pop);
                                }
                        }
 
@@ -1233,12 +1266,16 @@ namespace Mono.CSharp
                                ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
                                ec.MarkLabel (NullOperatorLabel);
 
-                               if (unwrap != null) {
+                               if (instance_address != null) {
+                                       instance_address.AddressOf (ec, AddressOp.Load);
+                               } else if (unwrap != null) {
                                        unwrap.Emit (ec);
                                        var tmp = ec.GetTemporaryLocal (unwrap.Type);
                                        ec.Emit (OpCodes.Stloc, tmp);
                                        ec.Emit (OpCodes.Ldloca, tmp);
                                        ec.FreeTemporaryLocal (tmp, unwrap.Type);
+                               } else if (!conditional_access_dup) {
+                                       instance.Emit (ec);
                                }
                        }
                }
@@ -1274,11 +1311,8 @@ namespace Mono.CSharp
                        instance.Emit (ec);
 
                        // Only to make verifier happy
-                       if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) {
+                       if (RequiresBoxing ())
                                ec.Emit (OpCodes.Box, instance_type);
-                       } else if (instance_type.IsStructOrEnum) {
-                               ec.Emit (OpCodes.Box, instance_type);
-                       }
                }
 
                public TypeSpec GetStackType (EmitContext ec)
@@ -1293,5 +1327,39 @@ namespace Mono.CSharp
 
                        return instance_type;
                }
+
+               bool RequiresBoxing ()
+               {
+                       var instance_type = instance.Type;
+                       if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
+                               return true;
+
+                       if (instance_type.IsStructOrEnum)
+                               return true;
+
+                       return false;
+               }
+
+               bool IsInexpensiveLoad ()
+               {
+                       if (instance is Constant)
+                               return instance.IsSideEffectFree;
+
+                       if (RequiresBoxing ())
+                               return false;
+
+                       var vr = instance as VariableReference;
+                       if (vr != null)
+                               return !vr.IsRef;
+
+                       if (instance is LocalTemporary)
+                               return true;
+
+                       var fe = instance as FieldExpr;
+                       if (fe != null)
+                               return fe.IsStatic || fe.InstanceExpression is This;
+
+                       return false;
+               }
        }
 }
index 2c6784c93b6d67cffee8af92e08605fbd16ae412..63cae1282ee984b2337b3fbdccfebbdc131795a6 100644 (file)
@@ -97,10 +97,9 @@ class C
 
     static int TestGeneric<T> (T t) where T : class, I
     {
-        // FIXME:
-        //var t1 = t?.Method ();
-        //if (t1 != null)
-        //  return 1;
+        var t1 = t?.Method ();
+        if (t1 != null)
+            return 1;
 
         T[] at = null;
         var t2 = at?.Length;
diff --git a/mcs/tests/test-null-operator-08.cs b/mcs/tests/test-null-operator-08.cs
new file mode 100644 (file)
index 0000000..b988e49
--- /dev/null
@@ -0,0 +1,93 @@
+using System;
+
+interface I
+{
+       void Foo (bool expected);
+}
+
+struct S : I
+{
+       bool flag;
+
+       public void Foo (bool expected)
+       {
+               Console.WriteLine (flag);
+               if (expected != flag)
+                       throw new ApplicationException ();
+
+               flag = true;
+       }
+}
+
+class Program
+{
+       static void M<T> (T x)
+       {
+               object s = x?.ToString ();
+               System.Console.WriteLine (s);
+
+               var h = x?.GetHashCode ();
+               System.Console.WriteLine (h);
+       }
+
+       static void M2<T> (T[] x)
+       {
+               object s = x?.ToString ();
+               System.Console.WriteLine (s);
+
+               var h = x?.GetHashCode ();
+               System.Console.WriteLine (h);
+       }
+
+       static void M2_2<T> (T[] x)
+       {
+               object s = x[0]?.ToString ();
+               System.Console.WriteLine (s);
+
+               var h = x[0]?.GetHashCode ();
+               System.Console.WriteLine (h);
+       }
+
+       static void M3<T> (T? x) where T : struct
+       {
+               object s = x?.ToString ();
+               System.Console.WriteLine (s);
+
+               var h = x?.GetHashCode ();
+               System.Console.WriteLine (h);
+       }
+
+       static void TestAddress_1<T> (T t) where T : I
+       {
+               t?.Foo (false);
+               t?.Foo (true);
+       }
+
+       static void TestAddress_2<T> (T[] t) where T : I
+       {
+               t[0]?.Foo (false);
+               t[0]?.Foo (true);
+       }
+
+       static void Main()
+       {
+               M<string> (null);
+               M (1);
+               M("X");
+
+               M2<int> (null);
+               M2<string> (null);
+               M2 (new [] { 1 });
+               M2 (new [] { "x" });
+
+               M2_2 (new string [1]);
+               M2_2 (new int [1]);
+
+               M3<int> (1);
+               M3<byte> (null);
+
+               TestAddress_1 (new S ());
+               var ar = new [] { new S () };
+               TestAddress_2 (ar);
+       }
+}
\ No newline at end of file
index da6f88d9cc397cd6a6a6795c4bf311dbc17df585..37782bb451fc77a032a929f7350469a516df7482 100644 (file)
         <size>22</size>\r
       </method>\r
       <method name="Int32 TestArray()" attrs="145">\r
-        <size>485</size>\r
+        <size>477</size>\r
       </method>\r
       <method name="Int32 TestReferenceType()" attrs="145">\r
-        <size>231</size>\r
+        <size>229</size>\r
       </method>\r
       <method name="Int32 TestGeneric[T](T)" attrs="145">\r
-        <size>58</size>\r
+        <size>118</size>\r
       </method>\r
       <method name="Int32 TestNullable()" attrs="145">\r
         <size>386</size>\r
         <size>7</size>\r
       </method>\r
       <method name="Int32 TestProperty()" attrs="145">\r
-        <size>368</size>\r
+        <size>359</size>\r
       </method>\r
       <method name="Int32 TestField()" attrs="145">\r
-        <size>360</size>\r
+        <size>351</size>\r
       </method>\r
     </type>\r
     <type name="CI">\r
         <size>2</size>\r
       </method>\r
       <method name="Void Main()" attrs="150">\r
-        <size>27</size>\r
+        <size>26</size>\r
       </method>\r
       <method name="Void .ctor()" attrs="6278">\r
         <size>7</size>\r
     </type>\r
     <type name="C">\r
       <method name="Int32 TestArrayAccess()" attrs="145">\r
-        <size>208</size>\r
+        <size>206</size>\r
       </method>\r
       <method name="Int32 TestIndexerAccess()" attrs="145">\r
-        <size>191</size>\r
+        <size>188</size>\r
       </method>\r
       <method name="Int32 Main()" attrs="145">\r
         <size>64</size>\r
   <test name="test-null-operator-06.cs">\r
     <type name="C">\r
       <method name="Int32 Main()" attrs="145">\r
-        <size>465</size>\r
+        <size>459</size>\r
       </method>\r
       <method name="Void .ctor()" attrs="6278">\r
         <size>7</size>\r
   <test name="test-null-operator-07.cs">\r
     <type name="C">\r
       <method name="Int32 Main()" attrs="145">\r
-        <size>95</size>\r
+        <size>93</size>\r
       </method>\r
       <method name="System.String EM(System.Object)" attrs="145">\r
         <size>26</size>\r
       </method>\r
     </type>\r
   </test>\r
+  <test name="test-null-operator-08.cs">\r
+    <type name="Program">\r
+      <method name="Void M[T](T)" attrs="145">\r
+        <size>88</size>\r
+      </method>\r
+      <method name="Void M2[T](T[])" attrs="145">\r
+        <size>64</size>\r
+      </method>\r
+      <method name="Void M2_2[T](T[])" attrs="145">\r
+        <size>114</size>\r
+      </method>\r
+      <method name="Void M3[T](System.Nullable`1[T])" attrs="145">\r
+        <size>106</size>\r
+      </method>\r
+      <method name="Void Main()" attrs="145">\r
+        <size>165</size>\r
+      </method>\r
+      <method name="Void .ctor()" attrs="6278">\r
+        <size>7</size>\r
+      </method>\r
+    </type>\r
+    <type name="S">\r
+      <method name="Void Foo(Boolean)" attrs="486">\r
+        <size>38</size>\r
+      </method>\r
+    </type>\r
+    <type name="Program">\r
+      <method name="Void TestAddress_1[T](T)" attrs="145">\r
+        <size>56</size>\r
+      </method>\r
+      <method name="Void TestAddress_2[T](T[])" attrs="145">\r
+        <size>82</size>\r
+      </method>\r
+    </type>\r
+  </test>\r
   <test name="test-partial-01.cs">\r
     <type name="Foo.Hello">\r
       <method name="Void .ctor()" attrs="6278">\r