예제 #1
0
 public void Run(Block block, BlockTransformContext context)
 {
     for (int i = 0; i < block.Instructions.Count; i++)
     {
         ILVariable    v;
         ILInstruction copiedExpr;
         if (block.Instructions[i].MatchStLoc(out v, out copiedExpr))
         {
             if (v.IsSingleDefinition && v.LoadCount == 0 && v.Kind == VariableKind.StackSlot)
             {
                 // dead store to stack
                 if (SemanticHelper.IsPure(copiedExpr.Flags))
                 {
                     // no-op -> delete
                     context.Step("remove dead store to stack: no-op -> delete", block.Instructions[i]);
                     block.Instructions.RemoveAt(i--);
                 }
                 else
                 {
                     // evaluate the value for its side-effects
                     context.Step("remove dead store to stack: evaluate the value for its side-effects", block.Instructions[i]);
                     copiedExpr.AddILRange(block.Instructions[i].ILRange);
                     block.Instructions[i] = copiedExpr;
                 }
             }
             else if (v.IsSingleDefinition && CanPerformCopyPropagation(v, copiedExpr))
             {
                 DoPropagate(v, copiedExpr, block, ref i, context);
             }
         }
     }
 }
예제 #2
0
 /// <summary>
 ///	stloc lockObj(lockExpression)
 ///	call Enter(ldloc lockObj)
 ///	.try BlockContainer {
 ///		Block lockBlock (incoming: 1) {
 ///			call WriteLine()
 ///			leave lockBlock (nop)
 ///		}
 ///
 ///	} finally BlockContainer {
 ///		Block exitBlock (incoming: 1) {
 ///			call Exit(ldloc lockObj)
 ///			leave exitBlock (nop)
 ///		}
 ///
 ///	}
 /// =>
 /// .lock (lockExpression) BlockContainer {
 ///     Block lockBlock (incoming: 1) {
 ///			call WriteLine()
 ///			leave lockBlock (nop)
 ///		}
 /// }
 /// </summary>
 bool TransformLockMCS(Block block, int i)
 {
     if (i < 2)
     {
         return(false);
     }
     if (!(block.Instructions[i] is TryFinally body) || !(block.Instructions[i - 2] is StLoc objectStore) ||
         !MatchCall(block.Instructions[i - 1] as Call, "Enter", objectStore.Variable))
     {
         return(false);
     }
     if (!objectStore.Variable.IsSingleDefinition)
     {
         return(false);
     }
     if (!(body.TryBlock is BlockContainer tryContainer) || tryContainer.EntryPoint.Instructions.Count == 0 || tryContainer.EntryPoint.IncomingEdgeCount != 1)
     {
         return(false);
     }
     if (!(body.FinallyBlock is BlockContainer finallyContainer) || !MatchExitBlock(finallyContainer.EntryPoint, null, objectStore.Variable))
     {
         return(false);
     }
     if (objectStore.Variable.LoadCount > 2)
     {
         return(false);
     }
     context.Step("LockTransformMCS", block);
     block.Instructions.RemoveAt(i - 1);
     block.Instructions.RemoveAt(i - 2);
     body.ReplaceWith(new LockInstruction(objectStore.Value, body.TryBlock).WithILRange(objectStore));
     return(true);
 }
예제 #3
0
        /// <code>
        /// stloc s(value)
        /// stloc l(ldloc s)
        /// stobj(..., ldloc s)
        /// -->
        /// stloc l(stobj (..., value))
        /// </code>
        /// -or-
        /// <code>
        /// stloc s(value)
        /// stobj (..., ldloc s)
        /// -->
        /// stloc s(stobj (..., value))
        /// </code>
        bool TransformInlineAssignmentStObj(Block block, int i)
        {
            var inst = block.Instructions[i] as StLoc;

            // in some cases it can be a compiler-generated local
            if (inst == null || (inst.Variable.Kind != VariableKind.StackSlot && inst.Variable.Kind != VariableKind.Local))
            {
                return(false);
            }
            var           nextInst = block.Instructions.ElementAtOrDefault(i + 1);
            ILInstruction replacement;
            StObj         fieldStore;
            ILVariable    local;

            if (nextInst is StLoc)               // instance fields
            {
                var localStore = (StLoc)nextInst;
                if (localStore.Variable.Kind == VariableKind.StackSlot || !localStore.Value.MatchLdLoc(inst.Variable))
                {
                    return(false);
                }
                var memberStore = block.Instructions.ElementAtOrDefault(i + 2);
                if (memberStore is StObj)
                {
                    fieldStore = memberStore as StObj;
                    if (!fieldStore.Value.MatchLdLoc(inst.Variable))
                    {
                        return(false);
                    }
                    replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
                }
                else                     // otherwise it must be local
                {
                    return(TransformInlineAssignmentLocal(block, i));
                }
                context.Step("Inline assignment to instance field", fieldStore);
                local = localStore.Variable;
                block.Instructions.RemoveAt(i + 1);
            }
            else if (nextInst is StObj)                 // static fields
            {
                fieldStore = (StObj)nextInst;
                if (!fieldStore.Value.MatchLdLoc(inst.Variable) || (fieldStore.Target.MatchLdFlda(out var target, out _) && target.MatchLdLoc(inst.Variable)))
                {
                    return(false);
                }
                context.Step("Inline assignment to static field", fieldStore);
                local       = inst.Variable;
                replacement = new StObj(fieldStore.Target, inst.Value, fieldStore.Type);
            }
            else
            {
                return(false);
            }
            block.Instructions.RemoveAt(i + 1);
            inst.ReplaceWith(new StLoc(local, replacement));
            return(true);
        }
예제 #4
0
 /// <summary>
 /// stloc s(valueInst)
 /// if (comp(ldloc s == ldnull)) {
 ///		stloc s(fallbackInst)
 /// }
 /// =>
 /// stloc s(if.notnull(valueInst, fallbackInst))
 /// </summary>
 bool TransformNullCoalescing(Block block, int i)
 {
     if (i == 0)
     {
         return(false);
     }
     if (!(block.Instructions[i - 1] is StLoc stloc))
     {
         return(false);
     }
     if (stloc.Variable.Kind != VariableKind.StackSlot)
     {
         return(false);
     }
     if (!block.Instructions[i].MatchIfInstruction(out var condition, out var trueInst))
     {
         return(false);
     }
     trueInst = Block.Unwrap(trueInst);
     if (condition.MatchCompEquals(out var left, out var right) && left.MatchLdLoc(stloc.Variable) && right.MatchLdNull() &&
         trueInst.MatchStLoc(stloc.Variable, out var fallbackValue)
         )
     {
         context.Step("TransformNullCoalescing", stloc);
         stloc.Value = new NullCoalescingInstruction(stloc.Value, fallbackValue);
         return(true);                // returning true removes the if instruction
     }
     return(false);
 }
예제 #5
0
 /// <summary>
 /// stloc obj(resourceExpression)
 /// .try BlockContainer {
 ///		Block IL_0003(incoming: 1) {
 ///			call WriteLine(ldstr "using (null)")
 ///			leave IL_0003(nop)
 ///		}
 ///	} finally BlockContainer {
 ///		Block IL_0012(incoming: 1) {
 ///			if (comp(ldloc obj != ldnull)) Block IL_001a  {
 ///				callvirt Dispose(ldnull)
 ///			}
 ///			leave IL_0012(nop)
 ///		}
 /// }
 /// leave IL_0000(nop)
 /// =>
 /// using (resourceExpression) {
 ///		BlockContainer {
 ///			Block IL_0003(incoming: 1) {
 ///				call WriteLine(ldstr "using (null)")
 ///				leave IL_0003(nop)
 ///			}
 ///		}
 /// }
 /// </summary>
 bool TransformUsing(Block block, int i)
 {
     if (i + 1 >= block.Instructions.Count)
     {
         return(false);
     }
     if (!(block.Instructions[i + 1] is TryFinally tryFinally) || !(block.Instructions[i] is StLoc storeInst))
     {
         return(false);
     }
     if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type)))
     {
         return(false);
     }
     if (storeInst.Variable.Kind != VariableKind.Local)
     {
         return(false);
     }
     if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
     {
         return(false);
     }
     if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))
     {
         return(false);
     }
     if (storeInst.Variable.StoreInstructions.Count > 1)
     {
         return(false);
     }
     if (!(tryFinally.FinallyBlock is BlockContainer container))
     {
         return(false);
     }
     if (!MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull()))
     {
         return(false);
     }
     context.Step("UsingTransform", tryFinally);
     storeInst.Variable.Kind = VariableKind.UsingLocal;
     block.Instructions.RemoveAt(i + 1);
     block.Instructions[i] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock)
     {
         IsRefStruct = context.Settings.IntroduceRefModifiersOnStructs && storeInst.Variable.Type.Kind == TypeKind.Struct && storeInst.Variable.Type.IsByRefLike
     }.WithILRange(storeInst);
     return(true);
 }
 /// <summary>
 /// stloc obj(resourceExpression)
 /// .try BlockContainer {
 ///		Block IL_0003(incoming: 1) {
 ///			call WriteLine(ldstr "using (null)")
 ///			leave IL_0003(nop)
 ///		}
 ///	} finally BlockContainer {
 ///		Block IL_0012(incoming: 1) {
 ///			if (comp(ldloc obj != ldnull)) Block IL_001a  {
 ///				callvirt Dispose(ldnull)
 ///			}
 ///			leave IL_0012(nop)
 ///		}
 /// }
 /// leave IL_0000(nop)
 /// =>
 /// using (resourceExpression) {
 ///		BlockContainer {
 ///			Block IL_0003(incoming: 1) {
 ///				call WriteLine(ldstr "using (null)")
 ///				leave IL_0003(nop)
 ///			}
 ///		}
 /// }
 /// </summary>
 bool TransformUsing(Block block, int i)
 {
     if (i < 1)
     {
         return(false);
     }
     if (!(block.Instructions[i] is TryFinally tryFinally) || !(block.Instructions[i - 1] is StLoc storeInst))
     {
         return(false);
     }
     if (!(storeInst.Value.MatchLdNull() || CheckResourceType(storeInst.Variable.Type)))
     {
         return(false);
     }
     if (storeInst.Variable.LoadInstructions.Any(ld => !ld.IsDescendantOf(tryFinally)))
     {
         return(false);
     }
     if (storeInst.Variable.AddressInstructions.Any(la => !la.IsDescendantOf(tryFinally) || (la.IsDescendantOf(tryFinally.TryBlock) && !ILInlining.IsUsedAsThisPointerInCall(la))))
     {
         return(false);
     }
     if (storeInst.Variable.StoreInstructions.Count > 1)
     {
         return(false);
     }
     if (!(tryFinally.FinallyBlock is BlockContainer container) || !MatchDisposeBlock(container, storeInst.Variable, storeInst.Value.MatchLdNull()))
     {
         return(false);
     }
     context.Step("UsingTransform", tryFinally);
     storeInst.Variable.Kind = VariableKind.UsingLocal;
     block.Instructions.RemoveAt(i);
     block.Instructions[i - 1] = new UsingInstruction(storeInst.Variable, storeInst.Value, tryFinally.TryBlock)
     {
         ILRange = storeInst.ILRange
     };
     return(true);
 }
예제 #7
0
        /// <summary>
        /// if (comp(ldsfld CachedAnonMethodDelegate == ldnull)) {
        ///     stsfld CachedAnonMethodDelegate(DelegateConstruction)
        /// }
        /// ... one usage of CachedAnonMethodDelegate ...
        /// =>
        /// ... one usage of DelegateConstruction ...
        /// </summary>
        bool CachedDelegateInitializationWithField(IfInstruction inst)
        {
            Block trueInst = inst.TrueInst as Block;

            if (trueInst == null || trueInst.Instructions.Count != 1 || !inst.FalseInst.MatchNop())
            {
                return(false);
            }
            var storeInst = trueInst.Instructions[0];

            if (!inst.Condition.MatchCompEquals(out ILInstruction left, out ILInstruction right) || !left.MatchLdsFld(out IField field) || !right.MatchLdNull())
            {
                return(false);
            }
            if (!storeInst.MatchStsFld(out IField field2, out ILInstruction value) || !field.Equals(field2) || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
            {
                return(false);
            }
            if (!DelegateConstruction.IsDelegateConstruction(value.UnwrapConv(ConversionKind.Invalid) as NewObj, true))
            {
                return(false);
            }
            var nextInstruction = inst.Parent.Children.ElementAtOrDefault(inst.ChildIndex + 1);

            if (nextInstruction == null)
            {
                return(false);
            }
            var usages = nextInstruction.Descendants.Where(i => i.MatchLdsFld(field)).ToArray();

            if (usages.Length != 1)
            {
                return(false);
            }
            context.Step("CachedDelegateInitializationWithField", inst);
            usages[0].ReplaceWith(value);
            return(true);
        }
예제 #8
0
 public void Run(Block block, BlockTransformContext context)
 {
     if (running)
     {
         throw new InvalidOperationException("LoopingBlockTransform already running. Transforms (and the CSharpDecompiler) are neither neither thread-safe nor re-entrant.");
     }
     running = true;
     try {
         int count = 1;
         do
         {
             block.ResetDirty();
             block.RunTransforms(children, context);
             if (block.IsDirty)
             {
                 context.Step($"Block is dirty; running loop iteration #{++count}.", block);
             }
         } while (block.IsDirty);
     } finally {
         running = false;
     }
 }
예제 #9
0
 /// <summary>
 /// stloc s(valueInst)
 /// if (comp(ldloc s == ldnull)) {
 ///		stloc s(fallbackInst)
 /// }
 /// =>
 /// stloc s(if.notnull(valueInst, fallbackInst))
 /// </summary>
 bool TransformNullCoalescing(Block block, int i)
 {
     if (i == 0)
     {
         return(false);
     }
     if (!(block.Instructions[i] is IfInstruction ifInstruction) || !(block.Instructions[i - 1] is StLoc stloc) || stloc.Variable.Kind != VariableKind.StackSlot)
     {
         return(false);
     }
     if (!ifInstruction.Condition.MatchCompEquals(out var left, out var right) || !left.MatchLdLoc(stloc.Variable) || !right.MatchLdNull())
     {
         return(false);
     }
     if (!ifInstruction.FalseInst.MatchNop() || !(ifInstruction.TrueInst is Block b) || b.Instructions.Count != 1 || !(b.Instructions[0] is StLoc fallbackStore) || fallbackStore.Variable != stloc.Variable)
     {
         return(false);
     }
     context.Step("TransformNullCoalescing", stloc);
     stloc.Value = new NullCoalescingInstruction(stloc.Value, fallbackStore.Value);
     return(true);
 }
예제 #10
0
        bool DoTransform(Block body, int pos)
        {
            ILInstruction inst = body.Instructions[pos];

            // Match stloc(v, newobj)
            if (inst.MatchStLoc(out var v, out var initInst) && (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot))
            {
                Block initializerBlock = null;
                IType instType;
                switch (initInst)
                {
                case NewObj newObjInst:
                    if (newObjInst.ILStackWasEmpty && v.Kind == VariableKind.Local && !context.Function.Method.IsConstructor)
                    {
                        // on statement level (no other expressions on IL stack),
                        // prefer to keep local variables (but not stack slots),
                        // unless we are in a constructor (where inlining object initializers might be critical
                        // for the base ctor call)
                        return(false);
                    }
                    // Do not try to transform display class usages or delegate construction.
                    // DelegateConstruction transform cannot deal with this.
                    if (DelegateConstruction.IsSimpleDisplayClass(newObjInst.Method.DeclaringType))
                    {
                        return(false);
                    }
                    if (DelegateConstruction.IsDelegateConstruction(newObjInst) || DelegateConstruction.IsPotentialClosure(context, newObjInst))
                    {
                        return(false);
                    }
                    instType = newObjInst.Method.DeclaringType;
                    break;

                case DefaultValue defaultVal:
                    instType = defaultVal.Type;
                    break;

                case Block existingInitializer:
                    if (existingInitializer.Type == BlockType.CollectionInitializer || existingInitializer.Type == BlockType.ObjectInitializer)
                    {
                        initializerBlock = existingInitializer;
                        var value = ((StLoc)initializerBlock.Instructions[0]).Value;
                        if (value is NewObj no)
                        {
                            instType = no.Method.DeclaringType;
                        }
                        else
                        {
                            instType = ((DefaultValue)value).Type;
                        }
                        break;
                    }
                    return(false);

                default:
                    return(false);
                }
                context.Step("CollectionOrObjectInitializer", inst);
                int initializerItemsCount  = 0;
                var blockType              = initializerBlock?.Type ?? BlockType.CollectionInitializer;
                var possibleIndexVariables = new Dictionary <ILVariable, (int Index, ILInstruction Value)>();
                // Detect initializer type by scanning the following statements
                // each must be a callvirt with ldloc v as first argument
                // if the method is a setter we're dealing with an object initializer
                // if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer
                while (pos + initializerItemsCount + 1 < body.Instructions.Count &&
                       IsPartOfInitializer(body.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockType, possibleIndexVariables))
                {
                    initializerItemsCount++;
                }
                var index = possibleIndexVariables.Where(info => info.Value.Index > -1).Min(info => (int?)info.Value.Index);
                if (index != null)
                {
                    initializerItemsCount = index.Value - pos - 1;
                }
                if (initializerItemsCount <= 0)
                {
                    return(false);
                }
                ILVariable finalSlot;
                if (initializerBlock == null)
                {
                    initializerBlock = new Block(blockType);
                    finalSlot        = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type);
                    initializerBlock.FinalInstruction = new LdLoc(finalSlot);
                    initializerBlock.Instructions.Add(new StLoc(finalSlot, initInst.Clone()));
                }
                else
                {
                    finalSlot = ((LdLoc)initializerBlock.FinalInstruction).Variable;
                }
                for (int i = 1; i <= initializerItemsCount; i++)
                {
                    switch (body.Instructions[i + pos])
                    {
                    case CallInstruction call:
                        if (!(call is CallVirt || call is Call))
                        {
                            continue;
                        }
                        var newCall   = (CallInstruction)call.Clone();
                        var newTarget = newCall.Arguments[0];
                        foreach (var load in newTarget.Descendants.OfType <IInstructionWithVariableOperand>())
                        {
                            if ((load is LdLoc || load is LdLoca) && load.Variable == v)
                            {
                                load.Variable = finalSlot;
                            }
                        }
                        initializerBlock.Instructions.Add(newCall);
                        break;

                    case StObj stObj:
                        var newStObj = (StObj)stObj.Clone();
                        foreach (var load in newStObj.Target.Descendants.OfType <IInstructionWithVariableOperand>())
                        {
                            if ((load is LdLoc || load is LdLoca) && load.Variable == v)
                            {
                                load.Variable = finalSlot;
                            }
                        }
                        initializerBlock.Instructions.Add(newStObj);
                        break;

                    case StLoc stLoc:
                        var newStLoc = (StLoc)stLoc.Clone();
                        initializerBlock.Instructions.Add(newStLoc);
                        break;
                    }
                }
                initInst.ReplaceWith(initializerBlock);
                for (int i = 0; i < initializerItemsCount; i++)
                {
                    body.Instructions.RemoveAt(pos + 1);
                }
                ILInlining.InlineIfPossible(body, ref pos, context);
            }
            return(true);
        }
예제 #11
0
        bool DoTransform(Block body, int pos)
        {
            if (pos >= body.Instructions.Count - 2)
            {
                return(false);
            }
            ILInstruction inst = body.Instructions[pos];
            ILVariable    v;
            ILInstruction newarrExpr;
            IType         elementType;

            int[] arrayLength;
            if (inst.MatchStLoc(out v, out newarrExpr) && MatchNewArr(newarrExpr, out elementType, out arrayLength))
            {
                ILInstruction[] values;
                int             initArrayPos;
                if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out values, out initArrayPos))
                {
                    context.Step("ForwardScanInitializeArrayRuntimeHelper", inst);
                    var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type);
                    var block     = BlockFromInitializer(tempStore, elementType, arrayLength, values);
                    body.Instructions[pos].ReplaceWith(new StLoc(v, block));
                    body.Instructions.RemoveAt(initArrayPos);
                    ILInlining.InlineIfPossible(body, ref pos, context);
                    return(true);
                }
                if (arrayLength.Length == 1)
                {
                    ILVariable finalStore;
                    int        instructionsToRemove;
                    if (HandleSimpleArrayInitializer(body, pos + 1, v, arrayLength[0], out finalStore, out values, out instructionsToRemove))
                    {
                        context.Step("HandleSimpleArrayInitializer", inst);
                        var block     = new Block(BlockType.ArrayInitializer);
                        var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type);
                        block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray())));
                        block.Instructions.AddRange(values.SelectWithIndex(
                                                        (i, value) => {
                            if (value == null)
                            {
                                value = GetNullExpression(elementType);
                            }
                            return(StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType));
                        }
                                                        ));
                        block.FinalInstruction = new LdLoc(tempStore);
                        body.Instructions[pos].ReplaceWith(new StLoc(finalStore ?? v, block));
                        RemoveInstructions(body, pos + 1, instructionsToRemove);
                        //body.Instructions.RemoveRange(pos + 1, values.Length + 1);
                        ILInlining.InlineIfPossible(body, ref pos, context);
                        return(true);
                    }
                    if (HandleJaggedArrayInitializer(body, pos + 1, v, arrayLength[0], out finalStore, out values, out instructionsToRemove))
                    {
                        context.Step("HandleJaggedArrayInitializer", inst);
                        var block     = new Block(BlockType.ArrayInitializer);
                        var tempStore = context.Function.RegisterVariable(VariableKind.InitializerTarget, v.Type);
                        block.Instructions.Add(new StLoc(tempStore, new NewArr(elementType, arrayLength.Select(l => new LdcI4(l)).ToArray())));
                        block.Instructions.AddRange(values.SelectWithIndex((i, value) => StElem(new LdLoc(tempStore), new[] { new LdcI4(i) }, value, elementType)));
                        block.FinalInstruction = new LdLoc(tempStore);
                        body.Instructions[pos].ReplaceWith(new StLoc(finalStore, block));
                        RemoveInstructions(body, pos + 1, instructionsToRemove);
                        ILInlining.InlineIfPossible(body, ref pos, context);
                        return(true);
                    }
                }
                // Put in a limit so that we don't consume too much memory if the code allocates a huge array
                // and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
//				const int maxConsecutiveDefaultValueExpressions = 300;
//				var operands = new List<ILInstruction>();
//				int numberOfInstructionsToRemove = 0;
//				for (int j = pos + 1; j < body.Instructions.Count; j++) {
//					var nextExpr = body.Instructions[j] as Void;
//					int arrayPos;
//					if (nextExpr != null && nextExpr is a.IsStoreToArray() &&
//					    nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
//					    v == v3 &&
//					    nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
//					    arrayPos >= operands.Count &&
//					    arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions &&
//					    !nextExpr.Arguments[2].ContainsReferenceTo(v3))
//					{
//						while (operands.Count < arrayPos)
//							operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
//						operands.Add(nextExpr.Arguments[2]);
//						numberOfInstructionsToRemove++;
//					} else {
//						break;
//					}
//				}
            }
            return(false);
        }