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); } }
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); } }
/* * 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); }
/* * 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); }
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); } }
/* * 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); }
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; } }
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)); } }