}
}
+ public ConditionalAccessContext ConditionalAccess { get; set; }
+
public TypeSpec CurrentType {
get { return member_context.CurrentType; }
}
public List<TryFinally> TryFinallyUnwind { get; set; }
+ public Label RecursivePatternLabel { get; set; }
+
#endregion
public void AddStatementEpilog (IExpressionCleanup cleanupExpression)
if (sf.IsHiddenLocation (loc))
return false;
-#if NET_4_0
methodSymbols.MarkSequencePoint (ig.ILOffset, sf.SourceFileEntry, loc.Row, loc.Column, false);
-#endif
return true;
}
if ((flags & Options.OmitDebugInfo) != 0)
return;
-#if NET_4_0
methodSymbols.StartBlock (CodeBlockEntry.Type.Lexical, ig.ILOffset);
-#endif
}
public void BeginCompilerScope ()
if ((flags & Options.OmitDebugInfo) != 0)
return;
-#if NET_4_0
methodSymbols.StartBlock (CodeBlockEntry.Type.CompilerGenerated, ig.ILOffset);
-#endif
}
public void EndExceptionBlock ()
if ((flags & Options.OmitDebugInfo) != 0)
return;
-#if NET_4_0
methodSymbols.EndBlock (ig.ILOffset);
-#endif
+ }
+
+ public void CloseConditionalAccess (TypeSpec type)
+ {
+ if (type != null)
+ Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (type));
+
+ MarkLabel (ConditionalAccess.EndLabel);
+ ConditionalAccess = null;
}
//
switch (type.BuiltinType) {
case BuiltinTypeSpec.Type.Bool:
//
- // Workaround MSIL limitation. Load bool element as single bit,
- // bool array can actually store any byte value
+ // bool array can actually store any byte value in underlying byte slot
+ // and C# spec does not specify any normalization rule, except the result
+ // is undefined
//
- ig.Emit (OpCodes.Ldelem_U1);
- ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Cgt_Un);
- break;
case BuiltinTypeSpec.Type.Byte:
ig.Emit (OpCodes.Ldelem_U1);
break;
ig.Emit (OpCodes.Ldind_U1);
break;
case BuiltinTypeSpec.Type.SByte:
- ig.Emit (OpCodes.Ldind_I1);
- break;
case BuiltinTypeSpec.Type.Bool:
ig.Emit (OpCodes.Ldind_I1);
- ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Cgt_Un);
break;
case BuiltinTypeSpec.Type.ULong:
case BuiltinTypeSpec.Type.Long:
ig.Emit (OpCodes.Stobj, type.GetMetaInfo ());
break;
+ case MemberKind.PointerType:
+ ig.Emit (OpCodes.Stind_I);
+ break;
default:
ig.Emit (OpCodes.Stind_Ref);
break;
}
}
+ public class ConditionalAccessContext
+ {
+ public ConditionalAccessContext (TypeSpec type, Label endLabel)
+ {
+ Type = type;
+ EndLabel = endLabel;
+ }
+
+ public bool Statement { get; set; }
+ public Label EndLabel { get; private set; }
+ public TypeSpec Type { get; private set; }
+ }
+
struct CallEmitter
{
public Expression InstanceExpression;
//
public bool HasAwaitArguments;
- public bool NullShortCircuit;
+ public bool ConditionalAccess;
//
// When dealing with await arguments the original arguments are converted
//
public Arguments EmittedArguments;
- public Label NullOperatorLabel;
-
public void Emit (EmitContext ec, MethodSpec method, Arguments Arguments, Location loc)
{
EmitPredefined (ec, method, Arguments, false, loc);
OpCode call_op;
LocalTemporary lt = null;
- InstanceEmitter ie = new InstanceEmitter ();
if (method.IsStatic) {
call_op = OpCodes.Call;
} else {
- if (IsVirtualCallRequired (InstanceExpression, method)) {
- call_op = OpCodes.Callvirt;
- } else {
- call_op = OpCodes.Call;
- }
+ call_op = IsVirtualCallRequired (InstanceExpression, method) ? OpCodes.Callvirt : OpCodes.Call;
if (HasAwaitArguments) {
instance_copy = InstanceExpression.EmitToField (ec);
- ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+ var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
if (Arguments == null) {
- ie.EmitLoad (ec);
+ ie.EmitLoad (ec, true);
}
} else if (!InstanceExpressionOnStack) {
- ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
- ie.NullShortCircuit = NullShortCircuit;
- ie.Emit (ec);
-
- if (NullShortCircuit) {
- NullOperatorLabel = ie.NullOperatorLabel;
- }
+ var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
+ ie.Emit (ec, ConditionalAccess);
if (DuplicateArguments) {
ec.Emit (OpCodes.Dup);
EmittedArguments = Arguments.Emit (ec, DuplicateArguments, HasAwaitArguments);
if (EmittedArguments != null) {
if (instance_copy != null) {
- ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
- ie.Emit (ec);
+ var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
+ ie.Emit (ec, ConditionalAccess);
if (lt != null)
lt.Release (ec);
//
if (statement && method.ReturnType.Kind != MemberKind.Void)
ec.Emit (OpCodes.Pop);
-
- if (NullShortCircuit && !DuplicateArguments) {
- ie.EmitResultLift (ec, method.ReturnType, statement);
- }
}
static MetaType[] GetVarargsTypes (MethodSpec method, Arguments arguments)
// 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;
{
readonly Expression instance;
readonly bool addressRequired;
- bool value_on_stack;
-
- public bool NullShortCircuit;
- public Label NullOperatorLabel;
public InstanceEmitter (Expression instance, bool addressLoad)
{
this.instance = instance;
this.addressRequired = addressLoad;
- NullShortCircuit = false;
- NullOperatorLabel = new Label ();
- value_on_stack = false;
}
- public void Emit (EmitContext ec)
+ public void Emit (EmitContext ec, bool conditionalAccess)
{
+ Label NullOperatorLabel;
Nullable.Unwrap unwrap;
- if (NullShortCircuit) {
+ if (conditionalAccess && Expression.IsNeverNull (instance))
+ conditionalAccess = false;
+
+ if (conditionalAccess) {
NullOperatorLabel = ec.DefineLabel ();
unwrap = instance as Nullable.Unwrap;
} else {
+ NullOperatorLabel = new Label ();
unwrap = null;
}
+ IMemoryLocation instance_address = null;
+ bool conditional_access_dup = false;
+
if (unwrap != null) {
unwrap.Store (ec);
unwrap.EmitCheck (ec);
- ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
- unwrap.Emit (ec);
- var tmp = ec.GetTemporaryLocal (unwrap.Type);
- ec.Emit (OpCodes.Stloc, tmp);
- ec.Emit (OpCodes.Ldloca, tmp);
- ec.FreeTemporaryLocal (tmp, unwrap.Type);
- return;
- }
+ ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+ } else {
+ 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, false);
+ ec.Emit (OpCodes.Dup);
+ ec.EmitLoadFromPtr (instance.Type);
- EmitLoad (ec);
+ conditional_access_dup = true;
+ } else {
+ instance.Emit (ec);
+ }
+ } else {
+ EmitLoad (ec, !conditionalAccess);
- if (NullShortCircuit) {
- ec.Emit (OpCodes.Dup);
- ec.Emit (OpCodes.Brfalse, NullOperatorLabel);
+ if (conditionalAccess) {
+ conditional_access_dup = !IsInexpensiveLoad ();
+ if (conditional_access_dup)
+ ec.Emit (OpCodes.Dup);
+ }
+ }
+
+ if (conditionalAccess) {
+ if (instance.Type.Kind == MemberKind.TypeParameter)
+ ec.Emit (OpCodes.Box, instance.Type);
+
+ ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
+
+ if (conditional_access_dup)
+ ec.Emit (OpCodes.Pop);
+ }
}
- value_on_stack = true;
+ if (conditionalAccess) {
+ if (!ec.ConditionalAccess.Statement) {
+ if (ec.ConditionalAccess.Type.IsNullableType)
+ Nullable.LiftedNull.Create (ec.ConditionalAccess.Type, Location.Null).Emit (ec);
+ else
+ ec.EmitNull ();
+ }
+
+ ec.Emit (OpCodes.Br, ec.ConditionalAccess.EndLabel);
+ ec.MarkLabel (NullOperatorLabel);
+
+ 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);
+ }
+ }
}
- public void EmitLoad (EmitContext ec)
+ public void EmitLoad (EmitContext ec, bool boxInstance)
{
var instance_type = instance.Type;
instance.Emit (ec);
// Only to make verifier happy
- if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type)) {
- ec.Emit (OpCodes.Box, instance_type);
- } else if (instance_type.IsStructOrEnum) {
+ if (boxInstance && RequiresBoxing ()) {
ec.Emit (OpCodes.Box, instance_type);
}
}
- public void EmitResultLift (EmitContext ec, TypeSpec type, bool statement)
+ public TypeSpec GetStackType (EmitContext ec)
{
- if (!NullShortCircuit)
- throw new InternalErrorException ();
+ var instance_type = instance.Type;
- bool value_rt = TypeSpec.IsValueType (type);
- TypeSpec lifted;
- if (value_rt) {
- if (type.IsNullableType)
- lifted = type;
- else {
- lifted = Nullable.NullableInfo.MakeType (ec.Module, type);
- ec.Emit (OpCodes.Newobj, Nullable.NullableInfo.GetConstructor (lifted));
- }
- } else {
- lifted = null;
- }
+ if (addressRequired)
+ return ReferenceContainer.MakeType (ec.Module, instance_type);
- var end = ec.DefineLabel ();
- if (value_on_stack || !statement) {
- ec.Emit (OpCodes.Br_S, end);
- }
+ if (instance_type.IsStructOrEnum)
+ return ec.Module.Compiler.BuiltinTypes.Object;
- ec.MarkLabel (NullOperatorLabel);
+ return instance_type;
+ }
- if (value_on_stack)
- ec.Emit (OpCodes.Pop);
+ bool RequiresBoxing ()
+ {
+ var instance_type = instance.Type;
+ if (instance_type.IsGenericParameter && !(instance is This) && TypeSpec.IsReferenceType (instance_type))
+ return true;
- if (!statement) {
- if (value_rt)
- Nullable.LiftedNull.Create (lifted, Location.Null).Emit (ec);
- else
- ec.EmitNull ();
- }
+ if (instance_type.IsStructOrEnum)
+ return true;
- ec.MarkLabel (end);
+ return false;
}
- public TypeSpec GetStackType (EmitContext ec)
+ //
+ // Returns true for cheap race-free load, where we can avoid using dup
+ //
+ bool IsInexpensiveLoad ()
{
- var instance_type = instance.Type;
+ if (instance is Constant)
+ return instance.IsSideEffectFree;
- if (addressRequired)
- return ReferenceContainer.MakeType (ec.Module, instance_type);
+ if (RequiresBoxing ())
+ return false;
- if (instance_type.IsStructOrEnum)
- return ec.Module.Compiler.BuiltinTypes.Object;
+ var vr = instance as VariableReference;
+ if (vr != null) {
+ // Load from captured local would be racy without dup
+ return !vr.IsRef && !vr.IsHoisted;
+ }
- return instance_type;
+ if (instance is LocalTemporary)
+ return true;
+
+ var fe = instance as FieldExpr;
+ if (fe != null)
+ return fe.IsStatic || fe.InstanceExpression is This;
+
+ return false;
}
}
}