/// <summary> /// Do all registers of the given instruction fit in the size that is available for them? /// </summary> private static bool AllRegistersFit(Instruction ins) { var registers = ins.Registers; var count = registers.Count; if (count == 0) { return(true); } var info = OpCodeInfo.Get(ins.OpCode); for (var i = 0; i < count; i++) { var size = info.GetUsage(i) & RegisterFlags.SizeMask; switch (size) { case RegisterFlags.Bits4: if (!registers[i].IsBits4) { return(false); } break; case RegisterFlags.Bits8: if (!registers[i].IsBits8) { return(false); } break; } } return(true); }
/// <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> /// 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> /// Replace all occurrences of oldTarget with newTarget. /// </summary> public void ReplaceTarget(Instruction oldTarget, Instruction newTarget) { var keys = targets.Where(x => x.Value == oldTarget).Select(entry => entry.Key).ToList(); foreach (var key in keys) { targets[key] = newTarget; } }
/// <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> /// Replace all occurrences of oldTarget with newTarget. /// </summary> public void ReplaceTarget(Instruction oldTarget, Instruction newTarget) { var count = targets.Count; for (var i = 0; i < count; i++) { if (targets[i] == oldTarget) { targets[i] = newTarget; } } }
/// <summary> /// Convert the given instruction into 1 or more dex instructions. /// </summary> internal static IEnumerable<Instruction> Convert(RL.Instruction source, RegisterMapper regMapper) { var dexIns = new Instruction(source.Code.ToDex(), source.Operand) { SequencePoint = source.SequencePoint }; var dexRegisters = dexIns.Registers; dexRegisters.AddRange(source.Registers.Select(x => regMapper[x])); if (!AllRegistersFit(dexIns) || dexIns.RequiresInvokeRange()) { // At least 1 register does not fit. // Insert a NOP first so we do not have to re-route when we insert spilling code. yield return new Instruction(OpCodes.Nop); } yield return dexIns; }
/// <summary> /// Get the instruction direct after the given instruction. /// </summary> internal static bool TryGetNext(this Instruction instruction, List<Instruction> instructions, out Instruction next) { next = null; for (var index = 0; index < instructions.Count; index++) { if (instructions[index] == instruction) { if (index == instructions.Count - 1) return false; next = instructions[index + 1]; return true; } } return false; }
/// <summary> /// Gets all registers used in the given instruction, include "next" registers in case of wide operands. /// </summary> private HashSet <Register> GetAllUsedRegisters(Instruction ins) { var set = new HashSet <Register>(); foreach (var r in ins.Registers) { set.Add(r); if (GetType(r) == RType.Wide) { // Include next register var next = allRegisters.First(x => x.Index == r.Index + 1); set.Add(next); } } return(set); }
/// <summary> /// Is the given register used as destination in the given instruction? /// </summary> public static bool IsDestinationIn(this Register register, Instruction ins) { var registers = ins.Registers; var count = registers.Count; if (count == 0) return false; var info = OpCodeInfo.Get(ins.OpCode); for (var i = 0; i < count; i++) { if (registers[i] == register) { if ((info.GetUsage(i) & RegisterFlags.Destination) == RegisterFlags.Destination) return true; } } return false; }
/// <summary> /// Convert the given instruction into 1 or more dex instructions. /// </summary> internal static IEnumerable <Instruction> Convert(RL.Instruction source, RegisterMapper regMapper) { var dexIns = new Instruction(source.Code.ToDex(), source.Operand) { SequencePoint = source.SequencePoint }; var dexRegisters = dexIns.Registers; dexRegisters.AddRange(source.Registers.Select(x => regMapper[x])); if (!AllRegistersFit(dexIns) || dexIns.RequiresInvokeRange()) { // At least 1 register does not fit. // Insert a NOP first so we do not have to re-route when we insert spilling code. yield return(new Instruction(OpCodes.Nop)); } yield return(dexIns); }
/// <summary> /// Is the given register used as source in the given instruction? /// </summary> public static bool IsSourceIn(this Register r, Instruction ins) { var registers = ins.Registers; var count = registers.Count; if (count == 0) return false; var info = OpCodeInfo.Get(ins.Code.ToDex()); for (var i = 0; i < count; i++) { if (registers[i] == r) { if ((info.GetUsage(i) & RegisterFlags.Source) == RegisterFlags.Source) return true; } } return false; }
/// <summary> /// Reroute all old targets to the new targets. /// </summary> public void Reroute(Instruction oldTarget, Instruction newTarget) { if (branches != null) { var count = branches.Count; for (var i = 0; i < count; i++) { var ins = branches[i]; if (ins.Operand == oldTarget) { ins.Operand = newTarget; } else if (ins.OpCode == OpCodes.Packed_switch || ins.OpCode== OpCodes.Sparse_switch) { var targets = (ISwitchData)ins.Operand; targets.ReplaceTarget(oldTarget, newTarget); } } } if (exceptionHandlers != null) { var count = exceptionHandlers.Length; for (var i = 0; i < count; i++) { var eh = exceptionHandlers[i]; if (eh.CatchAll == oldTarget) { eh.CatchAll = newTarget; } if (eh.TryEnd == oldTarget) { eh.TryEnd = GetPrevious(oldTarget); } if (eh.TryStart == oldTarget) { eh.TryStart = newTarget; } foreach (var c in eh.Catches) { if (c.Instruction == oldTarget) c.Instruction = newTarget; } } } // Save PDB info /*if ((oldTarget.SequencePoint != null) && (newTarget.SequencePoint == null)) { newTarget.SequencePoint = oldTarget.SequencePoint; }*/ }
/// <summary> /// Replace all occurrences of from to to in the register list of the given instruction. /// </summary> private static void ReplaceRegister(Instruction ins, Register from, Register to) { if (from == null) { throw new ArgumentNullException("from"); } if (to == null) { throw new ArgumentNullException("to"); } var registers = ins.Registers; var count = registers.Count; for (var i = 0; i < count; i++) { if (registers[i] == from) { registers[i] = to; } } }
/// <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; }
/// <summary> /// Convert a normal invoke_x to invoke_x_range. /// </summary> private void ConvertInvoke(Instruction ins) { var registers = ins.Registers; var count = registers.Count; if (count == 0) { return; } // Replace all registers with their "spill" variant (if any) for (var i = 0; i < count; i++) { Register sReg; LowRegisterState state; if (TryGetLowRegister(registers[i], out sReg, out state)) { ReplaceRegister(ins, registers[i], sReg); } } // By replacing the registers, it it possible to avoid the invoke_x_range opcodes? if (!ins.RequiresInvokeRange()) { return; } if (!IsValidInvokeRange(registers)) { // Use invoke frame for (var i = 0; i < count; i++) { var type = GetType(registers[i]); block.InsertBefore(ins, CreateMove(registers[i], invokeFrame[i], type)); registers[i] = invokeFrame[i]; } } // Change opcode ins.OpCode = ins.OpCode.InvokeToRange(); }
/// <summary> /// Do all registers of the given instruction fit in the size that is available for them? /// </summary> private static bool AllRegistersFit(Instruction ins) { var registers = ins.Registers; var count = registers.Count; if (count == 0) return true; var info = OpCodeInfo.Get(ins.OpCode); for (var i = 0; i < count; i++) { var size = info.GetUsage(i) & RegisterFlags.SizeMask; switch (size) { case RegisterFlags.Bits4: if (!registers[i].IsBits4) return false; break; case RegisterFlags.Bits8: if (!registers[i].IsBits8) return false; break; } } return true; }
/// <summary> /// Replace all occurrences of from to to in the register list of the given instruction. /// </summary> private static void ReplaceRegister(Instruction ins, Register from, Register to) { if (from == null) throw new ArgumentNullException("from"); if (to == null) throw new ArgumentNullException("to"); var registers = ins.Registers; var count = registers.Count; for (var i = 0; i < count; i++) { if (registers[i] == from) { registers[i] = to; } } }
/// <summary> /// Gets all registers used in the given instruction, include "next" registers in case of wide operands. /// </summary> private HashSet<Register> GetAllUsedRegisters(Instruction ins) { var set = new HashSet<Register>(); foreach (var r in ins.Registers) { set.Add(r); if (GetType(r) == RType.Wide) { // Include next register var next = allRegisters.First(x => x.Index == r.Index + 1); set.Add(next); } } return set; }
/// <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 PackedSwitchData ExtractPackedSwitch(Instruction ins, int offset) { int baseOffset = offset; var result = new PackedSwitchData(); ProcessPseudoCode(PseudoOpCodes.Packed_switch, ref offset); int targetcount = ReadShort(ref offset); result.FirstKey = ReadInt(ref offset); for (int i = 0; i < targetcount; i++) { int target = ReadInt(ref offset); lazyInstructionsSetters.Add(() => result.Targets.Add(Lookup[ins.Offset + target])); } if (offset - baseOffset != targetcount*2 + 4) throw new MalformedException("Unexpected Packed switch blocksize"); return result; }
public string FormatAddress(Instruction ins) { var offset = ins.Offset.ToString("X3").PadLeft(4); if(Format.HasFlag(FormatOptions.ShowJumpTargets)) { if (ExceptionHandlerOffsets.Contains(ins.Offset)) return offset + "!"; if (JumpTargetOffsets.Contains(ins.Offset)) return offset + ":"; else return offset + " "; } return offset; }
public string FormatOpCode(Instruction ins) { return OpCodesNames.GetName(ins.OpCode).ToLowerInvariant().PadLeft(20) + " "; }
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(")"); }
private static string Register(Instruction i) { return string.Join(", ", i.Registers.Select(x => string.Format("r{0}", x.Index)).ToArray()); }
private void ReadvB(Instruction ins) { ins.Registers.Add(methodDefinition.Body.Registers[upper[Ip++] >> 4]); }
private void ReadvA(Instruction ins) { ins.Registers.Add(methodDefinition.Body.Registers[upper[Ip] & 0xF]); }
private SparseSwitchData ExtractSparseSwitch(Instruction ins, int offset) { int baseOffset = offset; var result = new SparseSwitchData(); ProcessPseudoCode(PseudoOpCodes.Sparse_switch, ref offset); int targetcount = ReadShort(ref offset); var keys = new int[targetcount]; for (int i = 0; i < targetcount; i++) keys[i] = ReadInt(ref offset); for (int i = 0; i < targetcount; i++) { int index = i; // used for closure int target = ReadInt(ref offset); lazyInstructionsSetters.Add(() => result.Targets.Add(keys[index], Lookup[ins.Offset + target])); } if (offset - baseOffset != targetcount*4 + 2) throw new MalformedException("Unexpected Sparse switch blocksize"); return result; }
/// <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(); } }
public string FormatOperands(Instruction ins) { return FormatOperands(ins, _methodDef.Body, Format); }
private void SetRegistersByMask(Instruction ins, int registerMask) { int registerCount = registerMask >> 20; for (int i = 0; i < registerCount; i++) ins.Registers.Add(methodDefinition.Body.Registers[(registerMask >> (i*4)) & 0xF]); }
/// <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> /// Convert a normal invoke_x to invoke_x_range. /// </summary> private void ConvertInvoke(Instruction ins) { var registers = ins.Registers; var count = registers.Count; if (count == 0) return; // Replace all registers with their "spill" variant (if any) for (var i = 0; i < count; i++) { Register sReg; LowRegisterState state; if (TryGetLowRegister(registers[i], out sReg, out state)) { ReplaceRegister(ins, registers[i], sReg); } } // By replacing the registers, it it possible to avoid the invoke_x_range opcodes? if (!ins.RequiresInvokeRange()) return; if (!IsValidInvokeRange(registers)) { // Use invoke frame for (var i = 0; i < count; i++) { var type = GetType(registers[i]); block.InsertBefore(ins, CreateMove(registers[i], invokeFrame[i], type)); registers[i] = invokeFrame[i]; } } // Change opcode ins.OpCode = ins.OpCode.InvokeToRange(); }
public string FormatInstruction(Instruction ins) { return FormatAddress(ins) + " " + FormatOpCode(ins) + " " + FormatOperands(ins); }
/// <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; }
private void ReadvBBBB(Instruction ins) { ins.Registers.Add(methodDefinition.Body.Registers[Codes[Ip++]]); }
public void ReadFrom(BinaryReader reader) { var registers = methodDefinition.Body.Registers; instructionsSize = reader.ReadUInt32(); Codes = new int[instructionsSize]; lower = new int[instructionsSize]; upper = new int[instructionsSize]; for (int i = 0; i < instructionsSize; i++) { Codes[i] = reader.ReadUInt16(); lower[i] = Codes[i] & 0xFF; upper[i] = Codes[i] >> 8; } while (Ip < instructionsSize) { int offset; int registerCount; int registerMask; var ins = new Instruction(); ins.OpCode = (OpCodes) lower[Ip]; ins.Offset = Ip; Lookup.Add(Ip, ins); methodDefinition.Body.Instructions.Add(ins); switch (ins.OpCode) { case OpCodes.Nop: case OpCodes.Return_void: Ip++; break; case OpCodes.Move_result: case OpCodes.Move_result_wide: case OpCodes.Move_result_object: case OpCodes.Move_exception: case OpCodes.Return: case OpCodes.Return_wide: case OpCodes.Return_object: case OpCodes.Monitor_enter: case OpCodes.Monitor_exit: case OpCodes.Throw: // vAA ReadvAA(ins); break; case OpCodes.Move_object: case OpCodes.Move_wide: case OpCodes.Move: case OpCodes.Array_length: case OpCodes.Neg_int: case OpCodes.Not_int: case OpCodes.Neg_long: case OpCodes.Not_long: case OpCodes.Neg_float: case OpCodes.Neg_double: case OpCodes.Int_to_long: case OpCodes.Int_to_float: case OpCodes.Int_to_double: case OpCodes.Long_to_int: case OpCodes.Long_to_float: case OpCodes.Long_to_double: case OpCodes.Float_to_int: case OpCodes.Float_to_long: case OpCodes.Float_to_double: case OpCodes.Double_to_int: case OpCodes.Double_to_long: case OpCodes.Double_to_float: case OpCodes.Int_to_byte: case OpCodes.Int_to_char: case OpCodes.Int_to_short: case OpCodes.Add_int_2addr: case OpCodes.Sub_int_2addr: case OpCodes.Mul_int_2addr: case OpCodes.Div_int_2addr: case OpCodes.Rem_int_2addr: case OpCodes.And_int_2addr: case OpCodes.Or_int_2addr: case OpCodes.Xor_int_2addr: case OpCodes.Shl_int_2addr: case OpCodes.Shr_int_2addr: case OpCodes.Ushr_int_2addr: case OpCodes.Add_long_2addr: case OpCodes.Sub_long_2addr: case OpCodes.Mul_long_2addr: case OpCodes.Div_long_2addr: case OpCodes.Rem_long_2addr: case OpCodes.And_long_2addr: case OpCodes.Or_long_2addr: case OpCodes.Xor_long_2addr: case OpCodes.Shl_long_2addr: case OpCodes.Shr_long_2addr: case OpCodes.Ushr_long_2addr: case OpCodes.Add_float_2addr: case OpCodes.Sub_float_2addr: case OpCodes.Mul_float_2addr: case OpCodes.Div_float_2addr: case OpCodes.Rem_float_2addr: case OpCodes.Add_double_2addr: case OpCodes.Sub_double_2addr: case OpCodes.Mul_double_2addr: case OpCodes.Div_double_2addr: case OpCodes.Rem_double_2addr: // vA, vB ReadvA(ins); ReadvB(ins); break; case OpCodes.Move_wide_from16: case OpCodes.Move_from16: case OpCodes.Move_object_from16: // vAA, vBBBB ReadvAA(ins); ReadvBBBB(ins); break; case OpCodes.Move_16: case OpCodes.Move_wide_16: case OpCodes.Move_object_16: // vAAAA, vBBBB ReadvAAAA(ins); ReadvBBBB(ins); break; case OpCodes.Const_4: // vA, #+B ReadvA(ins); ins.Operand = (int) ReadNibble(); break; case OpCodes.Const_16: // vAA, #+BBBB ReadvAA(ins); ins.Operand = (int) ReadShort(ref Ip); break; case OpCodes.Const_wide_16: // vAA, #+BBBB ReadvAA(ins); ins.Operand = (long) ReadShort(ref Ip); break; case OpCodes.Const: // vAA, #+BBBBBBBB ReadvAA(ins); ins.Operand = (int) ReadInt(ref Ip); break; case OpCodes.Const_wide_32: // vAA, #+BBBBBBBB ReadvAA(ins); ins.Operand = (long) ReadInt(ref Ip); break; case OpCodes.Fill_array_data: // vAA, #+BBBBBBBB ReadvAA(ins); offset = ReadInt(ref Ip); ins.Operand = ExtractArrayData(ins.Offset + offset); break; case OpCodes.Const_high16: // vAA, #+BBBB0000 ReadvAA(ins); ins.Operand = ((long) ReadShort(ref Ip)) << 16; break; case OpCodes.Const_wide: // vAA, #+BBBBBBBBBBBBBBBB ReadvAA(ins); ins.Operand = ReadLong(ref Ip); break; case OpCodes.Const_wide_high16: // vAA, #+BBBB000000000000 ReadvAA(ins); ins.Operand = ((long) ReadShort(ref Ip)) << 48; break; case OpCodes.Const_string: // vAA, string@BBBB ReadvAA(ins); ins.Operand = dexReader.GetString(ReadShort(ref Ip)); break; case OpCodes.Const_string_jumbo: // vAA, string@BBBBBBBB ReadvAA(ins); ins.Operand = dexReader.GetString(ReadInt(ref Ip)); break; case OpCodes.Const_class: case OpCodes.New_instance: case OpCodes.Check_cast: // vAA, type@BBBB ReadvAA(ins); ins.Operand = dexReader.GetTypeReference(ReadShort(ref Ip)); break; case OpCodes.Instance_of: case OpCodes.New_array: // vA, vB, type@CCCC ReadvA(ins); ReadvB(ins); ins.Operand = dexReader.GetTypeReference(ReadShort(ref Ip)); break; case OpCodes.Filled_new_array: // {vD, vE, vF, vG, vA}, type@CCCC registerMask = upper[Ip++] << 16; ins.Operand = dexReader.GetTypeReference(ReadShort(ref Ip)); registerMask |= Codes[Ip++]; SetRegistersByMask(ins, registerMask); break; case OpCodes.Filled_new_array_range: // {vCCCC .. vNNNN}, type@BBBB registerCount = upper[Ip++] << 16; ins.Operand = dexReader.GetTypeReference(ReadShort(ref Ip)); ReadvBBBB(ins); for (int i = 1; i < registerCount; i++) ins.Registers.Add(registers[i + ins.Registers[0].Index]); break; case OpCodes.Goto: // +AA offset = (sbyte) ReadSByte(ref Ip); lazyInstructionsSetters.Add(() => ins.Operand = Lookup[ins.Offset + offset]); break; case OpCodes.Goto_16: // +AAAA Ip++; offset = (short) ReadShort(ref Ip); lazyInstructionsSetters.Add(() => ins.Operand = Lookup[ins.Offset + offset]); break; case OpCodes.Goto_32: // +AAAAAAAA Ip++; offset = ReadInt(ref Ip); lazyInstructionsSetters.Add(() => ins.Operand = Lookup[ins.Offset + offset]); break; case OpCodes.Packed_switch: // vAA, +BBBBBBBB ReadvAA(ins); offset = ReadInt(ref Ip); ins.Operand = ExtractPackedSwitch(ins, ins.Offset + offset); break; case OpCodes.Sparse_switch: // vAA, +BBBBBBBB ReadvAA(ins); offset = ReadInt(ref Ip); ins.Operand = ExtractSparseSwitch(ins, ins.Offset + offset); break; case OpCodes.Cmpl_float: case OpCodes.Cmpg_float: case OpCodes.Cmpl_double: case OpCodes.Cmpg_double: case OpCodes.Cmp_long: case OpCodes.Aget: case OpCodes.Aget_wide: case OpCodes.Aget_object: case OpCodes.Aget_boolean: case OpCodes.Aget_byte: case OpCodes.Aget_char: case OpCodes.Aget_short: case OpCodes.Aput: case OpCodes.Aput_wide: case OpCodes.Aput_object: case OpCodes.Aput_boolean: case OpCodes.Aput_byte: case OpCodes.Aput_char: case OpCodes.Aput_short: case OpCodes.Add_int: case OpCodes.Sub_int: case OpCodes.Mul_int: case OpCodes.Div_int: case OpCodes.Rem_int: case OpCodes.And_int: case OpCodes.Or_int: case OpCodes.Xor_int: case OpCodes.Shl_int: case OpCodes.Shr_int: case OpCodes.Ushr_int: case OpCodes.Add_long: case OpCodes.Sub_long: case OpCodes.Mul_long: case OpCodes.Div_long: case OpCodes.Rem_long: case OpCodes.And_long: case OpCodes.Or_long: case OpCodes.Xor_long: case OpCodes.Shl_long: case OpCodes.Shr_long: case OpCodes.Ushr_long: case OpCodes.Add_float: case OpCodes.Sub_float: case OpCodes.Mul_float: case OpCodes.Div_float: case OpCodes.Rem_float: case OpCodes.Add_double: case OpCodes.Sub_double: case OpCodes.Mul_double: case OpCodes.Div_double: case OpCodes.Rem_double: // vAA, vBB, vCC ReadvAA(ins); ReadvBB(ins); ReadvCC(ins); break; case OpCodes.If_eq: case OpCodes.If_ne: case OpCodes.If_lt: case OpCodes.If_ge: case OpCodes.If_gt: case OpCodes.If_le: // vA, vB, +CCCC ReadvA(ins); ReadvB(ins); offset = (short) ReadShort(ref Ip); lazyInstructionsSetters.Add(() => ins.Operand = Lookup[ins.Offset + offset]); break; case OpCodes.If_eqz: case OpCodes.If_nez: case OpCodes.If_ltz: case OpCodes.If_gez: case OpCodes.If_gtz: case OpCodes.If_lez: // vAA, +BBBB ReadvAA(ins); offset = (short) ReadShort(ref Ip); lazyInstructionsSetters.Add(() => ins.Operand = Lookup[ins.Offset + offset]); break; case OpCodes.Iget: case OpCodes.Iget_wide: case OpCodes.Iget_object: case OpCodes.Iget_boolean: case OpCodes.Iget_byte: case OpCodes.Iget_char: case OpCodes.Iget_short: case OpCodes.Iput: case OpCodes.Iput_wide: case OpCodes.Iput_object: case OpCodes.Iput_boolean: case OpCodes.Iput_byte: case OpCodes.Iput_char: case OpCodes.Iput_short: // vA, vB, field@CCCC ReadvA(ins); ReadvB(ins); ins.Operand = dexReader.GetFieldReference(ReadShort(ref Ip)); break; case OpCodes.Sget: case OpCodes.Sget_wide: case OpCodes.Sget_object: case OpCodes.Sget_boolean: case OpCodes.Sget_byte: case OpCodes.Sget_char: case OpCodes.Sget_short: case OpCodes.Sput: case OpCodes.Sput_wide: case OpCodes.Sput_object: case OpCodes.Sput_boolean: case OpCodes.Sput_byte: case OpCodes.Sput_char: case OpCodes.Sput_short: // vAA, field@BBBB ReadvAA(ins); ins.Operand = dexReader.GetFieldReference(ReadShort(ref Ip)); break; case OpCodes.Invoke_virtual: case OpCodes.Invoke_super: case OpCodes.Invoke_direct: case OpCodes.Invoke_static: case OpCodes.Invoke_interface: // {vD, vE, vF, vG, vA}, meth@CCCC registerMask = upper[Ip++] << 16; ins.Operand = dexReader.GetMethodReference(ReadShort(ref Ip)); registerMask |= Codes[Ip++]; SetRegistersByMask(ins, registerMask); break; case OpCodes.Invoke_virtual_range: case OpCodes.Invoke_super_range: case OpCodes.Invoke_direct_range: case OpCodes.Invoke_static_range: case OpCodes.Invoke_interface_range: // {vCCCC .. vNNNN}, meth@BBBB registerCount = ReadSByte(ref Ip); ins.Operand = dexReader.GetMethodReference(ReadShort(ref Ip)); ReadvBBBB(ins); for (int i = 1; i < registerCount; i++) ins.Registers.Add(registers[i + ins.Registers[0].Index]); break; case OpCodes.Add_int_lit16: case OpCodes.Rsub_int: case OpCodes.Mul_int_lit16: case OpCodes.Div_int_lit16: case OpCodes.Rem_int_lit16: case OpCodes.And_int_lit16: case OpCodes.Or_int_lit16: case OpCodes.Xor_int_lit16: // vA, vB, #+CCCC ReadvA(ins); ReadvB(ins); ins.Operand = (int) ReadShort(ref Ip); break; case OpCodes.Add_int_lit8: case OpCodes.Rsub_int_lit8: case OpCodes.Mul_int_lit8: case OpCodes.Div_int_lit8: case OpCodes.Rem_int_lit8: case OpCodes.And_int_lit8: case OpCodes.Or_int_lit8: case OpCodes.Xor_int_lit8: case OpCodes.Shl_int_lit8: case OpCodes.Shr_int_lit8: case OpCodes.Ushr_int_lit8: // vAA, vBB, #+CC ReadvAA(ins); ReadvBB(ins); ins.Operand = ReadSByte(ref Ip); break; default: throw new NotImplementedException(string.Concat("Unknown opcode:", ins.OpCode)); } LookupLast.Add(Ip - 1, ins); } if (Ip != instructionsSize) throw new MalformedException("Instruction pointer out of range"); foreach (Action action in lazyInstructionsSetters) action(); }
private void ReadvBB(Instruction ins) { ins.Registers.Add(methodDefinition.Body.Registers[lower[Ip]]); }
private void ReadvCC(Instruction ins) { ReadvAA(ins); }