changeset 3466:270a40a57b3d hs23.2-b08

Merge
author amurillo
date Fri, 29 Jun 2012 15:43:13 -0700
parents c4dedc59d44d 981f551d0f91
children 7a37cec9d0d4
files make/hotspot_version
diffstat 16 files changed, 475 insertions(+), 183 deletions(-) [+]
line wrap: on
line diff
--- a/make/hotspot_version	Wed Jun 27 17:19:10 2012 -0700
+++ b/make/hotspot_version	Fri Jun 29 15:43:13 2012 -0700
@@ -35,7 +35,7 @@
 
 HS_MAJOR_VER=23
 HS_MINOR_VER=2
-HS_BUILD_NUMBER=07
+HS_BUILD_NUMBER=08
 
 JDK_MAJOR_VER=1
 JDK_MINOR_VER=7
--- a/make/solaris/makefiles/dtrace.make	Wed Jun 27 17:19:10 2012 -0700
+++ b/make/solaris/makefiles/dtrace.make	Fri Jun 29 15:43:13 2012 -0700
@@ -137,13 +137,13 @@
     # implied else here is no stripping at all
     endif
   endif
-	[ -f $(XLIBJVM_DB_G_DEBUGINFO) ] || { ln -s $(XLIBJVM_DB_DEBUGINFO) $(XLIBJVM_DB_G_DEBUGINFO); }
+	[ -f $(XLIBJVM_DB_G_DEBUGINFO) ] || { cd $(XLIBJVM_DIR) && ln -s $(LIBJVM_DB_DEBUGINFO) $(LIBJVM_DB_G_DEBUGINFO); }
   ifeq ($(ZIP_DEBUGINFO_FILES),1)
 # Do this part in the $(XLIBJVM_DIR) subdir so $(XLIBJVM_DIR) is not
 # in the archived name:
 	( cd $(XLIBJVM_DIR) && $(ZIPEXE) -q -y $(LIBJVM_DB_DIZ) $(LIBJVM_DB_DEBUGINFO) $(LIBJVM_DB_G_DEBUGINFO) )
 	$(RM) $(XLIBJVM_DB_DEBUGINFO) $(XLIBJVM_DB_G_DEBUGINFO)
-	[ -f $(XLIBJVM_DB_G_DIZ) ] || { ln -s $(XLIBJVM_DB_DIZ) $(XLIBJVM_DB_G_DIZ); }
+	[ -f $(XLIBJVM_DB_G_DIZ) ] || { cd $(XLIBJVM_DIR) && ln -s $(LIBJVM_DB_DIZ) $(LIBJVM_DB_G_DIZ); }
   endif
 endif
 
@@ -170,13 +170,13 @@
     # implied else here is no stripping at all
     endif
   endif
-	[ -f $(XLIBJVM_DTRACE_G_DEBUGINFO) ] || { ln -s $(XLIBJVM_DTRACE_DEBUGINFO) $(XLIBJVM_DTRACE_G_DEBUGINFO); }
+	[ -f $(XLIBJVM_DTRACE_G_DEBUGINFO) ] || { cd $(XLIBJVM_DIR) && ln -s $(LIBJVM_DTRACE_DEBUGINFO) $(LIBJVM_DTRACE_G_DEBUGINFO); }
   ifeq ($(ZIP_DEBUGINFO_FILES),1)
 # Do this part in the $(XLIBJVM_DIR) subdir so $(XLIBJVM_DIR) is not
 # in the archived name:
 	( cd $(XLIBJVM_DIR) && $(ZIPEXE) -q -y $(LIBJVM_DTRACE_DIZ) $(LIBJVM_DTRACE_DEBUGINFO) $(LIBJVM_DTRACE_G_DEBUGINFO) )
 	$(RM) $(XLIBJVM_DTRACE_DEBUGINFO) $(XLIBJVM_DTRACE_G_DEBUGINFO)
-	[ -f $(XLIBJVM_DTRACE_G_DIZ) ] || { ln -s $(XLIBJVM_DTRACE_DIZ) $(XLIBJVM_DTRACE_G_DIZ); }
+	[ -f $(XLIBJVM_DTRACE_G_DIZ) ] || { cd $(XLIBJVM_DIR) && ln -s $(LIBJVM_DTRACE_DIZ) $(LIBJVM_DTRACE_G_DIZ); }
   endif
 endif
 
--- a/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -516,7 +516,12 @@
       }
     }
 
-    if (thread->thread_state() == _thread_in_Java) {
+    // We test if stub is already set (by the stack overflow code
+    // above) so it is not overwritten by the code that follows. This
+    // check is not required on other platforms, because on other
+    // platforms we check for SIGSEGV only or SIGBUS only, where here
+    // we have to check for both SIGSEGV and SIGBUS.
+    if (thread->thread_state() == _thread_in_Java && stub == NULL) {
       // Java thread running in Java code => find exception handler if any
       // a fault inside compiled code, the interpreter, or a stub
 
--- a/src/share/vm/classfile/symbolTable.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/classfile/symbolTable.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -43,33 +43,13 @@
 jint SymbolTable::_seed = 0;
 
 Symbol* SymbolTable::allocate_symbol(const u1* name, int len, TRAPS) {
-  // Don't allow symbols to be created which cannot fit in a Symbol*.
-  if (len > Symbol::max_length()) {
-    THROW_MSG_0(vmSymbols::java_lang_InternalError(),
-                "name is too long to represent");
-  }
+  assert (len <= Symbol::max_length(), "should be checked by caller");
+
   Symbol* sym = new (len) Symbol(name, len);
   assert(sym != NULL, "new should call vm_exit_out_of_memory if C_HEAP is exhausted");
   return sym;
 }
 
-bool SymbolTable::allocate_symbols(int names_count, const u1** names,
-                                   int* lengths, Symbol** syms, TRAPS) {
-  for (int i = 0; i< names_count; i++) {
-    if (lengths[i] > Symbol::max_length()) {
-      THROW_MSG_0(vmSymbols::java_lang_InternalError(),
-                  "name is too long to represent");
-    }
-  }
-
-  for (int i = 0; i< names_count; i++) {
-    int len = lengths[i];
-    syms[i] = new (len) Symbol(names[i], len);
-    assert(syms[i] != NULL, "new should call vm_exit_out_of_memory if "
-                            "C_HEAP is exhausted");
-  }
-  return true;
-}
 
 // Call function for all symbols in the symbol table.
 void SymbolTable::symbols_do(SymbolClosure *cl) {
@@ -94,9 +74,14 @@
   int total = 0;
   size_t memory_total = 0;
   for (int i = 0; i < the_table()->table_size(); ++i) {
-    for (HashtableEntry<Symbol*>** p = the_table()->bucket_addr(i); *p != NULL; ) {
-      HashtableEntry<Symbol*>* entry = *p;
-      if (entry->is_shared()) {
+    HashtableEntry<Symbol*>** p = the_table()->bucket_addr(i);
+    HashtableEntry<Symbol*>* entry = the_table()->bucket(i);
+    while (entry != NULL) {
+      // Shared entries are normally at the end of the bucket and if we run into
+      // a shared entry, then there is nothing more to remove. However, if we
+      // have rehashed the table, then the shared entries are no longer at the
+      // end of the bucket.
+      if (entry->is_shared() && !use_alternate_hashcode()) {
         break;
       }
       Symbol* s = entry->literal();
@@ -105,6 +90,7 @@
       assert(s != NULL, "just checking");
       // If reference count is zero, remove.
       if (s->refcount() == 0) {
+        assert(!entry->is_shared(), "shared entries should be kept live");
         delete s;
         removed++;
         *p = entry->next();
@@ -112,6 +98,8 @@
       } else {
         p = entry->next_addr();
       }
+      // get next entry
+      entry = (HashtableEntry<Symbol*>*)HashtableEntry<Symbol*>::make_ptr(*p);
     }
   }
   symbols_removed += removed;
@@ -134,7 +122,8 @@
 // with the existing strings.   Set flag to use the alternate hash code afterwards.
 void SymbolTable::rehash_table() {
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
-  assert(!DumpSharedSpaces, "this should never happen with -Xshare:dump");
+  // This should never happen with -Xshare:dump but it might in testing mode.
+  if (DumpSharedSpaces) return;
   // Create a new symbol table
   SymbolTable* new_table = new SymbolTable();
 
@@ -175,12 +164,11 @@
   return NULL;
 }
 
-// Pick hashing algorithm, but return value already given if not using a new
-// hash algorithm.
-unsigned int SymbolTable::hash_symbol(const char* s, int len, unsigned int hashValue) {
+// Pick hashing algorithm.
+unsigned int SymbolTable::hash_symbol(const char* s, int len) {
   return use_alternate_hashcode() ?
            AltHashing::murmur3_32(seed(), (const jbyte*)s, len) :
-           (hashValue != 0 ? hashValue : java_lang_String::to_hash(s, len));
+           java_lang_String::to_hash(s, len);
 }
 
 
@@ -200,6 +188,9 @@
   // Found
   if (s != NULL) return s;
 
+  // Grab SymbolTable_lock first.
+  MutexLocker ml(SymbolTable_lock, THREAD);
+
   // Otherwise, add to symbol to table
   return the_table()->basic_add(index, (u1*)name, len, hashValue, CHECK_NULL);
 }
@@ -237,6 +228,9 @@
   // We can't include the code in No_Safepoint_Verifier because of the
   // ResourceMark.
 
+  // Allocation must be done before grabbing the SymbolTable_lock lock
+  MutexLocker ml(SymbolTable_lock, THREAD);
+
   return the_table()->basic_add(index, (u1*)buffer, len, hashValue, CHECK_NULL);
 }
 
@@ -304,6 +298,9 @@
 void SymbolTable::add(constantPoolHandle cp, int names_count,
                       const char** names, int* lengths, int* cp_indices,
                       unsigned int* hashValues, TRAPS) {
+  // Grab SymbolTable_lock first.
+  MutexLocker ml(SymbolTable_lock, THREAD);
+
   SymbolTable* table = the_table();
   bool added = table->basic_add(cp, names_count, names, lengths,
                                 cp_indices, hashValues, CHECK);
@@ -318,38 +315,47 @@
   }
 }
 
-Symbol* SymbolTable::basic_add(int index, u1 *name, int len,
-                                 unsigned int hashValue_arg, TRAPS) {
+Symbol* SymbolTable::basic_add(int index_arg, u1 *name, int len,
+                               unsigned int hashValue_arg, TRAPS) {
   assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(),
          "proposed name of symbol must be stable");
 
-  // We assume that lookup() has been called already, that it failed,
-  // and symbol was not found.  We create the symbol here.
-  Symbol* sym = allocate_symbol(name, len, CHECK_NULL);
+  // Don't allow symbols to be created which cannot fit in a Symbol*.
+  if (len > Symbol::max_length()) {
+    THROW_MSG_0(vmSymbols::java_lang_InternalError(),
+                "name is too long to represent");
+  }
 
-  // Allocation must be done before grabbing the SymbolTable_lock lock
-  MutexLocker ml(SymbolTable_lock, THREAD);
-
-  assert(sym->equals((char*)name, len), "symbol must be properly initialized");
+  // Cannot hit a safepoint in this function because the "this" pointer can move.
+  No_Safepoint_Verifier nsv;
 
   // Check if the symbol table has been rehashed, if so, need to recalculate
-  // the hash value.
-  unsigned int hashValue = hash_symbol((const char*)name, len, hashValue_arg);
+  // the hash value and index.
+  unsigned int hashValue;
+  int index;
+  if (use_alternate_hashcode()) {
+    hashValue = hash_symbol((const char*)name, len);
+    index = hash_to_index(hashValue);
+  } else {
+    hashValue = hashValue_arg;
+    index = index_arg;
+  }
 
   // Since look-up was done lock-free, we need to check if another
   // thread beat us in the race to insert the symbol.
-
   Symbol* test = lookup(index, (char*)name, len, hashValue);
   if (test != NULL) {
-    // A race occurred and another thread introduced the symbol, this one
-    // will be dropped and collected.
-    delete sym;
+    // A race occurred and another thread introduced the symbol.
     assert(test->refcount() != 0, "lookup should have incremented the count");
     return test;
   }
 
+  // Create a new symbol.
+  Symbol* sym = allocate_symbol(name, len, CHECK_NULL);
+  assert(sym->equals((char*)name, len), "symbol must be properly initialized");
+
   HashtableEntry<Symbol*>* entry = new_entry(hashValue, sym);
-  sym->increment_refcount();
+  sym->increment_refcount();  // increment refcount in external hashtable
   add_entry(index, entry);
   return sym;
 }
@@ -358,21 +364,27 @@
                             const char** names, int* lengths,
                             int* cp_indices, unsigned int* hashValues,
                             TRAPS) {
-  Symbol* syms[symbol_alloc_batch_size];
-  bool allocated = allocate_symbols(names_count, (const u1**)names, lengths,
-                                    syms, CHECK_false);
-  if (!allocated) {
-    return false;
+
+  // Check symbol names are not too long.  If any are too long, don't add any.
+  for (int i = 0; i< names_count; i++) {
+    if (lengths[i] > Symbol::max_length()) {
+      THROW_MSG_0(vmSymbols::java_lang_InternalError(),
+                  "name is too long to represent");
+    }
   }
 
-  // Allocation must be done before grabbing the SymbolTable_lock lock
-  MutexLocker ml(SymbolTable_lock, THREAD);
+  // Cannot hit a safepoint in this function because the "this" pointer can move.
+  No_Safepoint_Verifier nsv;
 
   for (int i=0; i<names_count; i++) {
-    assert(syms[i]->equals(names[i], lengths[i]), "symbol must be properly initialized");
     // Check if the symbol table has been rehashed, if so, need to recalculate
     // the hash value.
-    unsigned int hashValue = hash_symbol(names[i], lengths[i], hashValues[i]);
+    unsigned int hashValue;
+    if (use_alternate_hashcode()) {
+      hashValue = hash_symbol(names[i], lengths[i]);
+    } else {
+      hashValue = hashValues[i];
+    }
     // Since look-up was done lock-free, we need to check if another
     // thread beat us in the race to insert the symbol.
     int index = hash_to_index(hashValue);
@@ -382,9 +394,9 @@
       // will be dropped and collected. Use test instead.
       cp->symbol_at_put(cp_indices[i], test);
       assert(test->refcount() != 0, "lookup should have incremented the count");
-      delete syms[i];
     } else {
-      Symbol* sym = syms[i];
+      Symbol* sym = allocate_symbol((const u1*)names[i], lengths[i], CHECK_(false));
+      assert(sym->equals(names[i], lengths[i]), "symbol must be properly initialized");  // why wouldn't it be???
       HashtableEntry<Symbol*>* entry = new_entry(hashValue, sym);
       sym->increment_refcount();  // increment refcount in external hashtable
       add_entry(index, entry);
@@ -573,9 +585,9 @@
 jint StringTable::_seed = 0;
 
 // Pick hashing algorithm
-unsigned int StringTable::hash_string(const jchar* s, int len, unsigned int hashValue) {
+unsigned int StringTable::hash_string(const jchar* s, int len) {
   return use_alternate_hashcode() ? AltHashing::murmur3_32(seed(), s, len) :
-            (hashValue != 0 ? hashValue : java_lang_String::to_hash(s, len));
+                                    java_lang_String::to_hash(s, len);
 }
 
 oop StringTable::lookup(int index, jchar* name,
@@ -597,29 +609,25 @@
 }
 
 
-oop StringTable::basic_add(int index, Handle string_or_null, jchar* name,
+oop StringTable::basic_add(int index_arg, Handle string, jchar* name,
                            int len, unsigned int hashValue_arg, TRAPS) {
-  debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
-  assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(),
-         "proposed name of symbol must be stable");
-
-  Handle string;
-  // try to reuse the string if possible
-  if (!string_or_null.is_null() && (!JavaObjectsInPerm || string_or_null()->is_perm())) {
-    string = string_or_null;
-  } else {
-    string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL);
-  }
-
-  // Allocation must be done before grapping the SymbolTable_lock lock
-  MutexLocker ml(StringTable_lock, THREAD);
 
   assert(java_lang_String::equals(string(), name, len),
          "string must be properly initialized");
+  // Cannot hit a safepoint in this function because the "this" pointer can move.
+  No_Safepoint_Verifier nsv;
 
   // Check if the symbol table has been rehashed, if so, need to recalculate
-  // the hash value before second lookup.
-  unsigned int hashValue = hash_string(name, len, hashValue_arg);
+  // the hash value and index before second lookup.
+  unsigned int hashValue;
+  int index;
+  if (use_alternate_hashcode()) {
+    hashValue = hash_string(name, len);
+    index = hash_to_index(hashValue);
+  } else {
+    hashValue = hashValue_arg;
+    index = index_arg;
+  }
 
   // Since look-up was done lock-free, we need to check if another
   // thread beat us in the race to insert the symbol.
@@ -650,13 +658,29 @@
                         int len, TRAPS) {
   unsigned int hashValue = hash_string(name, len);
   int index = the_table()->hash_to_index(hashValue);
-  oop string = the_table()->lookup(index, name, len, hashValue);
+  oop found_string = the_table()->lookup(index, name, len, hashValue);
 
   // Found
-  if (string != NULL) return string;
+  if (found_string != NULL) return found_string;
+
+  debug_only(StableMemoryChecker smc(name, len * sizeof(name[0])));
+  assert(!Universe::heap()->is_in_reserved(name) || GC_locker::is_active(),
+         "proposed name of symbol must be stable");
+
+  Handle string;
+  // try to reuse the string if possible
+  if (!string_or_null.is_null() && (!JavaObjectsInPerm || string_or_null()->is_perm())) {
+    string = string_or_null;
+  } else {
+    string = java_lang_String::create_tenured_from_unicode(name, len, CHECK_NULL);
+  }
+
+  // Grab the StringTable_lock before getting the_table() because it could
+  // change at safepoint.
+  MutexLocker ml(StringTable_lock, THREAD);
 
   // Otherwise, add to symbol to table
-  return the_table()->basic_add(index, string_or_null, name, len,
+  return the_table()->basic_add(index, string, name, len,
                                 hashValue, CHECK_NULL);
 }
 
@@ -699,18 +723,24 @@
   // entries at a safepoint.
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
   for (int i = 0; i < the_table()->table_size(); ++i) {
-    for (HashtableEntry<oop>** p = the_table()->bucket_addr(i); *p != NULL; ) {
-      HashtableEntry<oop>* entry = *p;
-      if (entry->is_shared()) {
+    HashtableEntry<oop>** p = the_table()->bucket_addr(i);
+    HashtableEntry<oop>* entry = the_table()->bucket(i);
+    while (entry != NULL) {
+      // Shared entries are normally at the end of the bucket and if we run into
+      // a shared entry, then there is nothing more to remove. However, if we
+      // have rehashed the table, then the shared entries are no longer at the
+      // end of the bucket.
+      if (entry->is_shared() && !use_alternate_hashcode()) {
         break;
       }
       assert(entry->literal() != NULL, "just checking");
-      if (is_alive->do_object_b(entry->literal())) {
+      if (entry->is_shared() || is_alive->do_object_b(entry->literal())) {
         p = entry->next_addr();
       } else {
         *p = entry->next();
         the_table()->free_entry(entry);
       }
+      entry = (HashtableEntry<oop>*)HashtableEntry<oop>::make_ptr(*p);
     }
   }
 }
@@ -781,7 +811,8 @@
 // with the existing strings.   Set flag to use the alternate hash code afterwards.
 void StringTable::rehash_table() {
   assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
-  assert(!DumpSharedSpaces, "this should never happen with -Xshare:dump");
+  // This should never happen with -Xshare:dump but it might in testing mode.
+  if (DumpSharedSpaces) return;
   StringTable* new_table = new StringTable();
 
   // Initialize new global seed for hashing.
--- a/src/share/vm/classfile/symbolTable.hpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/classfile/symbolTable.hpp	Fri Jun 29 15:43:13 2012 -0700
@@ -88,7 +88,6 @@
   static int symbols_counted;
 
   Symbol* allocate_symbol(const u1* name, int len, TRAPS);   // Assumes no characters larger than 0x7F
-  bool allocate_symbols(int names_count, const u1** names, int* lengths, Symbol** syms, TRAPS);
 
   // Adding elements
   Symbol* basic_add(int index, u1* name, int len,
@@ -144,7 +143,7 @@
     _the_table = new SymbolTable(t, number_of_entries);
   }
 
-  static unsigned int hash_symbol(const char* s, int len, unsigned int hashValue = 0);
+  static unsigned int hash_symbol(const char* s, int len);
 
   static Symbol* lookup(const char* name, int len, TRAPS);
   // lookup only, won't add. Also calculate hash.
@@ -279,7 +278,7 @@
   // Hashing algorithm, used as the hash value used by the
   //     StringTable for bucket selection and comparison (stored in the
   //     HashtableEntry structures).  This is used in the String.intern() method.
-  static unsigned int hash_string(const jchar* s, int len, unsigned int hashValue = 0);
+  static unsigned int hash_string(const jchar* s, int len);
 
   // Internal test.
   static void test_alt_hash() PRODUCT_RETURN;
--- a/src/share/vm/memory/universe.hpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/memory/universe.hpp	Fri Jun 29 15:43:13 2012 -0700
@@ -273,7 +273,7 @@
   }
 
   static klassOop typeArrayKlassObj(BasicType t) {
-    assert((uint)t < T_VOID+1, "range check");
+    assert((uint)t < T_VOID+1, err_msg("range check for type: %s", type2name(t)));
     assert(_typeArrayKlassObjs[t] != NULL, "domain check");
     return _typeArrayKlassObjs[t];
   }
--- a/src/share/vm/opto/callGenerator.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/opto/callGenerator.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -172,9 +172,11 @@
 
 JVMState* DynamicCallGenerator::generate(JVMState* jvms) {
   GraphKit kit(jvms);
+  Compile* C = kit.C;
+  PhaseGVN& gvn = kit.gvn();
 
-  if (kit.C->log() != NULL) {
-    kit.C->log()->elem("dynamic_call bci='%d'", jvms->bci());
+  if (C->log() != NULL) {
+    C->log()->elem("dynamic_call bci='%d'", jvms->bci());
   }
 
   // Get the constant pool cache from the caller class.
@@ -190,18 +192,21 @@
   size_t call_site_offset = cpcache->get_f1_offset(index);
 
   // Load the CallSite object from the constant pool cache.
-  const TypeOopPtr* cpcache_ptr = TypeOopPtr::make_from_constant(cpcache);
-  Node* cpcache_adr = kit.makecon(cpcache_ptr);
-  Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, cpcache_adr, call_site_offset);
-  Node* call_site = kit.make_load(kit.control(), call_site_adr, TypeInstPtr::BOTTOM, T_OBJECT, Compile::AliasIdxRaw);
+  const TypeOopPtr* cpcache_type   = TypeOopPtr::make_from_constant(cpcache);  // returns TypeAryPtr of type T_OBJECT
+  const TypeOopPtr* call_site_type = TypeOopPtr::make_from_klass(C->env()->CallSite_klass());
+  Node* cpcache_adr   = kit.makecon(cpcache_type);
+  Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, call_site_offset);
+  // The oops in the constant pool cache are not compressed; load then as raw pointers.
+  Node* call_site     = kit.make_load(kit.control(), call_site_adr, call_site_type, T_ADDRESS, Compile::AliasIdxRaw);
 
   // Load the target MethodHandle from the CallSite object.
-  Node* target_mh_adr = kit.basic_plus_adr(call_site, call_site, java_lang_invoke_CallSite::target_offset_in_bytes());
-  Node* target_mh = kit.make_load(kit.control(), target_mh_adr, TypeInstPtr::BOTTOM, T_OBJECT);
+  const TypeOopPtr* target_type = TypeOopPtr::make_from_klass(C->env()->MethodHandle_klass());
+  Node* target_mh_adr = kit.basic_plus_adr(call_site, java_lang_invoke_CallSite::target_offset_in_bytes());
+  Node* target_mh     = kit.make_load(kit.control(), target_mh_adr, target_type, T_OBJECT);
 
   address resolve_stub = SharedRuntime::get_resolve_opt_virtual_call_stub();
 
-  CallStaticJavaNode *call = new (kit.C, tf()->domain()->cnt()) CallStaticJavaNode(tf(), resolve_stub, method(), kit.bci());
+  CallStaticJavaNode* call = new (C, tf()->domain()->cnt()) CallStaticJavaNode(tf(), resolve_stub, method(), kit.bci());
   // invokedynamic is treated as an optimized invokevirtual.
   call->set_optimized_virtual(true);
   // Take extra care (in the presence of argument motion) not to trash the SP:
@@ -785,9 +790,10 @@
 
 JVMState* PredictedDynamicCallGenerator::generate(JVMState* jvms) {
   GraphKit kit(jvms);
+  Compile* C = kit.C;
   PhaseGVN& gvn = kit.gvn();
 
-  CompileLog* log = kit.C->log();
+  CompileLog* log = C->log();
   if (log != NULL) {
     log->elem("predicted_dynamic_call bci='%d'", jvms->bci());
   }
@@ -803,8 +809,8 @@
     Node* receiver = kit.argument(0);
 
     // Check if the MethodHandle is the expected one
-    Node* cmp = gvn.transform(new(kit.C, 3) CmpPNode(receiver, predicted_mh));
-    bol = gvn.transform(new(kit.C, 2) BoolNode(cmp, BoolTest::eq) );
+    Node* cmp = gvn.transform(new (C, 3) CmpPNode(receiver, predicted_mh));
+    bol = gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::eq) );
   } else {
     // Get the constant pool cache from the caller class.
     ciMethod* caller_method = jvms->method();
@@ -818,22 +824,25 @@
     size_t call_site_offset = cpcache->get_f1_offset(index);
 
     // Load the CallSite object from the constant pool cache.
-    const TypeOopPtr* cpcache_ptr = TypeOopPtr::make_from_constant(cpcache);
-    Node* cpcache_adr   = kit.makecon(cpcache_ptr);
-    Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, cpcache_adr, call_site_offset);
-    Node* call_site     = kit.make_load(kit.control(), call_site_adr, TypeInstPtr::BOTTOM, T_OBJECT, Compile::AliasIdxRaw);
+    const TypeOopPtr* cpcache_type   = TypeOopPtr::make_from_constant(cpcache);  // returns TypeAryPtr of type T_OBJECT
+    const TypeOopPtr* call_site_type = TypeOopPtr::make_from_klass(C->env()->CallSite_klass());
+    Node* cpcache_adr   = kit.makecon(cpcache_type);
+    Node* call_site_adr = kit.basic_plus_adr(cpcache_adr, call_site_offset);
+    // The oops in the constant pool cache are not compressed; load then as raw pointers.
+    Node* call_site     = kit.make_load(kit.control(), call_site_adr, call_site_type, T_ADDRESS, Compile::AliasIdxRaw);
 
     // Load the target MethodHandle from the CallSite object.
+    const TypeOopPtr* target_type = TypeOopPtr::make_from_klass(C->env()->MethodHandle_klass());
     Node* target_adr = kit.basic_plus_adr(call_site, call_site, java_lang_invoke_CallSite::target_offset_in_bytes());
-    Node* target_mh  = kit.make_load(kit.control(), target_adr, TypeInstPtr::BOTTOM, T_OBJECT);
+    Node* target_mh  = kit.make_load(kit.control(), target_adr, target_type, T_OBJECT);
 
     // Check if the MethodHandle is still the same.
-    Node* cmp = gvn.transform(new(kit.C, 3) CmpPNode(target_mh, predicted_mh));
-    bol = gvn.transform(new(kit.C, 2) BoolNode(cmp, BoolTest::eq) );
+    Node* cmp = gvn.transform(new (C, 3) CmpPNode(target_mh, predicted_mh));
+    bol = gvn.transform(new (C, 2) BoolNode(cmp, BoolTest::eq) );
   }
   IfNode* iff = kit.create_and_xform_if(kit.control(), bol, _hit_prob, COUNT_UNKNOWN);
-  kit.set_control( gvn.transform(new(kit.C, 1) IfTrueNode (iff)));
-  Node* slow_ctl = gvn.transform(new(kit.C, 1) IfFalseNode(iff));
+  kit.set_control( gvn.transform(new (C, 1) IfTrueNode (iff)));
+  Node* slow_ctl = gvn.transform(new (C, 1) IfFalseNode(iff));
 
   SafePointNode* slow_map = NULL;
   JVMState* slow_jvms;
@@ -882,7 +891,7 @@
 
   // Finish the diamond.
   kit.C->set_has_split_ifs(true); // Has chance for split-if optimization
-  RegionNode* region = new (kit.C, 3) RegionNode(3);
+  RegionNode* region = new (C, 3) RegionNode(3);
   region->init_req(1, kit.control());
   region->init_req(2, slow_map->control());
   kit.set_control(gvn.transform(region));
--- a/src/share/vm/opto/chaitin.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/opto/chaitin.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -1483,7 +1483,7 @@
 
   // Check for AddP-related opcodes
   if( !derived->is_Phi() ) {
-    assert( derived->as_Mach()->ideal_Opcode() == Op_AddP, "" );
+    assert(derived->as_Mach()->ideal_Opcode() == Op_AddP, err_msg("but is: %s", derived->Name()));
     Node *base = derived->in(AddPNode::Base);
     derived_base_map[derived->_idx] = base;
     return base;
--- a/src/share/vm/opto/library_call.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/opto/library_call.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -3533,8 +3533,10 @@
     }
 
     // Bail out if length is negative.
-    // ...Not needed, since the new_array will throw the right exception.
-    //generate_negative_guard(length, bailout, &length);
+    // Without this the new_array would throw
+    // NegativeArraySizeException but IllegalArgumentException is what
+    // should be thrown
+    generate_negative_guard(length, bailout, &length);
 
     if (bailout->req() > 1) {
       PreserveJVMState pjvms(this);
@@ -3558,7 +3560,9 @@
       // Extreme case:  Arrays.copyOf((Integer[])x, 10, String[].class).
       // This will fail a store-check if x contains any non-nulls.
       bool disjoint_bases = true;
-      bool length_never_negative = true;
+      // if start > orig_length then the length of the copy may be
+      // negative.
+      bool length_never_negative = !is_copyOfRange;
       generate_arraycopy(TypeAryPtr::OOPS, T_OBJECT,
                          original, start, newcopy, intcon(0), moved,
                          disjoint_bases, length_never_negative);
--- a/src/share/vm/opto/stringopts.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/opto/stringopts.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -112,6 +112,7 @@
     _arguments->ins_req(0, value);
     _mode.insert_before(0, mode);
   }
+
   void push_string(Node* value) {
     push(value, StringMode);
   }
@@ -125,9 +126,56 @@
     push(value, CharMode);
   }
 
+  static bool is_SB_toString(Node* call) {
+    if (call->is_CallStaticJava()) {
+      CallStaticJavaNode* csj = call->as_CallStaticJava();
+      ciMethod* m = csj->method();
+      if (m != NULL &&
+          (m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString ||
+           m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  static Node* skip_string_null_check(Node* value) {
+    // Look for a diamond shaped Null check of toString() result
+    // (could be code from String.valueOf()):
+    // (Proj == NULL) ? "null":"CastPP(Proj)#NotNULL
+    if (value->is_Phi()) {
+      int true_path = value->as_Phi()->is_diamond_phi();
+      if (true_path != 0) {
+        // phi->region->if_proj->ifnode->bool
+        BoolNode* b = value->in(0)->in(1)->in(0)->in(1)->as_Bool();
+        Node* cmp = b->in(1);
+        Node* v1 = cmp->in(1);
+        Node* v2 = cmp->in(2);
+        // Null check of the return of toString which can simply be skipped.
+        if (b->_test._test == BoolTest::ne &&
+            v2->bottom_type() == TypePtr::NULL_PTR &&
+            value->in(true_path)->Opcode() == Op_CastPP &&
+            value->in(true_path)->in(1) == v1 &&
+            v1->is_Proj() && is_SB_toString(v1->in(0))) {
+          return v1;
+        }
+      }
+    }
+    return value;
+  }
+
   Node* argument(int i) {
     return _arguments->in(i);
   }
+  Node* argument_uncast(int i) {
+    Node* arg = argument(i);
+    int amode = mode(i);
+    if (amode == StringConcat::StringMode ||
+        amode == StringConcat::StringNullCheckMode) {
+      arg = skip_string_null_check(arg);
+    }
+    return arg;
+  }
   void set_argument(int i, Node* value) {
     _arguments->set_req(i, value);
   }
@@ -206,9 +254,11 @@
 
 
 void StringConcat::eliminate_unneeded_control() {
-  eliminate_initialize(begin()->initialization());
   for (uint i = 0; i < _control.size(); i++) {
     Node* n = _control.at(i);
+    if (n->is_Allocate()) {
+      eliminate_initialize(n->as_Allocate()->initialization());
+    }
     if (n->is_Call()) {
       if (n != _end) {
         eliminate_call(n->as_Call());
@@ -239,14 +289,15 @@
   assert(result->_control.contains(other->_end), "what?");
   assert(result->_control.contains(_begin), "what?");
   for (int x = 0; x < num_arguments(); x++) {
-    if (argument(x) == arg) {
+    Node* argx = argument_uncast(x);
+    if (argx == arg) {
       // replace the toString result with the all the arguments that
       // made up the other StringConcat
       for (int y = 0; y < other->num_arguments(); y++) {
         result->append(other->argument(y), other->mode(y));
       }
     } else {
-      result->append(argument(x), mode(x));
+      result->append(argx, mode(x));
     }
   }
   result->set_allocation(other->_begin);
@@ -327,14 +378,9 @@
 
   while (worklist.size() > 0) {
     Node* ctrl = worklist.pop();
-    if (ctrl->is_CallStaticJava()) {
+    if (StringConcat::is_SB_toString(ctrl)) {
       CallStaticJavaNode* csj = ctrl->as_CallStaticJava();
-      ciMethod* m = csj->method();
-      if (m != NULL &&
-          (m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString ||
-           m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString)) {
-        string_calls.push(csj);
-      }
+      string_calls.push(csj);
     }
     if (ctrl->in(0) != NULL && !_visited.test_set(ctrl->in(0)->_idx)) {
       worklist.push(ctrl->in(0));
@@ -550,44 +596,40 @@
   for (int c = 0; c < concats.length(); c++) {
     StringConcat* sc = concats.at(c);
     for (int i = 0; i < sc->num_arguments(); i++) {
-      Node* arg = sc->argument(i);
-      if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) {
+      Node* arg = sc->argument_uncast(i);
+      if (arg->is_Proj() && StringConcat::is_SB_toString(arg->in(0))) {
         CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava();
-        if (csj->method() != NULL &&
-            (csj->method()->intrinsic_id() == vmIntrinsics::_StringBuilder_toString ||
-             csj->method()->intrinsic_id() == vmIntrinsics::_StringBuffer_toString)) {
-          for (int o = 0; o < concats.length(); o++) {
-            if (c == o) continue;
-            StringConcat* other = concats.at(o);
-            if (other->end() == csj) {
+        for (int o = 0; o < concats.length(); o++) {
+          if (c == o) continue;
+          StringConcat* other = concats.at(o);
+          if (other->end() == csj) {
+#ifndef PRODUCT
+            if (PrintOptimizeStringConcat) {
+              tty->print_cr("considering stacked concats");
+            }
+#endif
+
+            StringConcat* merged = sc->merge(other, arg);
+            if (merged->validate_control_flow()) {
 #ifndef PRODUCT
               if (PrintOptimizeStringConcat) {
-                tty->print_cr("considering stacked concats");
+                tty->print_cr("stacking would succeed");
               }
 #endif
-
-              StringConcat* merged = sc->merge(other, arg);
-              if (merged->validate_control_flow()) {
+              if (c < o) {
+                concats.remove_at(o);
+                concats.at_put(c, merged);
+              } else {
+                concats.remove_at(c);
+                concats.at_put(o, merged);
+              }
+              goto restart;
+            } else {
 #ifndef PRODUCT
-                if (PrintOptimizeStringConcat) {
-                  tty->print_cr("stacking would succeed");
-                }
+              if (PrintOptimizeStringConcat) {
+                tty->print_cr("stacking would fail");
+              }
 #endif
-                if (c < o) {
-                  concats.remove_at(o);
-                  concats.at_put(c, merged);
-                } else {
-                  concats.remove_at(c);
-                  concats.at_put(o, merged);
-                }
-                goto restart;
-              } else {
-#ifndef PRODUCT
-                if (PrintOptimizeStringConcat) {
-                  tty->print_cr("stacking would fail");
-                }
-#endif
-              }
             }
           }
         }
--- a/src/share/vm/opto/type.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/opto/type.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -2472,18 +2472,26 @@
 //------------------------------make_from_constant-----------------------------
 // Make a java pointer from an oop constant
 const TypeOopPtr* TypeOopPtr::make_from_constant(ciObject* o, bool require_constant) {
-  if (o->is_method_data() || o->is_method() || o->is_cpcache()) {
+  if (o->is_method_data() || o->is_method()) {
     // Treat much like a typeArray of bytes, like below, but fake the type...
-    const Type* etype = (Type*)get_const_basic_type(T_BYTE);
+    const BasicType bt = T_BYTE;
+    const Type* etype = get_const_basic_type(bt);
     const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
-    ciKlass *klass = ciTypeArrayKlass::make((BasicType) T_BYTE);
-    assert(o->can_be_constant(), "method data oops should be tenured");
-    const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
-    return arr;
+    ciKlass* klass = ciArrayKlass::make(ciType::make(bt));
+    assert(o->can_be_constant(), "should be tenured");
+    return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
+  } else if (o->is_cpcache()) {
+    // Treat much like a objArray, like below, but fake the type...
+    const BasicType bt = T_OBJECT;
+    const Type* etype = get_const_basic_type(bt);
+    const TypeAry* arr0 = TypeAry::make(etype, TypeInt::POS);
+    ciKlass* klass = ciArrayKlass::make(ciType::make(bt));
+    assert(o->can_be_constant(), "should be tenured");
+    return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
   } else {
     assert(o->is_java_object(), "must be java language object");
     assert(!o->is_null_object(), "null object not yet handled here.");
-    ciKlass *klass = o->klass();
+    ciKlass* klass = o->klass();
     if (klass->is_instance_klass()) {
       // Element is an instance
       if (require_constant) {
@@ -2494,8 +2502,7 @@
       return TypeInstPtr::make(o);
     } else if (klass->is_obj_array_klass()) {
       // Element is an object array. Recursively call ourself.
-      const Type *etype =
-        TypeOopPtr::make_from_klass_raw(klass->as_obj_array_klass()->element_klass());
+      const Type *etype = make_from_klass_raw(klass->as_obj_array_klass()->element_klass());
       const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()));
       // We used to pass NotNull in here, asserting that the sub-arrays
       // are all not-null.  This is not true in generally, as code can
@@ -2505,12 +2512,10 @@
       } else if (!o->should_be_constant()) {
         return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0);
       }
-      const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
-      return arr;
+      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
     } else if (klass->is_type_array_klass()) {
       // Element is an typeArray
-      const Type* etype =
-        (Type*)get_const_basic_type(klass->as_type_array_klass()->element_type());
+      const Type* etype = get_const_basic_type(klass->as_type_array_klass()->element_type());
       const TypeAry* arr0 = TypeAry::make(etype, TypeInt::make(o->as_array()->length()));
       // We used to pass NotNull in here, asserting that the array pointer
       // is not-null. That was not true in general.
@@ -2519,12 +2524,11 @@
       } else if (!o->should_be_constant()) {
         return TypeAryPtr::make(TypePtr::NotNull, arr0, klass, true, 0);
       }
-      const TypeAryPtr* arr = TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
-      return arr;
+      return TypeAryPtr::make(TypePtr::Constant, o, arr0, klass, true, 0);
     }
   }
 
-  ShouldNotReachHere();
+  fatal("unhandled object type");
   return NULL;
 }
 
--- a/src/share/vm/utilities/hashtable.cpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/utilities/hashtable.cpp	Fri Jun 29 15:43:13 2012 -0700
@@ -24,6 +24,7 @@
 
 #include "precompiled.hpp"
 #include "memory/allocation.inline.hpp"
+#include "memory/filemap.hpp"
 #include "memory/resourceArea.hpp"
 #include "oops/oop.inline.hpp"
 #include "runtime/safepoint.hpp"
@@ -119,8 +120,16 @@
       // Get a new index relative to the new table (can also change size)
       int index = new_table->hash_to_index(hashValue);
       p->set_hash(hashValue);
+      // Keep the shared bit in the Hashtable entry to indicate that this entry
+      // can't be deleted.   The shared bit is the LSB in the _next field so
+      // walking the hashtable past these entries requires
+      // BasicHashtableEntry::make_ptr() call.
+      bool keep_shared = p->is_shared();
       unlink_entry(p);
       new_table->add_entry(index, p);
+      if (keep_shared) {
+        p->set_shared();
+      }
       p = next;
     }
   }
@@ -135,6 +144,19 @@
   free_buckets();
 }
 
+void BasicHashtable::free_buckets() {
+  if (NULL != _buckets) {
+    // Don't delete the buckets in the shared space.  They aren't
+    // allocated by os::malloc
+    if (!UseSharedSpaces ||
+        !FileMapInfo::current_info()->is_in_shared_space(_buckets)) {
+       FREE_C_HEAP_ARRAY(HashtableBucket, _buckets);
+    }
+    _buckets = NULL;
+  }
+}
+
+
 // Reverse the order of elements in the hash buckets.
 
 void BasicHashtable::reverse() {
--- a/src/share/vm/utilities/hashtable.hpp	Wed Jun 27 17:19:10 2012 -0700
+++ b/src/share/vm/utilities/hashtable.hpp	Fri Jun 29 15:43:13 2012 -0700
@@ -217,12 +217,7 @@
   }
 
   // Free the buckets in this hashtable
-  void free_buckets() {
-    if (NULL != _buckets) {
-      FREE_C_HEAP_ARRAY(HashtableBucket, _buckets);
-      _buckets = NULL;
-    }
-  }
+  void free_buckets();
 
 public:
   int table_size() { return _table_size; }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/compiler/7174363/Test7174363.java	Fri Jun 29 15:43:13 2012 -0700
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/**
+ * @test
+ * @bug 7174363
+ * @summary crash with Arrays.copyOfRange(original, from, to) when from > original.length
+ *
+ * @run main/othervm -XX:-BackgroundCompilation Test7174363
+ */
+
+import java.util.*;
+
+public class Test7174363 {
+
+  static Object[] m(Object[] original, int from, int to) {
+    return Arrays.copyOfRange(original, from, to, Object[].class);
+  }
+
+  static public void main(String[] args) {
+    Object[] orig = new Object[10];
+    for (int i = 0; i < 20000; i++) {
+      try {
+        m(orig, 15, 20);
+      } catch(ArrayIndexOutOfBoundsException excp) {}
+    }
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/compiler/7179138/Test7179138_1.java	Fri Jun 29 15:43:13 2012 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 Skip Balk.  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7179138
+ * @summary Incorrect result with String concatenation optimization
+ * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation Test7179138_1
+ *
+ * @author Skip Balk
+ */
+
+public class Test7179138_1 {
+    public static void main(String[] args) throws Exception {
+        System.out.println("Java Version: " + System.getProperty("java.vm.version"));
+        long[] durations = new long[60];
+        for (int i = 0; i < 100000; i++) {
+            // this empty for-loop is required to reproduce this bug
+            for (long duration : durations) {
+                // do nothing
+            }
+            {
+                String s = "test";
+                int len = s.length();
+
+                s = new StringBuilder(String.valueOf(s)).append(s).toString();
+                len = len + len;
+
+                s = new StringBuilder(String.valueOf(s)).append(s).toString();
+                len = len + len;
+
+                s = new StringBuilder(String.valueOf(s)).append(s).toString();
+                len = len + len;
+
+                if (s.length() != len) {
+                    System.out.println("Failed at iteration: " + i);
+                    System.out.println("Length mismatch: " + s.length() + " <> " + len);
+                    System.out.println("Expected: \"" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "\"");
+                    System.out.println("Actual:   \"" + s + "\"");
+                    System.exit(97);
+                }
+            }
+        }
+    }
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/compiler/7179138/Test7179138_2.java	Fri Jun 29 15:43:13 2012 -0700
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012 Skip Balk.  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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7179138
+ * @summary Incorrect result with String concatenation optimization
+ * @run main/othervm -Xbatch -XX:+IgnoreUnrecognizedVMOptions -XX:-TieredCompilation Test7179138_2
+ *
+ * @author Skip Balk
+ */
+
+public class Test7179138_2 {
+    public static void main(String[] args) throws Exception {
+        System.out.println("Java Version: " + System.getProperty("java.vm.version"));
+        long[] durations = new long[60];
+        for (int i = 0; i < 100000; i++) {
+            // this empty for-loop is required to reproduce this bug
+            for (long duration : durations) {
+                // do nothing
+            }
+            {
+                String s = "test";
+                int len = s.length();
+
+                s = s + s;
+                len = len + len;
+
+                s = s + s;
+                len = len + len;
+
+                s = s + s;
+                len = len + len;
+
+                if (s.length() != len) {
+                    System.out.println("Failed at iteration: " + i);
+                    System.out.println("Length mismatch: " + s.length() + " <> " + len);
+                    System.out.println("Expected: \"" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "test" + "\"");
+                    System.out.println("Actual:   \"" + s + "\"");
+                    System.exit(0);
+                }
+            }
+        }
+    }
+}
+