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);
}
}
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);
}
}
}
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)
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;
+ }
}
}
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;
--- /dev/null
+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
<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