/// <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> /// Default ctor. /// </summary> internal BlockSpillCodeGenerator(BasicBlock block, RegisterMapper mapper, List<Register> lowRegisters, List<Register> invokeFrame, List<Register> allRegisters) { this.block = block; this.mapper = mapper; this.lowRegisters = new LowRegisterState[lowRegisters.Count]; LowRegisterState next = null; for (var i = lowRegisters.Count - 1; i >= 0; i--) { this.lowRegisters[i] = new LowRegisterState(lowRegisters[i], next); next = this.lowRegisters[i]; } this.invokeFrame = invokeFrame; this.allRegisters = allRegisters; }
/// <summary> /// Default ctor. /// </summary> internal BlockSpillCodeGenerator(BasicBlock block, RegisterMapper mapper, List <Register> lowRegisters, List <Register> invokeFrame, List <Register> allRegisters) { this.block = block; this.mapper = mapper; this.lowRegisters = new LowRegisterState[lowRegisters.Count]; LowRegisterState next = null; for (var i = lowRegisters.Count - 1; i >= 0; i--) { this.lowRegisters[i] = new LowRegisterState(lowRegisters[i], next); next = this.lowRegisters[i]; } this.invokeFrame = invokeFrame; this.allRegisters = allRegisters; }
/// <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> /// 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> /// Compile RL into the Dex method body. /// </summary> private void CompileToDex(DexTargetPackage targetPackage, bool generateDebugInfo, MapFile mapFile) { var dmethod = DexMethod; if (dmethod == null) throw new ArgumentException("No DexMethod set"); if ((dmethod.IsAbstract) || (dmethod.IsNative)) return; var rlBody = RLBody; if (rlBody == null) throw new ArgumentException("No RL body set"); // Ensure RL is optimized OptimizeRL(targetPackage.DexFile); // Compile to Dex var dbody = new Dot42.DexLib.Instructions.MethodBody(dmethod, 0); var dexCompiler = new DexCompiler(rlBody, dbody, InvocationFrame); regMapper = dexCompiler.Compile(); // Optimize code //dbody.UpdateInstructionOffsets(); DexOptimizer.DexOptimizer.Optimize(dbody); // Ensure correct offsets dbody.UpdateInstructionOffsets(); dmethod.Body = dbody; if (generateDebugInfo || (mapFile != null)) { // Add debug info var debugInfoBuilder = new DebugInfoBuilder(this); if (generateDebugInfo) debugInfoBuilder.CreateDebugInfo(dbody, regMapper, targetPackage); if (mapFile != null) debugInfoBuilder.AddDocumentMapping(mapFile); } }
/// <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 (!type.SetSourceFile(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; }
/// <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; }
/// <summary> /// Compile RL into Dex. /// </summary> public RegisterMapper Compile() { // Map registers var regMapper = new RegisterMapper(rlBody, frame); // Create instructions var maxOutgoingArguments = 0; var target = dexBody.Instructions; var insMap = new Dictionary <Instruction, Tuple <DexLib.Instructions.Instruction, DexLib.Instructions.Instruction> >(); foreach (var ins in rlBody.Instructions) { DexLib.Instructions.Instruction first = null; DexLib.Instructions.Instruction last = null; foreach (var dexIns in RL2Dex.Convert(ins, regMapper)) { target.Add(dexIns); if (first == null) { first = dexIns; } last = dexIns; } if ((first == null) || (last == null)) { throw new InvalidOperationException(); } insMap[ins] = Tuple.Create(first, last); // Gather statistics if (ins.Code.IsInvoke()) { maxOutgoingArguments = Math.Max(maxOutgoingArguments, ins.Registers.Count); } } // Update targets foreach (var dexIns in target) { var instruction = dexIns.Operand as Instruction; if (instruction != null) { dexIns.Operand = insMap[instruction].Item1; } if (dexIns.OpCode == OpCodes.Packed_switch) { var instructions = (Instruction[])dexIns.Operand; dexIns.Operand = new PackedSwitchData(instructions.Select(x => insMap[x].Item1)); } else if (dexIns.OpCode == OpCodes.Sparse_switch) { var targetPairs = (Tuple <int, Instruction>[])dexIns.Operand; var data = new SparseSwitchData(); foreach (var pair in targetPairs.OrderBy(x => x.Item1)) { data.Targets.Add(pair.Item1, insMap[pair.Item2].Item1); } dexIns.Operand = data; } } // Create exception handlers foreach (var handler in rlBody.Exceptions) { var dhandler = new ExceptionHandler(); dhandler.TryStart = insMap[handler.TryStart].Item1; dhandler.TryEnd = insMap[handler.TryEnd].Item2; dhandler.CatchAll = (handler.CatchAll != null) ? insMap[handler.CatchAll].Item1 : null; dexBody.Exceptions.Add(dhandler); foreach (var catchBlock in handler.Catches) { var dcatchBlock = new Catch(); dcatchBlock.Type = catchBlock.Type; dcatchBlock.Instruction = insMap[catchBlock.Instruction].Item1; dhandler.Catches.Add(dcatchBlock); } } // Add register spilling code (if needed) if (regMapper.SpillingRequired) { RegisterSpillingOptimizer.Transform(dexBody); RegisterSpilling.AddSpillingCode(dexBody, regMapper); } // Set statistics dexBody.Registers.AddRange(regMapper.All); dexBody.IncomingArguments = (ushort)regMapper.ArgumentCount; dexBody.OutgoingArguments = (ushort)maxOutgoingArguments; return(regMapper); }
/// <summary> /// Compile RL into Dex. /// </summary> public RegisterMapper Compile() { // Map registers var regMapper = new RegisterMapper(rlBody, frame); // Create instructions var maxOutgoingArguments = 0; var target = dexBody.Instructions; var insMap = new Dictionary<Instruction, Tuple<DexLib.Instructions.Instruction, DexLib.Instructions.Instruction>>(); foreach (var ins in rlBody.Instructions) { DexLib.Instructions.Instruction first = null; DexLib.Instructions.Instruction last = null; foreach (var dexIns in RL2Dex.Convert(ins, regMapper)) { target.Add(dexIns); if (first == null) first = dexIns; last = dexIns; } if ((first == null) || (last == null)) throw new InvalidOperationException(); insMap[ins] = Tuple.Create(first, last); // Gather statistics if (ins.Code.IsInvoke()) { maxOutgoingArguments = Math.Max(maxOutgoingArguments, ins.Registers.Count); } } // Update targets foreach (var dexIns in target) { var instruction = dexIns.Operand as Instruction; if (instruction != null) { dexIns.Operand = insMap[instruction].Item1; } if (dexIns.OpCode == OpCodes.Packed_switch) { var instructions = (Instruction[])dexIns.Operand; dexIns.Operand = new PackedSwitchData(instructions.Select(x => insMap[x].Item1)); } else if (dexIns.OpCode == OpCodes.Sparse_switch) { var targetPairs = (Tuple<int, Instruction>[])dexIns.Operand; var data = new SparseSwitchData(); foreach (var pair in targetPairs.OrderBy(x => x.Item1)) { data.Targets.Add(pair.Item1, insMap[pair.Item2].Item1); } dexIns.Operand = data; } } // Create exception handlers foreach (var handler in rlBody.Exceptions) { var dhandler = new ExceptionHandler(); dhandler.TryStart = insMap[handler.TryStart].Item1; dhandler.TryEnd = insMap[handler.TryEnd].Item2; dhandler.CatchAll = (handler.CatchAll != null) ? insMap[handler.CatchAll].Item1 : null; dexBody.Exceptions.Add(dhandler); foreach (var catchBlock in handler.Catches) { var dcatchBlock = new Catch(); dcatchBlock.Type = catchBlock.Type; dcatchBlock.Instruction = insMap[catchBlock.Instruction].Item1; dhandler.Catches.Add(dcatchBlock); } } // Add register spilling code (if needed) if (regMapper.SpillingRequired) { RegisterSpillingOptimizer.Transform(dexBody); RegisterSpilling.AddSpillingCode(dexBody, regMapper); } // Set statistics dexBody.Registers.AddRange(regMapper.All); dexBody.IncomingArguments = (ushort) regMapper.ArgumentCount; dexBody.OutgoingArguments = (ushort) maxOutgoingArguments; return regMapper; }
/// <summary> /// Compile RL into the Dex method body. /// </summary> private void CompileToDex(DexTargetPackage targetPackage, bool generateDebugInfo, MapFile mapFile) { var dmethod = DexMethod; if (dmethod == null) throw new ArgumentException("No DexMethod set"); if ((dmethod.IsAbstract) || (dmethod.IsNative)) return; var rlBody = RLBody; if (rlBody == null && dmethod.Body != null) // already satisfied from the cache? return; if (rlBody == null) throw new ArgumentException(string.Format("internal compiler error: No RL body set on method '{2}'.'{3}' => '{0}'.'{1}'", dmethod.Owner.Name, dmethod.Name, method == null ? null : method.DeclaringType.FullName, method == null ? null : method.Name)); // Ensure RL is optimized OptimizeRL(targetPackage.DexFile); // Compile to Dex var dbody = new Dot42.DexLib.Instructions.MethodBody(dmethod, 0); var dexCompiler = new DexCompiler(rlBody, dbody, InvocationFrame); regMapper = dexCompiler.Compile(); // Optimize code //dbody.UpdateInstructionOffsets(); DexOptimizer.DexOptimizer.Optimize(dbody); // Ensure correct offsets dbody.UpdateInstructionOffsets(); dmethod.Body = dbody; if (generateDebugInfo || (mapFile != null)) { // Add debug info var debugInfoBuilder = new DebugInfoBuilder(this); if (generateDebugInfo) debugInfoBuilder.CreateDebugInfo(dbody, regMapper, targetPackage); if (mapFile != null && dmethod.MapFileId != 0) debugInfoBuilder.AddDocumentMapping(mapFile); } }