void RewriteAsIsInst(ref BasicBlock block)
        {
            Scanner.LogDebug(1, $"REWRITE AS ISINST: {block.Count} {block}");

            var reference = Assembly.MainModule.ImportReference(InstanceType);

            int index = 1;

            BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Isinst, reference));
            BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Dup));
            BlockList.ReplaceInstructionAt(ref block, index++, Instruction.Create(OpCodes.Stloc, Variable));
            BlockList.RemoveInstructionAt(ref block, index);

            switch (block.BranchType)
            {
            case BranchType.False:
            case BranchType.True:
                break;

            case BranchType.None:
            case BranchType.Return:
                // Convert it into a bool.
                BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Ldnull));
                BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Cgt_Un));
                break;

            default:
                throw DebugHelpers.AssertFailUnexpected(Method, block, block.BranchType);
            }
        }
Esempio n. 2
0
        protected void RewriteConditional(ref BasicBlock block, int stackDepth, ConstantValue constant)
        {
            if (constant == ConstantValue.Throw)
            {
                Scanner.Rewriter.ReplaceWithThrow(ref block, stackDepth);
                return;
            }

            /*
             * The conditional call can be replaced with a constant.
             */

            switch (block.BranchType)
            {
            case BranchType.False:
                switch (constant)
                {
                case ConstantValue.False:
                case ConstantValue.Null:
                    Scanner.Rewriter.ReplaceWithBranch(ref block, stackDepth, true);
                    break;

                case ConstantValue.True:
                    Scanner.Rewriter.ReplaceWithBranch(ref block, stackDepth, false);
                    break;

                default:
                    throw DebugHelpers.AssertFailUnexpected(Method, block, block.BranchType);
                }
                break;

            case BranchType.True:
                switch (constant)
                {
                case ConstantValue.False:
                case ConstantValue.Null:
                    Scanner.Rewriter.ReplaceWithBranch(ref block, stackDepth, false);
                    break;

                case ConstantValue.True:
                    Scanner.Rewriter.ReplaceWithBranch(ref block, stackDepth, true);
                    break;

                default:
                    throw DebugHelpers.AssertFailUnexpected(Method, block, block.BranchType);
                }
                break;

            case BranchType.None:
            case BranchType.Return:
                Scanner.Rewriter.ReplaceWithConstant(ref block, stackDepth, constant);
                break;

            default:
                throw DebugHelpers.AssertFailUnexpected(Method, block, block.BranchType);
            }
        }
Esempio n. 3
0
        /*
         * Replace block @block with an `isinst`, but keep @index instructions at the beginning
         * of the block and the (optional) branch at the end.
         *
         * The block is expected to contain the following:
         *
         * - optional simple load instruction
         * - conditional call
         * - optional branch instruction
         *
         */
        public void ReplaceWithIsInst(ref BasicBlock block, int index, TypeDefinition type)
        {
            if (index < 0 || index >= block.Count)
            {
                throw new ArgumentOutOfRangeException(nameof(index));
            }

            if (block.Instructions [index].OpCode.Code != Code.Call)
            {
                DebugHelpers.AssertFailUnexpected(Method, block, block.Instructions [index]);
            }

            /*
             * The block consists of the following:
             *
             * - optional simple load instruction
             * - conditional call
             * - optional branch instruction
             *
             */

            var reference = Method.DeclaringType.Module.ImportReference(type);

            BlockList.ReplaceInstructionAt(ref block, index++, Instruction.Create(OpCodes.Isinst, reference));

            switch (block.BranchType)
            {
            case BranchType.False:
            case BranchType.True:
                DebugHelpers.Assert(index == block.Count - 1);
                break;

            case BranchType.None:
                // Convert it into a bool.
                DebugHelpers.Assert(index == block.Count);
                BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Ldnull));
                BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Cgt_Un));
                break;

            case BranchType.Return:
                // Convert it into a bool.
                DebugHelpers.Assert(index == block.Count - 1);
                BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Ldnull));
                BlockList.InsertInstructionAt(ref block, index++, Instruction.Create(OpCodes.Cgt_Un));
                break;

            default:
                throw DebugHelpers.AssertFailUnexpected(Method, block, block.BranchType);
            }

            BlockList.TryMergeBlock(ref block);
        }
Esempio n. 4
0
        /*
         * Replace block @block with a `throw new PlatformNotSupportedException ()`.
         *
         */
        public void ReplaceWithThrow(ref BasicBlock block, int stackDepth)
        {
            if (block.LastInstruction.OpCode.Code != Code.Call && block.LastInstruction.OpCode.Code != Code.Callvirt)
            {
                DebugHelpers.AssertFailUnexpected(Method, block, block.LastInstruction);
            }

            var list = new List <Instruction> {
                Context.CreateNewPlatformNotSupportedException(Method),
                Instruction.Create(OpCodes.Throw)
            };

            ReplaceWithInstructions(ref block, stackDepth, list);
        }
Esempio n. 5
0
        protected static void LookAheadAfterConditional(BasicBlockList blocks, ref BasicBlock bb, ref int index)
        {
            if (index + 1 >= blocks.Body.Instructions.Count)
            {
                throw new OptimizerAssertionException();
            }

            /*
             * Look ahead at the instruction immediately following the call to the
             * conditional support method (`IsWeakInstanceOf<T>()` or `IsFeatureSupported()`).
             *
             * If it's a branch, then we add it to the current block.  Since the conditional
             * method leaves a `bool` value on the stack, the following instruction can never
             * be an unconditional branch.
             *
             * At the end of this method, the current basic block will always look like this:
             *
             *   - (optional) simple load
             *   - conditional call
             *   - (optional) conditional branch.
             *
             * We will also close out the current block and start a new one after this.
             */

            var next = blocks.Body.Instructions [index + 1];
            var type = CecilHelper.GetBranchType(next);

            switch (type)
            {
            case BranchType.None:
                bb = null;
                break;

            case BranchType.False:
            case BranchType.True:
                blocks.AddJumpOrigin(bb, next, (Instruction)next.Operand);
                goto case BranchType.Return;

            case BranchType.Return:
                bb.AddInstruction(next);
                index++;
                bb = null;
                break;

            default:
                throw DebugHelpers.AssertFailUnexpected(blocks.Method, bb, type);
            }
        }
Esempio n. 6
0
        /*
         * Replace block @block with a constant @constant, optionally popping
         * @stackDepth extra values off the stack.
         */
        public void ReplaceWithConstant(ref BasicBlock block, int stackDepth, ConstantValue constant)
        {
            Instruction instruction;

            switch (constant)
            {
            case ConstantValue.False:
                instruction = Instruction.Create(OpCodes.Ldc_I4_0);
                break;

            case ConstantValue.True:
                instruction = Instruction.Create(OpCodes.Ldc_I4_1);
                break;

            case ConstantValue.Null:
                instruction = Instruction.Create(OpCodes.Ldnull);
                break;

            default:
                throw DebugHelpers.AssertFailUnexpected(Method, block, constant);
            }

            switch (block.BranchType)
            {
            case BranchType.None:
                ReplaceWithInstruction(ref block, stackDepth, instruction);
                break;

            case BranchType.Return:
                // Rewrite as constant, then put back the return
                Scanner.Rewriter.ReplaceWithInstruction(ref block, stackDepth, instruction, false);
                BlockList.InsertInstructionAt(ref block, block.Count, Instruction.Create(OpCodes.Ret));
                BlockList.TryMergeBlock(ref block);
                break;

            default:
                throw new OptimizerAssertionException($"{nameof (ReplaceWithConstant)} called on unsupported block type `{block.BranchType}`.");
            }
        }
        public bool RemoveUnusedVariables()
        {
            Scanner.LogDebug(1, $"REMOVE VARIABLES: {Method.Name}");

            if (Method.Body.HasExceptionHandlers)
            {
                return(false);
            }

            var removed   = false;
            var variables = new Dictionary <VariableDefinition, VariableEntry> ();

            for (int i = 0; i < Method.Body.Variables.Count; i++)
            {
                var variable = new VariableEntry(Method.Body.Variables [i], i);
                variables.Add(variable.Variable, variable);

                if (Scanner.DebugLevel > 1)
                {
                    Scanner.LogDebug(2, $"  VARIABLE: {variable}");
                }
            }

            foreach (var block in BlockList.Blocks)
            {
                Scanner.LogDebug(2, $"REMOVE VARIABLES #1: {block}");

                for (int i = 0; i < block.Instructions.Count; i++)
                {
                    var instruction = block.Instructions [i];
                    Scanner.LogDebug(2, $"    {CecilHelper.Format (instruction)}");

                    var variable = CecilHelper.GetVariable(Method.Body, instruction);
                    if (variable == null)
                    {
                        continue;
                    }

                    var entry = variables [variable];
                    if (entry == null)
                    {
                        throw DebugHelpers.AssertFail(Method, block, $"Cannot resolve variable from instruction `{CecilHelper.Format (instruction)}`.");
                    }

                    entry.Used = true;
                    if (entry.Modified)
                    {
                        continue;
                    }

                    switch (instruction.OpCode.Code)
                    {
                    case Code.Ldloc_0:
                    case Code.Ldloc_1:
                    case Code.Ldloc_2:
                    case Code.Ldloc_3:
                    case Code.Ldloc:
                    case Code.Ldloc_S:
                        continue;

                    case Code.Ldloca:
                    case Code.Ldloca_S:
                        entry.SetModified();
                        continue;

                    case Code.Stloc:
                    case Code.Stloc_0:
                    case Code.Stloc_1:
                    case Code.Stloc_2:
                    case Code.Stloc_3:
                    case Code.Stloc_S:
                        break;

                    default:
                        throw DebugHelpers.AssertFailUnexpected(Method, block, instruction);
                    }

                    if (i == 0 || entry.IsConstant)
                    {
                        entry.SetModified();
                        continue;
                    }

                    var load = block.Instructions [i - 1];
                    switch (block.Instructions [i - 1].OpCode.Code)
                    {
                    case Code.Ldc_I4_0:
                        entry.SetConstant(block, load, ConstantValue.Zero);
                        break;

                    case Code.Ldc_I4_1:
                        entry.SetConstant(block, load, ConstantValue.One);
                        break;

                    case Code.Ldnull:
                        entry.SetConstant(block, load, ConstantValue.Null);
                        break;

                    default:
                        entry.SetModified();
                        break;
                    }
                }
            }

            Scanner.LogDebug(1, $"REMOVE VARIABLES #1");

            for (int i = Method.Body.Variables.Count - 1; i >= 0; i--)
            {
                var variable = variables [Method.Body.Variables [i]];
                Scanner.LogDebug(2, $"    VARIABLE #{i}: {variable}");
                if (!variable.Used)
                {
                    Scanner.LogDebug(2, $"    --> REMOVE");
                    Scanner.LogDebug(1, $"REMOVE VARIABLES - REMOVE: {Method.Name}");
                    RemoveVariable(variable);
                    Method.Body.Variables.RemoveAt(i);
                    removed = true;
                    continue;
                }

                if (variable.IsConstant)
                {
                    Scanner.LogDebug(2, $"    --> CONSTANT ({variable.Value}): {variable.Instruction}");
                    Scanner.LogDebug(1, $"REMOVE VARIABLES - CONSTANT: {Method.Name}");
                    Scanner.DumpBlock(2, variable.Block);
                    var position = variable.Block.IndexOf(variable.Instruction);
                    var block    = variable.Block;
                    BlockList.RemoveInstructionAt(ref block, position + 1);
                    BlockList.RemoveInstructionAt(ref block, position);
                    RemoveVariable(variable);
                    Method.Body.Variables.RemoveAt(i);
                    removed = true;
                    continue;
                }
            }

            if (removed)
            {
                BlockList.ComputeOffsets();

                Scanner.LogDebug(1, $"REMOVE VARIABLES DONE: {removed}");
                Scanner.DumpBlocks(1);

                Scanner.Context.Options.OptimizerReport?.RemovedDeadVariables(Method);
            }

            return(removed);
        }
Esempio n. 8
0
        void AdjustJumpTargets(BasicBlock oldBlock, BasicBlock newBlock)
        {
            foreach (var origin in oldBlock.JumpOrigins)
            {
                if (newBlock == null)
                {
                    throw CannotRemoveTarget;
                }
                if (origin.Exception != null)
                {
                    AdjustException(origin.Exception);
                    newBlock.AddJumpOrigin(origin);
                }
                else
                {
                    newBlock.AddJumpOrigin(new JumpOrigin(newBlock, origin.OriginBlock, origin.Origin));
                    AdjustJump(origin);
                }
            }

            var oldInstruction = oldBlock.LastInstruction;

            Scanner.LogDebug(2, $"ADJUST JUMPS: {oldBlock} {newBlock} {oldInstruction}");
            Scanner.Context.Debug();

            foreach (var block in _block_list)
            {
                for (var j = 0; j < block.JumpOrigins.Count; j++)
                {
                    var origin = block.JumpOrigins [j];
                    Scanner.LogDebug(2, $"  ORIGIN: {origin}");
                    if (origin.Exception != null)
                    {
                        Scanner.LogDebug(2, $"  EXCEPTION ORIGIN: {origin}");
                        if (origin.Exception.TryStart == oldInstruction)
                        {
                            throw CannotRemoveTarget;
                        }
                        if (origin.Exception.HandlerStart == oldInstruction)
                        {
                            throw CannotRemoveTarget;
                        }
                    }
                    if (origin.Origin == oldInstruction)
                    {
                        throw CannotRemoveTarget;
                    }
                    if (origin.OriginBlock == oldBlock)
                    {
                        origin.OriginBlock = newBlock ?? throw CannotRemoveTarget;
                        continue;
                    }
                }
            }

            void AdjustException(ExceptionHandler handler)
            {
                if (handler.TryStart == oldBlock.FirstInstruction)
                {
                    handler.TryStart = newBlock.FirstInstruction;
                }
                if (handler.TryEnd == oldBlock.FirstInstruction)
                {
                    handler.TryEnd = newBlock.FirstInstruction;
                }
                if (handler.HandlerStart == oldBlock.FirstInstruction)
                {
                    handler.HandlerStart = newBlock.FirstInstruction;
                }
                if (handler.HandlerEnd == oldBlock.FirstInstruction)
                {
                    handler.HandlerEnd = newBlock.FirstInstruction;
                }
                if (handler.FilterStart == oldBlock.FirstInstruction)
                {
                    handler.FilterStart = newBlock.FirstInstruction;
                }
            }

            void AdjustJump(JumpOrigin origin)
            {
                var instruction = origin.Origin;

                if (instruction.OpCode.OperandType == OperandType.InlineSwitch)
                {
                    var labels = (Instruction [])instruction.Operand;
                    for (int i = 0; i < labels.Length; i++)
                    {
                        if (labels [i] != oldBlock.FirstInstruction)
                        {
                            continue;
                        }
                        labels [i] = newBlock?.FirstInstruction ?? throw CannotRemoveTarget;
                    }
                    return;
                }
                if (instruction.OpCode.OperandType != OperandType.InlineBrTarget &&
                    instruction.OpCode.OperandType != OperandType.ShortInlineBrTarget)
                {
                    throw DebugHelpers.AssertFailUnexpected(Method, origin.OriginBlock, instruction);
                }
                if (instruction.Operand != oldBlock.FirstInstruction)
                {
                    throw DebugHelpers.AssertFailUnexpected(Method, origin.OriginBlock, instruction);
                }
                instruction.Operand = newBlock?.FirstInstruction ?? throw CannotRemoveTarget;
            }
        }
Esempio n. 9
0
        public bool Initialize()
        {
            _bb_by_instruction.Clear();
            _block_list.Clear();
            _next_block_id = 0;

            foreach (var handler in Body.ExceptionHandlers)
            {
                if (handler.TryStart != null)
                {
                    EnsureExceptionBlock(BasicBlockType.Try, handler.TryStart, handler);
                }
                switch (handler.HandlerType)
                {
                case ExceptionHandlerType.Catch:
                    EnsureExceptionBlock(BasicBlockType.Catch, handler.HandlerStart, handler);
                    EnsureExceptionBlock(BasicBlockType.Normal, handler.HandlerEnd, handler);
                    break;

                case ExceptionHandlerType.Finally:
                    EnsureExceptionBlock(BasicBlockType.Finally, handler.HandlerStart, handler);
                    EnsureExceptionBlock(BasicBlockType.Normal, handler.HandlerEnd, handler);
                    break;

                default:
                    Scanner.LogDebug(1, $"Unknown exception type `{handler.HandlerType}` in `{Scanner.Method}`.");
                    return(false);
                }
            }

            foreach (var instruction in Body.Instructions)
            {
                switch (instruction.OpCode.OperandType)
                {
                case OperandType.InlineBrTarget:
                case OperandType.ShortInlineBrTarget:
                    EnsureBlock((Instruction)instruction.Operand);
                    break;

                case OperandType.InlineSwitch:
                    foreach (var label in (Instruction [])instruction.Operand)
                    {
                        EnsureBlock(label);
                    }
                    break;
                }
            }

            return(true);

            void EnsureExceptionBlock(BasicBlockType type, Instruction target, ExceptionHandler handler)
            {
                if (target == null)
                {
                    return;
                }
                var block = EnsureBlock(target);

                if (block.Type == BasicBlockType.Normal)
                {
                    block.Type = type;
                }
                else if (block.Type != type)
                {
                    throw DebugHelpers.AssertFailUnexpected(Method, block, type);
                }
                block.ExceptionHandlers.Add(handler);
                block.AddJumpOrigin(new JumpOrigin(block, handler));
            }
        }