示例#1
0
        public bool Transform(Dex target, MethodBody body)
        {
            bool hasChanges = false;

            var registerUsage = new RegisterUsageMap2(body);

            hasChanges = InlineIntConstsIntoBinOp(registerUsage);

            return(hasChanges);
        }
        public bool Transform(Dex target, MethodBody body)
        {
            bool hasChanges = false;

            var registerUsage = new RegisterUsageMap2(body);

            hasChanges = InlineIntConstsIntoBinOp(registerUsage);

            return hasChanges;
        }
        public bool Transform(Dex target, MethodBody body)
        {
            bool hasChanges = false;

            var graph = new ControlFlowGraph2(body);
            var registerUsage = new RegisterUsageMap2(graph);

            hasChanges = EliminateRegisterAssigments(registerUsage);
            
            return hasChanges;
        }
示例#4
0
        public bool Transform(Dex target, MethodBody body)
        {
            bool hasChanges = false;

            var graph         = new ControlFlowGraph2(body);
            var registerUsage = new RegisterUsageMap2(graph);

            hasChanges = EliminateRegisterAssigments(registerUsage);

            return(hasChanges);
        }
        /// <summary>
        ///  eliminate registers that
        ///  (1) are only assigned to.
        ///  (2) are assigned exacly once directly followed by the only read, which moves them
        ///      to another register, if in same block.
        ///  (3) Are assigned from another register excactly once and not written otherwise, 
        ///      and were the source register is never changed during the usage of that register.
        ///      We also make sure that the other register is never check-casted as this could
        ///      mean we are modifying the dex-visible type.
        /// </summary>
        private static bool EliminateRegisterAssigments(RegisterUsageMap2 registerUsage)
        {
            bool hasChanges = false;

            foreach (var usage in registerUsage.BasicUsages)
            {
                var reg = usage.Register;
                if ((!reg.IsTemp && reg.Category != RCategory.TempVariable) 
                    || reg.KeepWith == RFlags.KeepWithPrev
                    || reg.PreventOptimization)
                    continue;

                // (1) quick check: write only registers.
                if (usage.Reads.Count == 0)
                {
                    foreach (var ins in usage.Writes)
                        ins.Instruction.ConvertToNop();
                    continue;
                }

                // (2) fairly simple.
                if (usage.Writes.Count == 1 && usage.Reads.Count == 1
                    && usage.Writes[0].Block == usage.Reads[0].Block
                    && usage.Writes[0].Instruction.Next == usage.Reads[0].Instruction
                    && usage.Reads[0].Instruction.Code.IsMove())
                {
                    var replUsage = registerUsage.GetBasicUsage(usage.Reads[0].Instruction.Registers[0]);
                    if (replUsage.Register.KeepWith != usage.Register.KeepWith)
                    {
                        // this might happend for Android Extensions methods, were registers are 
                        // incorrectly allocated. These allocations seem to be difficult to fix.
                        // Best would be to give up the concept of two connected registers in RL 
                        // altogether, and only introduce that complexity during dex conversion.
                        continue;
                    }
                    registerUsage.ReplaceRegister(usage, replUsage);
                    continue;
                }

                // (3) more complicated.
                if (usage.Writes.Count != 1 || usage.MovesFromOtherRegisters.Count != 1)
                    continue;

                var move = usage.MovesFromOtherRegisters[0];
                var sourceReg = move.Instruction.Registers[1];

                if (sourceReg.KeepWith != reg.KeepWith) // see above.
                    continue;

                var sourceUsage = registerUsage.GetBasicUsage(sourceReg);

                if (sourceUsage.CheckCastsAndNewInstance.Count > 0)
                {
                    // I would think this is only be neccessary when we are in a try block,
                    // with a finally block. Most probably the dalvik verifier does not 
                    // correctly understand our finally routing code.
                    if (registerUsage.Body.Exceptions.Any())
                        continue;
                }

                foreach (var targetIns in usage.Reads)
                foreach (var sourceIns in sourceUsage.Writes)
                {
                    if (targetIns == sourceIns)
                    {
                        // if a register is read and assigned in the same instruction,
                        // the read is still valid.
                        continue;
                    }

                    if (registerUsage.Graph.IsReachable(targetIns, sourceIns, move))
                        goto unableToReplace;
                }

                if (reg.KeepWith == RFlags.KeepWithNext)
                {
                    var nextRegUsage = registerUsage.GetBasicUsage(registerUsage.Body.GetNext(reg));
                    var nextSourceUsage = registerUsage.GetBasicUsage(registerUsage.Body.GetNext(sourceReg));
                    if (nextRegUsage.Writes.Any() || nextSourceUsage.Writes.Any())
                    {
                        // I don't think this can happen, but better be on the safe side.
                        goto unableToReplace;
                    }
                }

                // replace the register usage.
                registerUsage.ReplaceRegister(usage, sourceUsage);
                hasChanges = true;

                unableToReplace:
                ;
            }
            return hasChanges;
        }
示例#6
0
        /// <summary>
        ///  eliminate registers that
        ///  (1) are only assigned to.
        ///  (2) are assigned exacly once directly followed by the only read, which moves them
        ///      to another register, if in same block.
        ///  (3) are 'const' exacly once and then only used in branch-with-comparison-to-zero operations.
        ///      we remove the branch and the assignment.
        ///  (4) Are assigned from another register excactly once and not written otherwise,
        ///      and were the source register is never changed during the usage of that register.
        ///      We also make sure that the other register is never check-casted as this could
        ///      mean we are modifying the dex-visible type.
        /// </summary>
        private static bool EliminateRegisterAssigments(RegisterUsageMap2 registerUsage)
        {
            bool hasChanges = false;

            foreach (var usage in registerUsage.BasicUsages)
            {
                var reg = usage.Register;
                if ((!reg.IsTemp && reg.Category != RCategory.TempVariable) ||
                    reg.KeepWith == RFlags.KeepWithPrev ||
                    reg.PreventOptimization)
                {
                    continue;
                }

                // (1) quick check: write only registers.
                if (usage.Reads.Count == 0)
                {
                    foreach (var ins in usage.Writes)
                    {
                        ins.Instruction.ConvertToNop();
                    }
                    continue;
                }

                // (2) fairly simple.
                if (usage.Writes.Count == 1 && usage.Reads.Count == 1 &&
                    usage.Writes[0].Block == usage.Reads[0].Block &&
                    usage.Writes[0].Instruction.Next == usage.Reads[0].Instruction &&
                    usage.Reads[0].Instruction.Code.IsMove())
                {
                    var replUsage = registerUsage.GetBasicUsage(usage.Reads[0].Instruction.Registers[0]);
                    if (replUsage.Register.KeepWith != usage.Register.KeepWith)
                    {
                        // this might happend for Android Extensions methods, were registers are
                        // incorrectly allocated. These allocations seem to be difficult to fix.
                        // Best would be to give up the concept of two connected registers in RL
                        // altogether, and only introduce that complexity during dex conversion.
                        continue;
                    }
                    registerUsage.ReplaceRegister(usage, replUsage);
                    continue;
                }

                // (3) still simple.
                if (usage.Writes.Count == 1 && usage.Writes[0].Instruction.Code.IsConst() &&
                    usage.Reads.All(r => PredictableBranchOptimizer.IsComparisonWithZero(r.Instruction.Code)))
                {
                    var writeInsBlock = usage.Writes[0];
                    int operand       = Convert.ToInt32(writeInsBlock.Instruction.Operand);
                    foreach (var insBlock in usage.Reads.ToArray())
                    {
                        usage.Remove(insBlock);

                        var  ins            = insBlock.Instruction;
                        bool willTakeBranch = PredictableBranchOptimizer.WillTakeBranch(ins.Code, operand);

                        if (willTakeBranch)
                        {
                            ins.Code = RCode.Goto;
                            ins.Registers.Clear();
                        }
                        else
                        {
                            ins.ConvertToNop();
                        }
                    }

                    usage.Remove(writeInsBlock);
                    writeInsBlock.Instruction.ConvertToNop();
                    continue;
                }

                // (4) more complicated.
                if (usage.Writes.Count != 1 || usage.MovesFromOtherRegisters.Count != 1)
                {
                    continue;
                }

                var move      = usage.MovesFromOtherRegisters[0];
                var sourceReg = move.Instruction.Registers[1];

                if (sourceReg.KeepWith != reg.KeepWith) // see above.
                {
                    continue;
                }

                var sourceUsage = registerUsage.GetBasicUsage(sourceReg);

                if (sourceUsage.CheckCastsAndNewInstance.Count > 0)
                {
                    // I would think this is only be neccessary when we are in a try block,
                    // with a finally block. Most probably the dalvik verifier does not
                    // correctly understand our finally routing code.
                    if (registerUsage.Body.Exceptions.Any())
                    {
                        continue;
                    }
                }

                foreach (var targetIns in usage.Reads)
                {
                    foreach (var sourceIns in sourceUsage.Writes)
                    {
                        if (targetIns == sourceIns)
                        {
                            // if a register is read and assigned in the same instruction,
                            // the read is still valid.
                            continue;
                        }

                        if (registerUsage.Graph.IsReachable(targetIns, sourceIns, move))
                        {
                            goto unableToReplace;
                        }
                    }
                }

                if (reg.KeepWith == RFlags.KeepWithNext)
                {
                    var nextRegUsage    = registerUsage.GetBasicUsage(registerUsage.Body.GetNext(reg));
                    var nextSourceUsage = registerUsage.GetBasicUsage(registerUsage.Body.GetNext(sourceReg));
                    if (nextRegUsage.Writes.Any() || nextSourceUsage.Writes.Any())
                    {
                        // I don't think this can happen, but better be on the safe side.
                        goto unableToReplace;
                    }
                }

                // replace the register usage.
                registerUsage.ReplaceRegister(usage, sourceUsage);
                hasChanges = true;

unableToReplace:
                ;
            }
            return(hasChanges);
        }
示例#7
0
        private bool InlineIntConstsIntoBinOp(RegisterUsageMap2 registerUsage)
        {
            bool hasChanges = false;

            foreach (var usage in registerUsage.BasicUsages)
            {
                if (usage.Register.Type != RType.Value)
                {
                    continue;
                }
                if (usage.Writes.Count != 1 || usage.Reads.Count != 1)
                {
                    continue;
                }

                var write = usage.Writes[0];
                var binOp = usage.Reads[0];

                if (write.Block != binOp.Block)
                {
                    continue;
                }

                var writeIns = write.Instruction;
                if (writeIns.Code != RCode.Const)
                {
                    continue;
                }

                int minValue, maxValue;
                // test next instruction
                if (!IsOptimizableBinOp(binOp.Instruction.Code, out minValue, out maxValue))
                {
                    continue;
                }

                // check range
                int value = Convert.ToInt32(writeIns.Operand);
                if (value < minValue || value > maxValue)
                {
                    continue;
                }

                var constReg = writeIns.Registers[0];
                var binOpIns = binOp.Instruction;
                if (binOpIns.Registers.Last() != constReg)
                {
                    continue;
                }

                if (binOpIns.Code == RCode.Sub_int || binOpIns.Code == RCode.Sub_int_2addr)
                {
                    // special handling: we need to invert and convert to an add.
                    value = -value;
                }

                // we found: a short const followed by a int-binary operation
                //           in the same block. The only use of the const
                //           is as the last register of the binary operation.
                bool fits8Bits = value >= sbyte.MinValue && value <= sbyte.MaxValue;
                binOpIns.Code = ToIntLit(binOpIns.Code, fits8Bits);

                var r0 = binOpIns.Registers[0];
                var r1 = binOpIns.Registers.Count == 2 ? binOpIns.Registers[0] : binOpIns.Registers[1];

                binOpIns.Registers.Clear();
                binOpIns.Registers.Add(r0);
                binOpIns.Registers.Add(r1);
                binOpIns.Operand = value;

                usage.Clear();
                writeIns.ConvertToNop();

                hasChanges = true;
            }

            return(hasChanges);
        }
        private bool InlineIntConstsIntoBinOp(RegisterUsageMap2 registerUsage)
        {
            bool hasChanges = false;

            foreach (var usage in registerUsage.BasicUsages)
            {
                if (usage.Register.Type != RType.Value)
                    continue;
                if (usage.Writes.Count != 1 || usage.Reads.Count != 1)
                    continue;

                var write = usage.Writes[0];
                var binOp = usage.Reads[0];

                if (write.Block != binOp.Block)
                    continue;

                var writeIns = write.Instruction;
                if (writeIns.Code != RCode.Const)
                    continue;

                int minValue, maxValue;
                // test next instruction
                if (!IsOptimizableBinOp(binOp.Instruction.Code, out minValue, out maxValue))
                    continue;

                // check range
                int value = Convert.ToInt32(writeIns.Operand);
                if (value < minValue || value > maxValue)
                    continue;

                var constReg = writeIns.Registers[0];
                var binOpIns = binOp.Instruction;
                if (binOpIns.Registers.Last() != constReg)
                    continue;
                
                if (binOpIns.Code == RCode.Sub_int || binOpIns.Code == RCode.Sub_int_2addr)
                {
                    // special handling: we need to invert and convert to an add. 
                    value = -value;
                }

                // we found: a short const followed by a int-binary operation
                //           in the same block. The only use of the const 
                //           is as the last register of the binary operation.
                bool fits8Bits = value >= sbyte.MinValue && value <= sbyte.MaxValue;
                binOpIns.Code = ToIntLit(binOpIns.Code, fits8Bits);

                var r0 = binOpIns.Registers[0];
                var r1 = binOpIns.Registers.Count == 2 ? binOpIns.Registers[0] : binOpIns.Registers[1];

                binOpIns.Registers.Clear();
                binOpIns.Registers.Add(r0);
                binOpIns.Registers.Add(r1);
                binOpIns.Operand = value;

                usage.Clear();
                writeIns.ConvertToNop();

                hasChanges = true;
            }

            return hasChanges;
        }