public void Transform(MethodBody body) { #if DEBUG //return; #endif var instructions = body.Instructions; var hasNops = instructions.Any(x => x.OpCode == OpCodes.Nop); if (!hasNops) return; var rerouter = new BranchReRouter(body); var i = 0; while (i < instructions.Count - 1) { var inst = instructions[i]; if (inst.OpCode != OpCodes.Nop) { i++; continue; } if (body.Exceptions.Any(x => (x.TryEnd == inst) /*|| (x.TryStart == inst)*/)) { i++; continue; } var next = instructions[i + 1]; rerouter.Reroute(inst, next); instructions.RemoveAt(i); } }
/// <summary> /// Optimize the given body /// </summary> internal static void Optimize(MethodBody body) { foreach (var transformation in optimizations) { transformation.Transform(body); } }
/// <summary> /// Add code to save low register to their original registers if needed. /// </summary> public void FinalizeBlock(MethodBody body) { // no need to restore registers after return or throw. if (block.Exit.OpCode.IsReturn() || block.Exit.OpCode == OpCodes.Throw) { return; } Instruction endingNop = null; foreach (var lowRegState in lowRegisters.Where(x => x.SaveToOriginalNeeded)) { if (endingNop == null) { endingNop = new Instruction(OpCodes.Nop); // Block exit can never be a branch target, so no need to reroute. if (block.Exit.OpCode.IsBranch()) { block.InsertBefore(block.Exit, endingNop); } else { block.InsertAfter(block.Exit, endingNop); } } // Add move instruction to save low register to original lowRegState.SaveToOriginalAndClear(endingNop, block, body, mapper.RegisterSpillingMap); } }
public static string FormatRegister(Register r, MethodBody body) { if (body.IsComing(r)) { int parameterIdx = r.Index - body.Registers.Count + body.IncomingArguments; return "p" + parameterIdx; } return "r" + r.Index; }
/// <summary> /// Add instructions that spill registers (when needed). /// </summary> internal void Generate(MethodBody body) { // Go over each instruction in the block. var instructions = block.Instructions.ToList(); foreach (var ins in instructions) { var registers = ins.Registers; var count = registers.Count; if (count == 0) { // No registers, so no spilling continue; } var isInvoke = ins.OpCode.IsInvoke(); if (isInvoke) { if (ins.RequiresInvokeRange()) { ConvertInvoke(ins); } } else { var info = OpCodeInfo.Get(ins.OpCode); for (var i = 0; i < count; i++) { Register r; LowRegisterState state; var spill = TryGetLowRegister(registers[i], out r, out state); if (!spill) { var size = info.GetUsage(i) & RegisterFlags.SizeMask; switch (size) { case RegisterFlags.Bits4: spill = !registers[i].IsBits4; break; case RegisterFlags.Bits8: spill = !registers[i].IsBits8; break; } } if (spill) { // Insert spilling code AddCode(ins, registers[i], body); } } } } }
public void Transform(MethodBody body) { // Prefix all aligned opcodes with a nop. // We'll decide later if we want to remove them. var instructions = body.Instructions; /*for (var i = 0; i < instructions.Count; i++ ) { switch(instructions[i].OpCode) { case OpCodes.Packed_switch: instructions.Insert(i++, new Instruction(OpCodes.Nop)); break; } }*/ body.UpdateInstructionOffsets(); for (var i = 0; i < instructions.Count; i++) { var ins = instructions[i]; switch (ins.OpCode) { /*case OpCodes.Packed_switch: if ((ins.Offset % 2) == 0) { instructions.RemoveAt(i - 1); } break;*/ case OpCodes.Goto: case OpCodes.Goto_16: case OpCodes.Goto_32: var offset = (ins.Operand as Instruction).Offset - ins.Offset; OpCodes opcode; if (IsI8(offset)) { opcode = OpCodes.Goto; } else if (IsI16(offset)) { opcode = OpCodes.Goto_16; } else { opcode = OpCodes.Goto_32; } if (ins.OpCode != opcode) { ins.OpCode = opcode; body.UpdateInstructionOffsets(); } break; } } }
/// <summary> /// Add instructions that spill registers (when needed). /// </summary> internal static void AddSpillingCode(MethodBody body, RegisterMapper mapper) { // Calculate the basic blocks var basicBlocks = BasicBlock.Find(body); var spillRegisters = mapper.SpillRegisters.ToList(); var invokeFrame = mapper.InvocationFrameRegisters.ToList(); var allRegisters = mapper.All.ToList(); foreach (var block in basicBlocks) { var generator = new BlockSpillCodeGenerator(block, mapper, spillRegisters, invokeFrame, allRegisters); generator.Generate(body); generator.FinalizeBlock(body); } }
/// <summary> /// Replace all references to oldRegister with newRegister in the given instruction set. /// </summary> public static void ReplaceRegisterWith(this IEnumerable<Instruction> instructions, Register oldRegister, Register newRegister, MethodBody body) { var oldRegister2 = (oldRegister.Type == RType.Wide) ? body.GetNext(oldRegister) : null; var newRegister2 = (newRegister.Type == RType.Wide) ? body.GetNext(newRegister) : null; if (oldRegister.IsKeepWithNext != newRegister.IsKeepWithNext) throw new ArgumentException("New register has different keep-with-next value"); foreach (var ins in instructions) { ins.ReplaceRegisterWith(oldRegister, newRegister); if (oldRegister2 != null) ins.ReplaceRegisterWith(oldRegister2, newRegister2); } }
/// <summary> /// Default ctor /// </summary> public BranchReRouter(MethodBody body) { this.body = body; List<Instruction> list = null; // Record instructions with branch targets foreach (var inst in body.Instructions) { if (inst.OpCode.IsBranch()) { list = list ?? new List<Instruction>(); list.Add(inst); } } branches = list; exceptionHandlers = body.Exceptions.Any() ? body.Exceptions.ToArray() : null; }
/// <summary> /// Add instructions that spill the given register. /// </summary> private void AddCode(Instruction ins, Register register, MethodBody body) { // Try to find an earlier map for the register { Register sReg; LowRegisterState state; if (TryGetLowRegister(register, out sReg, out state)) { // Already mapped, just replace. ReplaceRegister(ins, register, sReg); // Should we save the low register now? if ((!state.SaveToOriginalNeeded) && (sReg.IsDestinationIn(ins))) { state.SaveToOriginalNeeded = true; } return; } } // Allocate low register var sRegState = AllocateLowRegister(ins, register, body); // We can now use sReg as replacement for register. // Add move (only if the register is a source of the instruction) if (register.IsSourceIn(ins)) { block.InsertBefore(ins, CreateMove(register, sRegState.LowRegister, GetType(register))); } // See if we need to save the low register afterwards if ((!sRegState.SaveToOriginalNeeded) && (register.IsDestinationIn(ins))) { sRegState.SaveToOriginalNeeded = true; } // Replace the register ReplaceRegister(ins, register, sRegState.LowRegister); // Record start point in mapping sRegState.Mapping.FirstInstruction = ins; }
public void Transform(MethodBody body) { #if DEBUG //return; #endif var instructions = body.Instructions; var hasNops = instructions.Any(x => x.OpCode == OpCodes.Nop); if (!hasNops) return; var rerouter = new BranchReRouter(body); var i = 0; while (i < instructions.Count - 1) { var inst = instructions[i]; if (inst.OpCode != OpCodes.Nop) { i++; continue; } if (body.Exceptions.Any(x => (x.TryEnd == inst) /*|| (x.TryStart == inst)*/)) { i++; continue; } var next = instructions[i + 1]; // TODO: prevent removal of nop if the next instruction branches to this nop, // (i.e. a spin loop); or, better, handle this case in the RL to dex compiler. rerouter.Reroute(inst, next); instructions.RemoveAt(i); } }
/// <summary> /// Default ctor /// </summary> public DexCompiler(RL.MethodBody rlBody, MethodBody dexBody, InvocationFrame frame) { this.rlBody = rlBody; this.dexBody = dexBody; this.frame = frame; }
/// <summary> /// Note that referencing operands, i.e. TypeReferences, FieldReference or /// MethodRefrences are not cloned, but only shallow copied. /// If you indend to modify those operand values themself, be sure to clone /// them beforehand. /// </summary> public static MethodBody Clone(MethodDefinition targetMethod, MethodDefinition sourceMethod) { var sourceBody = sourceMethod.Body; MethodBody body = new MethodBody(targetMethod, sourceBody.Registers.Count) { IncomingArguments = sourceBody.IncomingArguments, OutgoingArguments = sourceBody.OutgoingArguments }; foreach (var sourceIns in sourceBody.Instructions) { var ins = new Instruction(sourceIns.OpCode); foreach (var r in sourceIns.Registers) ins.Registers.Add(body.Registers[r.Index]); ins.Offset = sourceIns.Offset; ins.SequencePoint = ins.SequencePoint; ins.Operand = sourceIns.Operand; body.Instructions.Add(ins); } // fix instruction references var insByOffset = body.Instructions.ToDictionary(i => i.Offset); foreach (var ins in body.Instructions) { var targetIns = ins.Operand as Instruction; var packedSwitch = ins.Operand as PackedSwitchData; var sparseSwitch = ins.Operand as SparseSwitchData; if (targetIns != null) { ins.Operand = insByOffset[targetIns.Offset]; } else if (packedSwitch != null) { var ps = new PackedSwitchData(packedSwitch.Targets.Select(si => insByOffset[si.Offset])); ps.FirstKey = packedSwitch.FirstKey; ins.Operand = ps; } else if (sparseSwitch != null) { var ss = new SparseSwitchData(); foreach (var ssd in sparseSwitch.Targets) { ss.Targets.Add(ssd.Key, insByOffset[ssd.Value.Offset]); } ins.Operand = ss; } } foreach (var sourceEx in sourceBody.Exceptions) { var ex = new ExceptionHandler(); if (sourceEx.TryStart != null) ex.TryStart = insByOffset[sourceEx.TryStart.Offset]; if (sourceEx.TryEnd != null) ex.TryEnd = insByOffset[sourceEx.TryEnd.Offset]; if (sourceEx.CatchAll != null) ex.CatchAll = insByOffset[sourceEx.CatchAll.Offset]; foreach (var sourceCatch in sourceEx.Catches) { var c = new Catch { Type = sourceCatch.Type, Instruction = insByOffset[sourceCatch.Instruction.Offset] }; ex.Catches.Add(c); } body.Exceptions.Add(ex); } if (sourceBody.DebugInfo != null) { var di = body.DebugInfo = new DebugInfo(body); di.LineStart = sourceBody.DebugInfo.LineStart; di.Parameters = sourceBody.DebugInfo.Parameters.ToList(); foreach (var sourceInstruction in sourceBody.DebugInfo.DebugInstructions) { di.DebugInstructions.Add(new DebugInstruction(sourceInstruction.OpCode, sourceInstruction.Operands.ToList())); } } return body; }
/// <summary> /// Save the state in the low register back to the original register when a save is needed. /// The save code is inserted directly before the given target instruction. /// </summary> public void SaveToOriginalAndClear(Instruction target, BasicBlock block, MethodBody body, RegisterSpillingMap map) { if (wideStart != null) { wideStart.SaveToOriginalAndClear(target, block, body, map); } else { if (SaveToOriginalNeeded) { var original = OriginalRegister; // Fix target to insert before if (target.OpCode.IsMoveResult()) { do { target = target.GetPrevious(body.Instructions); } while (target.OpCode == OpCodes.Nop); } // Insert save code var move = CreateMove(LowRegister, original, type); block.InsertBefore(target, move); // Save end in mapping Mapping.LastInstruction = move; // Record mapping map.Add(Mapping); } Clear(); } }
/// <summary> /// Allocate a low register and map it to the given original register. /// </summary> private LowRegisterState AllocateLowRegister(Instruction ins, Register register, MethodBody body) { // Prepare var type = GetType(register); var isWide = (type == RType.Wide); var regsNeeded = isWide ? 2 : 1; LowRegisterState regState = null; HashSet<Register> allRegistersUsedInIns = null; while (true) { // Try to allocate free spilling register regState = lowRegisters.FirstOrDefault(x => x.IsFreeFor(type)); if (regState != null) break; // Found a free low register // No spilling register is available. // Free another register first // If wide, free 2 registers if (nextSpillIndex + regsNeeded > lowRegisters.Length) nextSpillIndex = 0; regState = lowRegisters[nextSpillIndex]; var regStateNext = isWide ? lowRegisters[nextSpillIndex + 1] : null; // Update index so we take another the next time nextSpillIndex = (nextSpillIndex + regsNeeded) % (lowRegisters.Length - 1); // Do not allocate registers already used in the current instruction allRegistersUsedInIns = allRegistersUsedInIns ?? GetAllUsedRegisters(ins); if (allRegistersUsedInIns.Contains(regState.LowRegister)) continue; if ((regStateNext != null) && allRegistersUsedInIns.Contains(regStateNext.LowRegister)) continue; // Save sReg back to original register var map = mapper.RegisterSpillingMap; regState.SaveToOriginalAndClear(ins, block, body, map); if (regStateNext != null) regStateNext.SaveToOriginalAndClear(ins, block, body, map); } // Add mapping regState.SetInUseBy(register, type); return regState; }
/// <summary> /// Add code to save low register to their original registers if needed. /// </summary> public void FinalizeBlock(MethodBody body) { // no need to restore registers after return or throw. if (block.Exit.OpCode.IsReturn() || block.Exit.OpCode == OpCodes.Throw) return; Instruction endingNop = null; foreach (var lowRegState in lowRegisters.Where(x => x.SaveToOriginalNeeded)) { if (endingNop == null) { endingNop = new Instruction(OpCodes.Nop); // Block exit can never be a branch target, so no need to reroute. if (block.Exit.OpCode.IsBranch()) block.InsertBefore(block.Exit, endingNop); else block.InsertAfter(block.Exit, endingNop); } // Add move instruction to save low register to original lowRegState.SaveToOriginalAndClear(endingNop, block, body, mapper.RegisterSpillingMap); } }
/// <summary> /// Find all basic blocks in the given body. /// </summary> public static List<BasicBlock> Find(MethodBody body) { var instructions = body.Instructions; if (instructions.Count == 0) return new List<BasicBlock>(); var targetInstructions = body.Instructions.Select(x => x.Operand).OfType<Instruction>().ToList(); targetInstructions.AddRange(instructions.Select(x => x.Operand).OfType<ISwitchData>().SelectMany(x => x.GetTargets())); targetInstructions.AddRange(instructions.Where(x => x.OpCode.IsBranch()).Select(x => GetNextOrDefault(instructions, x))); targetInstructions.AddRange(body.Exceptions.Select(x => x.TryStart)); targetInstructions.AddRange(body.Exceptions.Select(x => GetNextOrDefault(instructions, x.TryEnd))); targetInstructions.AddRange(body.Exceptions.SelectMany(x => x.Catches, (h, y) => y.Instruction)); targetInstructions.AddRange(body.Exceptions.Select(x => x.CatchAll)); targetInstructions.Add(body.Instructions[0]); // Get sorted list with duplicates removed. var startInstructions = targetInstructions.Where(x => x != null).Distinct().OrderBy(instructions.IndexOf).ToList(); var result = new List<BasicBlock>(); for (var i = 0; i < startInstructions.Count; i++) { var entry = startInstructions[i]; var exit = (i + 1 < startInstructions.Count) ? GetPrevious(instructions, startInstructions[i + 1]) : body.Instructions[body.Instructions.Count - 1]; result.Add(new BasicBlock(body.Instructions, entry, exit)); } return result; }
/// <summary> /// Transform the given body. /// </summary> public bool Transform(Dex target, MethodBody body) { // Build the control flow graph var cfg = new ControlFlowGraph(body); // Go over each block to find registers that are initialized and may need initialization foreach (var iterator in cfg) { var block = iterator; var data = new BasicBlockData(block); block.Tag = data; // Go over all instructions in the block, finding source/destination registers foreach (var ins in block.Instructions) { var info = OpCodeInfo.Get(ins.Code.ToDex()); var count = ins.Registers.Count; for (var i = 0; i < count; i++) { var reg = ins.Registers[i]; if (reg.Category != RCategory.Argument) { var flags = info.GetUsage(i); if (flags.HasFlag(RegisterFlags.Source)) { // Must be initialize if (!data.Initialized.Contains(reg)) { data.MayNeedInitialization.Add(reg); } } if (flags.HasFlag(RegisterFlags.Destination)) { // Destination data.Initialized.Add(reg); } } } } } // Go over all blocks to collect the register that really need initialization var needInitialization = new HashSet<Register>(); foreach (var iterator in cfg) { var block = iterator; var data = (BasicBlockData)block.Tag; foreach (var regIterator in data.MayNeedInitialization) { // Short cut var reg = regIterator; if (needInitialization.Contains(reg)) continue; // If the register is initialized in all entry blocks, we do not need to initialize it if (block.EntryBlocks.Select(x => (BasicBlockData) x.Tag).Any(x => !x.IsInitialized(reg))) { // There is an entry block that does not initialize the register, so we have to initialize it needInitialization.Add(reg); } } } var index = 0; Register valueReg = null; Register objectReg = null; Register wideReg = null; var firstSourceLocation = body.Instructions[0].SequencePoint; foreach (var reg in needInitialization.OrderBy(x => x.Index)) { switch (reg.Type) { case RType.Value: if (valueReg == null) { body.Instructions.Insert(index++, new Instruction(RCode.Const, 0, new[] {reg}) { SequencePoint = firstSourceLocation }); valueReg = reg; } else if (valueReg != reg) { body.Instructions.Insert(index++, new Instruction(RCode.Move, reg, valueReg) { SequencePoint = firstSourceLocation }); } break; case RType.Object: if (objectReg == null) { body.Instructions.Insert(index++, new Instruction(RCode.Const, 0, new[] { reg }) { SequencePoint = firstSourceLocation }); objectReg = reg; } else if (objectReg != reg) { body.Instructions.Insert(index++, new Instruction(RCode.Move_object, reg, objectReg) { SequencePoint = firstSourceLocation }); } break; case RType.Wide: if (wideReg == null) { body.Instructions.Insert(index++, new Instruction(RCode.Const_wide, 0, new[] { reg }) { SequencePoint = firstSourceLocation }); wideReg = reg; } else if (wideReg != reg) { body.Instructions.Insert(index++, new Instruction(RCode.Move_wide, reg, wideReg) { SequencePoint = firstSourceLocation }); } break; } } return false; }
public static void Transform(MethodBody body) { //Debugger.Launch(); foreach (var ins in body.Instructions) { switch (ins.OpCode) { case OpCodes.Add_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Add_int); break; case OpCodes.Sub_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Sub_int); break; case OpCodes.Mul_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Mul_int); break; case OpCodes.Div_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Div_int); break; case OpCodes.Rem_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Rem_int); break; case OpCodes.And_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.And_int); break; case OpCodes.Or_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Or_int); break; case OpCodes.Xor_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Xor_int); break; case OpCodes.Shl_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Shl_int); break; case OpCodes.Shr_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Shr_int); break; case OpCodes.Ushr_int_2addr: OptimizeBinOp2Addr(ins, OpCodes.Ushr_int); break; case OpCodes.Add_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Add_long); break; case OpCodes.Sub_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Sub_long); break; case OpCodes.Mul_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Mul_long); break; case OpCodes.Div_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Div_long); break; case OpCodes.Rem_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Rem_long); break; case OpCodes.And_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.And_long); break; case OpCodes.Or_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Or_long); break; case OpCodes.Xor_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Xor_long); break; case OpCodes.Shl_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Shl_long); break; case OpCodes.Shr_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Shr_long); break; case OpCodes.Ushr_long_2addr: OptimizeBinOp2Addr(ins, OpCodes.Ushr_long); break; case OpCodes.Add_float_2addr: OptimizeBinOp2Addr(ins, OpCodes.Add_float); break; case OpCodes.Sub_float_2addr: OptimizeBinOp2Addr(ins, OpCodes.Sub_float); break; case OpCodes.Mul_float_2addr: OptimizeBinOp2Addr(ins, OpCodes.Mul_float); break; case OpCodes.Div_float_2addr: OptimizeBinOp2Addr(ins, OpCodes.Div_float); break; case OpCodes.Rem_float_2addr: OptimizeBinOp2Addr(ins, OpCodes.Rem_float); break; case OpCodes.Add_double_2addr: OptimizeBinOp2Addr(ins, OpCodes.Add_double); break; case OpCodes.Sub_double_2addr: OptimizeBinOp2Addr(ins, OpCodes.Sub_double); break; case OpCodes.Mul_double_2addr: OptimizeBinOp2Addr(ins, OpCodes.Mul_double); break; case OpCodes.Div_double_2addr: OptimizeBinOp2Addr(ins, OpCodes.Div_double); break; case OpCodes.Rem_double_2addr: OptimizeBinOp2Addr(ins, OpCodes.Rem_double); break; } } }
public DebugInfo(MethodBody body) { Owner = body; Parameters = new List<string>(); DebugInstructions = new List<DebugInstruction>(); }
/// <summary> /// Allocate a low register and map it to the given original register. /// </summary> private LowRegisterState AllocateLowRegister(Instruction ins, Register register, MethodBody body) { // Prepare var type = GetType(register); var isWide = (type == RType.Wide); var regsNeeded = isWide ? 2 : 1; LowRegisterState regState = null; HashSet <Register> allRegistersUsedInIns = null; while (true) { // Try to allocate free spilling register regState = lowRegisters.FirstOrDefault(x => x.IsFreeFor(type)); if (regState != null) { break; // Found a free low register } // No spilling register is available. // Free another register first // If wide, free 2 registers if (nextSpillIndex + regsNeeded > lowRegisters.Length) { nextSpillIndex = 0; } regState = lowRegisters[nextSpillIndex]; var regStateNext = isWide ? lowRegisters[nextSpillIndex + 1] : null; // Update index so we take another the next time nextSpillIndex = (nextSpillIndex + regsNeeded) % (lowRegisters.Length - 1); // Do not allocate registers already used in the current instruction allRegistersUsedInIns = allRegistersUsedInIns ?? GetAllUsedRegisters(ins); if (allRegistersUsedInIns.Contains(regState.LowRegister)) { continue; } if ((regStateNext != null) && allRegistersUsedInIns.Contains(regStateNext.LowRegister)) { continue; } // Save sReg back to original register var map = mapper.RegisterSpillingMap; regState.SaveToOriginalAndClear(ins, block, body, map); if (regStateNext != null) { regStateNext.SaveToOriginalAndClear(ins, block, body, map); } } // Add mapping regState.SetInUseBy(register, type); return(regState); }
private static void FormatOperand_Instruction(StringBuilder ops, Instruction ins, MethodBody body, Instruction target, bool compact) { ops.Append(JumpMarker); ops.Append(" "); ops.Append(target.Offset.ToString("X3")); int targetIdx = body.Instructions.IndexOf(target); int myIdx = body.Instructions.IndexOf(ins); ops.Append(!compact ? " ; " : "("); int offset = (targetIdx - myIdx); ops.Append(offset.ToString("+0;-0;+0")); if (compact) ops.Append(")"); }
public static string FormatOperands(Instruction ins, MethodBody body, FormatOptions options = FormatOptions.Default) { StringBuilder ops = new StringBuilder(); bool fullTypeNames = options.HasFlag(FormatOptions.FullTypeNames); foreach (var r in ins.Registers) { if (ops.Length > 0) { ops.Append(","); Align(ops, 4); } ops.Append(FormatRegister(r, body)); } if (ops.Length == 0) ops.Append(" "); if (ins.Operand != null) { Align(ops, 12); if (ins.Operand is string) { ops.Append("\""); ops.Append(ins.Operand); ops.Append("\""); } else if (ins.Operand is sbyte) { FormatOperand_Integer(ops, (int)(byte)(sbyte)ins.Operand, "X2"); } else if (ins.Operand is short) { FormatOperand_Integer(ops, (int) (short) ins.Operand); } else if (ins.Operand is int) { FormatOperand_Integer(ops, (int) ins.Operand); } else if (ins.Operand is long) { var l = (long) ins.Operand; ops.Append(l); ops.Append(" (0x"); ops.Append(l.ToString("X8")); ops.Append(")"); } else if (ins.Operand is Instruction) { var target = (Instruction) ins.Operand; FormatOperand_Instruction(ops, ins, body, target, false); } else if (ins.Operand is ClassReference) { var m = (ClassReference) ins.Operand; ops.Append(fullTypeNames ? m.ToString() : m.Name); } else if (ins.Operand is MethodReference) { var m = (MethodReference) ins.Operand; var owner = fullTypeNames || !(m.Owner is ClassReference) ? m.ToString() : ((ClassReference) m.Owner).Name; ops.Append(owner + "::" + m.Name + m.Prototype); } else if (ins.Operand is FieldReference) { var m = (FieldReference) ins.Operand; ops.Append(fullTypeNames ? m.ToString() : m.Owner.Name + "::" + m.Name + " : " + m.Type); } else if (ins.Operand is PackedSwitchData) { var d = (PackedSwitchData) ins.Operand; FormatOperand_Integer(ops, d.FirstKey); ops.Append(":"); foreach (var target in d.Targets) { ops.Append(" "); FormatOperand_Instruction(ops, ins, body, target, true); } } else if (ins.Operand is SparseSwitchData) { var d = (SparseSwitchData) ins.Operand; bool isFirst = true; foreach (var target in d.Targets) { if (!isFirst) ops.Append(" "); ops.Append(target.Key); ops.Append(": "); FormatOperand_Instruction(ops, ins, body, target.Value, true); isFirst = false; } } else { ops.Append(ins.Operand); } if (options.HasFlag(FormatOptions.DebugOperandTypes)) ops.AppendFormat(" [{0}]", ins.Operand.GetType().Name); } var bstrOperands = ops.ToString(); return bstrOperands; }
/// <summary> /// Create debug info for the given (otherwise completed) body. /// </summary> internal void CreateDebugInfo(MethodBody dbody, RegisterMapper regMapper, DexTargetPackage targetPackage) { var source = compiledMethod.ILSource; if ((source == null) || !source.HasBody || (source.Body.Instructions.Count == 0)) return; // Initialize var info = new DebugInfo(dbody); info.Parameters.AddRange(dbody.Owner.Prototype.Parameters.Select(x => x.Name ?? "?")); // Should be at a better location perhaps info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.SetPrologueEnd)); // Get instructions with valid sequence points var validTuples = dbody.Instructions.Select(x => Tuple.Create(x, x.SequencePoint as ISourceLocation)).Where(x => x.Item2 != null && !x.Item2.IsSpecial).ToList(); // Assign line numbers var lineNumbers = AssignLineNumbers(validTuples.Select(x => x.Item2)); // Set default document string lastUrl = null; var setLineStart = true; int lastLine = 0; int lastOffset = 0; var firstDocument = validTuples.Select(x => x.Item2).FirstOrDefault(x => x != null && x.Document != null); if (firstDocument != null) { lastUrl = firstDocument.Document; lastLine = firstDocument.StartLine; info.LineStart = (uint) lastLine; setLineStart = false; // Set on type or when different in debug info. var type = dbody.Owner.Owner; if (string.IsNullOrEmpty(type.SourceFile)) type.SourceFile = lastUrl; if (type.SourceFile != lastUrl) { // Make sure the file is set when needed lastUrl = null; } } // Build intermediate list var entries = new List<Entry>(); // Add line numbers foreach (var tuple in validTuples) { var ins = tuple.Item1; var seqp = tuple.Item2; var lineNumber = GetLineNumber(seqp, lineNumbers); // Line number if (setLineStart) { info.LineStart = (uint)lineNumber; setLineStart = false; } var url = seqp.Document; entries.Add(new PositionEntry(ins.Offset, lineNumber, url)); } // Add variables ControlFlowGraph cfg = null; foreach (var tuple in regMapper.VariableRegisters) { var reg = tuple.Register; var variable = tuple.Variable; if (variable.IsCompilerGenerated) continue; var dexType = variable.GetType(targetPackage); if (dexType == null) continue; // Find out in which basic blocks the variable is live cfg = cfg ?? new ControlFlowGraph(dbody); var startMap = new Dictionary<BasicBlock, Instruction>(); foreach (var block in cfg) { // First instruction from that first writes to the register. var firstWrite = block.Instructions.FirstOrDefault(reg.IsDestinationIn); if (firstWrite == null) continue; // The variable is valid the first instruction after the first write. Instruction start; if (!firstWrite.TryGetNext(dbody.Instructions, out start)) continue; startMap.Add(block, start); block.AddLiveRegisterAtExit(reg); } if (startMap.Count == 0) continue; // Generate start-restart-end entries VariableEndEntry lastBlockEndEntry = null; foreach (var block in cfg) { Instruction start; var started = false; if (block.IsLiveAtEntry(reg)) { // Live in the entire block if (lastBlockEndEntry == null) { // We have to start/restart entries.Add(new VariableStartEntry(block.Entry.Offset, reg, variable, dexType)); } else { // Remove the end-entry of the previous block entries.Remove(lastBlockEndEntry); } started = true; } else if (startMap.TryGetValue(block, out start)) { // Live from "start" entries.Add(new VariableStartEntry(start.Offset, reg, variable, dexType)); started = true; } Instruction next; lastBlockEndEntry = null; if (started && block.Exit.TryGetNext(dbody.Instructions, out next)) { // Add end block entries.Add(lastBlockEndEntry = new VariableEndEntry(next.Offset, reg, variable)); } } // Weave in splilling info var spillMappings = regMapper.RegisterSpillingMap.Find(reg).ToList(); if (spillMappings.Count > 0) { foreach (var mapping in spillMappings) { var alreadyStarted = IsStartedAt(entries, variable, mapping.FirstInstruction.Offset); if (alreadyStarted) { // Stop now var prev = mapping.FirstInstruction.GetPrevious(dbody.Instructions); entries.Add(new VariableEndEntry(prev.Offset, mapping.HighRegister, variable)); } // Add mappings for low register entries.Add(new VariableStartEntry(mapping.FirstInstruction.Offset, mapping.LowRegister, variable, dexType)); entries.Add(new VariableEndEntry(mapping.LastInstruction.Offset, mapping.LowRegister, variable)); if (alreadyStarted) { // Restart on high register Instruction next; if (mapping.LastInstruction.TryGetNext(dbody.Instructions, out next)) { entries.Add(new VariableStartEntry(next.Offset, mapping.HighRegister, variable, dexType)); } } } } } // Generate instructions entries.Sort(); var firstPositionEntry = true; var startedVarirables = new HashSet<Register>(); foreach (var entry in entries) { entry.Generate(info, ref lastLine, ref lastOffset, ref lastUrl, ref firstPositionEntry, startedVarirables); } // Terminate info.DebugInstructions.Add(new DebugInstruction(DebugOpCodes.EndSequence)); // Attached dbody.DebugInfo = info; }
public void Transform(MethodBody body) { //Debugger.Launch(); foreach (var ins in body.Instructions) { switch (ins.OpCode) { case OpCodes.Const: case OpCodes.Const_4: case OpCodes.Const_16: case OpCodes.Const_high16: if (IsI4(ins.Operand) && Is4Bits(ins.Registers[0])) { ins.OpCode = OpCodes.Const_4; } else if (IsI16(ins.Operand) && Is8Bits(ins.Registers[0])) { ins.OpCode = OpCodes.Const_16; } else if (IsIHigh16(ins.Operand) && Is8Bits(ins.Registers[0])) { ins.OpCode = OpCodes.Const_high16; } else { ins.OpCode = OpCodes.Const; } break; case OpCodes.Const_wide: case OpCodes.Const_wide_16: case OpCodes.Const_wide_32: case OpCodes.Const_wide_high16: { // Convert int to long if (ins.Operand is int) { ins.Operand = (long) ((int) ins.Operand); } } break; case OpCodes.Move: case OpCodes.Move_16: case OpCodes.Move_from16: if (Is4Bits(ins.Registers[0]) && Is4Bits(ins.Registers[1])) { ins.OpCode = OpCodes.Move; } else if (Is8Bits(ins.Registers[0])) { ins.OpCode = OpCodes.Move_from16; } else { ins.OpCode = OpCodes.Move_16; } break; case OpCodes.Move_wide: case OpCodes.Move_wide_16: case OpCodes.Move_wide_from16: if (Is4Bits(ins.Registers[0]) && Is4Bits(ins.Registers[1])) { ins.OpCode = OpCodes.Move_wide; } else if (Is8Bits(ins.Registers[0])) { ins.OpCode = OpCodes.Move_wide_from16; } else { ins.OpCode = OpCodes.Move_wide_16; } break; case OpCodes.Move_object: case OpCodes.Move_object_16: case OpCodes.Move_object_from16: if (Is4Bits(ins.Registers[0]) && Is4Bits(ins.Registers[1])) { ins.OpCode = OpCodes.Move_object; } else if (Is8Bits(ins.Registers[0])) { ins.OpCode = OpCodes.Move_object_from16; } else { ins.OpCode = OpCodes.Move_object_16; } break; } } }
public RegisterNames(MethodBody body, bool hasMethodParametersInLastRegister) { _body = body; _hasMethodParametersInLastRegister = hasMethodParametersInLastRegister; }