Пример #1
0
        private int GetFinalJumpCount(ASMachine machine, Jumper jumper, List <ASInstruction> cleaned, Dictionary <int, List <ASInstruction> > localReferences, Stack <ASInstruction> valuePushers)
        {
            var  magicCount = 0;
            var  locals     = new List <Local>();
            var  pushers    = new List <ASInstruction>();
            bool?isJumping  = jumper.RunCondition(machine);

            // Get the instructions that pushed the values the jump instruction used.
            int popCount = jumper.GetPopCount();

            for (int i = 0; i < popCount; i++)
            {
                ASInstruction pusher = valuePushers.Pop();
                if (!pushers.Contains(pusher))
                {
                    pushers.Add(pusher);
                    // Get the instructions that were pushed by a GetLocal/N.
                    // These are used to determine whether the jump should be kept, since a local register could change within the jump body.
                    if (Local.IsValid(pusher.OP))
                    {
                        locals.Add((Local)pusher);
                    }
                    else if (Primitive.IsValid(pusher.OP) ||
                             pusher.OP == OPCode.Dup)
                    {
                        magicCount++;
                    }
                }
            }

            // Output is not known, keep the instruction.
            if (isJumping == null)
            {
                cleaned.Add(jumper);
                return(0);
            }

            if (pushers.Count != (magicCount + locals.Count))
            {
                // One or more push instructions are wildcards, they have a 'history' of being modified.
                // Keep this jump instruction, result could change.
                cleaned.Add(jumper);
                return(0);
            }

            // Gather information about the jump instruction, and it's 'block' of instructions that are being jumped over(if 'isJumping = true').
            ASInstruction exit                = null;
            bool          isBackwardsJump     = false;
            IEnumerable <ASInstruction> block = null;

            if (!JumpExits.TryGetValue(jumper, out exit))
            {
                // This jump instruction should not be 'cleaned', keep it.
                cleaned.Add(jumper);
                return(0);
            }
            if (IsBackwardsJump(jumper))
            {
                isBackwardsJump = true;

                block = cleaned
                        .Skip(cleaned.IndexOf(exit) + 1)
                        .TakeWhile(i => i != jumper);
            }
            else
            {
                block = (jumper.Offset > 0 ? GetJumpBlock(jumper) : null);
            }


            if (isJumping == true && block != null)
            {
                if (isBackwardsJump)
                {
                    // Check if any of the locals used by the jump instruction is being set within the body.
                    // If the anwser is yes, removing the jump instruction is a bad idea, since the output of the condition could change.
                    foreach (Local local in locals)
                    {
                        foreach (ASInstruction instruction in block)
                        {
                            if (!Local.IsValid(instruction.OP))
                            {
                                continue;
                            }
                            if (Local.IsGetLocal(instruction.OP))
                            {
                                continue;
                            }
                            var bodyLocal = (Local)instruction;

                            if (bodyLocal.Register == local.Register)
                            {
                                // Do not remove the jump instruction, condition result may change.
                                cleaned.Add(jumper);
                                return(0);
                            }
                        }
                    }
                }

                foreach (KeyValuePair <Jumper, ASInstruction> jumpExit in JumpExits)
                {
                    if (jumpExit.Key == jumper)
                    {
                        continue;
                    }

                    bool hasEntry = block.Contains(jumpExit.Key);   // Does a jump instruction begin somewhere in the block?
                    bool hasExit  = block.Contains(jumpExit.Value); // Does the jump instruction end somewhere in the block?

                    ASInstruction afterLast       = _instructions[_indices[block.Last()] + 1];
                    bool          isExitAfterLast = (jumpExit.Value == afterLast); // Does the exit of the jump that is in the block come after the final instruction of the current block?

                    if (hasEntry && !hasExit && !isExitAfterLast ||
                        hasExit && !hasEntry)
                    {
                        // Keep the jump instruction, since it will corrupt the other jump instruction that is using it.
                        cleaned.Add(jumper);
                        return(0);
                    }
                }
                foreach (KeyValuePair <LookUpSwitchIns, ASInstruction[]> switchExit in SwitchExits)
                {
                    foreach (ASInstruction caseExit in switchExit.Value)
                    {
                        if (block.Contains(caseExit))
                        {
                            cleaned.Add(jumper);
                            return(0);
                        }
                    }
                }
            }

            foreach (Local local in locals)
            {
                List <ASInstruction> references = localReferences[local.Register];
                references.Remove(local);
            }

            // Remove the instructions that pushed values for the jump instruction that is to be removed.
            foreach (ASInstruction pusher in pushers)
            {
                cleaned.Remove(pusher);
            }

            if (isJumping == false || isBackwardsJump)
            {
                block = null;
            }
            JumpExits.Remove(jumper);
            return(block?.Count() ?? 0);
        }