# HG changeset patch # User amurillo # Date 1341009793 25200 # Node ID 270a40a57b3d05ca64070208dcbb895b5b509d8e # Parent c4dedc59d44dd30b345d43603e6d4fba4fc16715# Parent 981f551d0f91ee6752293165c127eed3227e40a3 Merge diff -r c4dedc59d44d -r 270a40a57b3d make/hotspot_version --- 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 diff -r c4dedc59d44d -r 270a40a57b3d make/solaris/makefiles/dtrace.make --- 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 diff -r c4dedc59d44d -r 270a40a57b3d src/os_cpu/bsd_x86/vm/os_bsd_x86.cpp --- 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 diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/classfile/symbolTable.cpp --- 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** p = the_table()->bucket_addr(i); *p != NULL; ) { - HashtableEntry* entry = *p; - if (entry->is_shared()) { + HashtableEntry** p = the_table()->bucket_addr(i); + HashtableEntry* 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*)HashtableEntry::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* 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; iequals(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* 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** p = the_table()->bucket_addr(i); *p != NULL; ) { - HashtableEntry* entry = *p; - if (entry->is_shared()) { + HashtableEntry** p = the_table()->bucket_addr(i); + HashtableEntry* 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*)HashtableEntry::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. diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/classfile/symbolTable.hpp --- 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; diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/memory/universe.hpp --- 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]; } diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/opto/callGenerator.cpp --- 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)); diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/opto/chaitin.cpp --- 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; diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/opto/library_call.cpp --- 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); diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/opto/stringopts.cpp --- 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"); - } + if (PrintOptimizeStringConcat) { + tty->print_cr("considering stacked concats"); + } #endif - StringConcat* merged = sc->merge(other, arg); - if (merged->validate_control_flow()) { + StringConcat* merged = sc->merge(other, arg); + if (merged->validate_control_flow()) { #ifndef PRODUCT - if (PrintOptimizeStringConcat) { - tty->print_cr("stacking would succeed"); - } + if (PrintOptimizeStringConcat) { + tty->print_cr("stacking would succeed"); + } #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; + 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"); - } + if (PrintOptimizeStringConcat) { + tty->print_cr("stacking would fail"); + } #endif - } } } } diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/opto/type.cpp --- 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; } diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/utilities/hashtable.cpp --- 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() { diff -r c4dedc59d44d -r 270a40a57b3d src/share/vm/utilities/hashtable.hpp --- 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; } diff -r c4dedc59d44d -r 270a40a57b3d test/compiler/7174363/Test7174363.java --- /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) {} + } + } +} diff -r c4dedc59d44d -r 270a40a57b3d test/compiler/7179138/Test7179138_1.java --- /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); + } + } + } + } +} + diff -r c4dedc59d44d -r 270a40a57b3d test/compiler/7179138/Test7179138_2.java --- /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); + } + } + } + } +} +