view src/share/vm/interpreter/rewriter.cpp @ 452:00b023ae2d78

6722113: CMS: Incorrect overflow handling during precleaning of Reference lists Summary: When we encounter marking stack overflow during precleaning of Reference lists, we were using the overflow list mechanism, which can cause problems on account of mutating the mark word of the header because of conflicts with mutator accesses and updates of that field. Instead we should use the usual mechanism for overflow handling in concurrent phases, namely dirtying of the card on which the overflowed object lies. Since precleaning effectively does a form of discovered list processing, albeit with discovery enabled, we needed to adjust some code to be correct in the face of interleaved processing and discovery. Reviewed-by: apetrusenko, jcoomes
author ysr
date Thu, 20 Nov 2008 12:27:41 -0800
parents a61af66fc99e
children 9a25e0c45327
line wrap: on
line source

/*
 * Copyright 1998-2005 Sun Microsystems, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
 * CA 95054 USA or visit www.sun.com if you need additional information or
 * have any questions.
 *
 */

# include "incls/_precompiled.incl"
# include "incls/_rewriter.cpp.incl"


// Computes an index_map (new_index -> original_index) for contant pool entries
// that are referred to by the interpreter at runtime via the constant pool cache.
void Rewriter::compute_index_maps(constantPoolHandle pool, intArray*& index_map, intStack*& inverse_index_map) {
  const int length  = pool->length();
  index_map         = new intArray(length, -1);
  // Choose an initial value large enough that we don't get frequent
  // calls to grow().
  inverse_index_map = new intStack(length / 2);
  for (int i = 0; i < length; i++) {
    switch (pool->tag_at(i).value()) {
      case JVM_CONSTANT_Fieldref          : // fall through
      case JVM_CONSTANT_Methodref         : // fall through
      case JVM_CONSTANT_InterfaceMethodref: {
        index_map->at_put(i, inverse_index_map->length());
        inverse_index_map->append(i);
      }
    }
  }
}


// Creates a constant pool cache given an inverse_index_map
constantPoolCacheHandle Rewriter::new_constant_pool_cache(intArray& inverse_index_map, TRAPS) {
  const int length = inverse_index_map.length();
  constantPoolCacheOop cache = oopFactory::new_constantPoolCache(length, CHECK_(constantPoolCacheHandle()));
  cache->initialize(inverse_index_map);
  return constantPoolCacheHandle(THREAD, cache);
}



// The new finalization semantics says that registration of
// finalizable objects must be performed on successful return from the
// Object.<init> constructor.  We could implement this trivially if
// <init> were never rewritten but since JVMTI allows this to occur, a
// more complicated solution is required.  A special return bytecode
// is used only by Object.<init> to signal the finalization
// registration point.  Additionally local 0 must be preserved so it's
// available to pass to the registration function.  For simplicty we
// require that local 0 is never overwritten so it's available as an
// argument for registration.

void Rewriter::rewrite_Object_init(methodHandle method, TRAPS) {
  RawBytecodeStream bcs(method);
  while (!bcs.is_last_bytecode()) {
    Bytecodes::Code opcode = bcs.raw_next();
    switch (opcode) {
      case Bytecodes::_return: *bcs.bcp() = Bytecodes::_return_register_finalizer; break;

      case Bytecodes::_istore:
      case Bytecodes::_lstore:
      case Bytecodes::_fstore:
      case Bytecodes::_dstore:
      case Bytecodes::_astore:
        if (bcs.get_index() != 0) continue;

        // fall through
      case Bytecodes::_istore_0:
      case Bytecodes::_lstore_0:
      case Bytecodes::_fstore_0:
      case Bytecodes::_dstore_0:
      case Bytecodes::_astore_0:
        THROW_MSG(vmSymbols::java_lang_IncompatibleClassChangeError(),
                  "can't overwrite local 0 in Object.<init>");
        break;
    }
  }
}


// Rewrites a method given the index_map information
methodHandle Rewriter::rewrite_method(methodHandle method, intArray& index_map, TRAPS) {

  int nof_jsrs = 0;
  bool has_monitor_bytecodes = false;

  {
    // We cannot tolerate a GC in this block, because we've
    // cached the bytecodes in 'code_base'. If the methodOop
    // moves, the bytecodes will also move.
    No_Safepoint_Verifier nsv;
    Bytecodes::Code c;

    // Bytecodes and their length
    const address code_base = method->code_base();
    const int code_length = method->code_size();

    int bc_length;
    for (int bci = 0; bci < code_length; bci += bc_length) {
      address bcp = code_base + bci;
      c = (Bytecodes::Code)(*bcp);

      // Since we have the code, see if we can get the length
      // directly. Some more complicated bytecodes will report
      // a length of zero, meaning we need to make another method
      // call to calculate the length.
      bc_length = Bytecodes::length_for(c);
      if (bc_length == 0) {
        bc_length = Bytecodes::length_at(bcp);

        // length_at will put us at the bytecode after the one modified
        // by 'wide'. We don't currently examine any of the bytecodes
        // modified by wide, but in case we do in the future...
        if (c == Bytecodes::_wide) {
          c = (Bytecodes::Code)bcp[1];
        }
      }

      assert(bc_length != 0, "impossible bytecode length");

      switch (c) {
        case Bytecodes::_lookupswitch   : {
#ifndef CC_INTERP
          Bytecode_lookupswitch* bc = Bytecode_lookupswitch_at(bcp);
          bc->set_code(
            bc->number_of_pairs() < BinarySwitchThreshold
            ? Bytecodes::_fast_linearswitch
            : Bytecodes::_fast_binaryswitch
          );
#endif
          break;
        }
        case Bytecodes::_getstatic      : // fall through
        case Bytecodes::_putstatic      : // fall through
        case Bytecodes::_getfield       : // fall through
        case Bytecodes::_putfield       : // fall through
        case Bytecodes::_invokevirtual  : // fall through
        case Bytecodes::_invokespecial  : // fall through
        case Bytecodes::_invokestatic   : // fall through
        case Bytecodes::_invokeinterface: {
          address p = bcp + 1;
          Bytes::put_native_u2(p, index_map[Bytes::get_Java_u2(p)]);
          break;
        }
        case Bytecodes::_jsr            : // fall through
        case Bytecodes::_jsr_w          : nof_jsrs++;                   break;
        case Bytecodes::_monitorenter   : // fall through
        case Bytecodes::_monitorexit    : has_monitor_bytecodes = true; break;
      }
    }
  }

  // Update access flags
  if (has_monitor_bytecodes) {
    method->set_has_monitor_bytecodes();
  }

  // The present of a jsr bytecode implies that the method might potentially
  // have to be rewritten, so we run the oopMapGenerator on the method
  if (nof_jsrs > 0) {
    method->set_has_jsrs();
    ResolveOopMapConflicts romc(method);
    methodHandle original_method = method;
    method = romc.do_potential_rewrite(CHECK_(methodHandle()));
    if (method() != original_method()) {
      // Insert invalid bytecode into original methodOop and set
      // interpreter entrypoint, so that a executing this method
      // will manifest itself in an easy recognizable form.
      address bcp = original_method->bcp_from(0);
      *bcp = (u1)Bytecodes::_shouldnotreachhere;
      int kind = Interpreter::method_kind(original_method);
      original_method->set_interpreter_kind(kind);
    }

    // Update monitor matching info.
    if (romc.monitor_safe()) {
      method->set_guaranteed_monitor_matching();
    }
  }

  // Setup method entrypoints for compiler and interpreter
  method->link_method(method, CHECK_(methodHandle()));

  return method;
}


void Rewriter::rewrite(instanceKlassHandle klass, TRAPS) {
  // gather starting points
  ResourceMark rm(THREAD);
  constantPoolHandle pool (THREAD, klass->constants());
  objArrayHandle methods  (THREAD, klass->methods());
  assert(pool->cache() == NULL, "constant pool cache must not be set yet");

  // determine index maps for methodOop rewriting
  intArray* index_map         = NULL;
  intStack* inverse_index_map = NULL;
  compute_index_maps(pool, index_map, inverse_index_map);

  // allocate constant pool cache
  constantPoolCacheHandle cache = new_constant_pool_cache(*inverse_index_map, CHECK);
  pool->set_cache(cache());
  cache->set_constant_pool(pool());

  if (RegisterFinalizersAtInit && klass->name() == vmSymbols::java_lang_Object()) {
    int i = methods->length();
    while (i-- > 0) {
      methodOop method = (methodOop)methods->obj_at(i);
      if (method->intrinsic_id() == vmIntrinsics::_Object_init) {
        // rewrite the return bytecodes of Object.<init> to register the
        // object for finalization if needed.
        methodHandle m(THREAD, method);
        rewrite_Object_init(m, CHECK);
        break;
      }
    }
  }

  // rewrite methods
  { int i = methods->length();
    while (i-- > 0) {
      methodHandle m(THREAD, (methodOop)methods->obj_at(i));
      m = rewrite_method(m, *index_map, CHECK);
      // Method might have gotten rewritten.
      methods->obj_at_put(i, m());
    }
  }
}