//
public Arguments EmittedArguments;
- public Label NullOperatorLabel;
-
public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
{
EmitPredefined (ec, method, Arguments, false, loc);
// It's non-virtual and will never be null and it can be determined
// whether it's known value or reference type by verifier
//
- if (!method.IsVirtual && (instance is This || instance is New || instance is ArrayCreation || instance is DelegateCreation) &&
- !instance.Type.IsGenericParameter)
+ if (!method.IsVirtual && Expression.IsNeverNull (instance) && !instance.Type.IsGenericParameter)
return false;
return true;
Label NullOperatorLabel;
Nullable.Unwrap unwrap;
+ if (conditionalAccess && Expression.IsNeverNull (instance))
+ conditionalAccess = false;
+
if (conditionalAccess) {
NullOperatorLabel = ec.DefineLabel ();
unwrap = instance as Nullable.Unwrap;
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;
+ }
}
}