PageRenderTime 77ms CodeModel.GetById 38ms RepoModel.GetById 0ms app.codeStats 0ms

/src/org/jruby/interpreter/Interpreter.java

https://github.com/brasten/jruby
Java | 299 lines | 177 code | 25 blank | 97 comment | 33 complexity | 551ef25018eb32843e8d835c73e5f93d MD5 | raw file
  1. package org.jruby.interpreter;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. import java.util.Date;
  6. import org.jruby.Ruby;
  7. import org.jruby.RubyModule;
  8. import org.jruby.ast.Node;
  9. import org.jruby.ast.RootNode;
  10. import org.jruby.compiler.ir.IRBuilder;
  11. import org.jruby.compiler.ir.IRMethod;
  12. import org.jruby.compiler.ir.IRScope;
  13. import org.jruby.compiler.ir.IRScript;
  14. import org.jruby.compiler.ir.compiler_pass.AddFrameInstructions;
  15. import org.jruby.compiler.ir.compiler_pass.CFG_Builder;
  16. import org.jruby.compiler.ir.compiler_pass.IR_Printer;
  17. import org.jruby.compiler.ir.compiler_pass.LiveVariableAnalysis;
  18. import org.jruby.compiler.ir.compiler_pass.opts.DeadCodeElimination;
  19. import org.jruby.compiler.ir.compiler_pass.opts.LocalOptimizationPass;
  20. import org.jruby.compiler.ir.compiler_pass.CallSplitter;
  21. import org.jruby.compiler.ir.instructions.BranchInstr;
  22. import org.jruby.compiler.ir.instructions.CallInstr;
  23. import org.jruby.compiler.ir.instructions.DefineClassMethodInstr;
  24. import org.jruby.compiler.ir.instructions.DefineInstanceMethodInstr;
  25. import org.jruby.compiler.ir.instructions.Instr;
  26. import org.jruby.compiler.ir.instructions.JumpInstr;
  27. import org.jruby.compiler.ir.operands.Label;
  28. import org.jruby.compiler.ir.operands.Operand;
  29. import org.jruby.compiler.ir.representations.BasicBlock;
  30. import org.jruby.compiler.ir.representations.CFG;
  31. import org.jruby.internal.runtime.methods.InterpretedIRMethod;
  32. import org.jruby.runtime.ThreadContext;
  33. import org.jruby.runtime.builtin.IRubyObject;
  34. import org.jruby.util.ByteList;
  35. public class Interpreter {
  36. public static IRubyObject interpret(Ruby runtime, Node rootNode, IRubyObject self) {
  37. IRScope scope = new IRBuilder().buildRoot((RootNode) rootNode);
  38. scope.runCompilerPass(new LocalOptimizationPass());
  39. scope.runCompilerPass(new CFG_Builder());
  40. scope.runCompilerPass(new LiveVariableAnalysis());
  41. scope.runCompilerPass(new DeadCodeElimination());
  42. scope.runCompilerPass(new AddFrameInstructions());
  43. scope.runCompilerPass(new CallSplitter());
  44. return Interpreter.interpretTop(runtime, scope, self);
  45. }
  46. public static void main(String[] args) {
  47. Ruby runtime = Ruby.getGlobalRuntime();
  48. boolean isDebug = args.length > 0 && args[0].equals("-debug");
  49. int i = isDebug ? 1 : 0;
  50. boolean isCommandLineScript = args.length > i && args[i].equals("-e");
  51. i += (isCommandLineScript ? 1 : 0);
  52. while (i < args.length) {
  53. long t1 = new Date().getTime();
  54. Node ast = buildAST(runtime, isCommandLineScript, args[i]);
  55. long t2 = new Date().getTime();
  56. IRScope scope = new IRBuilder().buildRoot((RootNode) ast);
  57. long t3 = new Date().getTime();
  58. if (isDebug) {
  59. System.out.println("## Before local optimization pass");
  60. scope.runCompilerPass(new IR_Printer());
  61. }
  62. scope.runCompilerPass(new LocalOptimizationPass());
  63. long t4 = new Date().getTime();
  64. if (isDebug) {
  65. System.out.println("## After local optimization");
  66. scope.runCompilerPass(new IR_Printer());
  67. }
  68. scope.runCompilerPass(new CFG_Builder());
  69. long t5 = new Date().getTime();
  70. if (isDebug) System.out.println("## After dead code elimination");
  71. scope.runCompilerPass(new LiveVariableAnalysis());
  72. long t7 = new Date().getTime();
  73. scope.runCompilerPass(new DeadCodeElimination());
  74. long t8 = new Date().getTime();
  75. scope.runCompilerPass(new AddFrameInstructions());
  76. long t9 = new Date().getTime();
  77. if (isDebug) scope.runCompilerPass(new IR_Printer());
  78. Interpreter.interpretTop(runtime, scope, runtime.getTopSelf());
  79. long t10 = new Date().getTime();
  80. System.out.println("Time to build AST : " + (t2 - t1));
  81. System.out.println("Time to build IR : " + (t3 - t2));
  82. System.out.println("Time to run local opts : " + (t4 - t3));
  83. System.out.println("Time to run build cfg : " + (t5 - t4));
  84. System.out.println("Time to run lva : " + (t7 - t5));
  85. System.out.println("Time to run dead code elim: " + (t8 - t7));
  86. System.out.println("Time to add frame instrs : " + (t9 - t8));
  87. System.out.println("Time to interpret : " + (t10 - t9));
  88. i++;
  89. }
  90. }
  91. public static Node buildAST(Ruby runtime, boolean isCommandLineScript, String arg) {
  92. // inline script
  93. if (isCommandLineScript) return runtime.parse(ByteList.create(arg), "-e", null, 0, false);
  94. // from file
  95. try {
  96. System.out.println("-- processing " + arg + " --");
  97. return runtime.parseFile(new FileInputStream(new File(arg)), arg, null, 0);
  98. } catch (IOException ioe) {
  99. throw new RuntimeException(ioe);
  100. }
  101. }
  102. private static int interpInstrsCount = 0;
  103. public static IRubyObject interpretTop(Ruby runtime, IRScope scope, IRubyObject self) {
  104. assert scope instanceof IRScript : "Must be an IRScript scope at Top!!!";
  105. IRScript root = (IRScript) scope;
  106. // We get the live object ball rolling here. This give a valid value for the top
  107. // of this lexical tree. All new scope can then retrieve and set based on lexical
  108. // parent.
  109. if (root.getStaticScope().getModule() == null) { // If an eval this may already be setup.
  110. root.getStaticScope().setModule(runtime.getObject());
  111. }
  112. IRMethod rootMethod = root.getRootClass().getRootMethod();
  113. RubyModule metaclass = self.getMetaClass();
  114. InterpretedIRMethod method = new InterpretedIRMethod(rootMethod, metaclass);
  115. IRubyObject rv = method.call(runtime.getCurrentContext(), self, metaclass, "", new IRubyObject[]{});
  116. System.out.println("-- Interpreted " + interpInstrsCount + " instructions");
  117. return rv;
  118. }
  119. /*
  120. public void interpretTop(IR_Scope scope) {
  121. IRubyObject self = runtime.getTopSelf();
  122. if (scope instanceof IR_Script) {
  123. interpretMethod(self, ((IR_Script) scope).getRootClass().getRootMethod());
  124. } else {
  125. System.out.println("BONED");
  126. }
  127. }
  128. public void interpretMethod(IRubyObject self, IRMethod method) {
  129. if (method == null) {
  130. System.out.println("Interpreting null method?");
  131. return;
  132. }
  133. System.out.print(method + "(");
  134. Operand operands[] = method.getCallArgs();
  135. for (int i = 0; i < operands.length; i++) {
  136. System.out.print(operands[i] + ", ");
  137. }
  138. System.out.println("EOP)");
  139. // Dummy start and end are canonical entry and exit points for a method/closures
  140. // we always getFallThroughBB(previous) to walk through unless we encounter explicit jump
  141. // IR_Scope
  142. // getLexicalScope <--- previous StaticScope equivalent
  143. // ThreadContext, self, receiver{, arg{, arg{, arg{, arg}}}}
  144. // Construct primitive array as simple store for temporary variables in method and pass along
  145. CFG cfg = method.getCFG();
  146. for (BasicBlock basicBlock : cfg.getNodes()) {
  147. System.out.println("NEW BB");
  148. for (Instr i : basicBlock.getInstrs()) {
  149. // .. interpret i ..
  150. if (i instanceof BranchInstr) {
  151. System.out.println("In branch");
  152. BranchInstr branch = (BranchInstr) i;
  153. boolean taken = false; // .. the interpreter will tell you whether the branch was taken or not ...
  154. if (taken) {
  155. basicBlock = cfg.getTargetBB(branch.getJumpTarget());
  156. } else {
  157. basicBlock = cfg.getFallThroughBB(basicBlock);
  158. }
  159. } else if (i instanceof JumpInstr) {
  160. System.out.println("In jump");
  161. JumpInstr jump = (JumpInstr) i;
  162. basicBlock = cfg.getTargetBB(jump.getJumpTarget());
  163. } else if (i instanceof CallInstr) {
  164. CallInstr callInstruction = (CallInstr) i;
  165. System.out.println("Call: " + callInstruction);
  166. // Does not need to be recursive...except for scope handling
  167. interpretMethod(self, callInstruction.getTargetMethod());
  168. } else if (i instanceof DefineClassMethodInstr) {
  169. if (((DefineClassMethodInstr) i).method.getCFG() != cfg) {
  170. System.out.println("def class method");
  171. // createClassMethod(self, (DefineClassMethodInstr) i);
  172. }
  173. } else if (i instanceof DefineInstanceMethodInstr) {
  174. System.out.println("def instance method");
  175. // createInstanceMethod(self, (DefineInstanceMethodInstr) i);
  176. } else {
  177. System.out.println("NOT HANDLING: " + i + ", (" + i.getClass() + ")");
  178. }
  179. //... handle returns ..
  180. }
  181. if (basicBlock == null) {
  182. //.. you are done with this cfg /method ..
  183. //.. pop call stack, etc ..
  184. }
  185. }
  186. }
  187. */
  188. public static IRubyObject interpret(ThreadContext context, CFG cfg, InterpreterContext interp) {
  189. try {
  190. BasicBlock basicBlock = cfg.getEntryBB();
  191. while (basicBlock != null) {
  192. Label jumpTarget = null;
  193. Instr prev = null;
  194. for (Instr instruction : basicBlock.getInstrs()) {
  195. // System.out.println("EXEC'ing: " + instruction);
  196. interpInstrsCount++;
  197. jumpTarget = instruction.interpret(interp, (IRubyObject) interp.getSelf());
  198. }
  199. // Explicit jump or implicit fall-through to next bb
  200. basicBlock = (jumpTarget == null) ? cfg.getFallThroughBB(basicBlock) : cfg.getTargetBB(jumpTarget);
  201. }
  202. return (IRubyObject) interp.getReturnValue();
  203. } finally {
  204. if (interp.getFrame() != null) {
  205. context.popFrame();
  206. interp.setFrame(null);
  207. }
  208. if (interp.hasAllocatedDynamicScope())
  209. context.postMethodScopeOnly();
  210. }
  211. }
  212. public static IRubyObject interpret_with_inline(ThreadContext context, CFG cfg, InterpreterContext interp) {
  213. try {
  214. BasicBlock basicBlock = cfg.getEntryBB();
  215. Instr skipTillInstr = null;
  216. while (basicBlock != null) {
  217. Label jumpTarget = null;
  218. Instr prev = null;
  219. for (Instr instruction : basicBlock.getInstrs()) {
  220. // Skip till we come back to previous execution point
  221. if (skipTillInstr != null && instruction != skipTillInstr)
  222. continue;
  223. skipTillInstr = null;
  224. // System.out.println("EXEC'ing: " + instruction);
  225. interpInstrsCount++;
  226. try {
  227. jumpTarget = instruction.interpret(interp, (IRubyObject) interp.getSelf());
  228. }
  229. catch (InlineMethodHint ih) {
  230. if (ih.inlineableMethod.getName() == "array_each") {
  231. System.out.println("Got inline method hint for: " + ih.inlineableMethod.getFullyQualifiedName() + ". inlining!");
  232. cfg.inlineMethod(ih.inlineableMethod, basicBlock, (CallInstr)instruction);
  233. interp.updateRenamedVariablesCount(cfg.getScope().getRenamedVariableSize());
  234. skipTillInstr = prev;
  235. /*
  236. System.out.println("--------------------");
  237. System.out.println("\nGraph:\n" + cfg.getGraph().toString());
  238. System.out.println("\nInstructions:\n" + cfg.toStringInstrs());
  239. System.out.println("--------------------");
  240. */
  241. break;
  242. } else {
  243. jumpTarget = instruction.interpret(interp, (IRubyObject) interp.getSelf());
  244. }
  245. }
  246. prev = instruction;
  247. }
  248. // Explicit jump or implicit fall-through to next bb for the situation when we haven't inlined
  249. if (skipTillInstr == null)
  250. basicBlock = (jumpTarget == null) ? cfg.getFallThroughBB(basicBlock) : cfg.getTargetBB(jumpTarget);
  251. }
  252. return (IRubyObject) interp.getReturnValue();
  253. } finally {
  254. if (interp.getFrame() != null) {
  255. context.popFrame();
  256. interp.setFrame(null);
  257. }
  258. if (interp.hasAllocatedDynamicScope())
  259. context.postMethodScopeOnly();
  260. }
  261. }
  262. }