Thu, 12 Nov 2009 09:24:21 -0800
6892658: C2 should optimize some stringbuilder patterns
Reviewed-by: kvn, twisti
1.1 --- a/src/share/vm/ci/ciEnv.cpp Fri Nov 27 07:56:58 2009 -0800 1.2 +++ b/src/share/vm/ci/ciEnv.cpp Thu Nov 12 09:24:21 2009 -0800 1.3 @@ -46,6 +46,9 @@ 1.4 ciInstanceKlass* ciEnv::_Thread; 1.5 ciInstanceKlass* ciEnv::_OutOfMemoryError; 1.6 ciInstanceKlass* ciEnv::_String; 1.7 +ciInstanceKlass* ciEnv::_StringBuffer; 1.8 +ciInstanceKlass* ciEnv::_StringBuilder; 1.9 +ciInstanceKlass* ciEnv::_Integer; 1.10 1.11 ciSymbol* ciEnv::_unloaded_cisymbol = NULL; 1.12 ciInstanceKlass* ciEnv::_unloaded_ciinstance_klass = NULL; 1.13 @@ -110,6 +113,8 @@ 1.14 _ArrayIndexOutOfBoundsException_instance = NULL; 1.15 _ArrayStoreException_instance = NULL; 1.16 _ClassCastException_instance = NULL; 1.17 + _the_null_string = NULL; 1.18 + _the_min_jint_string = NULL; 1.19 } 1.20 1.21 ciEnv::ciEnv(Arena* arena) { 1.22 @@ -163,6 +168,8 @@ 1.23 _ArrayIndexOutOfBoundsException_instance = NULL; 1.24 _ArrayStoreException_instance = NULL; 1.25 _ClassCastException_instance = NULL; 1.26 + _the_null_string = NULL; 1.27 + _the_min_jint_string = NULL; 1.28 } 1.29 1.30 ciEnv::~ciEnv() { 1.31 @@ -248,6 +255,22 @@ 1.32 return _ClassCastException_instance; 1.33 } 1.34 1.35 +ciInstance* ciEnv::the_null_string() { 1.36 + if (_the_null_string == NULL) { 1.37 + VM_ENTRY_MARK; 1.38 + _the_null_string = get_object(Universe::the_null_string())->as_instance(); 1.39 + } 1.40 + return _the_null_string; 1.41 +} 1.42 + 1.43 +ciInstance* ciEnv::the_min_jint_string() { 1.44 + if (_the_min_jint_string == NULL) { 1.45 + VM_ENTRY_MARK; 1.46 + _the_min_jint_string = get_object(Universe::the_min_jint_string())->as_instance(); 1.47 + } 1.48 + return _the_min_jint_string; 1.49 +} 1.50 + 1.51 // ------------------------------------------------------------------ 1.52 // ciEnv::get_method_from_handle 1.53 ciMethod* ciEnv::get_method_from_handle(jobject method) {
2.1 --- a/src/share/vm/ci/ciEnv.hpp Fri Nov 27 07:56:58 2009 -0800 2.2 +++ b/src/share/vm/ci/ciEnv.hpp Thu Nov 12 09:24:21 2009 -0800 2.3 @@ -82,6 +82,9 @@ 2.4 static ciInstanceKlass* _Thread; 2.5 static ciInstanceKlass* _OutOfMemoryError; 2.6 static ciInstanceKlass* _String; 2.7 + static ciInstanceKlass* _StringBuffer; 2.8 + static ciInstanceKlass* _StringBuilder; 2.9 + static ciInstanceKlass* _Integer; 2.10 2.11 static ciSymbol* _unloaded_cisymbol; 2.12 static ciInstanceKlass* _unloaded_ciinstance_klass; 2.13 @@ -97,6 +100,9 @@ 2.14 ciInstance* _ArrayStoreException_instance; 2.15 ciInstance* _ClassCastException_instance; 2.16 2.17 + ciInstance* _the_null_string; // The Java string "null" 2.18 + ciInstance* _the_min_jint_string; // The Java string "-2147483648" 2.19 + 2.20 // Look up a klass by name from a particular class loader (the accessor's). 2.21 // If require_local, result must be defined in that class loader, or NULL. 2.22 // If !require_local, a result from remote class loader may be reported, 2.23 @@ -310,6 +316,15 @@ 2.24 ciInstanceKlass* String_klass() { 2.25 return _String; 2.26 } 2.27 + ciInstanceKlass* StringBuilder_klass() { 2.28 + return _StringBuilder; 2.29 + } 2.30 + ciInstanceKlass* StringBuffer_klass() { 2.31 + return _StringBuffer; 2.32 + } 2.33 + ciInstanceKlass* Integer_klass() { 2.34 + return _Integer; 2.35 + } 2.36 ciInstance* NullPointerException_instance() { 2.37 assert(_NullPointerException_instance != NULL, "initialization problem"); 2.38 return _NullPointerException_instance; 2.39 @@ -324,6 +339,9 @@ 2.40 ciInstance* ArrayStoreException_instance(); 2.41 ciInstance* ClassCastException_instance(); 2.42 2.43 + ciInstance* the_null_string(); 2.44 + ciInstance* the_min_jint_string(); 2.45 + 2.46 static ciSymbol* unloaded_cisymbol() { 2.47 return _unloaded_cisymbol; 2.48 }
3.1 --- a/src/share/vm/ci/ciInstanceKlass.cpp Fri Nov 27 07:56:58 2009 -0800 3.2 +++ b/src/share/vm/ci/ciInstanceKlass.cpp Thu Nov 12 09:24:21 2009 -0800 3.3 @@ -341,6 +341,20 @@ 3.4 } 3.5 3.6 // ------------------------------------------------------------------ 3.7 +// ciInstanceKlass::get_field_by_name 3.8 +ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static) { 3.9 + VM_ENTRY_MARK; 3.10 + instanceKlass* k = get_instanceKlass(); 3.11 + fieldDescriptor fd; 3.12 + klassOop def = k->find_field(name->get_symbolOop(), signature->get_symbolOop(), is_static, &fd); 3.13 + if (def == NULL) { 3.14 + return NULL; 3.15 + } 3.16 + ciField* field = new (CURRENT_THREAD_ENV->arena()) ciField(&fd); 3.17 + return field; 3.18 +} 3.19 + 3.20 +// ------------------------------------------------------------------ 3.21 // ciInstanceKlass::non_static_fields. 3.22 3.23 class NonStaticFieldFiller: public FieldClosure {
4.1 --- a/src/share/vm/ci/ciInstanceKlass.hpp Fri Nov 27 07:56:58 2009 -0800 4.2 +++ b/src/share/vm/ci/ciInstanceKlass.hpp Thu Nov 12 09:24:21 2009 -0800 4.3 @@ -148,6 +148,7 @@ 4.4 4.5 ciInstanceKlass* get_canonical_holder(int offset); 4.6 ciField* get_field_by_offset(int field_offset, bool is_static); 4.7 + ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static); 4.8 4.9 GrowableArray<ciField*>* non_static_fields(); 4.10
5.1 --- a/src/share/vm/ci/ciObjectFactory.cpp Fri Nov 27 07:56:58 2009 -0800 5.2 +++ b/src/share/vm/ci/ciObjectFactory.cpp Thu Nov 12 09:24:21 2009 -0800 5.3 @@ -168,6 +168,15 @@ 5.4 ciEnv::_String = 5.5 get(SystemDictionary::string_klass()) 5.6 ->as_instance_klass(); 5.7 + ciEnv::_StringBuffer = 5.8 + get(SystemDictionary::stringBuffer_klass()) 5.9 + ->as_instance_klass(); 5.10 + ciEnv::_StringBuilder = 5.11 + get(SystemDictionary::StringBuilder_klass()) 5.12 + ->as_instance_klass(); 5.13 + ciEnv::_Integer = 5.14 + get(SystemDictionary::int_klass()) 5.15 + ->as_instance_klass(); 5.16 5.17 for (int len = -1; len != _ci_objects->length(); ) { 5.18 len = _ci_objects->length();
6.1 --- a/src/share/vm/classfile/systemDictionary.hpp Fri Nov 27 07:56:58 2009 -0800 6.2 +++ b/src/share/vm/classfile/systemDictionary.hpp Thu Nov 12 09:24:21 2009 -0800 6.3 @@ -150,6 +150,7 @@ 6.4 template(vector_klass, java_util_Vector, Pre) \ 6.5 template(hashtable_klass, java_util_Hashtable, Pre) \ 6.6 template(stringBuffer_klass, java_lang_StringBuffer, Pre) \ 6.7 + template(StringBuilder_klass, java_lang_StringBuilder, Pre) \ 6.8 \ 6.9 /* It's NULL in non-1.4 JDKs. */ \ 6.10 template(stackTraceElement_klass, java_lang_StackTraceElement, Opt) \
7.1 --- a/src/share/vm/classfile/vmSymbols.cpp Fri Nov 27 07:56:58 2009 -0800 7.2 +++ b/src/share/vm/classfile/vmSymbols.cpp Thu Nov 12 09:24:21 2009 -0800 7.3 @@ -303,6 +303,11 @@ 7.4 const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED; 7.5 return (flags & (req | neg)) == req; 7.6 } 7.7 +inline bool match_F_Y(jshort flags) { 7.8 + const int req = JVM_ACC_SYNCHRONIZED; 7.9 + const int neg = JVM_ACC_STATIC; 7.10 + return (flags & (req | neg)) == req; 7.11 +} 7.12 inline bool match_F_RN(jshort flags) { 7.13 const int req = JVM_ACC_NATIVE; 7.14 const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED; 7.15 @@ -361,6 +366,7 @@ 7.16 const char* sname = vmSymbols::name_for(signature_for(id)); 7.17 const char* fname = ""; 7.18 switch (flags_for(id)) { 7.19 + case F_Y: fname = "synchronized "; break; 7.20 case F_RN: fname = "native "; break; 7.21 case F_SN: fname = "native static "; break; 7.22 case F_S: fname = "static "; break;
8.1 --- a/src/share/vm/classfile/vmSymbols.hpp Fri Nov 27 07:56:58 2009 -0800 8.2 +++ b/src/share/vm/classfile/vmSymbols.hpp Thu Nov 12 09:24:21 2009 -0800 8.3 @@ -84,6 +84,7 @@ 8.4 template(java_lang_reflect_Field, "java/lang/reflect/Field") \ 8.5 template(java_lang_reflect_Array, "java/lang/reflect/Array") \ 8.6 template(java_lang_StringBuffer, "java/lang/StringBuffer") \ 8.7 + template(java_lang_StringBuilder, "java/lang/StringBuilder") \ 8.8 template(java_lang_CharSequence, "java/lang/CharSequence") \ 8.9 template(java_security_AccessControlContext, "java/security/AccessControlContext") \ 8.10 template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \ 8.11 @@ -334,6 +335,7 @@ 8.12 template(ptypes_name, "ptypes") \ 8.13 template(form_name, "form") \ 8.14 template(erasedType_name, "erasedType") \ 8.15 + template(append_name, "append") \ 8.16 \ 8.17 /* non-intrinsic name/signature pairs: */ \ 8.18 template(register_method_name, "register") \ 8.19 @@ -415,6 +417,13 @@ 8.20 template(string_signature, "Ljava/lang/String;") \ 8.21 template(reference_signature, "Ljava/lang/ref/Reference;") \ 8.22 template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \ 8.23 + template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \ 8.24 + template(int_StringBuilder_signature, "(I)Ljava/lang/StringBuilder;") \ 8.25 + template(char_StringBuilder_signature, "(C)Ljava/lang/StringBuilder;") \ 8.26 + template(String_StringBuffer_signature, "(Ljava/lang/String;)Ljava/lang/StringBuffer;") \ 8.27 + template(int_StringBuffer_signature, "(I)Ljava/lang/StringBuffer;") \ 8.28 + template(char_StringBuffer_signature, "(C)Ljava/lang/StringBuffer;") \ 8.29 + template(int_String_signature, "(I)Ljava/lang/String;") \ 8.30 /* signature symbols needed by intrinsics */ \ 8.31 VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \ 8.32 \ 8.33 @@ -814,10 +823,34 @@ 8.34 /*the compiler does have special inlining code for these; bytecode inline is just fine */ \ 8.35 \ 8.36 do_intrinsic(_fillInStackTrace, java_lang_Throwable, fillInStackTrace_name, void_throwable_signature, F_RNY) \ 8.37 - \ 8.38 - do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \ 8.39 - /* (symbol object_initializer_name defined above) */ \ 8.40 - \ 8.41 + \ 8.42 + do_intrinsic(_StringBuilder_void, java_lang_StringBuilder, object_initializer_name, void_method_signature, F_R) \ 8.43 + do_intrinsic(_StringBuilder_int, java_lang_StringBuilder, object_initializer_name, int_void_signature, F_R) \ 8.44 + do_intrinsic(_StringBuilder_String, java_lang_StringBuilder, object_initializer_name, string_void_signature, F_R) \ 8.45 + \ 8.46 + do_intrinsic(_StringBuilder_append_char, java_lang_StringBuilder, append_name, char_StringBuilder_signature, F_R) \ 8.47 + do_intrinsic(_StringBuilder_append_int, java_lang_StringBuilder, append_name, int_StringBuilder_signature, F_R) \ 8.48 + do_intrinsic(_StringBuilder_append_String, java_lang_StringBuilder, append_name, String_StringBuilder_signature, F_R) \ 8.49 + \ 8.50 + do_intrinsic(_StringBuilder_toString, java_lang_StringBuilder, toString_name, void_string_signature, F_R) \ 8.51 + \ 8.52 + do_intrinsic(_StringBuffer_void, java_lang_StringBuffer, object_initializer_name, void_method_signature, F_R) \ 8.53 + do_intrinsic(_StringBuffer_int, java_lang_StringBuffer, object_initializer_name, int_void_signature, F_R) \ 8.54 + do_intrinsic(_StringBuffer_String, java_lang_StringBuffer, object_initializer_name, string_void_signature, F_R) \ 8.55 + \ 8.56 + do_intrinsic(_StringBuffer_append_char, java_lang_StringBuffer, append_name, char_StringBuffer_signature, F_Y) \ 8.57 + do_intrinsic(_StringBuffer_append_int, java_lang_StringBuffer, append_name, int_StringBuffer_signature, F_Y) \ 8.58 + do_intrinsic(_StringBuffer_append_String, java_lang_StringBuffer, append_name, String_StringBuffer_signature, F_Y) \ 8.59 + \ 8.60 + do_intrinsic(_StringBuffer_toString, java_lang_StringBuffer, toString_name, void_string_signature, F_Y) \ 8.61 + \ 8.62 + do_intrinsic(_Integer_toString, java_lang_Integer, toString_name, int_String_signature, F_S) \ 8.63 + \ 8.64 + do_intrinsic(_String_String, java_lang_String, object_initializer_name, string_void_signature, F_R) \ 8.65 + \ 8.66 + do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \ 8.67 + /* (symbol object_initializer_name defined above) */ \ 8.68 + \ 8.69 do_intrinsic(_invoke, java_lang_reflect_Method, invoke_name, object_array_object_object_signature, F_R) \ 8.70 /* (symbols invoke_name and invoke_signature defined above) */ \ 8.71 \ 8.72 @@ -945,11 +978,12 @@ 8.73 enum Flags { 8.74 // AccessFlags syndromes relevant to intrinsics. 8.75 F_none = 0, 8.76 - F_R, // !static !synchronized (R="regular") 8.77 - F_S, // static !synchronized 8.78 - F_RN, // !static native !synchronized 8.79 - F_SN, // static native !synchronized 8.80 - F_RNY // !static native synchronized 8.81 + F_R, // !static ?native !synchronized (R="regular") 8.82 + F_S, // static ?native !synchronized 8.83 + F_Y, // !static ?native synchronized 8.84 + F_RN, // !static native !synchronized 8.85 + F_SN, // static native !synchronized 8.86 + F_RNY // !static native synchronized 8.87 }; 8.88 8.89 public:
9.1 --- a/src/share/vm/includeDB_compiler2 Fri Nov 27 07:56:58 2009 -0800 9.2 +++ b/src/share/vm/includeDB_compiler2 Thu Nov 12 09:24:21 2009 -0800 9.3 @@ -149,6 +149,7 @@ 9.4 c2compiler.hpp abstractCompiler.hpp 9.5 9.6 callGenerator.cpp addnode.hpp 9.7 +callGenerator.cpp bcEscapeAnalyzer.hpp 9.8 callGenerator.cpp callGenerator.hpp 9.9 callGenerator.cpp callnode.hpp 9.10 callGenerator.cpp cfgnode.hpp 9.11 @@ -321,6 +322,7 @@ 9.12 compile.cpp rootnode.hpp 9.13 compile.cpp runtime.hpp 9.14 compile.cpp signature.hpp 9.15 +compile.cpp stringopts.hpp 9.16 compile.cpp stubRoutines.hpp 9.17 compile.cpp systemDictionary.hpp 9.18 compile.cpp timer.hpp 9.19 @@ -476,12 +478,16 @@ 9.20 graphKit.cpp runtime.hpp 9.21 graphKit.cpp sharedRuntime.hpp 9.22 9.23 +graphKit.hpp addnode.hpp 9.24 graphKit.hpp callnode.hpp 9.25 graphKit.hpp cfgnode.hpp 9.26 graphKit.hpp ciEnv.hpp 9.27 +graphKit.hpp divnode.hpp 9.28 graphKit.hpp compile.hpp 9.29 graphKit.hpp deoptimization.hpp 9.30 graphKit.hpp phaseX.hpp 9.31 +graphKit.hpp mulnode.hpp 9.32 +graphKit.hpp subnode.hpp 9.33 graphKit.hpp type.hpp 9.34 9.35 idealKit.cpp addnode.hpp 9.36 @@ -490,7 +496,10 @@ 9.37 idealKit.cpp idealKit.hpp 9.38 idealKit.cpp runtime.hpp 9.39 9.40 +idealKit.hpp addnode.hpp 9.41 +idealKit.hpp cfgnode.hpp 9.42 idealKit.hpp connode.hpp 9.43 +idealKit.hpp divnode.hpp 9.44 idealKit.hpp mulnode.hpp 9.45 idealKit.hpp phaseX.hpp 9.46 idealKit.hpp subnode.hpp 9.47 @@ -641,6 +650,7 @@ 9.48 macro.cpp callnode.hpp 9.49 macro.cpp cfgnode.hpp 9.50 macro.cpp compile.hpp 9.51 +macro.cpp compileLog.hpp 9.52 macro.cpp connode.hpp 9.53 macro.cpp locknode.hpp 9.54 macro.cpp loopnode.hpp 9.55 @@ -993,6 +1003,21 @@ 9.56 split_if.cpp connode.hpp 9.57 split_if.cpp loopnode.hpp 9.58 9.59 +stringopts.hpp phaseX.hpp 9.60 +stringopts.hpp node.hpp 9.61 + 9.62 +stringopts.cpp addnode.hpp 9.63 +stringopts.cpp callnode.hpp 9.64 +stringopts.cpp callGenerator.hpp 9.65 +stringopts.cpp compileLog.hpp 9.66 +stringopts.cpp divnode.hpp 9.67 +stringopts.cpp idealKit.hpp 9.68 +stringopts.cpp graphKit.hpp 9.69 +stringopts.cpp rootnode.hpp 9.70 +stringopts.cpp runtime.hpp 9.71 +stringopts.cpp subnode.hpp 9.72 +stringopts.cpp stringopts.hpp 9.73 + 9.74 stubGenerator_<arch_model>.cpp runtime.hpp 9.75 9.76 stubRoutines.cpp runtime.hpp
10.1 --- a/src/share/vm/includeDB_core Fri Nov 27 07:56:58 2009 -0800 10.2 +++ b/src/share/vm/includeDB_core Thu Nov 12 09:24:21 2009 -0800 10.3 @@ -570,6 +570,7 @@ 10.4 ciEnv.hpp dependencies.hpp 10.5 ciEnv.hpp exceptionHandlerTable.hpp 10.6 ciEnv.hpp oopMap.hpp 10.7 +ciEnv.hpp systemDictionary.hpp 10.8 ciEnv.hpp thread.hpp 10.9 10.10 ciExceptionHandler.cpp ciExceptionHandler.hpp
11.1 --- a/src/share/vm/memory/universe.cpp Fri Nov 27 07:56:58 2009 -0800 11.2 +++ b/src/share/vm/memory/universe.cpp Thu Nov 12 09:24:21 2009 -0800 11.3 @@ -67,6 +67,8 @@ 11.4 objArrayOop Universe::_the_empty_system_obj_array = NULL; 11.5 objArrayOop Universe::_the_empty_class_klass_array = NULL; 11.6 objArrayOop Universe::_the_array_interfaces_array = NULL; 11.7 +oop Universe::_the_null_string = NULL; 11.8 +oop Universe::_the_min_jint_string = NULL; 11.9 LatestMethodOopCache* Universe::_finalizer_register_cache = NULL; 11.10 LatestMethodOopCache* Universe::_loader_addClass_cache = NULL; 11.11 ActiveMethodOopsCache* Universe::_reflect_invoke_cache = NULL; 11.12 @@ -187,6 +189,8 @@ 11.13 f->do_oop((oop*)&_the_empty_system_obj_array); 11.14 f->do_oop((oop*)&_the_empty_class_klass_array); 11.15 f->do_oop((oop*)&_the_array_interfaces_array); 11.16 + f->do_oop((oop*)&_the_null_string); 11.17 + f->do_oop((oop*)&_the_min_jint_string); 11.18 _finalizer_register_cache->oops_do(f); 11.19 _loader_addClass_cache->oops_do(f); 11.20 _reflect_invoke_cache->oops_do(f); 11.21 @@ -289,6 +293,9 @@ 11.22 11.23 klassOop ok = SystemDictionary::object_klass(); 11.24 11.25 + _the_null_string = StringTable::intern("null", CHECK); 11.26 + _the_min_jint_string = StringTable::intern("-2147483648", CHECK); 11.27 + 11.28 if (UseSharedSpaces) { 11.29 // Verify shared interfaces array. 11.30 assert(_the_array_interfaces_array->obj_at(0) ==
12.1 --- a/src/share/vm/memory/universe.hpp Fri Nov 27 07:56:58 2009 -0800 12.2 +++ b/src/share/vm/memory/universe.hpp Thu Nov 12 09:24:21 2009 -0800 12.3 @@ -169,6 +169,8 @@ 12.4 static objArrayOop _the_empty_system_obj_array; // Canonicalized system obj array 12.5 static objArrayOop _the_empty_class_klass_array; // Canonicalized obj array of type java.lang.Class 12.6 static objArrayOop _the_array_interfaces_array; // Canonicalized 2-array of cloneable & serializable klasses 12.7 + static oop _the_null_string; // A cache of "null" as a Java string 12.8 + static oop _the_min_jint_string; // A cache of "-2147483648" as a Java string 12.9 static LatestMethodOopCache* _finalizer_register_cache; // static method for registering finalizable objects 12.10 static LatestMethodOopCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector 12.11 static ActiveMethodOopsCache* _reflect_invoke_cache; // method for security checks 12.12 @@ -310,6 +312,8 @@ 12.13 static objArrayOop the_empty_system_obj_array () { return _the_empty_system_obj_array; } 12.14 static objArrayOop the_empty_class_klass_array () { return _the_empty_class_klass_array; } 12.15 static objArrayOop the_array_interfaces_array() { return _the_array_interfaces_array; } 12.16 + static oop the_null_string() { return _the_null_string; } 12.17 + static oop the_min_jint_string() { return _the_min_jint_string; } 12.18 static methodOop finalizer_register_method() { return _finalizer_register_cache->get_methodOop(); } 12.19 static methodOop loader_addClass_method() { return _loader_addClass_cache->get_methodOop(); } 12.20 static ActiveMethodOopsCache* reflect_invoke_cache() { return _reflect_invoke_cache; }
13.1 --- a/src/share/vm/opto/c2_globals.cpp Fri Nov 27 07:56:58 2009 -0800 13.2 +++ b/src/share/vm/opto/c2_globals.cpp Thu Nov 12 09:24:21 2009 -0800 13.3 @@ -25,4 +25,4 @@ 13.4 # include "incls/_precompiled.incl" 13.5 # include "incls/_c2_globals.cpp.incl" 13.6 13.7 -C2_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_NOTPRODUCT_FLAG) 13.8 +C2_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_EXPERIMENTAL_FLAG, MATERIALIZE_NOTPRODUCT_FLAG)
14.1 --- a/src/share/vm/opto/c2_globals.hpp Fri Nov 27 07:56:58 2009 -0800 14.2 +++ b/src/share/vm/opto/c2_globals.hpp Thu Nov 12 09:24:21 2009 -0800 14.3 @@ -26,7 +26,7 @@ 14.4 // Defines all globals flags used by the server compiler. 14.5 // 14.6 14.7 -#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \ 14.8 +#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, experimental, notproduct) \ 14.9 \ 14.10 notproduct(intx, CompileZapFirst, 0, \ 14.11 "If +ZapDeadCompiledLocals, " \ 14.12 @@ -394,6 +394,12 @@ 14.13 product(bool, UseOptoBiasInlining, true, \ 14.14 "Generate biased locking code in C2 ideal graph") \ 14.15 \ 14.16 + experimental(bool, OptimizeStringConcat, false, \ 14.17 + "Optimize the construction of Strings by StringBuilder") \ 14.18 + \ 14.19 + notproduct(bool, PrintOptimizeStringConcat, false, \ 14.20 + "Print information about transformations performed on Strings") \ 14.21 + \ 14.22 product(intx, ValueSearchLimit, 1000, \ 14.23 "Recursion limit in PhaseMacroExpand::value_from_mem_phi") \ 14.24 \ 14.25 @@ -413,4 +419,4 @@ 14.26 product(bool, BlockLayoutRotateLoops, true, \ 14.27 "Allow back branches to be fall throughs in the block layour") \ 14.28 14.29 -C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_NOTPRODUCT_FLAG) 14.30 +C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_EXPERIMENTAL_FLAG, DECLARE_NOTPRODUCT_FLAG)
15.1 --- a/src/share/vm/opto/callGenerator.cpp Fri Nov 27 07:56:58 2009 -0800 15.2 +++ b/src/share/vm/opto/callGenerator.cpp Thu Nov 12 09:24:21 2009 -0800 15.3 @@ -98,12 +98,21 @@ 15.4 //---------------------------DirectCallGenerator------------------------------ 15.5 // Internal class which handles all out-of-line calls w/o receiver type checks. 15.6 class DirectCallGenerator : public CallGenerator { 15.7 -public: 15.8 - DirectCallGenerator(ciMethod* method) 15.9 - : CallGenerator(method) 15.10 + private: 15.11 + CallStaticJavaNode* _call_node; 15.12 + // Force separate memory and I/O projections for the exceptional 15.13 + // paths to facilitate late inlinig. 15.14 + bool _separate_io_proj; 15.15 + 15.16 + public: 15.17 + DirectCallGenerator(ciMethod* method, bool separate_io_proj) 15.18 + : CallGenerator(method), 15.19 + _separate_io_proj(separate_io_proj) 15.20 { 15.21 } 15.22 virtual JVMState* generate(JVMState* jvms); 15.23 + 15.24 + CallStaticJavaNode* call_node() const { return _call_node; } 15.25 }; 15.26 15.27 JVMState* DirectCallGenerator::generate(JVMState* jvms) { 15.28 @@ -129,9 +138,10 @@ 15.29 call->set_optimized_virtual(true); 15.30 } 15.31 kit.set_arguments_for_java_call(call); 15.32 - kit.set_edges_for_java_call(call); 15.33 - Node* ret = kit.set_results_for_java_call(call); 15.34 + kit.set_edges_for_java_call(call, false, _separate_io_proj); 15.35 + Node* ret = kit.set_results_for_java_call(call, _separate_io_proj); 15.36 kit.push_node(method()->return_type()->basic_type(), ret); 15.37 + _call_node = call; // Save the call node in case we need it later 15.38 return kit.transfer_exceptions_into_jvms(); 15.39 } 15.40 15.41 @@ -238,9 +248,9 @@ 15.42 return new ParseGenerator(m, expected_uses, true); 15.43 } 15.44 15.45 -CallGenerator* CallGenerator::for_direct_call(ciMethod* m) { 15.46 +CallGenerator* CallGenerator::for_direct_call(ciMethod* m, bool separate_io_proj) { 15.47 assert(!m->is_abstract(), "for_direct_call mismatch"); 15.48 - return new DirectCallGenerator(m); 15.49 + return new DirectCallGenerator(m, separate_io_proj); 15.50 } 15.51 15.52 CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) { 15.53 @@ -248,6 +258,108 @@ 15.54 return new VirtualCallGenerator(m, vtable_index); 15.55 } 15.56 15.57 +// Allow inlining decisions to be delayed 15.58 +class LateInlineCallGenerator : public DirectCallGenerator { 15.59 + CallGenerator* _inline_cg; 15.60 + 15.61 + public: 15.62 + LateInlineCallGenerator(ciMethod* method, CallGenerator* inline_cg) : 15.63 + DirectCallGenerator(method, true), _inline_cg(inline_cg) {} 15.64 + 15.65 + virtual bool is_late_inline() const { return true; } 15.66 + 15.67 + // Convert the CallStaticJava into an inline 15.68 + virtual void do_late_inline(); 15.69 + 15.70 + JVMState* generate(JVMState* jvms) { 15.71 + // Record that this call site should be revisited once the main 15.72 + // parse is finished. 15.73 + Compile::current()->add_late_inline(this); 15.74 + 15.75 + // Emit the CallStaticJava and request separate projections so 15.76 + // that the late inlining logic can distinguish between fall 15.77 + // through and exceptional uses of the memory and io projections 15.78 + // as is done for allocations and macro expansion. 15.79 + return DirectCallGenerator::generate(jvms); 15.80 + } 15.81 + 15.82 +}; 15.83 + 15.84 + 15.85 +void LateInlineCallGenerator::do_late_inline() { 15.86 + // Can't inline it 15.87 + if (call_node() == NULL || call_node()->outcnt() == 0 || 15.88 + call_node()->in(0) == NULL || call_node()->in(0)->is_top()) 15.89 + return; 15.90 + 15.91 + CallStaticJavaNode* call = call_node(); 15.92 + 15.93 + // Make a clone of the JVMState that appropriate to use for driving a parse 15.94 + Compile* C = Compile::current(); 15.95 + JVMState* jvms = call->jvms()->clone_shallow(C); 15.96 + uint size = call->req(); 15.97 + SafePointNode* map = new (C, size) SafePointNode(size, jvms); 15.98 + for (uint i1 = 0; i1 < size; i1++) { 15.99 + map->init_req(i1, call->in(i1)); 15.100 + } 15.101 + 15.102 + // Make sure the state is a MergeMem for parsing. 15.103 + if (!map->in(TypeFunc::Memory)->is_MergeMem()) { 15.104 + map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory))); 15.105 + } 15.106 + 15.107 + // Make enough space for the expression stack and transfer the incoming arguments 15.108 + int nargs = method()->arg_size(); 15.109 + jvms->set_map(map); 15.110 + map->ensure_stack(jvms, jvms->method()->max_stack()); 15.111 + if (nargs > 0) { 15.112 + for (int i1 = 0; i1 < nargs; i1++) { 15.113 + map->set_req(i1 + jvms->argoff(), call->in(TypeFunc::Parms + i1)); 15.114 + } 15.115 + } 15.116 + 15.117 + CompileLog* log = C->log(); 15.118 + if (log != NULL) { 15.119 + log->head("late_inline method='%d'", log->identify(method())); 15.120 + JVMState* p = jvms; 15.121 + while (p != NULL) { 15.122 + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); 15.123 + p = p->caller(); 15.124 + } 15.125 + log->tail("late_inline"); 15.126 + } 15.127 + 15.128 + // Setup default node notes to be picked up by the inlining 15.129 + Node_Notes* old_nn = C->default_node_notes(); 15.130 + if (old_nn != NULL) { 15.131 + Node_Notes* entry_nn = old_nn->clone(C); 15.132 + entry_nn->set_jvms(jvms); 15.133 + C->set_default_node_notes(entry_nn); 15.134 + } 15.135 + 15.136 + // Now perform the inling using the synthesized JVMState 15.137 + JVMState* new_jvms = _inline_cg->generate(jvms); 15.138 + if (new_jvms == NULL) return; // no change 15.139 + if (C->failing()) return; 15.140 + 15.141 + // Capture any exceptional control flow 15.142 + GraphKit kit(new_jvms); 15.143 + 15.144 + // Find the result object 15.145 + Node* result = C->top(); 15.146 + int result_size = method()->return_type()->size(); 15.147 + if (result_size != 0 && !kit.stopped()) { 15.148 + result = (result_size == 1) ? kit.pop() : kit.pop_pair(); 15.149 + } 15.150 + 15.151 + kit.replace_call(call, result); 15.152 +} 15.153 + 15.154 + 15.155 +CallGenerator* CallGenerator::for_late_inline(ciMethod* method, CallGenerator* inline_cg) { 15.156 + return new LateInlineCallGenerator(method, inline_cg); 15.157 +} 15.158 + 15.159 15.160 //---------------------------WarmCallGenerator-------------------------------- 15.161 // Internal class which handles initial deferral of inlining decisions. 15.162 @@ -315,70 +427,7 @@ 15.163 } 15.164 15.165 void WarmCallInfo::make_hot() { 15.166 - Compile* C = Compile::current(); 15.167 - // Replace the callnode with something better. 15.168 - CallJavaNode* call = this->call()->as_CallJava(); 15.169 - ciMethod* method = call->method(); 15.170 - int nargs = method->arg_size(); 15.171 - JVMState* jvms = call->jvms()->clone_shallow(C); 15.172 - uint size = TypeFunc::Parms + MAX2(2, nargs); 15.173 - SafePointNode* map = new (C, size) SafePointNode(size, jvms); 15.174 - for (uint i1 = 0; i1 < (uint)(TypeFunc::Parms + nargs); i1++) { 15.175 - map->init_req(i1, call->in(i1)); 15.176 - } 15.177 - jvms->set_map(map); 15.178 - jvms->set_offsets(map->req()); 15.179 - jvms->set_locoff(TypeFunc::Parms); 15.180 - jvms->set_stkoff(TypeFunc::Parms); 15.181 - GraphKit kit(jvms); 15.182 - 15.183 - JVMState* new_jvms = _hot_cg->generate(kit.jvms()); 15.184 - if (new_jvms == NULL) return; // no change 15.185 - if (C->failing()) return; 15.186 - 15.187 - kit.set_jvms(new_jvms); 15.188 - Node* res = C->top(); 15.189 - int res_size = method->return_type()->size(); 15.190 - if (res_size != 0) { 15.191 - kit.inc_sp(-res_size); 15.192 - res = kit.argument(0); 15.193 - } 15.194 - GraphKit ekit(kit.combine_and_pop_all_exception_states()->jvms()); 15.195 - 15.196 - // Replace the call: 15.197 - for (DUIterator i = call->outs(); call->has_out(i); i++) { 15.198 - Node* n = call->out(i); 15.199 - Node* nn = NULL; // replacement 15.200 - if (n->is_Proj()) { 15.201 - ProjNode* nproj = n->as_Proj(); 15.202 - assert(nproj->_con < (uint)(TypeFunc::Parms + (res_size ? 1 : 0)), "sane proj"); 15.203 - if (nproj->_con == TypeFunc::Parms) { 15.204 - nn = res; 15.205 - } else { 15.206 - nn = kit.map()->in(nproj->_con); 15.207 - } 15.208 - if (nproj->_con == TypeFunc::I_O) { 15.209 - for (DUIterator j = nproj->outs(); nproj->has_out(j); j++) { 15.210 - Node* e = nproj->out(j); 15.211 - if (e->Opcode() == Op_CreateEx) { 15.212 - e->replace_by(ekit.argument(0)); 15.213 - } else if (e->Opcode() == Op_Catch) { 15.214 - for (DUIterator k = e->outs(); e->has_out(k); k++) { 15.215 - CatchProjNode* p = e->out(j)->as_CatchProj(); 15.216 - if (p->is_handler_proj()) { 15.217 - p->replace_by(ekit.control()); 15.218 - } else { 15.219 - p->replace_by(kit.control()); 15.220 - } 15.221 - } 15.222 - } 15.223 - } 15.224 - } 15.225 - } 15.226 - NOT_PRODUCT(if (!nn) n->dump(2)); 15.227 - assert(nn != NULL, "don't know what to do with this user"); 15.228 - n->replace_by(nn); 15.229 - } 15.230 + Unimplemented(); 15.231 } 15.232 15.233 void WarmCallInfo::make_cold() {
16.1 --- a/src/share/vm/opto/callGenerator.hpp Fri Nov 27 07:56:58 2009 -0800 16.2 +++ b/src/share/vm/opto/callGenerator.hpp Thu Nov 12 09:24:21 2009 -0800 16.3 @@ -57,6 +57,13 @@ 16.4 // is_trap: Does not return to the caller. (E.g., uncommon trap.) 16.5 virtual bool is_trap() const { return false; } 16.6 16.7 + // is_late_inline: supports conversion of call into an inline 16.8 + virtual bool is_late_inline() const { return false; } 16.9 + // Replace the call with an inline version of the code 16.10 + virtual void do_late_inline() { ShouldNotReachHere(); } 16.11 + 16.12 + virtual CallStaticJavaNode* call_node() const { ShouldNotReachHere(); return NULL; } 16.13 + 16.14 // Note: It is possible for a CG to be both inline and virtual. 16.15 // (The hashCode intrinsic does a vtable check and an inlined fast path.) 16.16 16.17 @@ -92,9 +99,12 @@ 16.18 static CallGenerator* for_osr(ciMethod* m, int osr_bci); 16.19 16.20 // How to generate vanilla out-of-line call sites: 16.21 - static CallGenerator* for_direct_call(ciMethod* m); // static, special 16.22 + static CallGenerator* for_direct_call(ciMethod* m, bool separate_io_projs = false); // static, special 16.23 static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface 16.24 16.25 + // How to generate a replace a direct call with an inline version 16.26 + static CallGenerator* for_late_inline(ciMethod* m, CallGenerator* inline_cg); 16.27 + 16.28 // How to make a call but defer the decision whether to inline or not. 16.29 static CallGenerator* for_warm_call(WarmCallInfo* ci, 16.30 CallGenerator* if_cold,
17.1 --- a/src/share/vm/opto/callnode.cpp Fri Nov 27 07:56:58 2009 -0800 17.2 +++ b/src/share/vm/opto/callnode.cpp Thu Nov 12 09:24:21 2009 -0800 17.3 @@ -693,6 +693,84 @@ 17.4 } 17.5 17.6 17.7 +void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj) { 17.8 + projs->fallthrough_proj = NULL; 17.9 + projs->fallthrough_catchproj = NULL; 17.10 + projs->fallthrough_ioproj = NULL; 17.11 + projs->catchall_ioproj = NULL; 17.12 + projs->catchall_catchproj = NULL; 17.13 + projs->fallthrough_memproj = NULL; 17.14 + projs->catchall_memproj = NULL; 17.15 + projs->resproj = NULL; 17.16 + projs->exobj = NULL; 17.17 + 17.18 + for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) { 17.19 + ProjNode *pn = fast_out(i)->as_Proj(); 17.20 + if (pn->outcnt() == 0) continue; 17.21 + switch (pn->_con) { 17.22 + case TypeFunc::Control: 17.23 + { 17.24 + // For Control (fallthrough) and I_O (catch_all_index) we have CatchProj -> Catch -> Proj 17.25 + projs->fallthrough_proj = pn; 17.26 + DUIterator_Fast jmax, j = pn->fast_outs(jmax); 17.27 + const Node *cn = pn->fast_out(j); 17.28 + if (cn->is_Catch()) { 17.29 + ProjNode *cpn = NULL; 17.30 + for (DUIterator_Fast kmax, k = cn->fast_outs(kmax); k < kmax; k++) { 17.31 + cpn = cn->fast_out(k)->as_Proj(); 17.32 + assert(cpn->is_CatchProj(), "must be a CatchProjNode"); 17.33 + if (cpn->_con == CatchProjNode::fall_through_index) 17.34 + projs->fallthrough_catchproj = cpn; 17.35 + else { 17.36 + assert(cpn->_con == CatchProjNode::catch_all_index, "must be correct index."); 17.37 + projs->catchall_catchproj = cpn; 17.38 + } 17.39 + } 17.40 + } 17.41 + break; 17.42 + } 17.43 + case TypeFunc::I_O: 17.44 + if (pn->_is_io_use) 17.45 + projs->catchall_ioproj = pn; 17.46 + else 17.47 + projs->fallthrough_ioproj = pn; 17.48 + for (DUIterator j = pn->outs(); pn->has_out(j); j++) { 17.49 + Node* e = pn->out(j); 17.50 + if (e->Opcode() == Op_CreateEx && e->in(0)->is_CatchProj()) { 17.51 + assert(projs->exobj == NULL, "only one"); 17.52 + projs->exobj = e; 17.53 + } 17.54 + } 17.55 + break; 17.56 + case TypeFunc::Memory: 17.57 + if (pn->_is_io_use) 17.58 + projs->catchall_memproj = pn; 17.59 + else 17.60 + projs->fallthrough_memproj = pn; 17.61 + break; 17.62 + case TypeFunc::Parms: 17.63 + projs->resproj = pn; 17.64 + break; 17.65 + default: 17.66 + assert(false, "unexpected projection from allocation node."); 17.67 + } 17.68 + } 17.69 + 17.70 + // The resproj may not exist because the result couuld be ignored 17.71 + // and the exception object may not exist if an exception handler 17.72 + // swallows the exception but all the other must exist and be found. 17.73 + assert(projs->fallthrough_proj != NULL, "must be found"); 17.74 + assert(projs->fallthrough_catchproj != NULL, "must be found"); 17.75 + assert(projs->fallthrough_memproj != NULL, "must be found"); 17.76 + assert(projs->fallthrough_ioproj != NULL, "must be found"); 17.77 + assert(projs->catchall_catchproj != NULL, "must be found"); 17.78 + if (separate_io_proj) { 17.79 + assert(projs->catchall_memproj != NULL, "must be found"); 17.80 + assert(projs->catchall_ioproj != NULL, "must be found"); 17.81 + } 17.82 +} 17.83 + 17.84 + 17.85 //============================================================================= 17.86 uint CallJavaNode::size_of() const { return sizeof(*this); } 17.87 uint CallJavaNode::cmp( const Node &n ) const {
18.1 --- a/src/share/vm/opto/callnode.hpp Fri Nov 27 07:56:58 2009 -0800 18.2 +++ b/src/share/vm/opto/callnode.hpp Thu Nov 12 09:24:21 2009 -0800 18.3 @@ -470,6 +470,23 @@ 18.4 #endif 18.5 }; 18.6 18.7 + 18.8 +// Simple container for the outgoing projections of a call. Useful 18.9 +// for serious surgery on calls. 18.10 +class CallProjections : public StackObj { 18.11 +public: 18.12 + Node* fallthrough_proj; 18.13 + Node* fallthrough_catchproj; 18.14 + Node* fallthrough_memproj; 18.15 + Node* fallthrough_ioproj; 18.16 + Node* catchall_catchproj; 18.17 + Node* catchall_memproj; 18.18 + Node* catchall_ioproj; 18.19 + Node* resproj; 18.20 + Node* exobj; 18.21 +}; 18.22 + 18.23 + 18.24 //------------------------------CallNode--------------------------------------- 18.25 // Call nodes now subsume the function of debug nodes at callsites, so they 18.26 // contain the functionality of a full scope chain of debug nodes. 18.27 @@ -521,6 +538,11 @@ 18.28 // or returns NULL if there is no one. 18.29 Node *result_cast(); 18.30 18.31 + // Collect all the interesting edges from a call for use in 18.32 + // replacing the call by something else. Used by macro expansion 18.33 + // and the late inlining support. 18.34 + void extract_projections(CallProjections* projs, bool separate_io_proj); 18.35 + 18.36 virtual uint match_edge(uint idx) const; 18.37 18.38 #ifndef PRODUCT 18.39 @@ -529,6 +551,7 @@ 18.40 #endif 18.41 }; 18.42 18.43 + 18.44 //------------------------------CallJavaNode----------------------------------- 18.45 // Make a static or dynamic subroutine call node using Java calling 18.46 // convention. (The "Java" calling convention is the compiler's calling
19.1 --- a/src/share/vm/opto/compile.cpp Fri Nov 27 07:56:58 2009 -0800 19.2 +++ b/src/share/vm/opto/compile.cpp Thu Nov 12 09:24:21 2009 -0800 19.3 @@ -224,6 +224,32 @@ 19.4 } 19.5 19.6 19.7 +void Compile::gvn_replace_by(Node* n, Node* nn) { 19.8 + for (DUIterator_Last imin, i = n->last_outs(imin); i >= imin; ) { 19.9 + Node* use = n->last_out(i); 19.10 + bool is_in_table = initial_gvn()->hash_delete(use); 19.11 + uint uses_found = 0; 19.12 + for (uint j = 0; j < use->len(); j++) { 19.13 + if (use->in(j) == n) { 19.14 + if (j < use->req()) 19.15 + use->set_req(j, nn); 19.16 + else 19.17 + use->set_prec(j, nn); 19.18 + uses_found++; 19.19 + } 19.20 + } 19.21 + if (is_in_table) { 19.22 + // reinsert into table 19.23 + initial_gvn()->hash_find_insert(use); 19.24 + } 19.25 + record_for_igvn(use); 19.26 + i -= uses_found; // we deleted 1 or more copies of this edge 19.27 + } 19.28 +} 19.29 + 19.30 + 19.31 + 19.32 + 19.33 // Identify all nodes that are reachable from below, useful. 19.34 // Use breadth-first pass that records state in a Unique_Node_List, 19.35 // recursive traversal is slower. 19.36 @@ -554,6 +580,28 @@ 19.37 rethrow_exceptions(kit.transfer_exceptions_into_jvms()); 19.38 } 19.39 19.40 + if (!failing() && has_stringbuilder()) { 19.41 + { 19.42 + // remove useless nodes to make the usage analysis simpler 19.43 + ResourceMark rm; 19.44 + PhaseRemoveUseless pru(initial_gvn(), &for_igvn); 19.45 + } 19.46 + 19.47 + { 19.48 + ResourceMark rm; 19.49 + print_method("Before StringOpts", 3); 19.50 + PhaseStringOpts pso(initial_gvn(), &for_igvn); 19.51 + print_method("After StringOpts", 3); 19.52 + } 19.53 + 19.54 + // now inline anything that we skipped the first time around 19.55 + while (_late_inlines.length() > 0) { 19.56 + CallGenerator* cg = _late_inlines.pop(); 19.57 + cg->do_late_inline(); 19.58 + } 19.59 + } 19.60 + assert(_late_inlines.length() == 0, "should have been processed"); 19.61 + 19.62 print_method("Before RemoveUseless", 3); 19.63 19.64 // Remove clutter produced by parsing. 19.65 @@ -820,6 +868,7 @@ 19.66 _fixed_slots = 0; 19.67 set_has_split_ifs(false); 19.68 set_has_loops(has_method() && method()->has_loops()); // first approximation 19.69 + set_has_stringbuilder(false); 19.70 _deopt_happens = true; // start out assuming the worst 19.71 _trap_can_recompile = false; // no traps emitted yet 19.72 _major_progress = true; // start out assuming good things will happen 19.73 @@ -2240,6 +2289,30 @@ 19.74 break; 19.75 } 19.76 19.77 + case Op_Proj: { 19.78 + if (OptimizeStringConcat) { 19.79 + ProjNode* p = n->as_Proj(); 19.80 + if (p->_is_io_use) { 19.81 + // Separate projections were used for the exception path which 19.82 + // are normally removed by a late inline. If it wasn't inlined 19.83 + // then they will hang around and should just be replaced with 19.84 + // the original one. 19.85 + Node* proj = NULL; 19.86 + // Replace with just one 19.87 + for (SimpleDUIterator i(p->in(0)); i.has_next(); i.next()) { 19.88 + Node *use = i.get(); 19.89 + if (use->is_Proj() && p != use && use->as_Proj()->_con == p->_con) { 19.90 + proj = use; 19.91 + break; 19.92 + } 19.93 + } 19.94 + assert(p != NULL, "must be found"); 19.95 + p->subsume_by(proj); 19.96 + } 19.97 + } 19.98 + break; 19.99 + } 19.100 + 19.101 case Op_Phi: 19.102 if (n->as_Phi()->bottom_type()->isa_narrowoop()) { 19.103 // The EncodeP optimization may create Phi with the same edges
20.1 --- a/src/share/vm/opto/compile.hpp Fri Nov 27 07:56:58 2009 -0800 20.2 +++ b/src/share/vm/opto/compile.hpp Thu Nov 12 09:24:21 2009 -0800 20.3 @@ -149,6 +149,7 @@ 20.4 bool _has_loops; // True if the method _may_ have some loops 20.5 bool _has_split_ifs; // True if the method _may_ have some split-if 20.6 bool _has_unsafe_access; // True if the method _may_ produce faults in unsafe loads or stores. 20.7 + bool _has_stringbuilder; // True StringBuffers or StringBuilders are allocated 20.8 uint _trap_hist[trapHistLength]; // Cumulative traps 20.9 bool _trap_can_recompile; // Have we emitted a recompiling trap? 20.10 uint _decompile_count; // Cumulative decompilation counts. 20.11 @@ -219,6 +220,9 @@ 20.12 Unique_Node_List* _for_igvn; // Initial work-list for next round of Iterative GVN 20.13 WarmCallInfo* _warm_calls; // Sorted work-list for heat-based inlining. 20.14 20.15 + GrowableArray<CallGenerator*> _late_inlines; // List of CallGenerators to be revisited after 20.16 + // main parsing has finished. 20.17 + 20.18 // Matching, CFG layout, allocation, code generation 20.19 PhaseCFG* _cfg; // Results of CFG finding 20.20 bool _select_24_bit_instr; // We selected an instruction with a 24-bit result 20.21 @@ -298,6 +302,8 @@ 20.22 void set_has_split_ifs(bool z) { _has_split_ifs = z; } 20.23 bool has_unsafe_access() const { return _has_unsafe_access; } 20.24 void set_has_unsafe_access(bool z) { _has_unsafe_access = z; } 20.25 + bool has_stringbuilder() const { return _has_stringbuilder; } 20.26 + void set_has_stringbuilder(bool z) { _has_stringbuilder = z; } 20.27 void set_trap_count(uint r, uint c) { assert(r < trapHistLength, "oob"); _trap_hist[r] = c; } 20.28 uint trap_count(uint r) const { assert(r < trapHistLength, "oob"); return _trap_hist[r]; } 20.29 bool trap_can_recompile() const { return _trap_can_recompile; } 20.30 @@ -475,6 +481,7 @@ 20.31 // Decide how to build a call. 20.32 // The profile factor is a discount to apply to this site's interp. profile. 20.33 CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float profile_factor); 20.34 + bool should_delay_inlining(ciMethod* call_method, JVMState* jvms); 20.35 20.36 // Report if there were too many traps at a current method and bci. 20.37 // Report if a trap was recorded, and/or PerMethodTrapLimit was exceeded. 20.38 @@ -495,6 +502,11 @@ 20.39 void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; } 20.40 void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; } 20.41 20.42 + // Replace n by nn using initial_gvn, calling hash_delete and 20.43 + // record_for_igvn as needed. 20.44 + void gvn_replace_by(Node* n, Node* nn); 20.45 + 20.46 + 20.47 void identify_useful_nodes(Unique_Node_List &useful); 20.48 void remove_useless_nodes (Unique_Node_List &useful); 20.49 20.50 @@ -502,6 +514,9 @@ 20.51 void set_warm_calls(WarmCallInfo* l) { _warm_calls = l; } 20.52 WarmCallInfo* pop_warm_call(); 20.53 20.54 + // Record this CallGenerator for inlining at the end of parsing. 20.55 + void add_late_inline(CallGenerator* cg) { _late_inlines.push(cg); } 20.56 + 20.57 // Matching, CFG layout, allocation, code generation 20.58 PhaseCFG* cfg() { return _cfg; } 20.59 bool select_24_bit_instr() const { return _select_24_bit_instr; }
21.1 --- a/src/share/vm/opto/doCall.cpp Fri Nov 27 07:56:58 2009 -0800 21.2 +++ b/src/share/vm/opto/doCall.cpp Thu Nov 12 09:24:21 2009 -0800 21.3 @@ -128,6 +128,12 @@ 21.4 21.5 if (allow_inline) { 21.6 CallGenerator* cg = CallGenerator::for_inline(call_method, expected_uses); 21.7 + if (require_inline && cg != NULL && should_delay_inlining(call_method, jvms)) { 21.8 + // Delay the inlining of this method to give us the 21.9 + // opportunity to perform some high level optimizations 21.10 + // first. 21.11 + return CallGenerator::for_late_inline(call_method, cg); 21.12 + } 21.13 if (cg == NULL) { 21.14 // Fall through. 21.15 } else if (require_inline || !InlineWarmCalls) { 21.16 @@ -225,10 +231,63 @@ 21.17 } else { 21.18 // Class Hierarchy Analysis or Type Profile reveals a unique target, 21.19 // or it is a static or special call. 21.20 - return CallGenerator::for_direct_call(call_method); 21.21 + return CallGenerator::for_direct_call(call_method, should_delay_inlining(call_method, jvms)); 21.22 } 21.23 } 21.24 21.25 +// Return true for methods that shouldn't be inlined early so that 21.26 +// they are easier to analyze and optimize as intrinsics. 21.27 +bool Compile::should_delay_inlining(ciMethod* call_method, JVMState* jvms) { 21.28 + if (has_stringbuilder()) { 21.29 + 21.30 + if ((call_method->holder() == C->env()->StringBuilder_klass() || 21.31 + call_method->holder() == C->env()->StringBuffer_klass()) && 21.32 + (jvms->method()->holder() == C->env()->StringBuilder_klass() || 21.33 + jvms->method()->holder() == C->env()->StringBuffer_klass())) { 21.34 + // Delay SB calls only when called from non-SB code 21.35 + return false; 21.36 + } 21.37 + 21.38 + switch (call_method->intrinsic_id()) { 21.39 + case vmIntrinsics::_StringBuilder_void: 21.40 + case vmIntrinsics::_StringBuilder_int: 21.41 + case vmIntrinsics::_StringBuilder_String: 21.42 + case vmIntrinsics::_StringBuilder_append_char: 21.43 + case vmIntrinsics::_StringBuilder_append_int: 21.44 + case vmIntrinsics::_StringBuilder_append_String: 21.45 + case vmIntrinsics::_StringBuilder_toString: 21.46 + case vmIntrinsics::_StringBuffer_void: 21.47 + case vmIntrinsics::_StringBuffer_int: 21.48 + case vmIntrinsics::_StringBuffer_String: 21.49 + case vmIntrinsics::_StringBuffer_append_char: 21.50 + case vmIntrinsics::_StringBuffer_append_int: 21.51 + case vmIntrinsics::_StringBuffer_append_String: 21.52 + case vmIntrinsics::_StringBuffer_toString: 21.53 + case vmIntrinsics::_Integer_toString: 21.54 + return true; 21.55 + 21.56 + case vmIntrinsics::_String_String: 21.57 + { 21.58 + Node* receiver = jvms->map()->in(jvms->argoff() + 1); 21.59 + if (receiver->is_Proj() && receiver->in(0)->is_CallStaticJava()) { 21.60 + CallStaticJavaNode* csj = receiver->in(0)->as_CallStaticJava(); 21.61 + ciMethod* m = csj->method(); 21.62 + if (m != NULL && 21.63 + (m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString || 21.64 + m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString)) 21.65 + // Delay String.<init>(new SB()) 21.66 + return true; 21.67 + } 21.68 + return false; 21.69 + } 21.70 + 21.71 + default: 21.72 + return false; 21.73 + } 21.74 + } 21.75 + return false; 21.76 +} 21.77 + 21.78 21.79 // uncommon-trap call-sites where callee is unloaded, uninitialized or will not link 21.80 bool Parse::can_not_compile_call_site(ciMethod *dest_method, ciInstanceKlass* klass) {
22.1 --- a/src/share/vm/opto/graphKit.cpp Fri Nov 27 07:56:58 2009 -0800 22.2 +++ b/src/share/vm/opto/graphKit.cpp Thu Nov 12 09:24:21 2009 -0800 22.3 @@ -1351,8 +1351,8 @@ 22.4 } 22.5 22.6 //------------------------------set_all_memory_call---------------------------- 22.7 -void GraphKit::set_all_memory_call(Node* call) { 22.8 - Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) ); 22.9 +void GraphKit::set_all_memory_call(Node* call, bool separate_io_proj) { 22.10 + Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory, separate_io_proj) ); 22.11 set_all_memory(newmem); 22.12 } 22.13 22.14 @@ -1573,7 +1573,7 @@ 22.15 //---------------------------set_edges_for_java_call--------------------------- 22.16 // Connect a newly created call into the current JVMS. 22.17 // A return value node (if any) is returned from set_edges_for_java_call. 22.18 -void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw) { 22.19 +void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw, bool separate_io_proj) { 22.20 22.21 // Add the predefined inputs: 22.22 call->init_req( TypeFunc::Control, control() ); 22.23 @@ -1595,13 +1595,13 @@ 22.24 // Re-use the current map to produce the result. 22.25 22.26 set_control(_gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Control))); 22.27 - set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O ))); 22.28 - set_all_memory_call(xcall); 22.29 + set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O , separate_io_proj))); 22.30 + set_all_memory_call(xcall, separate_io_proj); 22.31 22.32 //return xcall; // no need, caller already has it 22.33 } 22.34 22.35 -Node* GraphKit::set_results_for_java_call(CallJavaNode* call) { 22.36 +Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_proj) { 22.37 if (stopped()) return top(); // maybe the call folded up? 22.38 22.39 // Capture the return value, if any. 22.40 @@ -1614,8 +1614,15 @@ 22.41 // Note: Since any out-of-line call can produce an exception, 22.42 // we always insert an I_O projection from the call into the result. 22.43 22.44 - make_slow_call_ex(call, env()->Throwable_klass(), false); 22.45 - 22.46 + make_slow_call_ex(call, env()->Throwable_klass(), separate_io_proj); 22.47 + 22.48 + if (separate_io_proj) { 22.49 + // The caller requested separate projections be used by the fall 22.50 + // through and exceptional paths, so replace the projections for 22.51 + // the fall through path. 22.52 + set_i_o(_gvn.transform( new (C, 1) ProjNode(call, TypeFunc::I_O) )); 22.53 + set_all_memory(_gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) )); 22.54 + } 22.55 return ret; 22.56 } 22.57 22.58 @@ -1678,6 +1685,59 @@ 22.59 } 22.60 } 22.61 22.62 + 22.63 +// Replace the call with the current state of the kit. 22.64 +void GraphKit::replace_call(CallNode* call, Node* result) { 22.65 + JVMState* ejvms = NULL; 22.66 + if (has_exceptions()) { 22.67 + ejvms = transfer_exceptions_into_jvms(); 22.68 + } 22.69 + 22.70 + SafePointNode* final_state = stop(); 22.71 + 22.72 + // Find all the needed outputs of this call 22.73 + CallProjections callprojs; 22.74 + call->extract_projections(&callprojs, true); 22.75 + 22.76 + // Replace all the old call edges with the edges from the inlining result 22.77 + C->gvn_replace_by(callprojs.fallthrough_catchproj, final_state->in(TypeFunc::Control)); 22.78 + C->gvn_replace_by(callprojs.fallthrough_memproj, final_state->in(TypeFunc::Memory)); 22.79 + C->gvn_replace_by(callprojs.fallthrough_ioproj, final_state->in(TypeFunc::I_O)); 22.80 + 22.81 + // Replace the result with the new result if it exists and is used 22.82 + if (callprojs.resproj != NULL && result != NULL) { 22.83 + C->gvn_replace_by(callprojs.resproj, result); 22.84 + } 22.85 + 22.86 + if (ejvms == NULL) { 22.87 + // No exception edges to simply kill off those paths 22.88 + C->gvn_replace_by(callprojs.catchall_catchproj, C->top()); 22.89 + C->gvn_replace_by(callprojs.catchall_memproj, C->top()); 22.90 + C->gvn_replace_by(callprojs.catchall_ioproj, C->top()); 22.91 + } else { 22.92 + GraphKit ekit(ejvms); 22.93 + 22.94 + // Load my combined exception state into the kit, with all phis transformed: 22.95 + SafePointNode* ex_map = ekit.combine_and_pop_all_exception_states(); 22.96 + 22.97 + Node* ex_oop = ekit.use_exception_state(ex_map); 22.98 + 22.99 + C->gvn_replace_by(callprojs.catchall_catchproj, ekit.control()); 22.100 + C->gvn_replace_by(callprojs.catchall_memproj, ekit.reset_memory()); 22.101 + C->gvn_replace_by(callprojs.catchall_ioproj, ekit.i_o()); 22.102 + 22.103 + // Replace the old exception object with the newly created one 22.104 + if (callprojs.exobj != NULL) { 22.105 + C->gvn_replace_by(callprojs.exobj, ex_oop); 22.106 + } 22.107 + } 22.108 + 22.109 + // Disconnect the call from the graph 22.110 + call->disconnect_inputs(NULL); 22.111 + C->gvn_replace_by(call, C->top()); 22.112 +} 22.113 + 22.114 + 22.115 //------------------------------increment_counter------------------------------ 22.116 // for statistics: increment a VM counter by 1 22.117 22.118 @@ -3459,4 +3519,3 @@ 22.119 sync_kit(ideal); 22.120 } 22.121 #undef __ 22.122 -
23.1 --- a/src/share/vm/opto/graphKit.hpp Fri Nov 27 07:56:58 2009 -0800 23.2 +++ b/src/share/vm/opto/graphKit.hpp Thu Nov 12 09:24:21 2009 -0800 23.3 @@ -279,6 +279,34 @@ 23.4 } 23.5 Node* basic_plus_adr(Node* base, Node* ptr, Node* offset); 23.6 23.7 + 23.8 + // Some convenient shortcuts for common nodes 23.9 + Node* IfTrue(IfNode* iff) { return _gvn.transform(new (C,1) IfTrueNode(iff)); } 23.10 + Node* IfFalse(IfNode* iff) { return _gvn.transform(new (C,1) IfFalseNode(iff)); } 23.11 + 23.12 + Node* AddI(Node* l, Node* r) { return _gvn.transform(new (C,3) AddINode(l, r)); } 23.13 + Node* SubI(Node* l, Node* r) { return _gvn.transform(new (C,3) SubINode(l, r)); } 23.14 + Node* MulI(Node* l, Node* r) { return _gvn.transform(new (C,3) MulINode(l, r)); } 23.15 + Node* DivI(Node* ctl, Node* l, Node* r) { return _gvn.transform(new (C,3) DivINode(ctl, l, r)); } 23.16 + 23.17 + Node* AndI(Node* l, Node* r) { return _gvn.transform(new (C,3) AndINode(l, r)); } 23.18 + Node* OrI(Node* l, Node* r) { return _gvn.transform(new (C,3) OrINode(l, r)); } 23.19 + Node* XorI(Node* l, Node* r) { return _gvn.transform(new (C,3) XorINode(l, r)); } 23.20 + 23.21 + Node* MaxI(Node* l, Node* r) { return _gvn.transform(new (C,3) MaxINode(l, r)); } 23.22 + Node* MinI(Node* l, Node* r) { return _gvn.transform(new (C,3) MinINode(l, r)); } 23.23 + 23.24 + Node* LShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) LShiftINode(l, r)); } 23.25 + Node* RShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) RShiftINode(l, r)); } 23.26 + Node* URShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) URShiftINode(l, r)); } 23.27 + 23.28 + Node* CmpI(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpINode(l, r)); } 23.29 + Node* CmpL(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpLNode(l, r)); } 23.30 + Node* CmpP(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpPNode(l, r)); } 23.31 + Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new (C,2) BoolNode(cmp, relop)); } 23.32 + 23.33 + Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(new (C,4) AddPNode(b, a, o)); } 23.34 + 23.35 // Convert between int and long, and size_t. 23.36 // (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.) 23.37 Node* ConvI2L(Node* offset); 23.38 @@ -400,7 +428,7 @@ 23.39 void set_all_memory(Node* newmem); 23.40 23.41 // Create a memory projection from the call, then set_all_memory. 23.42 - void set_all_memory_call(Node* call); 23.43 + void set_all_memory_call(Node* call, bool separate_io_proj = false); 23.44 23.45 // Create a LoadNode, reading from the parser's memory state. 23.46 // (Note: require_atomic_access is useful only with T_LONG.) 23.47 @@ -543,12 +571,12 @@ 23.48 // Transform the call, and update the basics: control, i_o, memory. 23.49 // (The next step is usually to call set_results_for_java_call.) 23.50 void set_edges_for_java_call(CallJavaNode* call, 23.51 - bool must_throw = false); 23.52 + bool must_throw = false, bool separate_io_proj = false); 23.53 23.54 // Finish up a java call that was started by set_edges_for_java_call. 23.55 // Call add_exception on any throw arising from the call. 23.56 // Return the call result (transformed). 23.57 - Node* set_results_for_java_call(CallJavaNode* call); 23.58 + Node* set_results_for_java_call(CallJavaNode* call, bool separate_io_proj = false); 23.59 23.60 // Similar to set_edges_for_java_call, but simplified for runtime calls. 23.61 void set_predefined_output_for_runtime_call(Node* call) { 23.62 @@ -559,6 +587,11 @@ 23.63 const TypePtr* hook_mem); 23.64 Node* set_predefined_input_for_runtime_call(SafePointNode* call); 23.65 23.66 + // Replace the call with the current state of the kit. Requires 23.67 + // that the call was generated with separate io_projs so that 23.68 + // exceptional control flow can be handled properly. 23.69 + void replace_call(CallNode* call, Node* result); 23.70 + 23.71 // helper functions for statistics 23.72 void increment_counter(address counter_addr); // increment a debug counter 23.73 void increment_counter(Node* counter_addr); // increment a debug counter
24.1 --- a/src/share/vm/opto/macro.cpp Fri Nov 27 07:56:58 2009 -0800 24.2 +++ b/src/share/vm/opto/macro.cpp Thu Nov 12 09:24:21 2009 -0800 24.3 @@ -912,15 +912,29 @@ 24.4 return false; 24.5 } 24.6 24.7 + CompileLog* log = C->log(); 24.8 + if (log != NULL) { 24.9 + Node* klass = alloc->in(AllocateNode::KlassNode); 24.10 + const TypeKlassPtr* tklass = _igvn.type(klass)->is_klassptr(); 24.11 + log->head("eliminate_allocation type='%d'", 24.12 + log->identify(tklass->klass())); 24.13 + JVMState* p = alloc->jvms(); 24.14 + while (p != NULL) { 24.15 + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); 24.16 + p = p->caller(); 24.17 + } 24.18 + log->tail("eliminate_allocation"); 24.19 + } 24.20 + 24.21 process_users_of_allocation(alloc); 24.22 24.23 #ifndef PRODUCT 24.24 -if (PrintEliminateAllocations) { 24.25 - if (alloc->is_AllocateArray()) 24.26 - tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx); 24.27 - else 24.28 - tty->print_cr("++++ Eliminated: %d Allocate", alloc->_idx); 24.29 -} 24.30 + if (PrintEliminateAllocations) { 24.31 + if (alloc->is_AllocateArray()) 24.32 + tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx); 24.33 + else 24.34 + tty->print_cr("++++ Eliminated: %d Allocate", alloc->_idx); 24.35 + } 24.36 #endif 24.37 24.38 return true; 24.39 @@ -1639,6 +1653,18 @@ 24.40 } // if (!oldbox->is_eliminated()) 24.41 } // if (alock->is_Lock() && !lock->is_coarsened()) 24.42 24.43 + CompileLog* log = C->log(); 24.44 + if (log != NULL) { 24.45 + log->head("eliminate_lock lock='%d'", 24.46 + alock->is_Lock()); 24.47 + JVMState* p = alock->jvms(); 24.48 + while (p != NULL) { 24.49 + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); 24.50 + p = p->caller(); 24.51 + } 24.52 + log->tail("eliminate_lock"); 24.53 + } 24.54 + 24.55 #ifndef PRODUCT 24.56 if (PrintEliminateLocks) { 24.57 if (alock->is_Lock()) {
25.1 --- a/src/share/vm/opto/memnode.cpp Fri Nov 27 07:56:58 2009 -0800 25.2 +++ b/src/share/vm/opto/memnode.cpp Thu Nov 12 09:24:21 2009 -0800 25.3 @@ -1503,6 +1503,8 @@ 25.4 } 25.5 } 25.6 } else if (tp->base() == Type::InstPtr) { 25.7 + const TypeInstPtr* tinst = tp->is_instptr(); 25.8 + ciKlass* klass = tinst->klass(); 25.9 assert( off != Type::OffsetBot || 25.10 // arrays can be cast to Objects 25.11 tp->is_oopptr()->klass()->is_java_lang_Object() || 25.12 @@ -1510,6 +1512,25 @@ 25.13 phase->C->has_unsafe_access(), 25.14 "Field accesses must be precise" ); 25.15 // For oop loads, we expect the _type to be precise 25.16 + if (OptimizeStringConcat && klass == phase->C->env()->String_klass() && 25.17 + adr->is_AddP() && off != Type::OffsetBot) { 25.18 + // For constant Strings treat the fields as compile time constants. 25.19 + Node* base = adr->in(AddPNode::Base); 25.20 + if (base->Opcode() == Op_ConP) { 25.21 + const TypeOopPtr* t = phase->type(base)->isa_oopptr(); 25.22 + ciObject* string = t->const_oop(); 25.23 + ciConstant constant = string->as_instance()->field_value_by_offset(off); 25.24 + if (constant.basic_type() == T_INT) { 25.25 + return TypeInt::make(constant.as_int()); 25.26 + } else if (constant.basic_type() == T_ARRAY) { 25.27 + if (adr->bottom_type()->is_ptr_to_narrowoop()) { 25.28 + return TypeNarrowOop::make_from_constant(constant.as_object()); 25.29 + } else { 25.30 + return TypeOopPtr::make_from_constant(constant.as_object()); 25.31 + } 25.32 + } 25.33 + } 25.34 + } 25.35 } else if (tp->base() == Type::KlassPtr) { 25.36 assert( off != Type::OffsetBot || 25.37 // arrays can be cast to Objects
26.1 --- a/src/share/vm/opto/node.hpp Fri Nov 27 07:56:58 2009 -0800 26.2 +++ b/src/share/vm/opto/node.hpp Thu Nov 12 09:24:21 2009 -0800 26.3 @@ -661,18 +661,25 @@ 26.4 return (_flags & Flag_is_Call) != 0; 26.5 } 26.6 26.7 + CallNode* isa_Call() const { 26.8 + return is_Call() ? as_Call() : NULL; 26.9 + } 26.10 + 26.11 CallNode *as_Call() const { // Only for CallNode (not for MachCallNode) 26.12 assert((_class_id & ClassMask_Call) == Class_Call, "invalid node class"); 26.13 return (CallNode*)this; 26.14 } 26.15 26.16 - #define DEFINE_CLASS_QUERY(type) \ 26.17 - bool is_##type() const { \ 26.18 + #define DEFINE_CLASS_QUERY(type) \ 26.19 + bool is_##type() const { \ 26.20 return ((_class_id & ClassMask_##type) == Class_##type); \ 26.21 - } \ 26.22 - type##Node *as_##type() const { \ 26.23 - assert(is_##type(), "invalid node class"); \ 26.24 - return (type##Node*)this; \ 26.25 + } \ 26.26 + type##Node *as_##type() const { \ 26.27 + assert(is_##type(), "invalid node class"); \ 26.28 + return (type##Node*)this; \ 26.29 + } \ 26.30 + type##Node* isa_##type() const { \ 26.31 + return (is_##type()) ? as_##type() : NULL; \ 26.32 } 26.33 26.34 DEFINE_CLASS_QUERY(AbstractLock) 26.35 @@ -1249,6 +1256,24 @@ 26.36 #undef I_VDUI_ONLY 26.37 #undef VDUI_ONLY 26.38 26.39 +// An Iterator that truly follows the iterator pattern. Doesn't 26.40 +// support deletion but could be made to. 26.41 +// 26.42 +// for (SimpleDUIterator i(n); i.has_next(); i.next()) { 26.43 +// Node* m = i.get(); 26.44 +// 26.45 +class SimpleDUIterator : public StackObj { 26.46 + private: 26.47 + Node* node; 26.48 + DUIterator_Fast i; 26.49 + DUIterator_Fast imax; 26.50 + public: 26.51 + SimpleDUIterator(Node* n): node(n), i(n->fast_outs(imax)) {} 26.52 + bool has_next() { return i < imax; } 26.53 + void next() { i++; } 26.54 + Node* get() { return node->fast_out(i); } 26.55 +}; 26.56 + 26.57 26.58 //----------------------------------------------------------------------------- 26.59 // Map dense integer indices to Nodes. Uses classic doubling-array trick. 26.60 @@ -1290,6 +1315,12 @@ 26.61 public: 26.62 Node_List() : Node_Array(Thread::current()->resource_area()), _cnt(0) {} 26.63 Node_List(Arena *a) : Node_Array(a), _cnt(0) {} 26.64 + bool contains(Node* n) { 26.65 + for (uint e = 0; e < size(); e++) { 26.66 + if (at(e) == n) return true; 26.67 + } 26.68 + return false; 26.69 + } 26.70 void insert( uint i, Node *n ) { Node_Array::insert(i,n); _cnt++; } 26.71 void remove( uint i ) { Node_Array::remove(i); _cnt--; } 26.72 void push( Node *b ) { map(_cnt++,b); }
27.1 --- a/src/share/vm/opto/parseHelper.cpp Fri Nov 27 07:56:58 2009 -0800 27.2 +++ b/src/share/vm/opto/parseHelper.cpp Thu Nov 12 09:24:21 2009 -0800 27.3 @@ -221,6 +221,14 @@ 27.4 27.5 // Push resultant oop onto stack 27.6 push(obj); 27.7 + 27.8 + // Keep track of whether opportunities exist for StringBuilder 27.9 + // optimizations. 27.10 + if (OptimizeStringConcat && 27.11 + (klass == C->env()->StringBuilder_klass() || 27.12 + klass == C->env()->StringBuffer_klass())) { 27.13 + C->set_has_stringbuilder(true); 27.14 + } 27.15 } 27.16 27.17 #ifndef PRODUCT
28.1 --- a/src/share/vm/opto/phase.hpp Fri Nov 27 07:56:58 2009 -0800 28.2 +++ b/src/share/vm/opto/phase.hpp Thu Nov 12 09:24:21 2009 -0800 28.3 @@ -44,6 +44,7 @@ 28.4 BlockLayout, // Linear ordering of blocks 28.5 Register_Allocation, // Register allocation, duh 28.6 LIVE, // Dragon-book LIVE range problem 28.7 + StringOpts, // StringBuilder related optimizations 28.8 Interference_Graph, // Building the IFG 28.9 Coalesce, // Coalescing copies 28.10 Ideal_Loop, // Find idealized trip-counted loops
29.1 --- a/src/share/vm/opto/phaseX.hpp Fri Nov 27 07:56:58 2009 -0800 29.2 +++ b/src/share/vm/opto/phaseX.hpp Thu Nov 12 09:24:21 2009 -0800 29.3 @@ -345,7 +345,11 @@ 29.4 Node *hash_find(const Node *n) { return _table.hash_find(n); } 29.5 29.6 // Used after parsing to eliminate values that are no longer in program 29.7 - void remove_useless_nodes(VectorSet &useful) { _table.remove_useless_nodes(useful); } 29.8 + void remove_useless_nodes(VectorSet &useful) { 29.9 + _table.remove_useless_nodes(useful); 29.10 + // this may invalidate cached cons so reset the cache 29.11 + init_con_caches(); 29.12 + } 29.13 29.14 virtual ConNode* uncached_makecon(const Type* t); // override from PhaseTransform 29.15
30.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 30.2 +++ b/src/share/vm/opto/stringopts.cpp Thu Nov 12 09:24:21 2009 -0800 30.3 @@ -0,0 +1,1395 @@ 30.4 +/* 30.5 + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. 30.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 30.7 + * 30.8 + * This code is free software; you can redistribute it and/or modify it 30.9 + * under the terms of the GNU General Public License version 2 only, as 30.10 + * published by the Free Software Foundation. 30.11 + * 30.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 30.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 30.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 30.15 + * version 2 for more details (a copy is included in the LICENSE file that 30.16 + * accompanied this code). 30.17 + * 30.18 + * You should have received a copy of the GNU General Public License version 30.19 + * 2 along with this work; if not, write to the Free Software Foundation, 30.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 30.21 + * 30.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 30.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 30.24 + * have any questions. 30.25 + * 30.26 + */ 30.27 + 30.28 +#include "incls/_precompiled.incl" 30.29 +#include "incls/_stringopts.cpp.incl" 30.30 + 30.31 +#define __ kit. 30.32 + 30.33 +class StringConcat : public ResourceObj { 30.34 + private: 30.35 + PhaseStringOpts* _stringopts; 30.36 + Node* _string_alloc; 30.37 + AllocateNode* _begin; // The allocation the begins the pattern 30.38 + CallStaticJavaNode* _end; // The final call of the pattern. Will either be 30.39 + // SB.toString or or String.<init>(SB.toString) 30.40 + bool _multiple; // indicates this is a fusion of two or more 30.41 + // separate StringBuilders 30.42 + 30.43 + Node* _arguments; // The list of arguments to be concatenated 30.44 + GrowableArray<int> _mode; // into a String along with a mode flag 30.45 + // indicating how to treat the value. 30.46 + 30.47 + Node_List _control; // List of control nodes that will be deleted 30.48 + Node_List _uncommon_traps; // Uncommon traps that needs to be rewritten 30.49 + // to restart at the initial JVMState. 30.50 + public: 30.51 + // Mode for converting arguments to Strings 30.52 + enum { 30.53 + StringMode, 30.54 + IntMode, 30.55 + CharMode 30.56 + }; 30.57 + 30.58 + StringConcat(PhaseStringOpts* stringopts, CallStaticJavaNode* end): 30.59 + _end(end), 30.60 + _begin(NULL), 30.61 + _multiple(false), 30.62 + _string_alloc(NULL), 30.63 + _stringopts(stringopts) { 30.64 + _arguments = new (_stringopts->C, 1) Node(1); 30.65 + _arguments->del_req(0); 30.66 + } 30.67 + 30.68 + bool validate_control_flow(); 30.69 + 30.70 + void merge_add() { 30.71 +#if 0 30.72 + // XXX This is place holder code for reusing an existing String 30.73 + // allocation but the logic for checking the state safety is 30.74 + // probably inadequate at the moment. 30.75 + CallProjections endprojs; 30.76 + sc->end()->extract_projections(&endprojs, false); 30.77 + if (endprojs.resproj != NULL) { 30.78 + for (SimpleDUIterator i(endprojs.resproj); i.has_next(); i.next()) { 30.79 + CallStaticJavaNode *use = i.get()->isa_CallStaticJava(); 30.80 + if (use != NULL && use->method() != NULL && 30.81 + use->method()->holder() == C->env()->String_klass() && 30.82 + use->method()->name() == ciSymbol::object_initializer_name() && 30.83 + use->in(TypeFunc::Parms + 1) == endprojs.resproj) { 30.84 + // Found useless new String(sb.toString()) so reuse the newly allocated String 30.85 + // when creating the result instead of allocating a new one. 30.86 + sc->set_string_alloc(use->in(TypeFunc::Parms)); 30.87 + sc->set_end(use); 30.88 + } 30.89 + } 30.90 + } 30.91 +#endif 30.92 + } 30.93 + 30.94 + StringConcat* merge(StringConcat* other, Node* arg); 30.95 + 30.96 + void set_allocation(AllocateNode* alloc) { 30.97 + _begin = alloc; 30.98 + } 30.99 + 30.100 + void append(Node* value, int mode) { 30.101 + _arguments->add_req(value); 30.102 + _mode.append(mode); 30.103 + } 30.104 + void push(Node* value, int mode) { 30.105 + _arguments->ins_req(0, value); 30.106 + _mode.insert_before(0, mode); 30.107 + } 30.108 + void push_string(Node* value) { 30.109 + push(value, StringMode); 30.110 + } 30.111 + void push_int(Node* value) { 30.112 + push(value, IntMode); 30.113 + } 30.114 + void push_char(Node* value) { 30.115 + push(value, CharMode); 30.116 + } 30.117 + 30.118 + Node* argument(int i) { 30.119 + return _arguments->in(i); 30.120 + } 30.121 + void set_argument(int i, Node* value) { 30.122 + _arguments->set_req(i, value); 30.123 + } 30.124 + int num_arguments() { 30.125 + return _mode.length(); 30.126 + } 30.127 + int mode(int i) { 30.128 + return _mode.at(i); 30.129 + } 30.130 + void add_control(Node* ctrl) { 30.131 + assert(!_control.contains(ctrl), "only push once"); 30.132 + _control.push(ctrl); 30.133 + } 30.134 + CallStaticJavaNode* end() { return _end; } 30.135 + AllocateNode* begin() { return _begin; } 30.136 + Node* string_alloc() { return _string_alloc; } 30.137 + 30.138 + void eliminate_unneeded_control(); 30.139 + void eliminate_initialize(InitializeNode* init); 30.140 + void eliminate_call(CallNode* call); 30.141 + 30.142 + void maybe_log_transform() { 30.143 + CompileLog* log = _stringopts->C->log(); 30.144 + if (log != NULL) { 30.145 + log->head("replace_string_concat arguments='%d' string_alloc='%d' multiple='%d'", 30.146 + num_arguments(), 30.147 + _string_alloc != NULL, 30.148 + _multiple); 30.149 + JVMState* p = _begin->jvms(); 30.150 + while (p != NULL) { 30.151 + log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method())); 30.152 + p = p->caller(); 30.153 + } 30.154 + log->tail("replace_string_concat"); 30.155 + } 30.156 + } 30.157 + 30.158 + void convert_uncommon_traps(GraphKit& kit, const JVMState* jvms) { 30.159 + for (uint u = 0; u < _uncommon_traps.size(); u++) { 30.160 + Node* uct = _uncommon_traps.at(u); 30.161 + 30.162 + // Build a new call using the jvms state of the allocate 30.163 + address call_addr = SharedRuntime::uncommon_trap_blob()->instructions_begin(); 30.164 + const TypeFunc* call_type = OptoRuntime::uncommon_trap_Type(); 30.165 + int size = call_type->domain()->cnt(); 30.166 + const TypePtr* no_memory_effects = NULL; 30.167 + Compile* C = _stringopts->C; 30.168 + CallStaticJavaNode* call = new (C, size) CallStaticJavaNode(call_type, call_addr, "uncommon_trap", 30.169 + jvms->bci(), no_memory_effects); 30.170 + for (int e = 0; e < TypeFunc::Parms; e++) { 30.171 + call->init_req(e, uct->in(e)); 30.172 + } 30.173 + // Set the trap request to record intrinsic failure if this trap 30.174 + // is taken too many times. Ideally we would handle then traps by 30.175 + // doing the original bookkeeping in the MDO so that if it caused 30.176 + // the code to be thrown out we could still recompile and use the 30.177 + // optimization. Failing the uncommon traps doesn't really mean 30.178 + // that the optimization is a bad idea but there's no other way to 30.179 + // do the MDO updates currently. 30.180 + int trap_request = Deoptimization::make_trap_request(Deoptimization::Reason_intrinsic, 30.181 + Deoptimization::Action_make_not_entrant); 30.182 + call->init_req(TypeFunc::Parms, __ intcon(trap_request)); 30.183 + kit.add_safepoint_edges(call); 30.184 + 30.185 + _stringopts->gvn()->transform(call); 30.186 + C->gvn_replace_by(uct, call); 30.187 + uct->disconnect_inputs(NULL); 30.188 + } 30.189 + } 30.190 + 30.191 + void cleanup() { 30.192 + // disconnect the hook node 30.193 + _arguments->disconnect_inputs(NULL); 30.194 + } 30.195 +}; 30.196 + 30.197 + 30.198 +void StringConcat::eliminate_unneeded_control() { 30.199 + eliminate_initialize(begin()->initialization()); 30.200 + for (uint i = 0; i < _control.size(); i++) { 30.201 + Node* n = _control.at(i); 30.202 + if (n->is_Call()) { 30.203 + if (n != _end) { 30.204 + eliminate_call(n->as_Call()); 30.205 + } 30.206 + } else if (n->is_IfTrue()) { 30.207 + Compile* C = _stringopts->C; 30.208 + C->gvn_replace_by(n, n->in(0)->in(0)); 30.209 + C->gvn_replace_by(n->in(0), C->top()); 30.210 + } 30.211 + } 30.212 +} 30.213 + 30.214 + 30.215 +StringConcat* StringConcat::merge(StringConcat* other, Node* arg) { 30.216 + StringConcat* result = new StringConcat(_stringopts, _end); 30.217 + for (uint x = 0; x < _control.size(); x++) { 30.218 + Node* n = _control.at(x); 30.219 + if (n->is_Call()) { 30.220 + result->_control.push(n); 30.221 + } 30.222 + } 30.223 + for (uint x = 0; x < other->_control.size(); x++) { 30.224 + Node* n = other->_control.at(x); 30.225 + if (n->is_Call()) { 30.226 + result->_control.push(n); 30.227 + } 30.228 + } 30.229 + assert(result->_control.contains(other->_end), "what?"); 30.230 + assert(result->_control.contains(_begin), "what?"); 30.231 + for (int x = 0; x < num_arguments(); x++) { 30.232 + if (argument(x) == arg) { 30.233 + // replace the toString result with the all the arguments that 30.234 + // made up the other StringConcat 30.235 + for (int y = 0; y < other->num_arguments(); y++) { 30.236 + result->append(other->argument(y), other->mode(y)); 30.237 + } 30.238 + } else { 30.239 + result->append(argument(x), mode(x)); 30.240 + } 30.241 + } 30.242 + result->set_allocation(other->_begin); 30.243 + result->_multiple = true; 30.244 + return result; 30.245 +} 30.246 + 30.247 + 30.248 +void StringConcat::eliminate_call(CallNode* call) { 30.249 + Compile* C = _stringopts->C; 30.250 + CallProjections projs; 30.251 + call->extract_projections(&projs, false); 30.252 + if (projs.fallthrough_catchproj != NULL) { 30.253 + C->gvn_replace_by(projs.fallthrough_catchproj, call->in(TypeFunc::Control)); 30.254 + } 30.255 + if (projs.fallthrough_memproj != NULL) { 30.256 + C->gvn_replace_by(projs.fallthrough_memproj, call->in(TypeFunc::Memory)); 30.257 + } 30.258 + if (projs.catchall_memproj != NULL) { 30.259 + C->gvn_replace_by(projs.catchall_memproj, C->top()); 30.260 + } 30.261 + if (projs.fallthrough_ioproj != NULL) { 30.262 + C->gvn_replace_by(projs.fallthrough_ioproj, call->in(TypeFunc::I_O)); 30.263 + } 30.264 + if (projs.catchall_ioproj != NULL) { 30.265 + C->gvn_replace_by(projs.catchall_ioproj, C->top()); 30.266 + } 30.267 + if (projs.catchall_catchproj != NULL) { 30.268 + // EA can't cope with the partially collapsed graph this 30.269 + // creates so put it on the worklist to be collapsed later. 30.270 + for (SimpleDUIterator i(projs.catchall_catchproj); i.has_next(); i.next()) { 30.271 + Node *use = i.get(); 30.272 + int opc = use->Opcode(); 30.273 + if (opc == Op_CreateEx || opc == Op_Region) { 30.274 + _stringopts->record_dead_node(use); 30.275 + } 30.276 + } 30.277 + C->gvn_replace_by(projs.catchall_catchproj, C->top()); 30.278 + } 30.279 + if (projs.resproj != NULL) { 30.280 + C->gvn_replace_by(projs.resproj, C->top()); 30.281 + } 30.282 + C->gvn_replace_by(call, C->top()); 30.283 +} 30.284 + 30.285 +void StringConcat::eliminate_initialize(InitializeNode* init) { 30.286 + Compile* C = _stringopts->C; 30.287 + 30.288 + // Eliminate Initialize node. 30.289 + assert(init->outcnt() <= 2, "only a control and memory projection expected"); 30.290 + assert(init->req() <= InitializeNode::RawStores, "no pending inits"); 30.291 + Node *ctrl_proj = init->proj_out(TypeFunc::Control); 30.292 + if (ctrl_proj != NULL) { 30.293 + C->gvn_replace_by(ctrl_proj, init->in(TypeFunc::Control)); 30.294 + } 30.295 + Node *mem_proj = init->proj_out(TypeFunc::Memory); 30.296 + if (mem_proj != NULL) { 30.297 + Node *mem = init->in(TypeFunc::Memory); 30.298 + C->gvn_replace_by(mem_proj, mem); 30.299 + } 30.300 + C->gvn_replace_by(init, C->top()); 30.301 + init->disconnect_inputs(NULL); 30.302 +} 30.303 + 30.304 +Node_List PhaseStringOpts::collect_toString_calls() { 30.305 + Node_List string_calls; 30.306 + Node_List worklist; 30.307 + 30.308 + _visited.Clear(); 30.309 + 30.310 + // Prime the worklist 30.311 + for (uint i = 1; i < C->root()->len(); i++) { 30.312 + Node* n = C->root()->in(i); 30.313 + if (n != NULL && !_visited.test_set(n->_idx)) { 30.314 + worklist.push(n); 30.315 + } 30.316 + } 30.317 + 30.318 + while (worklist.size() > 0) { 30.319 + Node* ctrl = worklist.pop(); 30.320 + if (ctrl->is_CallStaticJava()) { 30.321 + CallStaticJavaNode* csj = ctrl->as_CallStaticJava(); 30.322 + ciMethod* m = csj->method(); 30.323 + if (m != NULL && 30.324 + (m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString || 30.325 + m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString)) { 30.326 + string_calls.push(csj); 30.327 + } 30.328 + } 30.329 + if (ctrl->in(0) != NULL && !_visited.test_set(ctrl->in(0)->_idx)) { 30.330 + worklist.push(ctrl->in(0)); 30.331 + } 30.332 + if (ctrl->is_Region()) { 30.333 + for (uint i = 1; i < ctrl->len(); i++) { 30.334 + if (ctrl->in(i) != NULL && !_visited.test_set(ctrl->in(i)->_idx)) { 30.335 + worklist.push(ctrl->in(i)); 30.336 + } 30.337 + } 30.338 + } 30.339 + } 30.340 + return string_calls; 30.341 +} 30.342 + 30.343 + 30.344 +StringConcat* PhaseStringOpts::build_candidate(CallStaticJavaNode* call) { 30.345 + ciMethod* m = call->method(); 30.346 + ciSymbol* string_sig; 30.347 + ciSymbol* int_sig; 30.348 + ciSymbol* char_sig; 30.349 + if (m->holder() == C->env()->StringBuilder_klass()) { 30.350 + string_sig = ciSymbol::String_StringBuilder_signature(); 30.351 + int_sig = ciSymbol::int_StringBuilder_signature(); 30.352 + char_sig = ciSymbol::char_StringBuilder_signature(); 30.353 + } else if (m->holder() == C->env()->StringBuffer_klass()) { 30.354 + string_sig = ciSymbol::String_StringBuffer_signature(); 30.355 + int_sig = ciSymbol::int_StringBuffer_signature(); 30.356 + char_sig = ciSymbol::char_StringBuffer_signature(); 30.357 + } else { 30.358 + return NULL; 30.359 + } 30.360 +#ifndef PRODUCT 30.361 + if (PrintOptimizeStringConcat) { 30.362 + tty->print("considering toString call in "); 30.363 + call->jvms()->dump_spec(tty); tty->cr(); 30.364 + } 30.365 +#endif 30.366 + 30.367 + StringConcat* sc = new StringConcat(this, call); 30.368 + 30.369 + AllocateNode* alloc = NULL; 30.370 + InitializeNode* init = NULL; 30.371 + 30.372 + // possible opportunity for StringBuilder fusion 30.373 + CallStaticJavaNode* cnode = call; 30.374 + while (cnode) { 30.375 + Node* recv = cnode->in(TypeFunc::Parms)->uncast(); 30.376 + if (recv->is_Proj()) { 30.377 + recv = recv->in(0); 30.378 + } 30.379 + cnode = recv->isa_CallStaticJava(); 30.380 + if (cnode == NULL) { 30.381 + alloc = recv->isa_Allocate(); 30.382 + if (alloc == NULL) { 30.383 + break; 30.384 + } 30.385 + // Find the constructor call 30.386 + Node* result = alloc->result_cast(); 30.387 + if (result == NULL || !result->is_CheckCastPP()) { 30.388 + // strange looking allocation 30.389 +#ifndef PRODUCT 30.390 + if (PrintOptimizeStringConcat) { 30.391 + tty->print("giving up because allocation looks strange "); 30.392 + alloc->jvms()->dump_spec(tty); tty->cr(); 30.393 + } 30.394 +#endif 30.395 + break; 30.396 + } 30.397 + Node* constructor = NULL; 30.398 + for (SimpleDUIterator i(result); i.has_next(); i.next()) { 30.399 + CallStaticJavaNode *use = i.get()->isa_CallStaticJava(); 30.400 + if (use != NULL && use->method() != NULL && 30.401 + use->method()->name() == ciSymbol::object_initializer_name() && 30.402 + use->method()->holder() == m->holder()) { 30.403 + // Matched the constructor. 30.404 + ciSymbol* sig = use->method()->signature()->as_symbol(); 30.405 + if (sig == ciSymbol::void_method_signature() || 30.406 + sig == ciSymbol::int_void_signature() || 30.407 + sig == ciSymbol::string_void_signature()) { 30.408 + if (sig == ciSymbol::string_void_signature()) { 30.409 + // StringBuilder(String) so pick this up as the first argument 30.410 + assert(use->in(TypeFunc::Parms + 1) != NULL, "what?"); 30.411 + sc->push_string(use->in(TypeFunc::Parms + 1)); 30.412 + } 30.413 + // The int variant takes an initial size for the backing 30.414 + // array so just treat it like the void version. 30.415 + constructor = use; 30.416 + } else { 30.417 +#ifndef PRODUCT 30.418 + if (PrintOptimizeStringConcat) { 30.419 + tty->print("unexpected constructor signature: %s", sig->as_utf8()); 30.420 + } 30.421 +#endif 30.422 + } 30.423 + break; 30.424 + } 30.425 + } 30.426 + if (constructor == NULL) { 30.427 + // couldn't find constructor 30.428 +#ifndef PRODUCT 30.429 + if (PrintOptimizeStringConcat) { 30.430 + tty->print("giving up because couldn't find constructor "); 30.431 + alloc->jvms()->dump_spec(tty); 30.432 + } 30.433 +#endif 30.434 + break; 30.435 + } 30.436 + 30.437 + // Walked all the way back and found the constructor call so see 30.438 + // if this call converted into a direct string concatenation. 30.439 + sc->add_control(call); 30.440 + sc->add_control(constructor); 30.441 + sc->add_control(alloc); 30.442 + sc->set_allocation(alloc); 30.443 + if (sc->validate_control_flow()) { 30.444 + return sc; 30.445 + } else { 30.446 + return NULL; 30.447 + } 30.448 + } else if (cnode->method() == NULL) { 30.449 + break; 30.450 + } else if (cnode->method()->holder() == m->holder() && 30.451 + cnode->method()->name() == ciSymbol::append_name() && 30.452 + (cnode->method()->signature()->as_symbol() == string_sig || 30.453 + cnode->method()->signature()->as_symbol() == char_sig || 30.454 + cnode->method()->signature()->as_symbol() == int_sig)) { 30.455 + sc->add_control(cnode); 30.456 + Node* arg = cnode->in(TypeFunc::Parms + 1); 30.457 + if (cnode->method()->signature()->as_symbol() == int_sig) { 30.458 + sc->push_int(arg); 30.459 + } else if (cnode->method()->signature()->as_symbol() == char_sig) { 30.460 + sc->push_char(arg); 30.461 + } else { 30.462 + if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) { 30.463 + CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava(); 30.464 + if (csj->method() != NULL && 30.465 + csj->method()->holder() == C->env()->Integer_klass() && 30.466 + csj->method()->name() == ciSymbol::toString_name()) { 30.467 + sc->add_control(csj); 30.468 + sc->push_int(csj->in(TypeFunc::Parms)); 30.469 + continue; 30.470 + } 30.471 + } 30.472 + sc->push_string(arg); 30.473 + } 30.474 + continue; 30.475 + } else { 30.476 + // some unhandled signature 30.477 +#ifndef PRODUCT 30.478 + if (PrintOptimizeStringConcat) { 30.479 + tty->print("giving up because encountered unexpected signature "); 30.480 + cnode->tf()->dump(); tty->cr(); 30.481 + cnode->in(TypeFunc::Parms + 1)->dump(); 30.482 + } 30.483 +#endif 30.484 + break; 30.485 + } 30.486 + } 30.487 + return NULL; 30.488 +} 30.489 + 30.490 + 30.491 +PhaseStringOpts::PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List*): 30.492 + Phase(StringOpts), 30.493 + _gvn(gvn), 30.494 + _visited(Thread::current()->resource_area()) { 30.495 + 30.496 + assert(OptimizeStringConcat, "shouldn't be here"); 30.497 + 30.498 + size_table_field = C->env()->Integer_klass()->get_field_by_name(ciSymbol::make("sizeTable"), 30.499 + ciSymbol::make("[I"), true); 30.500 + if (size_table_field == NULL) { 30.501 + // Something wrong so give up. 30.502 + assert(false, "why can't we find Integer.sizeTable?"); 30.503 + return; 30.504 + } 30.505 + 30.506 + // Collect the types needed to talk about the various slices of memory 30.507 + const TypeInstPtr* string_type = TypeInstPtr::make(TypePtr::NotNull, C->env()->String_klass(), 30.508 + false, NULL, 0); 30.509 + 30.510 + const TypePtr* value_field_type = string_type->add_offset(java_lang_String::value_offset_in_bytes()); 30.511 + const TypePtr* offset_field_type = string_type->add_offset(java_lang_String::offset_offset_in_bytes()); 30.512 + const TypePtr* count_field_type = string_type->add_offset(java_lang_String::count_offset_in_bytes()); 30.513 + 30.514 + value_field_idx = C->get_alias_index(value_field_type); 30.515 + count_field_idx = C->get_alias_index(count_field_type); 30.516 + offset_field_idx = C->get_alias_index(offset_field_type); 30.517 + char_adr_idx = C->get_alias_index(TypeAryPtr::CHARS); 30.518 + 30.519 + // For each locally allocated StringBuffer see if the usages can be 30.520 + // collapsed into a single String construction. 30.521 + 30.522 + // Run through the list of allocation looking for SB.toString to see 30.523 + // if it's possible to fuse the usage of the SB into a single String 30.524 + // construction. 30.525 + GrowableArray<StringConcat*> concats; 30.526 + Node_List toStrings = collect_toString_calls(); 30.527 + while (toStrings.size() > 0) { 30.528 + StringConcat* sc = build_candidate(toStrings.pop()->as_CallStaticJava()); 30.529 + if (sc != NULL) { 30.530 + concats.push(sc); 30.531 + } 30.532 + } 30.533 + 30.534 + // try to coalesce separate concats 30.535 + restart: 30.536 + for (int c = 0; c < concats.length(); c++) { 30.537 + StringConcat* sc = concats.at(c); 30.538 + for (int i = 0; i < sc->num_arguments(); i++) { 30.539 + Node* arg = sc->argument(i); 30.540 + if (arg->is_Proj() && arg->in(0)->is_CallStaticJava()) { 30.541 + CallStaticJavaNode* csj = arg->in(0)->as_CallStaticJava(); 30.542 + if (csj->method() != NULL && 30.543 + (csj->method()->holder() == C->env()->StringBuffer_klass() || 30.544 + csj->method()->holder() == C->env()->StringBuilder_klass()) && 30.545 + csj->method()->name() == ciSymbol::toString_name()) { 30.546 + for (int o = 0; o < concats.length(); o++) { 30.547 + if (c == o) continue; 30.548 + StringConcat* other = concats.at(o); 30.549 + if (other->end() == csj) { 30.550 +#ifndef PRODUCT 30.551 + if (PrintOptimizeStringConcat) { 30.552 + tty->print_cr("considering stacked concats"); 30.553 + } 30.554 +#endif 30.555 + 30.556 + StringConcat* merged = sc->merge(other, arg); 30.557 + if (merged->validate_control_flow()) { 30.558 +#ifndef PRODUCT 30.559 + if (PrintOptimizeStringConcat) { 30.560 + tty->print_cr("stacking would succeed"); 30.561 + } 30.562 +#endif 30.563 + if (c < o) { 30.564 + concats.remove_at(o); 30.565 + concats.at_put(c, merged); 30.566 + } else { 30.567 + concats.remove_at(c); 30.568 + concats.at_put(o, merged); 30.569 + } 30.570 + goto restart; 30.571 + } else { 30.572 +#ifndef PRODUCT 30.573 + if (PrintOptimizeStringConcat) { 30.574 + tty->print_cr("stacking would fail"); 30.575 + } 30.576 +#endif 30.577 + } 30.578 + } 30.579 + } 30.580 + } 30.581 + } 30.582 + } 30.583 + } 30.584 + 30.585 + 30.586 + for (int c = 0; c < concats.length(); c++) { 30.587 + StringConcat* sc = concats.at(c); 30.588 + replace_string_concat(sc); 30.589 + } 30.590 + 30.591 + remove_dead_nodes(); 30.592 +} 30.593 + 30.594 +void PhaseStringOpts::record_dead_node(Node* dead) { 30.595 + dead_worklist.push(dead); 30.596 +} 30.597 + 30.598 +void PhaseStringOpts::remove_dead_nodes() { 30.599 + // Delete any dead nodes to make things clean enough that escape 30.600 + // analysis doesn't get unhappy. 30.601 + while (dead_worklist.size() > 0) { 30.602 + Node* use = dead_worklist.pop(); 30.603 + int opc = use->Opcode(); 30.604 + switch (opc) { 30.605 + case Op_Region: { 30.606 + uint i = 1; 30.607 + for (i = 1; i < use->req(); i++) { 30.608 + if (use->in(i) != C->top()) { 30.609 + break; 30.610 + } 30.611 + } 30.612 + if (i >= use->req()) { 30.613 + for (SimpleDUIterator i(use); i.has_next(); i.next()) { 30.614 + Node* m = i.get(); 30.615 + if (m->is_Phi()) { 30.616 + dead_worklist.push(m); 30.617 + } 30.618 + } 30.619 + C->gvn_replace_by(use, C->top()); 30.620 + } 30.621 + break; 30.622 + } 30.623 + case Op_AddP: 30.624 + case Op_CreateEx: { 30.625 + // Recurisvely clean up references to CreateEx so EA doesn't 30.626 + // get unhappy about the partially collapsed graph. 30.627 + for (SimpleDUIterator i(use); i.has_next(); i.next()) { 30.628 + Node* m = i.get(); 30.629 + if (m->is_AddP()) { 30.630 + dead_worklist.push(m); 30.631 + } 30.632 + } 30.633 + C->gvn_replace_by(use, C->top()); 30.634 + break; 30.635 + } 30.636 + case Op_Phi: 30.637 + if (use->in(0) == C->top()) { 30.638 + C->gvn_replace_by(use, C->top()); 30.639 + } 30.640 + break; 30.641 + } 30.642 + } 30.643 +} 30.644 + 30.645 + 30.646 +bool StringConcat::validate_control_flow() { 30.647 + // We found all the calls and arguments now lets see if it's 30.648 + // safe to transform the graph as we would expect. 30.649 + 30.650 + // Check to see if this resulted in too many uncommon traps previously 30.651 + if (Compile::current()->too_many_traps(_begin->jvms()->method(), _begin->jvms()->bci(), 30.652 + Deoptimization::Reason_intrinsic)) { 30.653 + return false; 30.654 + } 30.655 + 30.656 + // Walk backwards over the control flow from toString to the 30.657 + // allocation and make sure all the control flow is ok. This 30.658 + // means it's either going to be eliminated once the calls are 30.659 + // removed or it can safely be transformed into an uncommon 30.660 + // trap. 30.661 + 30.662 + int null_check_count = 0; 30.663 + Unique_Node_List ctrl_path; 30.664 + 30.665 + assert(_control.contains(_begin), "missing"); 30.666 + assert(_control.contains(_end), "missing"); 30.667 + 30.668 + // Collect the nodes that we know about and will eliminate into ctrl_path 30.669 + for (uint i = 0; i < _control.size(); i++) { 30.670 + // Push the call and it's control projection 30.671 + Node* n = _control.at(i); 30.672 + if (n->is_Allocate()) { 30.673 + AllocateNode* an = n->as_Allocate(); 30.674 + InitializeNode* init = an->initialization(); 30.675 + ctrl_path.push(init); 30.676 + ctrl_path.push(init->as_Multi()->proj_out(0)); 30.677 + } 30.678 + if (n->is_Call()) { 30.679 + CallNode* cn = n->as_Call(); 30.680 + ctrl_path.push(cn); 30.681 + ctrl_path.push(cn->proj_out(0)); 30.682 + ctrl_path.push(cn->proj_out(0)->unique_out()); 30.683 + ctrl_path.push(cn->proj_out(0)->unique_out()->as_Catch()->proj_out(0)); 30.684 + } else { 30.685 + ShouldNotReachHere(); 30.686 + } 30.687 + } 30.688 + 30.689 + // Skip backwards through the control checking for unexpected contro flow 30.690 + Node* ptr = _end; 30.691 + bool fail = false; 30.692 + while (ptr != _begin) { 30.693 + if (ptr->is_Call() && ctrl_path.member(ptr)) { 30.694 + ptr = ptr->in(0); 30.695 + } else if (ptr->is_CatchProj() && ctrl_path.member(ptr)) { 30.696 + ptr = ptr->in(0)->in(0)->in(0); 30.697 + assert(ctrl_path.member(ptr), "should be a known piece of control"); 30.698 + } else if (ptr->is_IfTrue()) { 30.699 + IfNode* iff = ptr->in(0)->as_If(); 30.700 + BoolNode* b = iff->in(1)->isa_Bool(); 30.701 + Node* cmp = b->in(1); 30.702 + Node* v1 = cmp->in(1); 30.703 + Node* v2 = cmp->in(2); 30.704 + Node* otherproj = iff->proj_out(1 - ptr->as_Proj()->_con); 30.705 + 30.706 + // Null check of the return of append which can simply be eliminated 30.707 + if (b->_test._test == BoolTest::ne && 30.708 + v2->bottom_type() == TypePtr::NULL_PTR && 30.709 + v1->is_Proj() && ctrl_path.member(v1->in(0))) { 30.710 + // NULL check of the return value of the append 30.711 + null_check_count++; 30.712 + if (otherproj->outcnt() == 1) { 30.713 + CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); 30.714 + if (call != NULL && call->_name != NULL && strcmp(call->_name, "uncommon_trap") == 0) { 30.715 + ctrl_path.push(call); 30.716 + } 30.717 + } 30.718 + _control.push(ptr); 30.719 + ptr = ptr->in(0)->in(0); 30.720 + continue; 30.721 + } 30.722 + 30.723 + // A test which leads to an uncommon trap which should be safe. 30.724 + // Later this trap will be converted into a trap that restarts 30.725 + // at the beginning. 30.726 + if (otherproj->outcnt() == 1) { 30.727 + CallStaticJavaNode* call = otherproj->unique_out()->isa_CallStaticJava(); 30.728 + if (call != NULL && call->_name != NULL && strcmp(call->_name, "uncommon_trap") == 0) { 30.729 + // control flow leads to uct so should be ok 30.730 + _uncommon_traps.push(call); 30.731 + ctrl_path.push(call); 30.732 + ptr = ptr->in(0)->in(0); 30.733 + continue; 30.734 + } 30.735 + } 30.736 + 30.737 +#ifndef PRODUCT 30.738 + // Some unexpected control flow we don't know how to handle. 30.739 + if (PrintOptimizeStringConcat) { 30.740 + tty->print_cr("failing with unknown test"); 30.741 + b->dump(); 30.742 + cmp->dump(); 30.743 + v1->dump(); 30.744 + v2->dump(); 30.745 + tty->cr(); 30.746 + } 30.747 +#endif 30.748 + break; 30.749 + } else if (ptr->is_Proj() && ptr->in(0)->is_Initialize()) { 30.750 + ptr = ptr->in(0)->in(0); 30.751 + } else if (ptr->is_Region()) { 30.752 + Node* copy = ptr->as_Region()->is_copy(); 30.753 + if (copy != NULL) { 30.754 + ptr = copy; 30.755 + continue; 30.756 + } 30.757 + if (ptr->req() == 3 && 30.758 + ptr->in(1) != NULL && ptr->in(1)->is_Proj() && 30.759 + ptr->in(2) != NULL && ptr->in(2)->is_Proj() && 30.760 + ptr->in(1)->in(0) == ptr->in(2)->in(0) && 30.761 + ptr->in(1)->in(0) != NULL && ptr->in(1)->in(0)->is_If()) { 30.762 + // Simple diamond. 30.763 + // XXX should check for possibly merging stores. simple data merges are ok. 30.764 + ptr = ptr->in(1)->in(0)->in(0); 30.765 + continue; 30.766 + } 30.767 +#ifndef PRODUCT 30.768 + if (PrintOptimizeStringConcat) { 30.769 + tty->print_cr("fusion would fail for region"); 30.770 + _begin->dump(); 30.771 + ptr->dump(2); 30.772 + } 30.773 +#endif 30.774 + fail = true; 30.775 + break; 30.776 + } else { 30.777 + // other unknown control 30.778 + if (!fail) { 30.779 +#ifndef PRODUCT 30.780 + if (PrintOptimizeStringConcat) { 30.781 + tty->print_cr("fusion would fail for"); 30.782 + _begin->dump(); 30.783 + } 30.784 +#endif 30.785 + fail = true; 30.786 + } 30.787 +#ifndef PRODUCT 30.788 + if (PrintOptimizeStringConcat) { 30.789 + ptr->dump(); 30.790 + } 30.791 +#endif 30.792 + ptr = ptr->in(0); 30.793 + } 30.794 + } 30.795 +#ifndef PRODUCT 30.796 + if (PrintOptimizeStringConcat && fail) { 30.797 + tty->cr(); 30.798 + } 30.799 +#endif 30.800 + if (fail) return !fail; 30.801 + 30.802 + // Validate that all these results produced are contained within 30.803 + // this cluster of objects. First collect all the results produced 30.804 + // by calls in the region. 30.805 + _stringopts->_visited.Clear(); 30.806 + Node_List worklist; 30.807 + Node* final_result = _end->proj_out(TypeFunc::Parms); 30.808 + for (uint i = 0; i < _control.size(); i++) { 30.809 + CallNode* cnode = _control.at(i)->isa_Call(); 30.810 + if (cnode != NULL) { 30.811 + _stringopts->_visited.test_set(cnode->_idx); 30.812 + } 30.813 + Node* result = cnode != NULL ? cnode->proj_out(TypeFunc::Parms) : NULL; 30.814 + if (result != NULL && result != final_result) { 30.815 + worklist.push(result); 30.816 + } 30.817 + } 30.818 + 30.819 + Node* last_result = NULL; 30.820 + while (worklist.size() > 0) { 30.821 + Node* result = worklist.pop(); 30.822 + if (_stringopts->_visited.test_set(result->_idx)) 30.823 + continue; 30.824 + for (SimpleDUIterator i(result); i.has_next(); i.next()) { 30.825 + Node *use = i.get(); 30.826 + if (ctrl_path.member(use)) { 30.827 + // already checked this 30.828 + continue; 30.829 + } 30.830 + int opc = use->Opcode(); 30.831 + if (opc == Op_CmpP || opc == Op_Node) { 30.832 + ctrl_path.push(use); 30.833 + continue; 30.834 + } 30.835 + if (opc == Op_CastPP || opc == Op_CheckCastPP) { 30.836 + for (SimpleDUIterator j(use); j.has_next(); j.next()) { 30.837 + worklist.push(j.get()); 30.838 + } 30.839 + worklist.push(use->in(1)); 30.840 + ctrl_path.push(use); 30.841 + continue; 30.842 + } 30.843 +#ifndef PRODUCT 30.844 + if (PrintOptimizeStringConcat) { 30.845 + if (result != last_result) { 30.846 + last_result = result; 30.847 + tty->print_cr("extra uses for result:"); 30.848 + last_result->dump(); 30.849 + } 30.850 + use->dump(); 30.851 + } 30.852 +#endif 30.853 + fail = true; 30.854 + break; 30.855 + } 30.856 + } 30.857 + 30.858 +#ifndef PRODUCT 30.859 + if (PrintOptimizeStringConcat && !fail) { 30.860 + ttyLocker ttyl; 30.861 + tty->cr(); 30.862 + tty->print("fusion would succeed (%d %d) for ", null_check_count, _uncommon_traps.size()); 30.863 + _begin->jvms()->dump_spec(tty); tty->cr(); 30.864 + for (int i = 0; i < num_arguments(); i++) { 30.865 + argument(i)->dump(); 30.866 + } 30.867 + _control.dump(); 30.868 + tty->cr(); 30.869 + } 30.870 +#endif 30.871 + 30.872 + return !fail; 30.873 +} 30.874 + 30.875 +Node* PhaseStringOpts::fetch_static_field(GraphKit& kit, ciField* field) { 30.876 + const TypeKlassPtr* klass_type = TypeKlassPtr::make(field->holder()); 30.877 + Node* klass_node = __ makecon(klass_type); 30.878 + BasicType bt = field->layout_type(); 30.879 + ciType* field_klass = field->type(); 30.880 + 30.881 + const Type *type; 30.882 + if( bt == T_OBJECT ) { 30.883 + if (!field->type()->is_loaded()) { 30.884 + type = TypeInstPtr::BOTTOM; 30.885 + } else if (field->is_constant()) { 30.886 + // This can happen if the constant oop is non-perm. 30.887 + ciObject* con = field->constant_value().as_object(); 30.888 + // Do not "join" in the previous type; it doesn't add value, 30.889 + // and may yield a vacuous result if the field is of interface type. 30.890 + type = TypeOopPtr::make_from_constant(con)->isa_oopptr(); 30.891 + assert(type != NULL, "field singleton type must be consistent"); 30.892 + } else { 30.893 + type = TypeOopPtr::make_from_klass(field_klass->as_klass()); 30.894 + } 30.895 + } else { 30.896 + type = Type::get_const_basic_type(bt); 30.897 + } 30.898 + 30.899 + return kit.make_load(NULL, kit.basic_plus_adr(klass_node, field->offset_in_bytes()), 30.900 + type, T_OBJECT, 30.901 + C->get_alias_index(klass_type->add_offset(field->offset_in_bytes()))); 30.902 +} 30.903 + 30.904 +Node* PhaseStringOpts::int_stringSize(GraphKit& kit, Node* arg) { 30.905 + RegionNode *final_merge = new (C, 3) RegionNode(3); 30.906 + kit.gvn().set_type(final_merge, Type::CONTROL); 30.907 + Node* final_size = new (C, 3) PhiNode(final_merge, TypeInt::INT); 30.908 + kit.gvn().set_type(final_size, TypeInt::INT); 30.909 + 30.910 + IfNode* iff = kit.create_and_map_if(kit.control(), 30.911 + __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), 30.912 + PROB_FAIR, COUNT_UNKNOWN); 30.913 + Node* is_min = __ IfFalse(iff); 30.914 + final_merge->init_req(1, is_min); 30.915 + final_size->init_req(1, __ intcon(11)); 30.916 + 30.917 + kit.set_control(__ IfTrue(iff)); 30.918 + if (kit.stopped()) { 30.919 + final_merge->init_req(2, C->top()); 30.920 + final_size->init_req(2, C->top()); 30.921 + } else { 30.922 + 30.923 + // int size = (i < 0) ? stringSize(-i) + 1 : stringSize(i); 30.924 + RegionNode *r = new (C, 3) RegionNode(3); 30.925 + kit.gvn().set_type(r, Type::CONTROL); 30.926 + Node *phi = new (C, 3) PhiNode(r, TypeInt::INT); 30.927 + kit.gvn().set_type(phi, TypeInt::INT); 30.928 + Node *size = new (C, 3) PhiNode(r, TypeInt::INT); 30.929 + kit.gvn().set_type(size, TypeInt::INT); 30.930 + Node* chk = __ CmpI(arg, __ intcon(0)); 30.931 + Node* p = __ Bool(chk, BoolTest::lt); 30.932 + IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_FAIR, COUNT_UNKNOWN); 30.933 + Node* lessthan = __ IfTrue(iff); 30.934 + Node* greaterequal = __ IfFalse(iff); 30.935 + r->init_req(1, lessthan); 30.936 + phi->init_req(1, __ SubI(__ intcon(0), arg)); 30.937 + size->init_req(1, __ intcon(1)); 30.938 + r->init_req(2, greaterequal); 30.939 + phi->init_req(2, arg); 30.940 + size->init_req(2, __ intcon(0)); 30.941 + kit.set_control(r); 30.942 + C->record_for_igvn(r); 30.943 + C->record_for_igvn(phi); 30.944 + C->record_for_igvn(size); 30.945 + 30.946 + // for (int i=0; ; i++) 30.947 + // if (x <= sizeTable[i]) 30.948 + // return i+1; 30.949 + RegionNode *loop = new (C, 3) RegionNode(3); 30.950 + loop->init_req(1, kit.control()); 30.951 + kit.gvn().set_type(loop, Type::CONTROL); 30.952 + 30.953 + Node *index = new (C, 3) PhiNode(loop, TypeInt::INT); 30.954 + index->init_req(1, __ intcon(0)); 30.955 + kit.gvn().set_type(index, TypeInt::INT); 30.956 + kit.set_control(loop); 30.957 + Node* sizeTable = fetch_static_field(kit, size_table_field); 30.958 + 30.959 + Node* value = kit.load_array_element(NULL, sizeTable, index, TypeAryPtr::INTS); 30.960 + C->record_for_igvn(value); 30.961 + Node* limit = __ CmpI(phi, value); 30.962 + Node* limitb = __ Bool(limit, BoolTest::le); 30.963 + IfNode* iff2 = kit.create_and_map_if(kit.control(), limitb, PROB_MIN, COUNT_UNKNOWN); 30.964 + Node* lessEqual = __ IfTrue(iff2); 30.965 + Node* greater = __ IfFalse(iff2); 30.966 + 30.967 + loop->init_req(2, greater); 30.968 + index->init_req(2, __ AddI(index, __ intcon(1))); 30.969 + 30.970 + kit.set_control(lessEqual); 30.971 + C->record_for_igvn(loop); 30.972 + C->record_for_igvn(index); 30.973 + 30.974 + final_merge->init_req(2, kit.control()); 30.975 + final_size->init_req(2, __ AddI(__ AddI(index, size), __ intcon(1))); 30.976 + } 30.977 + 30.978 + kit.set_control(final_merge); 30.979 + C->record_for_igvn(final_merge); 30.980 + C->record_for_igvn(final_size); 30.981 + 30.982 + return final_size; 30.983 +} 30.984 + 30.985 +void PhaseStringOpts::int_getChars(GraphKit& kit, Node* arg, Node* char_array, Node* start, Node* end) { 30.986 + RegionNode *final_merge = new (C, 4) RegionNode(4); 30.987 + kit.gvn().set_type(final_merge, Type::CONTROL); 30.988 + Node *final_mem = PhiNode::make(final_merge, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); 30.989 + kit.gvn().set_type(final_mem, Type::MEMORY); 30.990 + 30.991 + // need to handle Integer.MIN_VALUE specially because negating doesn't make it positive 30.992 + { 30.993 + // i == MIN_VALUE 30.994 + IfNode* iff = kit.create_and_map_if(kit.control(), 30.995 + __ Bool(__ CmpI(arg, __ intcon(0x80000000)), BoolTest::ne), 30.996 + PROB_FAIR, COUNT_UNKNOWN); 30.997 + 30.998 + Node* old_mem = kit.memory(char_adr_idx); 30.999 + 30.1000 + kit.set_control(__ IfFalse(iff)); 30.1001 + if (kit.stopped()) { 30.1002 + // Statically not equal to MIN_VALUE so this path is dead 30.1003 + final_merge->init_req(3, kit.control()); 30.1004 + } else { 30.1005 + copy_string(kit, __ makecon(TypeInstPtr::make(C->env()->the_min_jint_string())), 30.1006 + char_array, start); 30.1007 + final_merge->init_req(3, kit.control()); 30.1008 + final_mem->init_req(3, kit.memory(char_adr_idx)); 30.1009 + } 30.1010 + 30.1011 + kit.set_control(__ IfTrue(iff)); 30.1012 + kit.set_memory(old_mem, char_adr_idx); 30.1013 + } 30.1014 + 30.1015 + 30.1016 + // Simplified version of Integer.getChars 30.1017 + 30.1018 + // int q, r; 30.1019 + // int charPos = index; 30.1020 + Node* charPos = end; 30.1021 + 30.1022 + // char sign = 0; 30.1023 + 30.1024 + Node* i = arg; 30.1025 + Node* sign = __ intcon(0); 30.1026 + 30.1027 + // if (i < 0) { 30.1028 + // sign = '-'; 30.1029 + // i = -i; 30.1030 + // } 30.1031 + { 30.1032 + IfNode* iff = kit.create_and_map_if(kit.control(), 30.1033 + __ Bool(__ CmpI(arg, __ intcon(0)), BoolTest::lt), 30.1034 + PROB_FAIR, COUNT_UNKNOWN); 30.1035 + 30.1036 + RegionNode *merge = new (C, 3) RegionNode(3); 30.1037 + kit.gvn().set_type(merge, Type::CONTROL); 30.1038 + i = new (C, 3) PhiNode(merge, TypeInt::INT); 30.1039 + kit.gvn().set_type(i, TypeInt::INT); 30.1040 + sign = new (C, 3) PhiNode(merge, TypeInt::INT); 30.1041 + kit.gvn().set_type(sign, TypeInt::INT); 30.1042 + 30.1043 + merge->init_req(1, __ IfTrue(iff)); 30.1044 + i->init_req(1, __ SubI(__ intcon(0), arg)); 30.1045 + sign->init_req(1, __ intcon('-')); 30.1046 + merge->init_req(2, __ IfFalse(iff)); 30.1047 + i->init_req(2, arg); 30.1048 + sign->init_req(2, __ intcon(0)); 30.1049 + 30.1050 + kit.set_control(merge); 30.1051 + 30.1052 + C->record_for_igvn(merge); 30.1053 + C->record_for_igvn(i); 30.1054 + C->record_for_igvn(sign); 30.1055 + } 30.1056 + 30.1057 + // for (;;) { 30.1058 + // q = i / 10; 30.1059 + // r = i - ((q << 3) + (q << 1)); // r = i-(q*10) ... 30.1060 + // buf [--charPos] = digits [r]; 30.1061 + // i = q; 30.1062 + // if (i == 0) break; 30.1063 + // } 30.1064 + 30.1065 + { 30.1066 + RegionNode *head = new (C, 3) RegionNode(3); 30.1067 + head->init_req(1, kit.control()); 30.1068 + kit.gvn().set_type(head, Type::CONTROL); 30.1069 + Node *i_phi = new (C, 3) PhiNode(head, TypeInt::INT); 30.1070 + i_phi->init_req(1, i); 30.1071 + kit.gvn().set_type(i_phi, TypeInt::INT); 30.1072 + charPos = PhiNode::make(head, charPos); 30.1073 + kit.gvn().set_type(charPos, TypeInt::INT); 30.1074 + Node *mem = PhiNode::make(head, kit.memory(char_adr_idx), Type::MEMORY, TypeAryPtr::CHARS); 30.1075 + kit.gvn().set_type(mem, Type::MEMORY); 30.1076 + kit.set_control(head); 30.1077 + kit.set_memory(mem, char_adr_idx); 30.1078 + 30.1079 + Node* q = __ DivI(kit.null(), i_phi, __ intcon(10)); 30.1080 + Node* r = __ SubI(i_phi, __ AddI(__ LShiftI(q, __ intcon(3)), 30.1081 + __ LShiftI(q, __ intcon(1)))); 30.1082 + Node* m1 = __ SubI(charPos, __ intcon(1)); 30.1083 + Node* ch = __ AddI(r, __ intcon('0')); 30.1084 + 30.1085 + Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), 30.1086 + ch, T_CHAR, char_adr_idx); 30.1087 + 30.1088 + 30.1089 + IfNode* iff = kit.create_and_map_if(head, __ Bool(__ CmpI(q, __ intcon(0)), BoolTest::ne), 30.1090 + PROB_FAIR, COUNT_UNKNOWN); 30.1091 + Node* ne = __ IfTrue(iff); 30.1092 + Node* eq = __ IfFalse(iff); 30.1093 + 30.1094 + head->init_req(2, ne); 30.1095 + mem->init_req(2, st); 30.1096 + i_phi->init_req(2, q); 30.1097 + charPos->init_req(2, m1); 30.1098 + 30.1099 + charPos = m1; 30.1100 + 30.1101 + kit.set_control(eq); 30.1102 + kit.set_memory(st, char_adr_idx); 30.1103 + 30.1104 + C->record_for_igvn(head); 30.1105 + C->record_for_igvn(mem); 30.1106 + C->record_for_igvn(i_phi); 30.1107 + C->record_for_igvn(charPos); 30.1108 + } 30.1109 + 30.1110 + { 30.1111 + // if (sign != 0) { 30.1112 + // buf [--charPos] = sign; 30.1113 + // } 30.1114 + IfNode* iff = kit.create_and_map_if(kit.control(), 30.1115 + __ Bool(__ CmpI(sign, __ intcon(0)), BoolTest::ne), 30.1116 + PROB_FAIR, COUNT_UNKNOWN); 30.1117 + 30.1118 + final_merge->init_req(2, __ IfFalse(iff)); 30.1119 + final_mem->init_req(2, kit.memory(char_adr_idx)); 30.1120 + 30.1121 + kit.set_control(__ IfTrue(iff)); 30.1122 + if (kit.stopped()) { 30.1123 + final_merge->init_req(1, C->top()); 30.1124 + final_mem->init_req(1, C->top()); 30.1125 + } else { 30.1126 + Node* m1 = __ SubI(charPos, __ intcon(1)); 30.1127 + Node* st = __ store_to_memory(kit.control(), kit.array_element_address(char_array, m1, T_CHAR), 30.1128 + sign, T_CHAR, char_adr_idx); 30.1129 + 30.1130 + final_merge->init_req(1, kit.control()); 30.1131 + final_mem->init_req(1, st); 30.1132 + } 30.1133 + 30.1134 + kit.set_control(final_merge); 30.1135 + kit.set_memory(final_mem, char_adr_idx); 30.1136 + 30.1137 + C->record_for_igvn(final_merge); 30.1138 + C->record_for_igvn(final_mem); 30.1139 + } 30.1140 +} 30.1141 + 30.1142 + 30.1143 +Node* PhaseStringOpts::copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start) { 30.1144 + Node* string = str; 30.1145 + Node* offset = kit.make_load(NULL, 30.1146 + kit.basic_plus_adr(string, string, java_lang_String::offset_offset_in_bytes()), 30.1147 + TypeInt::INT, T_INT, offset_field_idx); 30.1148 + Node* count = kit.make_load(NULL, 30.1149 + kit.basic_plus_adr(string, string, java_lang_String::count_offset_in_bytes()), 30.1150 + TypeInt::INT, T_INT, count_field_idx); 30.1151 + const TypeAryPtr* value_type = TypeAryPtr::make(TypePtr::NotNull, 30.1152 + TypeAry::make(TypeInt::CHAR,TypeInt::POS), 30.1153 + ciTypeArrayKlass::make(T_CHAR), true, 0); 30.1154 + Node* value = kit.make_load(NULL, 30.1155 + kit.basic_plus_adr(string, string, java_lang_String::value_offset_in_bytes()), 30.1156 + value_type, T_OBJECT, value_field_idx); 30.1157 + 30.1158 + // copy the contents 30.1159 + if (offset->is_Con() && count->is_Con() && value->is_Con() && count->get_int() < unroll_string_copy_length) { 30.1160 + // For small constant strings just emit individual stores. 30.1161 + // A length of 6 seems like a good space/speed tradeof. 30.1162 + int c = count->get_int(); 30.1163 + int o = offset->get_int(); 30.1164 + const TypeOopPtr* t = kit.gvn().type(value)->isa_oopptr(); 30.1165 + ciTypeArray* value_array = t->const_oop()->as_type_array(); 30.1166 + for (int e = 0; e < c; e++) { 30.1167 + __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), 30.1168 + __ intcon(value_array->char_at(o + e)), T_CHAR, char_adr_idx); 30.1169 + start = __ AddI(start, __ intcon(1)); 30.1170 + } 30.1171 + } else { 30.1172 + Node* src_ptr = kit.array_element_address(value, offset, T_CHAR); 30.1173 + Node* dst_ptr = kit.array_element_address(char_array, start, T_CHAR); 30.1174 + Node* c = count; 30.1175 + Node* extra = NULL; 30.1176 +#ifdef _LP64 30.1177 + c = __ ConvI2L(c); 30.1178 + extra = C->top(); 30.1179 +#endif 30.1180 + Node* call = kit.make_runtime_call(GraphKit::RC_LEAF|GraphKit::RC_NO_FP, 30.1181 + OptoRuntime::fast_arraycopy_Type(), 30.1182 + CAST_FROM_FN_PTR(address, StubRoutines::jshort_disjoint_arraycopy()), 30.1183 + "jshort_disjoint_arraycopy", TypeAryPtr::CHARS, 30.1184 + src_ptr, dst_ptr, c, extra); 30.1185 + start = __ AddI(start, count); 30.1186 + } 30.1187 + return start; 30.1188 +} 30.1189 + 30.1190 + 30.1191 +void PhaseStringOpts::replace_string_concat(StringConcat* sc) { 30.1192 + // Log a little info about the transformation 30.1193 + sc->maybe_log_transform(); 30.1194 + 30.1195 + // pull the JVMState of the allocation into a SafePointNode to serve as 30.1196 + // as a shim for the insertion of the new code. 30.1197 + JVMState* jvms = sc->begin()->jvms()->clone_shallow(C); 30.1198 + uint size = sc->begin()->req(); 30.1199 + SafePointNode* map = new (C, size) SafePointNode(size, jvms); 30.1200 + 30.1201 + // copy the control and memory state from the final call into our 30.1202 + // new starting state. This allows any preceeding tests to feed 30.1203 + // into the new section of code. 30.1204 + for (uint i1 = 0; i1 < TypeFunc::Parms; i1++) { 30.1205 + map->init_req(i1, sc->end()->in(i1)); 30.1206 + } 30.1207 + // blow away old allocation arguments 30.1208 + for (uint i1 = TypeFunc::Parms; i1 < jvms->debug_start(); i1++) { 30.1209 + map->init_req(i1, C->top()); 30.1210 + } 30.1211 + // Copy the rest of the inputs for the JVMState 30.1212 + for (uint i1 = jvms->debug_start(); i1 < sc->begin()->req(); i1++) { 30.1213 + map->init_req(i1, sc->begin()->in(i1)); 30.1214 + } 30.1215 + // Make sure the memory state is a MergeMem for parsing. 30.1216 + if (!map->in(TypeFunc::Memory)->is_MergeMem()) { 30.1217 + map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory))); 30.1218 + } 30.1219 + 30.1220 + jvms->set_map(map); 30.1221 + map->ensure_stack(jvms, jvms->method()->max_stack()); 30.1222 + 30.1223 + 30.1224 + // disconnect all the old StringBuilder calls from the graph 30.1225 + sc->eliminate_unneeded_control(); 30.1226 + 30.1227 + // At this point all the old work has been completely removed from 30.1228 + // the graph and the saved JVMState exists at the point where the 30.1229 + // final toString call used to be. 30.1230 + GraphKit kit(jvms); 30.1231 + 30.1232 + // There may be uncommon traps which are still using the 30.1233 + // intermediate states and these need to be rewritten to point at 30.1234 + // the JVMState at the beginning of the transformation. 30.1235 + sc->convert_uncommon_traps(kit, jvms); 30.1236 + 30.1237 + // Now insert the logic to compute the size of the string followed 30.1238 + // by all the logic to construct array and resulting string. 30.1239 + 30.1240 + Node* null_string = __ makecon(TypeInstPtr::make(C->env()->the_null_string())); 30.1241 + 30.1242 + // Create a region for the overflow checks to merge into. 30.1243 + int args = MAX2(sc->num_arguments(), 1); 30.1244 + RegionNode* overflow = new (C, args) RegionNode(args); 30.1245 + kit.gvn().set_type(overflow, Type::CONTROL); 30.1246 + 30.1247 + // Create a hook node to hold onto the individual sizes since they 30.1248 + // are need for the copying phase. 30.1249 + Node* string_sizes = new (C, args) Node(args); 30.1250 + 30.1251 + Node* length = __ intcon(0); 30.1252 + for (int argi = 0; argi < sc->num_arguments(); argi++) { 30.1253 + Node* arg = sc->argument(argi); 30.1254 + switch (sc->mode(argi)) { 30.1255 + case StringConcat::IntMode: { 30.1256 + Node* string_size = int_stringSize(kit, arg); 30.1257 + 30.1258 + // accumulate total 30.1259 + length = __ AddI(length, string_size); 30.1260 + 30.1261 + // Cache this value for the use by int_toString 30.1262 + string_sizes->init_req(argi, string_size); 30.1263 + break; 30.1264 + } 30.1265 + case StringConcat::StringMode: { 30.1266 + const Type* type = kit.gvn().type(arg); 30.1267 + if (type == TypePtr::NULL_PTR) { 30.1268 + // replace the argument with the null checked version 30.1269 + arg = null_string; 30.1270 + sc->set_argument(argi, arg); 30.1271 + } else if (!type->higher_equal(TypeInstPtr::NOTNULL)) { 30.1272 + // s = s != null ? s : "null"; 30.1273 + // length = length + (s.count - s.offset); 30.1274 + RegionNode *r = new (C, 3) RegionNode(3); 30.1275 + kit.gvn().set_type(r, Type::CONTROL); 30.1276 + Node *phi = new (C, 3) PhiNode(r, type->join(TypeInstPtr::NOTNULL)); 30.1277 + kit.gvn().set_type(phi, phi->bottom_type()); 30.1278 + Node* p = __ Bool(__ CmpP(arg, kit.null()), BoolTest::ne); 30.1279 + IfNode* iff = kit.create_and_map_if(kit.control(), p, PROB_MIN, COUNT_UNKNOWN); 30.1280 + Node* notnull = __ IfTrue(iff); 30.1281 + Node* isnull = __ IfFalse(iff); 30.1282 + r->init_req(1, notnull); 30.1283 + phi->init_req(1, arg); 30.1284 + r->init_req(2, isnull); 30.1285 + phi->init_req(2, null_string); 30.1286 + kit.set_control(r); 30.1287 + C->record_for_igvn(r); 30.1288 + C->record_for_igvn(phi); 30.1289 + // replace the argument with the null checked version 30.1290 + arg = phi; 30.1291 + sc->set_argument(argi, arg); 30.1292 + } 30.1293 + // Node* offset = kit.make_load(NULL, kit.basic_plus_adr(arg, arg, offset_offset), 30.1294 + // TypeInt::INT, T_INT, offset_field_idx); 30.1295 + Node* count = kit.make_load(NULL, kit.basic_plus_adr(arg, arg, java_lang_String::count_offset_in_bytes()), 30.1296 + TypeInt::INT, T_INT, count_field_idx); 30.1297 + length = __ AddI(length, count); 30.1298 + string_sizes->init_req(argi, NULL); 30.1299 + break; 30.1300 + } 30.1301 + case StringConcat::CharMode: { 30.1302 + // one character only 30.1303 + length = __ AddI(length, __ intcon(1)); 30.1304 + break; 30.1305 + } 30.1306 + default: 30.1307 + ShouldNotReachHere(); 30.1308 + } 30.1309 + if (argi > 0) { 30.1310 + // Check that the sum hasn't overflowed 30.1311 + IfNode* iff = kit.create_and_map_if(kit.control(), 30.1312 + __ Bool(__ CmpI(length, __ intcon(0)), BoolTest::lt), 30.1313 + PROB_MIN, COUNT_UNKNOWN); 30.1314 + kit.set_control(__ IfFalse(iff)); 30.1315 + overflow->set_req(argi, __ IfTrue(iff)); 30.1316 + } 30.1317 + } 30.1318 + 30.1319 + { 30.1320 + // Hook 30.1321 + PreserveJVMState pjvms(&kit); 30.1322 + kit.set_control(overflow); 30.1323 + kit.uncommon_trap(Deoptimization::Reason_intrinsic, 30.1324 + Deoptimization::Action_make_not_entrant); 30.1325 + } 30.1326 + 30.1327 + // length now contains the number of characters needed for the 30.1328 + // char[] so create a new AllocateArray for the char[] 30.1329 + Node* char_array = NULL; 30.1330 + { 30.1331 + PreserveReexecuteState preexecs(&kit); 30.1332 + // The original jvms is for an allocation of either a String or 30.1333 + // StringBuffer so no stack adjustment is necessary for proper 30.1334 + // reexecution. If we deoptimize in the slow path the bytecode 30.1335 + // will be reexecuted and the char[] allocation will be thrown away. 30.1336 + kit.jvms()->set_should_reexecute(true); 30.1337 + char_array = kit.new_array(__ makecon(TypeKlassPtr::make(ciTypeArrayKlass::make(T_CHAR))), 30.1338 + length, 1); 30.1339 + } 30.1340 + 30.1341 + // Mark the allocation so that zeroing is skipped since the code 30.1342 + // below will overwrite the entire array 30.1343 + AllocateArrayNode* char_alloc = AllocateArrayNode::Ideal_array_allocation(char_array, _gvn); 30.1344 + char_alloc->maybe_set_complete(_gvn); 30.1345 + 30.1346 + // Now copy the string representations into the final char[] 30.1347 + Node* start = __ intcon(0); 30.1348 + for (int argi = 0; argi < sc->num_arguments(); argi++) { 30.1349 + Node* arg = sc->argument(argi); 30.1350 + switch (sc->mode(argi)) { 30.1351 + case StringConcat::IntMode: { 30.1352 + Node* end = __ AddI(start, string_sizes->in(argi)); 30.1353 + // getChars words backwards so pass the ending point as well as the start 30.1354 + int_getChars(kit, arg, char_array, start, end); 30.1355 + start = end; 30.1356 + break; 30.1357 + } 30.1358 + case StringConcat::StringMode: { 30.1359 + start = copy_string(kit, arg, char_array, start); 30.1360 + break; 30.1361 + } 30.1362 + case StringConcat::CharMode: { 30.1363 + __ store_to_memory(kit.control(), kit.array_element_address(char_array, start, T_CHAR), 30.1364 + arg, T_CHAR, char_adr_idx); 30.1365 + start = __ AddI(start, __ intcon(1)); 30.1366 + break; 30.1367 + } 30.1368 + default: 30.1369 + ShouldNotReachHere(); 30.1370 + } 30.1371 + } 30.1372 + 30.1373 + // If we're not reusing an existing String allocation then allocate one here. 30.1374 + Node* result = sc->string_alloc(); 30.1375 + if (result == NULL) { 30.1376 + PreserveReexecuteState preexecs(&kit); 30.1377 + // The original jvms is for an allocation of either a String or 30.1378 + // StringBuffer so no stack adjustment is necessary for proper 30.1379 + // reexecution. 30.1380 + kit.jvms()->set_should_reexecute(true); 30.1381 + result = kit.new_instance(__ makecon(TypeKlassPtr::make(C->env()->String_klass()))); 30.1382 + } 30.1383 + 30.1384 + // Intialize the string 30.1385 + kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::offset_offset_in_bytes()), 30.1386 + __ intcon(0), T_INT, offset_field_idx); 30.1387 + kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::count_offset_in_bytes()), 30.1388 + length, T_INT, count_field_idx); 30.1389 + kit.store_to_memory(kit.control(), kit.basic_plus_adr(result, java_lang_String::value_offset_in_bytes()), 30.1390 + char_array, T_OBJECT, value_field_idx); 30.1391 + 30.1392 + // hook up the outgoing control and result 30.1393 + kit.replace_call(sc->end(), result); 30.1394 + 30.1395 + // Unhook any hook nodes 30.1396 + string_sizes->disconnect_inputs(NULL); 30.1397 + sc->cleanup(); 30.1398 +}
31.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 31.2 +++ b/src/share/vm/opto/stringopts.hpp Thu Nov 12 09:24:21 2009 -0800 31.3 @@ -0,0 +1,83 @@ 31.4 +/* 31.5 + * Copyright 2009 Sun Microsystems, Inc. All Rights Reserved. 31.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 31.7 + * 31.8 + * This code is free software; you can redistribute it and/or modify it 31.9 + * under the terms of the GNU General Public License version 2 only, as 31.10 + * published by the Free Software Foundation. 31.11 + * 31.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 31.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 31.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 31.15 + * version 2 for more details (a copy is included in the LICENSE file that 31.16 + * accompanied this code). 31.17 + * 31.18 + * You should have received a copy of the GNU General Public License version 31.19 + * 2 along with this work; if not, write to the Free Software Foundation, 31.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 31.21 + * 31.22 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 31.23 + * CA 95054 USA or visit www.sun.com if you need additional information or 31.24 + * have any questions. 31.25 + * 31.26 + */ 31.27 + 31.28 +class StringConcat; 31.29 + 31.30 +class PhaseStringOpts : public Phase { 31.31 + friend class StringConcat; 31.32 + 31.33 + private: 31.34 + PhaseGVN* _gvn; 31.35 + 31.36 + // List of dead nodes to clean up aggressively at the end 31.37 + Unique_Node_List dead_worklist; 31.38 + 31.39 + // Memory slices needed for code gen 31.40 + int char_adr_idx; 31.41 + int value_field_idx; 31.42 + int count_field_idx; 31.43 + int offset_field_idx; 31.44 + 31.45 + // Integer.sizeTable - used for int to String conversion 31.46 + ciField* size_table_field; 31.47 + 31.48 + // A set for use by various stages 31.49 + VectorSet _visited; 31.50 + 31.51 + // Collect a list of all SB.toString calls 31.52 + Node_List collect_toString_calls(); 31.53 + 31.54 + // Examine the use of the SB alloc to see if it can be replace with 31.55 + // a single string construction. 31.56 + StringConcat* build_candidate(CallStaticJavaNode* call); 31.57 + 31.58 + // Replace all the SB calls in concat with an optimization String allocation 31.59 + void replace_string_concat(StringConcat* concat); 31.60 + 31.61 + // Load the value of a static field, performing any constant folding. 31.62 + Node* fetch_static_field(GraphKit& kit, ciField* field); 31.63 + 31.64 + // Compute the number of characters required to represent the int value 31.65 + Node* int_stringSize(GraphKit& kit, Node* value); 31.66 + 31.67 + // Copy the characters representing value into char_array starting at start 31.68 + void int_getChars(GraphKit& kit, Node* value, Node* char_array, Node* start, Node* end); 31.69 + 31.70 + // Copy of the contents of the String str into char_array starting at index start. 31.71 + Node* copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start); 31.72 + 31.73 + // Clean up any leftover nodes 31.74 + void record_dead_node(Node* node); 31.75 + void remove_dead_nodes(); 31.76 + 31.77 + PhaseGVN* gvn() { return _gvn; } 31.78 + 31.79 + enum { 31.80 + // max length of constant string copy unrolling in copy_string 31.81 + unroll_string_copy_length = 6 31.82 + }; 31.83 + 31.84 + public: 31.85 + PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List* worklist); 31.86 +};
32.1 --- a/src/share/vm/opto/type.hpp Fri Nov 27 07:56:58 2009 -0800 32.2 +++ b/src/share/vm/opto/type.hpp Thu Nov 12 09:24:21 2009 -0800 32.3 @@ -847,9 +847,6 @@ 32.4 // Constant pointer to array 32.5 static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot); 32.6 32.7 - // Convenience 32.8 - static const TypeAryPtr *make(ciObject* o); 32.9 - 32.10 // Return a 'ptr' version of this type 32.11 virtual const Type *cast_to_ptr_type(PTR ptr) const; 32.12
33.1 --- a/src/share/vm/runtime/globals.cpp Fri Nov 27 07:56:58 2009 -0800 33.2 +++ b/src/share/vm/runtime/globals.cpp Thu Nov 12 09:24:21 2009 -0800 33.3 @@ -46,7 +46,8 @@ 33.4 bool Flag::is_unlocked() const { 33.5 if (strcmp(kind, "{diagnostic}") == 0) { 33.6 return UnlockDiagnosticVMOptions; 33.7 - } else if (strcmp(kind, "{experimental}") == 0) { 33.8 + } else if (strcmp(kind, "{experimental}") == 0 || 33.9 + strcmp(kind, "{C2 experimental}") == 0) { 33.10 return UnlockExperimentalVMOptions; 33.11 } else { 33.12 return true; 33.13 @@ -169,6 +170,7 @@ 33.14 #define C2_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 product}", DEFAULT }, 33.15 #define C2_PD_PRODUCT_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C2 pd product}", DEFAULT }, 33.16 #define C2_DIAGNOSTIC_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 diagnostic}", DEFAULT }, 33.17 +#define C2_EXPERIMENTAL_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 experimental}", DEFAULT }, 33.18 #ifdef PRODUCT 33.19 #define C2_DEVELOP_FLAG_STRUCT(type, name, value, doc) /* flag is constant */ 33.20 #define C2_PD_DEVELOP_FLAG_STRUCT(type, name, doc) /* flag is constant */ 33.21 @@ -190,7 +192,7 @@ 33.22 C1_FLAGS(C1_DEVELOP_FLAG_STRUCT, C1_PD_DEVELOP_FLAG_STRUCT, C1_PRODUCT_FLAG_STRUCT, C1_PD_PRODUCT_FLAG_STRUCT, C1_NOTPRODUCT_FLAG_STRUCT) 33.23 #endif 33.24 #ifdef COMPILER2 33.25 - C2_FLAGS(C2_DEVELOP_FLAG_STRUCT, C2_PD_DEVELOP_FLAG_STRUCT, C2_PRODUCT_FLAG_STRUCT, C2_PD_PRODUCT_FLAG_STRUCT, C2_DIAGNOSTIC_FLAG_STRUCT, C2_NOTPRODUCT_FLAG_STRUCT) 33.26 + C2_FLAGS(C2_DEVELOP_FLAG_STRUCT, C2_PD_DEVELOP_FLAG_STRUCT, C2_PRODUCT_FLAG_STRUCT, C2_PD_PRODUCT_FLAG_STRUCT, C2_DIAGNOSTIC_FLAG_STRUCT, C2_EXPERIMENTAL_FLAG_STRUCT, C2_NOTPRODUCT_FLAG_STRUCT) 33.27 #endif 33.28 {0, NULL, NULL} 33.29 };
34.1 --- a/src/share/vm/runtime/globals_extension.hpp Fri Nov 27 07:56:58 2009 -0800 34.2 +++ b/src/share/vm/runtime/globals_extension.hpp Thu Nov 12 09:24:21 2009 -0800 34.3 @@ -64,6 +64,7 @@ 34.4 #define C2_PRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), 34.5 #define C2_PD_PRODUCT_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name), 34.6 #define C2_DIAGNOSTIC_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), 34.7 +#define C2_EXPERIMENTAL_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name), 34.8 #ifdef PRODUCT 34.9 #define C2_DEVELOP_FLAG_MEMBER(type, name, value, doc) /* flag is constant */ 34.10 #define C2_PD_DEVELOP_FLAG_MEMBER(type, name, doc) /* flag is constant */ 34.11 @@ -84,7 +85,7 @@ 34.12 C1_FLAGS(C1_DEVELOP_FLAG_MEMBER, C1_PD_DEVELOP_FLAG_MEMBER, C1_PRODUCT_FLAG_MEMBER, C1_PD_PRODUCT_FLAG_MEMBER, C1_NOTPRODUCT_FLAG_MEMBER) 34.13 #endif 34.14 #ifdef COMPILER2 34.15 - C2_FLAGS(C2_DEVELOP_FLAG_MEMBER, C2_PD_DEVELOP_FLAG_MEMBER, C2_PRODUCT_FLAG_MEMBER, C2_PD_PRODUCT_FLAG_MEMBER, C2_DIAGNOSTIC_FLAG_MEMBER, C2_NOTPRODUCT_FLAG_MEMBER) 34.16 + C2_FLAGS(C2_DEVELOP_FLAG_MEMBER, C2_PD_DEVELOP_FLAG_MEMBER, C2_PRODUCT_FLAG_MEMBER, C2_PD_PRODUCT_FLAG_MEMBER, C2_DIAGNOSTIC_FLAG_MEMBER, C2_EXPERIMENTAL_FLAG_MEMBER, C2_NOTPRODUCT_FLAG_MEMBER) 34.17 #endif 34.18 NUM_CommandLineFlag 34.19 } CommandLineFlag; 34.20 @@ -130,6 +131,7 @@ 34.21 #define C2_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), 34.22 #define C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type), 34.23 #define C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), 34.24 +#define C2_EXPERIMENTAL_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type), 34.25 #ifdef PRODUCT 34.26 #define C2_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) /* flag is constant */ 34.27 #define C2_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) /* flag is constant */ 34.28 @@ -181,6 +183,7 @@ 34.29 C2_PRODUCT_FLAG_MEMBER_WITH_TYPE, 34.30 C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE, 34.31 C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE, 34.32 + C2_EXPERIMENTAL_FLAG_MEMBER_WITH_TYPE, 34.33 C2_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE) 34.34 #endif 34.35 NUM_CommandLineFlagWithType
35.1 --- a/src/share/vm/utilities/growableArray.hpp Fri Nov 27 07:56:58 2009 -0800 35.2 +++ b/src/share/vm/utilities/growableArray.hpp Thu Nov 12 09:24:21 2009 -0800 35.3 @@ -278,6 +278,17 @@ 35.4 _len--; 35.5 } 35.6 35.7 + // inserts the given element before the element at index i 35.8 + void insert_before(const int idx, const E& elem) { 35.9 + check_nesting(); 35.10 + if (_len == _max) grow(_len); 35.11 + for (int j = _len - 1; j >= idx; j--) { 35.12 + _data[j + 1] = _data[j]; 35.13 + } 35.14 + _len++; 35.15 + _data[idx] = elem; 35.16 + } 35.17 + 35.18 void appendAll(const GrowableArray<E>* l) { 35.19 for (int i = 0; i < l->_len; i++) { 35.20 raw_at_put_grow(_len, l->_data[i], 0);