public InterpreterContext ensureInstrsReady() {
    if (IRRuntimeHelpers.isDebug() && !displayedCFG) {
      LOG.info(
          "Executing '"
              + closure
              + "' (pushScope="
              + pushScope
              + ", reuseParentScope="
              + reuseParentScope);
      LOG.info(closure.debugOutput());
      displayedCFG = true;
    }

    if (interpreterContext == null) {
      if (Options.IR_PRINT.load()) {
        ByteArrayOutputStream baos = IRDumper.printIR(closure, false);

        LOG.info(
            "Printing simple IR for " + closure.getName(), "\n" + new String(baos.toByteArray()));
      }

      interpreterContext = closure.getInterpreterContext();
      fullInterpreterContext = interpreterContext;
    }
    return interpreterContext;
  }
Exemple #2
0
  private static void loadJRubyProperties(File dotfile) {
    FileInputStream fis = null;

    try {
      // update system properties with long form jruby properties from .jrubyrc
      Properties sysProps = System.getProperties();
      Properties newProps = new Properties();
      // load properties and re-set as jruby.*
      fis = new FileInputStream(dotfile);
      newProps.load(fis);
      for (Map.Entry entry : newProps.entrySet()) {
        sysProps.put("jruby." + entry.getKey(), entry.getValue());
      }
    } catch (IOException ioe) {
      LOG.debug("exception loading " + dotfile, ioe);
    } catch (SecurityException se) {
      LOG.debug("exception loading " + dotfile, se);
    } finally {
      if (fis != null)
        try {
          fis.close();
        } catch (Exception e) {
        }
    }
  }
Exemple #3
0
 protected void doDebug() {
   // FIXME: This is printing out IRScope CFG but JIT may be active and it might not reflect
   // currently executing.  Move into JIT and into interp since they will be getting CFG from
   // different sources
   // FIXME: This is only printing out CFG once.  If we keep applying more passes then we
   // will want to print out after those new passes.
   ensureInstrsReady();
   LOG.info("Executing '" + method.getName() + "'");
   if (!displayedCFG) {
     LOG.info(method.debugOutput());
     displayedCFG = true;
   }
 }
  private Instr[] prepareInstructionsForInterpretation() {
    checkRelinearization();

    if (linearizedInstrArray != null) return linearizedInstrArray; // Already prepared

    try {
      buildLinearization(); // FIXME: compiler passes should have done this
      depends(linearization());
    } catch (RuntimeException e) {
      LOG.error("Error linearizing cfg: ", e);
      CFG c = cfg();
      LOG.error("\nGraph:\n" + c.toStringGraph());
      LOG.error("\nInstructions:\n" + c.toStringInstrs());
      throw e;
    }

    // Set up IPCs
    HashMap<Label, Integer> labelIPCMap = new HashMap<Label, Integer>();
    List<Instr> newInstrs = new ArrayList<Instr>();
    int ipc = 0;
    for (BasicBlock b : linearizedBBList) {
      labelIPCMap.put(b.getLabel(), ipc);
      List<Instr> bbInstrs = b.getInstrs();
      int bbInstrsLength = bbInstrs.size();
      for (int i = 0; i < bbInstrsLength; i++) {
        Instr instr = bbInstrs.get(i);

        if (instr instanceof Specializeable) {
          instr = ((Specializeable) instr).specializeForInterpretation();
          bbInstrs.set(i, instr);
        }

        if (!(instr instanceof ReceiveSelfInstr)) {
          newInstrs.add(instr);
          ipc++;
        }
      }
    }

    // Set up label PCs
    setupLabelPCs(labelIPCMap);

    // Exit BB ipc
    cfg().getExitBB().getLabel().setTargetPC(ipc + 1);

    linearizedInstrArray = newInstrs.toArray(new Instr[newInstrs.size()]);
    return linearizedInstrArray;
  }
Exemple #5
0
  public IRubyObject call(
      ThreadContext context,
      IRubyObject self,
      RubyModule clazz,
      DynamicScope evalScope,
      Block block,
      String backtraceName) {
    if (IRRuntimeHelpers.isDebug()) {
      LOG.info("Graph:\n" + cfg().toStringGraph());
      LOG.info("CFG:\n" + cfg().toStringInstrs());
    }

    // FIXME: Do not push new empty arg array in every time
    return Interpreter.INTERPRET_EVAL(
        context, self, this, clazz, new IRubyObject[] {}, backtraceName, block, null);
  }
  void finish(boolean close) throws BadDescriptorException, IOException {
    synchronized (refCounter) {
      // if refcount is at or below zero, we're no longer valid
      if (refCounter.get() <= 0) {
        throw new BadDescriptorException();
      }

      // if channel is already closed, we're no longer valid
      if (!channel.isOpen()) {
        throw new BadDescriptorException();
      }

      // otherwise decrement and possibly close as normal
      if (close) {
        int count = refCounter.decrementAndGet();

        if (DEBUG) LOG.info("Descriptor for fileno {} refs: {}", internalFileno, count);

        if (count <= 0) {
          // if we're the last referrer, close the channel
          try {
            channel.close();
          } finally {
            unregisterDescriptor(internalFileno);
          }
        }
      }
    }
  }
  /** Run any necessary passes to get the IR ready for compilation */
  public Tuple<Instr[], Map<Integer, Label[]>> prepareForCompilation() {
    // Build CFG and run compiler passes, if necessary
    if (getCFG() == null) runCompilerPasses();

    // Add this always since we dont re-JIT a previously
    // JIT-ted closure.  But, check if there are other
    // smarts available to us and eliminate adding this
    // code to every closure there is.
    //
    // Add a global ensure block to catch uncaught breaks
    // and throw a LocalJumpError.
    if (this instanceof IRClosure && ((IRClosure) this).addGEBForUncaughtBreaks()) {
      this.relinearizeCFG = true;
    }

    try {
      buildLinearization(); // FIXME: compiler passes should have done this
      depends(linearization());
    } catch (RuntimeException e) {
      LOG.error("Error linearizing cfg: ", e);
      CFG c = cfg();
      LOG.error("\nGraph:\n" + c.toStringGraph());
      LOG.error("\nInstructions:\n" + c.toStringInstrs());
      throw e;
    }

    // Set up IPCs
    // FIXME: Would be nice to collapse duplicate labels; for now, using Label[]
    HashMap<Integer, Label[]> ipcLabelMap = new HashMap<Integer, Label[]>();
    List<Instr> newInstrs = new ArrayList<Instr>();
    int ipc = 0;
    for (BasicBlock b : linearizedBBList) {
      Label l = b.getLabel();
      ipcLabelMap.put(ipc, catLabels(ipcLabelMap.get(ipc), l));
      for (Instr i : b.getInstrs()) {
        if (!(i instanceof ReceiveSelfInstr)) {
          newInstrs.add(i);
          ipc++;
        }
      }
    }

    return new Tuple<Instr[], Map<Integer, Label[]>>(
        newInstrs.toArray(new Instr[newInstrs.size()]), ipcLabelMap);
  }
Exemple #8
0
 public void setFlag(Node node, Flag modifier) {
   if (dump) {
     LOG.info(
         "[ASTInspector] "
             + name
             + "\n\tset flag "
             + modifier
             + " because of "
             + node.getNodeType()
             + " at "
             + node.getPosition());
   }
   flags |= modifier.flag;
 }
Exemple #9
0
  private void finish(boolean close) throws BadDescriptorException, IOException {
    try {
      flushWrite();

      if (DEBUG) LOG.info("Descriptor for fileno {} closed by stream", descriptor.getFileno());
    } finally {
      buffer = EMPTY_BUFFER;

      // clear runtime so it doesn't get stuck in memory (JRUBY-2933)
      runtime = null;

      // finish descriptor
      descriptor.finish(close);
    }
  }
  /**
   * Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open
   * channel but with a specified fileno.
   *
   * @param fileno The fileno to use for the new descriptor
   * @return A duplicate ChannelDescriptor based on this one
   */
  public ChannelDescriptor dup2(int fileno) {
    synchronized (refCounter) {
      refCounter.incrementAndGet();

      if (DEBUG) LOG.info("Reopen fileno {}, refs now: {}", fileno, refCounter.get());

      return new ChannelDescriptor(
          channel,
          fileno,
          originalModes,
          fileDescriptor,
          refCounter,
          canBeSeekable,
          isInAppendMode);
    }
  }
Exemple #11
0
  /** Ensure close (especially flush) when we're finished with. */
  @Override
  public void finalize() throws Throwable {
    super.finalize();

    if (closedExplicitly) return;

    if (DEBUG) {
      LOG.info("finalize() for not explicitly closed stream");
    }

    // FIXME: I got a bunch of NPEs when I didn't check for nulls here...HOW?!
    if (descriptor != null && descriptor.isOpen()) {
      // tidy up
      finish(autoclose);
    }
  }
  // SSS FIXME: Extremely inefficient
  public int getEnsurerPC(Instr excInstr) {
    depends(cfg());

    for (BasicBlock b : linearizedBBList) {
      for (Instr i : b.getInstrs()) {
        if (i == excInstr) {
          BasicBlock ensurerBB = cfg.getEnsurerBBFor(b);
          return (ensurerBB == null) ? -1 : ensurerBB.getLabel().getTargetPC();
        }
      }
    }

    // SSS FIXME: Cannot happen! Throw runtime exception
    LOG.error("Fell through looking for ensurer ipc for " + excInstr);
    return -1;
  }
  static void log(DefaultMethod method, String name, String message, String... reason) {
    String className = method.getImplementationClass().getBaseName();

    if (className == null) className = "<anon class>";

    StringBuilder builder =
        new StringBuilder(message + ":" + className + "." + name + " at " + method.getPosition());

    if (reason.length > 0) {
      builder.append(" because of: \"");
      for (int i = 0; i < reason.length; i++) {
        builder.append(reason[i]);
      }
      builder.append('"');
    }

    LOG.info(builder.toString());
  }
  /**
   * Mimics the POSIX dup2(2) function, returning a new descriptor that references the same open
   * channel but with a specified fileno. This differs from the fileno version by making the target
   * descriptor into a new reference to the current descriptor's channel, closing what it originally
   * pointed to and preserving its original fileno.
   *
   * @param other the descriptor to dup this one into
   */
  public void dup2Into(ChannelDescriptor other) throws BadDescriptorException, IOException {
    synchronized (refCounter) {
      refCounter.incrementAndGet();

      if (DEBUG) LOG.info("Reopen fileno {}, refs now: {}", internalFileno, refCounter.get());

      other.close();

      other.channel = channel;
      other.originalModes = originalModes;
      other.fileDescriptor = fileDescriptor;
      other.refCounter = refCounter;
      other.canBeSeekable = canBeSeekable;
      other.readableChannel = readableChannel;
      other.writableChannel = writableChannel;
      other.seekableChannel = seekableChannel;
    }
  }
Exemple #15
0
  public static IRubyObject interpret(Ruby runtime, Node rootNode, IRubyObject self) {
    if (runtime.is1_9()) IRBuilder.setRubyVersion("1.9");

    IRScriptBody root =
        (IRScriptBody)
            IRBuilder.createIRBuilder(runtime, runtime.getIRManager())
                .buildRoot((RootNode) rootNode);

    // We get the live object ball rolling here.  This give a valid value for the top
    // of this lexical tree.  All new scope can then retrieve and set based on lexical parent.
    if (root.getStaticScope().getModule() == null) { // If an eval this may already be setup.
      root.getStaticScope().setModule(runtime.getObject());
    }

    RubyModule currModule = root.getStaticScope().getModule();

    // Scope state for root?
    IRStaticScopeFactory.newIRLocalScope(null).setModule(currModule);
    ThreadContext context = runtime.getCurrentContext();

    try {
      runBeginEndBlocks(
          root.getBeginBlocks(), context, self, null); // FIXME: No temp vars yet...not needed?
      InterpretedIRMethod method = new InterpretedIRMethod(root, currModule);
      IRubyObject rv = method.call(context, self, currModule, "(root)", IRubyObject.NULL_ARRAY);
      runBeginEndBlocks(
          root.getEndBlocks(), context, self, null); // FIXME: No temp vars yet...not needed?
      if ((IRRuntimeHelpers.isDebug() || IRRuntimeHelpers.inProfileMode())
          && interpInstrsCount > 10000) {
        LOG.info("-- Interpreted instructions: {}", interpInstrsCount);
        /*
        for (Operation o: opStats.keySet()) {
            System.out.println(o + " = " + opStats.get(o).count);
        }
        */
      }
      return rv;
    } catch (IRBreakJump bj) {
      throw IRException.BREAK_LocalJumpError.getException(context.runtime);
    }
  }
Exemple #16
0
  public static IRubyObject interpretTop(Ruby runtime, IRScope scope, IRubyObject self) {
    assert scope instanceof IRScript : "Must be an IRScript scope at Top!!!";

    IRScript root = (IRScript) scope;

    // We get the live object ball rolling here.  This give a valid value for the top
    // of this lexical tree.  All new scope can then retrieve and set based on lexical parent.
    if (root.getStaticScope().getModule() == null) { // If an eval this may already be setup.
      root.getStaticScope().setModule(runtime.getObject());
    }

    RubyModule currModule = root.getStaticScope().getModule();
    IRMethod rootMethod = root.getRootClass().getRootMethod();
    InterpretedIRMethod method = new InterpretedIRMethod(rootMethod, currModule);
    ThreadContext context = runtime.getCurrentContext();

    IRubyObject rv = method.call(context, self, currModule, "", IRubyObject.NULL_ARRAY);
    if (isDebug()) LOG.debug("-- Interpreted instructions: {}", interpInstrsCount);

    return rv;
  }
 public static void saveToCodeCache(
     Ruby ruby, byte[] bytecode, String packageName, File cachedClassFile) {
   String codeCache = RubyInstanceConfig.JIT_CODE_CACHE;
   File codeCacheDir = new File(codeCache);
   if (!codeCacheDir.exists()) {
     ruby.getWarnings().warn("jruby.jit.codeCache directory " + codeCacheDir + " does not exist");
   } else if (!codeCacheDir.isDirectory()) {
     ruby.getWarnings()
         .warn("jruby.jit.codeCache directory " + codeCacheDir + " is not a directory");
   } else if (!codeCacheDir.canWrite()) {
     ruby.getWarnings().warn("jruby.jit.codeCache directory " + codeCacheDir + " is not writable");
   } else {
     if (!new File(codeCache, packageName).isDirectory()) {
       boolean createdDirs = new File(codeCache, packageName).mkdirs();
       if (!createdDirs) {
         ruby.getWarnings()
             .warn("could not create JIT cache dir: " + new File(codeCache, packageName));
       }
     }
     // write to code cache
     FileOutputStream fos = null;
     try {
       if (RubyInstanceConfig.JIT_LOADING_DEBUG)
         LOG.info("writing jitted code to to " + cachedClassFile);
       fos = new FileOutputStream(cachedClassFile);
       fos.write(bytecode);
     } catch (Exception e) {
       e.printStackTrace();
       // ignore
     } finally {
       try {
         fos.close();
       } catch (Exception e) {
       }
     }
   }
 }
Exemple #18
0
  public static void ivarSet(VariableSite site, IRubyObject self, IRubyObject value)
      throws Throwable {
    RubyClass realClass = self.getMetaClass().getRealClass();
    VariableAccessor accessor = realClass.getVariableAccessorForWrite(site.name());

    // set variable value and fold by returning value
    MethodHandle setValue;
    boolean direct = false;

    if (accessor instanceof FieldVariableAccessor) {
      direct = true;
      int offset = ((FieldVariableAccessor) accessor).getOffset();
      Class cls = REIFIED_OBJECT_CLASSES[offset];
      setValue = findStatic(cls, "setVariableChecked", methodType(void.class, cls, Object.class));
      setValue =
          explicitCastArguments(
              setValue, methodType(void.class, IRubyObject.class, IRubyObject.class));
    } else {
      setValue =
          findStatic(
              accessor.getClass(),
              "setVariableChecked",
              methodType(
                  void.class, RubyBasicObject.class, RubyClass.class, int.class, Object.class));
      setValue =
          explicitCastArguments(
              setValue,
              methodType(
                  void.class, IRubyObject.class, RubyClass.class, int.class, IRubyObject.class));
      setValue = insertArguments(setValue, 1, realClass, accessor.getIndex());
    }

    // prepare fallback
    MethodHandle fallback = null;
    if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
        LOG.info(
            site.name()
                + "\tset on type "
                + self.getMetaClass().id
                + " failed (polymorphic)"
                + extractSourceInfo(site));
      fallback =
          findStatic(
              Bootstrap.class,
              "ivarSetFail",
              methodType(void.class, VariableSite.class, IRubyObject.class, IRubyObject.class));
      fallback = fallback.bindTo(site);
      site.setTarget(fallback);
      fallback.invokeExact(self, value);
    } else {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
        if (direct) {
          LOG.info(
              site.name()
                  + "\tset field on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        } else {
          LOG.info(
              site.name()
                  + "\tset on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        }
      }
      fallback = site.getTarget();
      site.incrementChainCount();
    }

    // prepare test
    MethodHandle test =
        findStatic(
            InvocationLinker.class,
            "testRealClass",
            methodType(boolean.class, int.class, IRubyObject.class));
    test = insertArguments(test, 0, accessor.getClassId());
    test = dropArguments(test, 1, IRubyObject.class);

    setValue = guardWithTest(test, setValue, fallback);

    if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
      LOG.info(
          site.name()
              + "\tset on class "
              + self.getMetaClass().id
              + " bound directly"
              + extractSourceInfo(site));
    site.setTarget(setValue);

    setValue.invokeExact(self, value);
  }
Exemple #19
0
  public static IRubyObject ivarGet(VariableSite site, IRubyObject self) throws Throwable {
    RubyClass realClass = self.getMetaClass().getRealClass();
    VariableAccessor accessor = realClass.getVariableAccessorForRead(site.name());

    // produce nil if the variable has not been initialize
    MethodHandle nullToNil =
        findStatic(
            Helpers.class,
            "nullToNil",
            methodType(IRubyObject.class, IRubyObject.class, IRubyObject.class));
    nullToNil = insertArguments(nullToNil, 1, self.getRuntime().getNil());
    nullToNil = explicitCastArguments(nullToNil, methodType(IRubyObject.class, Object.class));

    // get variable value and filter with nullToNil
    MethodHandle getValue;
    boolean direct = false;

    if (accessor instanceof FieldVariableAccessor) {
      direct = true;
      int offset = ((FieldVariableAccessor) accessor).getOffset();
      Class cls = REIFIED_OBJECT_CLASSES[offset];
      getValue = lookup().findGetter(cls, "var" + offset, Object.class);
      getValue = explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class));
    } else {
      getValue =
          findStatic(
              VariableAccessor.class,
              "getVariable",
              methodType(Object.class, RubyBasicObject.class, int.class));
      getValue =
          explicitCastArguments(getValue, methodType(Object.class, IRubyObject.class, int.class));
      getValue = insertArguments(getValue, 1, accessor.getIndex());
    }

    getValue = filterReturnValue(getValue, nullToNil);

    // prepare fallback
    MethodHandle fallback = null;
    if (site.chainCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load()) {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
        LOG.info(
            site.name()
                + "\tqet on type "
                + self.getMetaClass().id
                + " failed (polymorphic)"
                + extractSourceInfo(site));
      fallback =
          findStatic(
              Bootstrap.class,
              "ivarGetFail",
              methodType(IRubyObject.class, VariableSite.class, IRubyObject.class));
      fallback = fallback.bindTo(site);
      site.setTarget(fallback);
      return (IRubyObject) fallback.invokeWithArguments(self);
    } else {
      if (Options.INVOKEDYNAMIC_LOG_BINDING.load()) {
        if (direct) {
          LOG.info(
              site.name()
                  + "\tget field on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        } else {
          LOG.info(
              site.name()
                  + "\tget on type "
                  + self.getMetaClass().id
                  + " added to PIC"
                  + extractSourceInfo(site));
        }
      }
      fallback = site.getTarget();
      site.incrementChainCount();
    }

    // prepare test
    MethodHandle test =
        findStatic(
            InvocationLinker.class,
            "testRealClass",
            methodType(boolean.class, int.class, IRubyObject.class));
    test = insertArguments(test, 0, accessor.getClassId());

    getValue = guardWithTest(test, getValue, fallback);

    if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
      LOG.info(
          site.name()
              + "\tget on class "
              + self.getMetaClass().id
              + " bound directly"
              + extractSourceInfo(site));
    site.setTarget(getValue);

    return (IRubyObject) getValue.invokeExact(self);
  }
    @SuppressWarnings("unchecked")
    protected void compile() {
      if (bytecode != null) return;

      // check if we have a cached compiled version on disk
      String codeCache = RubyInstanceConfig.JIT_CODE_CACHE;
      File cachedClassFile = new File(codeCache + "/" + className + ".class");

      if (codeCache != null && cachedClassFile.exists()) {
        FileInputStream fis = null;
        try {
          if (RubyInstanceConfig.JIT_LOADING_DEBUG)
            LOG.info("loading cached code from: " + cachedClassFile);
          fis = new FileInputStream(cachedClassFile);
          bytecode = new byte[(int) fis.getChannel().size()];
          fis.read(bytecode);
          name = new ClassReader(bytecode).getClassName();
          return;
        } catch (Exception e) {
          // ignore and proceed to compile
        } finally {
          try {
            fis.close();
          } catch (Exception e) {
          }
        }
      }

      // Time the compilation
      long start = System.nanoTime();

      asmCompiler.startScript(staticScope);
      final ASTCompiler compiler = ruby.getInstanceConfig().newCompiler();

      CompilerCallback args =
          new CompilerCallback() {
            public void call(BodyCompiler context) {
              compiler.compileArgs(argsNode, context, true);
            }
          };

      ASTInspector inspector = new ASTInspector();
      if (ruby.getInstanceConfig().isJitDumping()) {
        inspector = new ASTInspector(className, true);
      }
      // check args first, since body inspection can depend on args
      inspector.inspect(argsNode);
      inspector.inspect(bodyNode);

      BodyCompiler methodCompiler;
      if (bodyNode != null) {
        // we have a body, do a full-on method
        methodCompiler = asmCompiler.startFileMethod(args, staticScope, inspector);
        compiler.compileBody(bodyNode, methodCompiler, true);
      } else {
        // If we don't have a body, check for required or opt args
        // if opt args, they could have side effects
        // if required args, need to raise errors if too few args passed
        // otherwise, method does nothing, make it a nop
        if (argsNode != null
            && (argsNode.getRequiredArgsCount() > 0 || argsNode.getOptionalArgsCount() > 0)) {
          methodCompiler = asmCompiler.startFileMethod(args, staticScope, inspector);
          methodCompiler.loadNil();
        } else {
          methodCompiler = asmCompiler.startFileMethod(null, staticScope, inspector);
          methodCompiler.loadNil();
          jitCallConfig = CallConfiguration.FrameNoneScopeNone;
        }
      }
      methodCompiler.endBody();
      asmCompiler.endScript(false, false);

      // if we haven't already decided on a do-nothing call
      if (jitCallConfig == null) {
        jitCallConfig = inspector.getCallConfig();
      }

      bytecode = asmCompiler.getClassByteArray();
      if (ruby.getInstanceConfig().isJitDumping()) {
        TraceClassVisitor tcv = new TraceClassVisitor(new PrintWriter(System.out));
        new ClassReader(bytecode).accept(tcv, 0);
      }

      if (bytecode.length > ruby.getInstanceConfig().getJitMaxSize()) {
        bytecode = null;
        throw new NotCompilableException(
            "JITed method size exceeds configured max of "
                + ruby.getInstanceConfig().getJitMaxSize());
      }

      if (codeCache != null) {
        JITCompiler.saveToCodeCache(ruby, bytecode, packageName, cachedClassFile);
      }

      counts.compiledCount.incrementAndGet();
      counts.compileTime.addAndGet(System.nanoTime() - start);
      counts.codeSize.addAndGet(bytecode.length);
      counts.averageCompileTime.set(counts.compileTime.get() / counts.compiledCount.get());
      counts.averageCodeSize.set(counts.codeSize.get() / counts.compiledCount.get());
      synchronized (counts) {
        if (counts.largestCodeSize.get() < bytecode.length) {
          counts.largestCodeSize.set(bytecode.length);
        }
      }
    }
Exemple #21
0
 public static void addScopeAwareMethods(String... methods) {
   if (DEBUG) LOG.debug("Adding scope-aware method names: {}", Arrays.toString(methods));
   SCOPE_AWARE_METHODS.addAll(Arrays.asList(methods));
 }
Exemple #22
0
 private void printError(String message) {
   LOG.error(message + "\nGraph:\n" + this + "\nInstructions:\n" + toStringInstrs());
 }
Exemple #23
0
  public static IRubyObject interpret(ThreadContext context, CFG cfg, InterpreterContext interp) {
    Ruby runtime = context.getRuntime();
    boolean inClosure = (cfg.getScope() instanceof IRClosure);
    boolean passThroughBreak = false;

    try {
      interp.setMethodExitLabel(
          cfg.getExitBB().getLabel()); // used by return and break instructions!

      Instr[] instrs = cfg.prepareForInterpretation();
      int n = instrs.length;
      int ipc = 0;
      Instr lastInstr = null;
      while (ipc < n) {
        interpInstrsCount++;
        lastInstr = instrs[ipc];

        if (isDebug()) LOG.debug("I: {}", lastInstr);

        try {
          Label jumpTarget = lastInstr.interpret(interp);
          ipc = (jumpTarget == null) ? ipc + 1 : jumpTarget.getTargetPC();
        }
        // SSS FIXME: This only catches Ruby exceptions
        // What about Java exceptions?
        catch (org.jruby.exceptions.RaiseException re) {
          ipc = cfg.getRescuerPC(lastInstr);

          if (ipc == -1) throw re; // No one rescued exception, pass it on!

          interp.setException(re.getException());
        }
      }

      // If a closure, and lastInstr was a return, have to return from the nearest method!
      IRubyObject rv = (IRubyObject) interp.getReturnValue();

      if (lastInstr instanceof ReturnInstr && inClosure && !interp.inLambda()) {
        throw new IRReturnJump(((ReturnInstr) lastInstr).methodToReturnFrom, rv);
      }
      // If a closure, and lastInstr was a break, have to return from the nearest closure!
      else if (lastInstr instanceof BREAK_Instr) {
        if (!inClosure) throw runtime.newLocalJumpError(Reason.BREAK, rv, "unexpected break");

        passThroughBreak = true;
        RuntimeHelpers.breakJump(context, rv);
      }

      return rv;
    } catch (JumpException.BreakJump bj) {
      if (passThroughBreak) throw bj;
      return (IRubyObject) bj.getValue();
    } catch (IRReturnJump rj) {
      // - If we are in a lambda, stop propagating
      // - If not in a lambda
      //   - if in a closure, pass it along
      //   - if not in a closure, we got this return jump from a closure further up the call stack.
      //     So, continue popping the call stack till we get to the right method
      if (!interp.inLambda() && (inClosure || (rj.methodToReturnFrom != cfg.getScope())))
        throw rj; // pass it along

      return (IRubyObject) rj.returnValue;
    } finally {
      if (interp.getFrame() != null) {
        context.popFrame();
        interp.setFrame(null);
      }

      if (interp.hasAllocatedDynamicScope()) context.postMethodScopeOnly();
    }
  }
Exemple #24
0
 public void disable() {
   if (dump) LOG.debug("[ASTInspector] {} DISABLED", name);
   flags = 0xFFFFFFFF;
 }
Exemple #25
0
 private static void debug(RubyThread thread, String message) {
   if (DEBUG) LOG.debug(Thread.currentThread() + "(" + thread.status + "): " + message);
 }
Exemple #26
0
  private static IRubyObject interpret(
      ThreadContext context,
      IRubyObject self,
      IRScope scope,
      Visibility visibility,
      RubyModule implClass,
      IRubyObject[] args,
      Block block,
      Block.Type blockType) {
    Instr[] instrs = scope.getInstrsForInterpretation();

    // The base IR may not have been processed yet
    if (instrs == null) instrs = scope.prepareForInterpretation(blockType == Block.Type.LAMBDA);

    int numTempVars = scope.getTemporaryVariableSize();
    Object[] temp = numTempVars > 0 ? new Object[numTempVars] : null;
    int n = instrs.length;
    int ipc = 0;
    Instr instr = null;
    Object exception = null;
    int kwArgHashCount =
        (scope.receivesKeywordArgs() && args[args.length - 1] instanceof RubyHash) ? 1 : 0;
    DynamicScope currDynScope = context.getCurrentScope();

    // Counter tpCount = null;

    // Init profiling this scope
    boolean debug = IRRuntimeHelpers.isDebug();
    boolean profile = IRRuntimeHelpers.inProfileMode();
    Integer scopeVersion = profile ? initProfiling(scope) : 0;

    // Enter the looooop!
    while (ipc < n) {
      instr = instrs[ipc];
      ipc++;
      Operation operation = instr.getOperation();
      if (debug) {
        LOG.info("I: {}", instr);
        interpInstrsCount++;
      } else if (profile) {
        if (operation.modifiesCode()) codeModificationsCount++;
        interpInstrsCount++;
        /*
        Counter cnt = opStats.get(operation);
        if (cnt == null) {
            cnt = new Counter();
            opStats.put(operation, cnt);
        }
        cnt.count++;
        */
      }

      try {
        switch (operation.opClass) {
          case ARG_OP:
            {
              receiveArg(
                  context,
                  instr,
                  operation,
                  args,
                  kwArgHashCount,
                  currDynScope,
                  temp,
                  exception,
                  block);
              break;
            }
          case BRANCH_OP:
            {
              if (operation == Operation.JUMP) {
                ipc = ((JumpInstr) instr).getJumpTarget().getTargetPC();
              } else {
                ipc = instr.interpretAndGetNewIPC(context, currDynScope, self, temp, ipc);
              }
              break;
            }
          case CALL_OP:
            {
              if (profile) updateCallSite(instr, scope, scopeVersion);
              processCall(
                  context, instr, operation, scope, currDynScope, temp, self, block, blockType);
              break;
            }
          case BOOK_KEEPING_OP:
            {
              switch (operation) {
                case PUSH_FRAME:
                  {
                    context.preMethodFrameAndClass(
                        implClass, scope.getName(), self, block, scope.getStaticScope());
                    context.setCurrentVisibility(visibility);
                    break;
                  }
                case PUSH_BINDING:
                  {
                    // SSS NOTE: Method scopes only!
                    //
                    // Blocks are a headache -- so, these instrs. are only added to IRMethods.
                    // Blocks have more complicated logic for pushing a dynamic scope (see
                    // InterpretedIRBlockBody)
                    // Changed by DPR
                    currDynScope =
                        DynamicScope.newDynamicScope(
                            scope.getStaticScope(), context.getCurrentScope().getDepth());
                    context.pushScope(currDynScope);
                    break;
                  }
                case CHECK_ARITY:
                  ((CheckArityInstr) instr).checkArity(context.runtime, args.length);
                  break;
                case POP_FRAME:
                  context.popFrame();
                  context.popRubyClass();
                  break;
                case POP_BINDING:
                  context.popScope();
                  break;
                case THREAD_POLL:
                  if (profile) {
                    // SSS: Not being used currently
                    // tpCount.count++;
                    globalThreadPollCount++;

                    // 20K is arbitrary
                    // Every 20K profile counts, spit out profile stats
                    if (globalThreadPollCount % 20000 == 0) {
                      analyzeProfile();
                      // outputProfileStats();
                    }
                  }
                  context.callThreadPoll();
                  break;
                case LINE_NUM:
                  context.setLine(((LineNumberInstr) instr).lineNumber);
                  break;
                case RECORD_END_BLOCK:
                  ((RecordEndBlockInstr) instr).interpret();
                  break;
              }
              break;
            }
          case OTHER_OP:
            {
              Object result = null;
              switch (operation) {
                  // --------- Return flavored instructions --------
                case BREAK:
                  {
                    BreakInstr bi = (BreakInstr) instr;
                    IRubyObject rv =
                        (IRubyObject)
                            bi.getReturnValue().retrieve(context, self, currDynScope, temp);
                    // This also handles breaks in lambdas -- by converting them to a return
                    return IRRuntimeHelpers.initiateBreak(
                        context, scope, bi.getScopeToReturnTo().getScopeId(), rv, blockType);
                  }
                case RETURN:
                  {
                    return (IRubyObject)
                        retrieveOp(
                            ((ReturnBase) instr).getReturnValue(),
                            context,
                            self,
                            currDynScope,
                            temp);
                  }
                case NONLOCAL_RETURN:
                  {
                    NonlocalReturnInstr ri = (NonlocalReturnInstr) instr;
                    IRubyObject rv =
                        (IRubyObject)
                            retrieveOp(ri.getReturnValue(), context, self, currDynScope, temp);
                    ipc = n;
                    // If not in a lambda, check if this was a non-local return
                    if (!IRRuntimeHelpers.inLambda(blockType)) {
                      IRRuntimeHelpers.initiateNonLocalReturn(
                          context, scope, ri.methodToReturnFrom, rv);
                    }
                    return rv;
                  }

                  // ---------- Common instruction ---------
                case COPY:
                  {
                    CopyInstr c = (CopyInstr) instr;
                    result = retrieveOp(c.getSource(), context, self, currDynScope, temp);
                    setResult(temp, currDynScope, c.getResult(), result);
                    break;
                  }

                case GET_FIELD:
                  {
                    GetFieldInstr gfi = (GetFieldInstr) instr;
                    IRubyObject object =
                        (IRubyObject) gfi.getSource().retrieve(context, self, currDynScope, temp);
                    VariableAccessor a = gfi.getAccessor(object);
                    result = a == null ? null : (IRubyObject) a.get(object);
                    if (result == null) {
                      result = context.nil;
                    }
                    setResult(temp, currDynScope, gfi.getResult(), result);
                    break;
                  }

                case SEARCH_CONST:
                  {
                    SearchConstInstr sci = (SearchConstInstr) instr;
                    result = sci.getCachedConst();
                    if (!sci.isCached(context, result))
                      result = sci.cache(context, currDynScope, self, temp);
                    setResult(temp, currDynScope, sci.getResult(), result);
                    break;
                  }

                  // ---------- All the rest ---------
                default:
                  result = instr.interpret(context, currDynScope, self, temp, block);
                  setResult(temp, currDynScope, instr, result);
                  break;
              }

              break;
            }
        }
      } catch (Throwable t) {
        // Unrescuable:
        //    IRReturnJump, ThreadKill, RubyContinuation, MainExitException, etc.
        //    These cannot be rescued -- only run ensure blocks
        //
        // Others:
        //    IRBreakJump, Ruby exceptions, errors, and other java exceptions.
        //    These can be rescued -- run rescue blocks

        if (debug)
          LOG.info(
              "in scope: "
                  + scope
                  + ", caught Java throwable: "
                  + t
                  + "; excepting instr: "
                  + instr);
        ipc = (t instanceof Unrescuable) ? scope.getEnsurerPC(instr) : scope.getRescuerPC(instr);
        if (debug) LOG.info("ipc for rescuer/ensurer: " + ipc);

        if (ipc == -1) {
          Helpers.throwException((Throwable) t);
        } else {
          exception = t;
        }
      }
    }

    // Control should never get here!
    // SSS FIXME: But looks like BEGIN/END blocks get here -- needs fixing
    return null;
  }
Exemple #27
0
 public void setFlag(Flag modifier) {
   if (dump) {
     LOG.info("[ASTInspector] " + name + "\n\tset flag " + modifier);
   }
   flags |= modifier.flag;
 }
Exemple #28
0
  /**
   * Update the given call site using the new target, wrapping with appropriate guard and
   * argument-juggling logic. Return a handle suitable for invoking with the site's original method
   * type.
   */
  MethodHandle updateInvocationTarget(
      MethodHandle target,
      IRubyObject self,
      RubyModule testClass,
      CacheEntry entry,
      SwitchPoint switchPoint) {
    if (target == null
        || clearCount > Options.INVOKEDYNAMIC_MAXFAIL.load()
        || (!hasSeenType(testClass.id)
            && seenTypesCount() + 1 > Options.INVOKEDYNAMIC_MAXPOLY.load())) {
      setTarget(target = prepareBinder().invokeVirtualQuiet(lookup(), "fail"));
    } else {
      MethodHandle fallback;
      MethodHandle gwt;

      // if we've cached no types, and the site is bound and we haven't seen this new type...
      if (seenTypesCount() > 0 && getTarget() != null && !hasSeenType(testClass.id)) {
        // stack it up into a PIC
        if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
          LOG.info(methodName + "\tadded to PIC " + logMethod(entry.method));
        fallback = getTarget();
      } else {
        // wipe out site with this new type and method
        String bind = boundOnce ? "rebind" : "bind";
        if (Options.INVOKEDYNAMIC_LOG_BINDING.load())
          LOG.info(
              methodName
                  + "\ttriggered site #"
                  + siteID
                  + " "
                  + bind); // + " (" + file() + ":" + line() + ")");
        fallback = this.fallback;
        clearTypes();
      }

      addType(testClass.id);

      SmartHandle test;
      SmartBinder selfTest = SmartBinder.from(signature.asFold(boolean.class)).permute("self");

      if (self instanceof RubySymbol
          || self instanceof RubyFixnum
          || self instanceof RubyFloat
          || self instanceof RubyNil
          || self instanceof RubyBoolean.True
          || self instanceof RubyBoolean.False) {

        test =
            selfTest
                .insert(1, "selfJavaType", self.getClass())
                .cast(boolean.class, Object.class, Class.class)
                .invoke(TEST_CLASS);

      } else {

        test =
            SmartBinder.from(signature.changeReturn(boolean.class))
                .permute("self")
                .insert(0, "selfClass", RubyClass.class, testClass)
                .invokeStaticQuiet(Bootstrap.LOOKUP, Bootstrap.class, "testType");
      }

      gwt = MethodHandles.guardWithTest(test.handle(), target, fallback);

      // wrap in switchpoint for mutation invalidation
      gwt = switchPoint.guardWithTest(gwt, fallback);

      setTarget(gwt);
    }

    return target;
  }