提交 5097d521 编写于 作者: N never

6892658: C2 should optimize some stringbuilder patterns

Reviewed-by: kvn, twisti
上级 3a8ec81f
......@@ -46,6 +46,9 @@ ciInstanceKlass* ciEnv::_Throwable;
ciInstanceKlass* ciEnv::_Thread;
ciInstanceKlass* ciEnv::_OutOfMemoryError;
ciInstanceKlass* ciEnv::_String;
ciInstanceKlass* ciEnv::_StringBuffer;
ciInstanceKlass* ciEnv::_StringBuilder;
ciInstanceKlass* ciEnv::_Integer;
ciSymbol* ciEnv::_unloaded_cisymbol = NULL;
ciInstanceKlass* ciEnv::_unloaded_ciinstance_klass = NULL;
......@@ -110,6 +113,8 @@ ciEnv::ciEnv(CompileTask* task, int system_dictionary_modification_counter) {
_ArrayIndexOutOfBoundsException_instance = NULL;
_ArrayStoreException_instance = NULL;
_ClassCastException_instance = NULL;
_the_null_string = NULL;
_the_min_jint_string = NULL;
}
ciEnv::ciEnv(Arena* arena) {
......@@ -163,6 +168,8 @@ ciEnv::ciEnv(Arena* arena) {
_ArrayIndexOutOfBoundsException_instance = NULL;
_ArrayStoreException_instance = NULL;
_ClassCastException_instance = NULL;
_the_null_string = NULL;
_the_min_jint_string = NULL;
}
ciEnv::~ciEnv() {
......@@ -248,6 +255,22 @@ ciInstance* ciEnv::ClassCastException_instance() {
return _ClassCastException_instance;
}
ciInstance* ciEnv::the_null_string() {
if (_the_null_string == NULL) {
VM_ENTRY_MARK;
_the_null_string = get_object(Universe::the_null_string())->as_instance();
}
return _the_null_string;
}
ciInstance* ciEnv::the_min_jint_string() {
if (_the_min_jint_string == NULL) {
VM_ENTRY_MARK;
_the_min_jint_string = get_object(Universe::the_min_jint_string())->as_instance();
}
return _the_min_jint_string;
}
// ------------------------------------------------------------------
// ciEnv::get_method_from_handle
ciMethod* ciEnv::get_method_from_handle(jobject method) {
......
......@@ -82,6 +82,9 @@ private:
static ciInstanceKlass* _Thread;
static ciInstanceKlass* _OutOfMemoryError;
static ciInstanceKlass* _String;
static ciInstanceKlass* _StringBuffer;
static ciInstanceKlass* _StringBuilder;
static ciInstanceKlass* _Integer;
static ciSymbol* _unloaded_cisymbol;
static ciInstanceKlass* _unloaded_ciinstance_klass;
......@@ -97,6 +100,9 @@ private:
ciInstance* _ArrayStoreException_instance;
ciInstance* _ClassCastException_instance;
ciInstance* _the_null_string; // The Java string "null"
ciInstance* _the_min_jint_string; // The Java string "-2147483648"
// Look up a klass by name from a particular class loader (the accessor's).
// If require_local, result must be defined in that class loader, or NULL.
// If !require_local, a result from remote class loader may be reported,
......@@ -310,6 +316,15 @@ public:
ciInstanceKlass* String_klass() {
return _String;
}
ciInstanceKlass* StringBuilder_klass() {
return _StringBuilder;
}
ciInstanceKlass* StringBuffer_klass() {
return _StringBuffer;
}
ciInstanceKlass* Integer_klass() {
return _Integer;
}
ciInstance* NullPointerException_instance() {
assert(_NullPointerException_instance != NULL, "initialization problem");
return _NullPointerException_instance;
......@@ -324,6 +339,9 @@ public:
ciInstance* ArrayStoreException_instance();
ciInstance* ClassCastException_instance();
ciInstance* the_null_string();
ciInstance* the_min_jint_string();
static ciSymbol* unloaded_cisymbol() {
return _unloaded_cisymbol;
}
......
......@@ -340,6 +340,20 @@ ciField* ciInstanceKlass::get_field_by_offset(int field_offset, bool is_static)
return field;
}
// ------------------------------------------------------------------
// ciInstanceKlass::get_field_by_name
ciField* ciInstanceKlass::get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static) {
VM_ENTRY_MARK;
instanceKlass* k = get_instanceKlass();
fieldDescriptor fd;
klassOop def = k->find_field(name->get_symbolOop(), signature->get_symbolOop(), is_static, &fd);
if (def == NULL) {
return NULL;
}
ciField* field = new (CURRENT_THREAD_ENV->arena()) ciField(&fd);
return field;
}
// ------------------------------------------------------------------
// ciInstanceKlass::non_static_fields.
......
......@@ -148,6 +148,7 @@ public:
ciInstanceKlass* get_canonical_holder(int offset);
ciField* get_field_by_offset(int field_offset, bool is_static);
ciField* get_field_by_name(ciSymbol* name, ciSymbol* signature, bool is_static);
GrowableArray<ciField*>* non_static_fields();
......
......@@ -168,6 +168,15 @@ void ciObjectFactory::init_shared_objects() {
ciEnv::_String =
get(SystemDictionary::string_klass())
->as_instance_klass();
ciEnv::_StringBuffer =
get(SystemDictionary::stringBuffer_klass())
->as_instance_klass();
ciEnv::_StringBuilder =
get(SystemDictionary::StringBuilder_klass())
->as_instance_klass();
ciEnv::_Integer =
get(SystemDictionary::int_klass())
->as_instance_klass();
for (int len = -1; len != _ci_objects->length(); ) {
len = _ci_objects->length();
......
......@@ -150,6 +150,7 @@ class SymbolPropertyTable;
template(vector_klass, java_util_Vector, Pre) \
template(hashtable_klass, java_util_Hashtable, Pre) \
template(stringBuffer_klass, java_lang_StringBuffer, Pre) \
template(StringBuilder_klass, java_lang_StringBuilder, Pre) \
\
/* It's NULL in non-1.4 JDKs. */ \
template(stackTraceElement_klass, java_lang_StackTraceElement, Opt) \
......
......@@ -303,6 +303,11 @@ inline bool match_F_R(jshort flags) {
const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED;
return (flags & (req | neg)) == req;
}
inline bool match_F_Y(jshort flags) {
const int req = JVM_ACC_SYNCHRONIZED;
const int neg = JVM_ACC_STATIC;
return (flags & (req | neg)) == req;
}
inline bool match_F_RN(jshort flags) {
const int req = JVM_ACC_NATIVE;
const int neg = JVM_ACC_STATIC | JVM_ACC_SYNCHRONIZED;
......@@ -361,6 +366,7 @@ const char* vmIntrinsics::short_name_as_C_string(vmIntrinsics::ID id, char* buf,
const char* sname = vmSymbols::name_for(signature_for(id));
const char* fname = "";
switch (flags_for(id)) {
case F_Y: fname = "synchronized "; break;
case F_RN: fname = "native "; break;
case F_SN: fname = "native static "; break;
case F_S: fname = "static "; break;
......
......@@ -84,6 +84,7 @@
template(java_lang_reflect_Field, "java/lang/reflect/Field") \
template(java_lang_reflect_Array, "java/lang/reflect/Array") \
template(java_lang_StringBuffer, "java/lang/StringBuffer") \
template(java_lang_StringBuilder, "java/lang/StringBuilder") \
template(java_lang_CharSequence, "java/lang/CharSequence") \
template(java_security_AccessControlContext, "java/security/AccessControlContext") \
template(java_security_ProtectionDomain, "java/security/ProtectionDomain") \
......@@ -334,6 +335,7 @@
template(ptypes_name, "ptypes") \
template(form_name, "form") \
template(erasedType_name, "erasedType") \
template(append_name, "append") \
\
/* non-intrinsic name/signature pairs: */ \
template(register_method_name, "register") \
......@@ -415,6 +417,13 @@
template(string_signature, "Ljava/lang/String;") \
template(reference_signature, "Ljava/lang/ref/Reference;") \
template(concurrenthashmap_signature, "Ljava/util/concurrent/ConcurrentHashMap;") \
template(String_StringBuilder_signature, "(Ljava/lang/String;)Ljava/lang/StringBuilder;") \
template(int_StringBuilder_signature, "(I)Ljava/lang/StringBuilder;") \
template(char_StringBuilder_signature, "(C)Ljava/lang/StringBuilder;") \
template(String_StringBuffer_signature, "(Ljava/lang/String;)Ljava/lang/StringBuffer;") \
template(int_StringBuffer_signature, "(I)Ljava/lang/StringBuffer;") \
template(char_StringBuffer_signature, "(C)Ljava/lang/StringBuffer;") \
template(int_String_signature, "(I)Ljava/lang/String;") \
/* signature symbols needed by intrinsics */ \
VM_INTRINSICS_DO(VM_INTRINSIC_IGNORE, VM_SYMBOL_IGNORE, VM_SYMBOL_IGNORE, template, VM_ALIAS_IGNORE) \
\
......@@ -815,6 +824,30 @@
\
do_intrinsic(_fillInStackTrace, java_lang_Throwable, fillInStackTrace_name, void_throwable_signature, F_RNY) \
\
do_intrinsic(_StringBuilder_void, java_lang_StringBuilder, object_initializer_name, void_method_signature, F_R) \
do_intrinsic(_StringBuilder_int, java_lang_StringBuilder, object_initializer_name, int_void_signature, F_R) \
do_intrinsic(_StringBuilder_String, java_lang_StringBuilder, object_initializer_name, string_void_signature, F_R) \
\
do_intrinsic(_StringBuilder_append_char, java_lang_StringBuilder, append_name, char_StringBuilder_signature, F_R) \
do_intrinsic(_StringBuilder_append_int, java_lang_StringBuilder, append_name, int_StringBuilder_signature, F_R) \
do_intrinsic(_StringBuilder_append_String, java_lang_StringBuilder, append_name, String_StringBuilder_signature, F_R) \
\
do_intrinsic(_StringBuilder_toString, java_lang_StringBuilder, toString_name, void_string_signature, F_R) \
\
do_intrinsic(_StringBuffer_void, java_lang_StringBuffer, object_initializer_name, void_method_signature, F_R) \
do_intrinsic(_StringBuffer_int, java_lang_StringBuffer, object_initializer_name, int_void_signature, F_R) \
do_intrinsic(_StringBuffer_String, java_lang_StringBuffer, object_initializer_name, string_void_signature, F_R) \
\
do_intrinsic(_StringBuffer_append_char, java_lang_StringBuffer, append_name, char_StringBuffer_signature, F_Y) \
do_intrinsic(_StringBuffer_append_int, java_lang_StringBuffer, append_name, int_StringBuffer_signature, F_Y) \
do_intrinsic(_StringBuffer_append_String, java_lang_StringBuffer, append_name, String_StringBuffer_signature, F_Y) \
\
do_intrinsic(_StringBuffer_toString, java_lang_StringBuffer, toString_name, void_string_signature, F_Y) \
\
do_intrinsic(_Integer_toString, java_lang_Integer, toString_name, int_String_signature, F_S) \
\
do_intrinsic(_String_String, java_lang_String, object_initializer_name, string_void_signature, F_R) \
\
do_intrinsic(_Object_init, java_lang_Object, object_initializer_name, void_method_signature, F_R) \
/* (symbol object_initializer_name defined above) */ \
\
......@@ -945,8 +978,9 @@ class vmIntrinsics: AllStatic {
enum Flags {
// AccessFlags syndromes relevant to intrinsics.
F_none = 0,
F_R, // !static !synchronized (R="regular")
F_S, // static !synchronized
F_R, // !static ?native !synchronized (R="regular")
F_S, // static ?native !synchronized
F_Y, // !static ?native synchronized
F_RN, // !static native !synchronized
F_SN, // static native !synchronized
F_RNY // !static native synchronized
......
......@@ -149,6 +149,7 @@ c2compiler.cpp runtime.hpp
c2compiler.hpp abstractCompiler.hpp
callGenerator.cpp addnode.hpp
callGenerator.cpp bcEscapeAnalyzer.hpp
callGenerator.cpp callGenerator.hpp
callGenerator.cpp callnode.hpp
callGenerator.cpp cfgnode.hpp
......@@ -321,6 +322,7 @@ compile.cpp phaseX.hpp
compile.cpp rootnode.hpp
compile.cpp runtime.hpp
compile.cpp signature.hpp
compile.cpp stringopts.hpp
compile.cpp stubRoutines.hpp
compile.cpp systemDictionary.hpp
compile.cpp timer.hpp
......@@ -476,12 +478,16 @@ graphKit.cpp rootnode.hpp
graphKit.cpp runtime.hpp
graphKit.cpp sharedRuntime.hpp
graphKit.hpp addnode.hpp
graphKit.hpp callnode.hpp
graphKit.hpp cfgnode.hpp
graphKit.hpp ciEnv.hpp
graphKit.hpp divnode.hpp
graphKit.hpp compile.hpp
graphKit.hpp deoptimization.hpp
graphKit.hpp phaseX.hpp
graphKit.hpp mulnode.hpp
graphKit.hpp subnode.hpp
graphKit.hpp type.hpp
idealKit.cpp addnode.hpp
......@@ -490,7 +496,10 @@ idealKit.cpp cfgnode.hpp
idealKit.cpp idealKit.hpp
idealKit.cpp runtime.hpp
idealKit.hpp addnode.hpp
idealKit.hpp cfgnode.hpp
idealKit.hpp connode.hpp
idealKit.hpp divnode.hpp
idealKit.hpp mulnode.hpp
idealKit.hpp phaseX.hpp
idealKit.hpp subnode.hpp
......@@ -641,6 +650,7 @@ macro.cpp addnode.hpp
macro.cpp callnode.hpp
macro.cpp cfgnode.hpp
macro.cpp compile.hpp
macro.cpp compileLog.hpp
macro.cpp connode.hpp
macro.cpp locknode.hpp
macro.cpp loopnode.hpp
......@@ -993,6 +1003,21 @@ split_if.cpp callnode.hpp
split_if.cpp connode.hpp
split_if.cpp loopnode.hpp
stringopts.hpp phaseX.hpp
stringopts.hpp node.hpp
stringopts.cpp addnode.hpp
stringopts.cpp callnode.hpp
stringopts.cpp callGenerator.hpp
stringopts.cpp compileLog.hpp
stringopts.cpp divnode.hpp
stringopts.cpp idealKit.hpp
stringopts.cpp graphKit.hpp
stringopts.cpp rootnode.hpp
stringopts.cpp runtime.hpp
stringopts.cpp subnode.hpp
stringopts.cpp stringopts.hpp
stubGenerator_<arch_model>.cpp runtime.hpp
stubRoutines.cpp runtime.hpp
......
......@@ -570,6 +570,7 @@ ciEnv.hpp debugInfoRec.hpp
ciEnv.hpp dependencies.hpp
ciEnv.hpp exceptionHandlerTable.hpp
ciEnv.hpp oopMap.hpp
ciEnv.hpp systemDictionary.hpp
ciEnv.hpp thread.hpp
ciExceptionHandler.cpp ciExceptionHandler.hpp
......
......@@ -67,6 +67,8 @@ typeArrayOop Universe::_the_empty_int_array = NULL;
objArrayOop Universe::_the_empty_system_obj_array = NULL;
objArrayOop Universe::_the_empty_class_klass_array = NULL;
objArrayOop Universe::_the_array_interfaces_array = NULL;
oop Universe::_the_null_string = NULL;
oop Universe::_the_min_jint_string = NULL;
LatestMethodOopCache* Universe::_finalizer_register_cache = NULL;
LatestMethodOopCache* Universe::_loader_addClass_cache = NULL;
ActiveMethodOopsCache* Universe::_reflect_invoke_cache = NULL;
......@@ -187,6 +189,8 @@ void Universe::oops_do(OopClosure* f, bool do_all) {
f->do_oop((oop*)&_the_empty_system_obj_array);
f->do_oop((oop*)&_the_empty_class_klass_array);
f->do_oop((oop*)&_the_array_interfaces_array);
f->do_oop((oop*)&_the_null_string);
f->do_oop((oop*)&_the_min_jint_string);
_finalizer_register_cache->oops_do(f);
_loader_addClass_cache->oops_do(f);
_reflect_invoke_cache->oops_do(f);
......@@ -289,6 +293,9 @@ void Universe::genesis(TRAPS) {
klassOop ok = SystemDictionary::object_klass();
_the_null_string = StringTable::intern("null", CHECK);
_the_min_jint_string = StringTable::intern("-2147483648", CHECK);
if (UseSharedSpaces) {
// Verify shared interfaces array.
assert(_the_array_interfaces_array->obj_at(0) ==
......
......@@ -169,6 +169,8 @@ class Universe: AllStatic {
static objArrayOop _the_empty_system_obj_array; // Canonicalized system obj array
static objArrayOop _the_empty_class_klass_array; // Canonicalized obj array of type java.lang.Class
static objArrayOop _the_array_interfaces_array; // Canonicalized 2-array of cloneable & serializable klasses
static oop _the_null_string; // A cache of "null" as a Java string
static oop _the_min_jint_string; // A cache of "-2147483648" as a Java string
static LatestMethodOopCache* _finalizer_register_cache; // static method for registering finalizable objects
static LatestMethodOopCache* _loader_addClass_cache; // method for registering loaded classes in class loader vector
static ActiveMethodOopsCache* _reflect_invoke_cache; // method for security checks
......@@ -310,6 +312,8 @@ class Universe: AllStatic {
static objArrayOop the_empty_system_obj_array () { return _the_empty_system_obj_array; }
static objArrayOop the_empty_class_klass_array () { return _the_empty_class_klass_array; }
static objArrayOop the_array_interfaces_array() { return _the_array_interfaces_array; }
static oop the_null_string() { return _the_null_string; }
static oop the_min_jint_string() { return _the_min_jint_string; }
static methodOop finalizer_register_method() { return _finalizer_register_cache->get_methodOop(); }
static methodOop loader_addClass_method() { return _loader_addClass_cache->get_methodOop(); }
static ActiveMethodOopsCache* reflect_invoke_cache() { return _reflect_invoke_cache; }
......
......@@ -25,4 +25,4 @@
# include "incls/_precompiled.incl"
# include "incls/_c2_globals.cpp.incl"
C2_FLAGS(MATERIALIZE_DEVELOPER_FLAG, MATERIALIZE_PD_DEVELOPER_FLAG, MATERIALIZE_PRODUCT_FLAG, MATERIALIZE_PD_PRODUCT_FLAG, MATERIALIZE_DIAGNOSTIC_FLAG, MATERIALIZE_NOTPRODUCT_FLAG)
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)
......@@ -26,7 +26,7 @@
// Defines all globals flags used by the server compiler.
//
#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, notproduct) \
#define C2_FLAGS(develop, develop_pd, product, product_pd, diagnostic, experimental, notproduct) \
\
notproduct(intx, CompileZapFirst, 0, \
"If +ZapDeadCompiledLocals, " \
......@@ -394,6 +394,12 @@
product(bool, UseOptoBiasInlining, true, \
"Generate biased locking code in C2 ideal graph") \
\
experimental(bool, OptimizeStringConcat, false, \
"Optimize the construction of Strings by StringBuilder") \
\
notproduct(bool, PrintOptimizeStringConcat, false, \
"Print information about transformations performed on Strings") \
\
product(intx, ValueSearchLimit, 1000, \
"Recursion limit in PhaseMacroExpand::value_from_mem_phi") \
\
......@@ -413,4 +419,4 @@
product(bool, BlockLayoutRotateLoops, true, \
"Allow back branches to be fall throughs in the block layour") \
C2_FLAGS(DECLARE_DEVELOPER_FLAG, DECLARE_PD_DEVELOPER_FLAG, DECLARE_PRODUCT_FLAG, DECLARE_PD_PRODUCT_FLAG, DECLARE_DIAGNOSTIC_FLAG, DECLARE_NOTPRODUCT_FLAG)
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)
......@@ -98,12 +98,21 @@ JVMState* ParseGenerator::generate(JVMState* jvms) {
//---------------------------DirectCallGenerator------------------------------
// Internal class which handles all out-of-line calls w/o receiver type checks.
class DirectCallGenerator : public CallGenerator {
public:
DirectCallGenerator(ciMethod* method)
: CallGenerator(method)
private:
CallStaticJavaNode* _call_node;
// Force separate memory and I/O projections for the exceptional
// paths to facilitate late inlinig.
bool _separate_io_proj;
public:
DirectCallGenerator(ciMethod* method, bool separate_io_proj)
: CallGenerator(method),
_separate_io_proj(separate_io_proj)
{
}
virtual JVMState* generate(JVMState* jvms);
CallStaticJavaNode* call_node() const { return _call_node; }
};
JVMState* DirectCallGenerator::generate(JVMState* jvms) {
......@@ -129,9 +138,10 @@ JVMState* DirectCallGenerator::generate(JVMState* jvms) {
call->set_optimized_virtual(true);
}
kit.set_arguments_for_java_call(call);
kit.set_edges_for_java_call(call);
Node* ret = kit.set_results_for_java_call(call);
kit.set_edges_for_java_call(call, false, _separate_io_proj);
Node* ret = kit.set_results_for_java_call(call, _separate_io_proj);
kit.push_node(method()->return_type()->basic_type(), ret);
_call_node = call; // Save the call node in case we need it later
return kit.transfer_exceptions_into_jvms();
}
......@@ -238,9 +248,9 @@ CallGenerator* CallGenerator::for_osr(ciMethod* m, int osr_bci) {
return new ParseGenerator(m, expected_uses, true);
}
CallGenerator* CallGenerator::for_direct_call(ciMethod* m) {
CallGenerator* CallGenerator::for_direct_call(ciMethod* m, bool separate_io_proj) {
assert(!m->is_abstract(), "for_direct_call mismatch");
return new DirectCallGenerator(m);
return new DirectCallGenerator(m, separate_io_proj);
}
CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) {
......@@ -248,6 +258,108 @@ CallGenerator* CallGenerator::for_virtual_call(ciMethod* m, int vtable_index) {
return new VirtualCallGenerator(m, vtable_index);
}
// Allow inlining decisions to be delayed
class LateInlineCallGenerator : public DirectCallGenerator {
CallGenerator* _inline_cg;
public:
LateInlineCallGenerator(ciMethod* method, CallGenerator* inline_cg) :
DirectCallGenerator(method, true), _inline_cg(inline_cg) {}
virtual bool is_late_inline() const { return true; }
// Convert the CallStaticJava into an inline
virtual void do_late_inline();
JVMState* generate(JVMState* jvms) {
// Record that this call site should be revisited once the main
// parse is finished.
Compile::current()->add_late_inline(this);
// Emit the CallStaticJava and request separate projections so
// that the late inlining logic can distinguish between fall
// through and exceptional uses of the memory and io projections
// as is done for allocations and macro expansion.
return DirectCallGenerator::generate(jvms);
}
};
void LateInlineCallGenerator::do_late_inline() {
// Can't inline it
if (call_node() == NULL || call_node()->outcnt() == 0 ||
call_node()->in(0) == NULL || call_node()->in(0)->is_top())
return;
CallStaticJavaNode* call = call_node();
// Make a clone of the JVMState that appropriate to use for driving a parse
Compile* C = Compile::current();
JVMState* jvms = call->jvms()->clone_shallow(C);
uint size = call->req();
SafePointNode* map = new (C, size) SafePointNode(size, jvms);
for (uint i1 = 0; i1 < size; i1++) {
map->init_req(i1, call->in(i1));
}
// Make sure the state is a MergeMem for parsing.
if (!map->in(TypeFunc::Memory)->is_MergeMem()) {
map->set_req(TypeFunc::Memory, MergeMemNode::make(C, map->in(TypeFunc::Memory)));
}
// Make enough space for the expression stack and transfer the incoming arguments
int nargs = method()->arg_size();
jvms->set_map(map);
map->ensure_stack(jvms, jvms->method()->max_stack());
if (nargs > 0) {
for (int i1 = 0; i1 < nargs; i1++) {
map->set_req(i1 + jvms->argoff(), call->in(TypeFunc::Parms + i1));
}
}
CompileLog* log = C->log();
if (log != NULL) {
log->head("late_inline method='%d'", log->identify(method()));
JVMState* p = jvms;
while (p != NULL) {
log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method()));
p = p->caller();
}
log->tail("late_inline");
}
// Setup default node notes to be picked up by the inlining
Node_Notes* old_nn = C->default_node_notes();
if (old_nn != NULL) {
Node_Notes* entry_nn = old_nn->clone(C);
entry_nn->set_jvms(jvms);
C->set_default_node_notes(entry_nn);
}
// Now perform the inling using the synthesized JVMState
JVMState* new_jvms = _inline_cg->generate(jvms);
if (new_jvms == NULL) return; // no change
if (C->failing()) return;
// Capture any exceptional control flow
GraphKit kit(new_jvms);
// Find the result object
Node* result = C->top();
int result_size = method()->return_type()->size();
if (result_size != 0 && !kit.stopped()) {
result = (result_size == 1) ? kit.pop() : kit.pop_pair();
}
kit.replace_call(call, result);
}
CallGenerator* CallGenerator::for_late_inline(ciMethod* method, CallGenerator* inline_cg) {
return new LateInlineCallGenerator(method, inline_cg);
}
//---------------------------WarmCallGenerator--------------------------------
// Internal class which handles initial deferral of inlining decisions.
......@@ -315,70 +427,7 @@ JVMState* WarmCallGenerator::generate(JVMState* jvms) {
}
void WarmCallInfo::make_hot() {
Compile* C = Compile::current();
// Replace the callnode with something better.
CallJavaNode* call = this->call()->as_CallJava();
ciMethod* method = call->method();
int nargs = method->arg_size();
JVMState* jvms = call->jvms()->clone_shallow(C);
uint size = TypeFunc::Parms + MAX2(2, nargs);
SafePointNode* map = new (C, size) SafePointNode(size, jvms);
for (uint i1 = 0; i1 < (uint)(TypeFunc::Parms + nargs); i1++) {
map->init_req(i1, call->in(i1));
}
jvms->set_map(map);
jvms->set_offsets(map->req());
jvms->set_locoff(TypeFunc::Parms);
jvms->set_stkoff(TypeFunc::Parms);
GraphKit kit(jvms);
JVMState* new_jvms = _hot_cg->generate(kit.jvms());
if (new_jvms == NULL) return; // no change
if (C->failing()) return;
kit.set_jvms(new_jvms);
Node* res = C->top();
int res_size = method->return_type()->size();
if (res_size != 0) {
kit.inc_sp(-res_size);
res = kit.argument(0);
}
GraphKit ekit(kit.combine_and_pop_all_exception_states()->jvms());
// Replace the call:
for (DUIterator i = call->outs(); call->has_out(i); i++) {
Node* n = call->out(i);
Node* nn = NULL; // replacement
if (n->is_Proj()) {
ProjNode* nproj = n->as_Proj();
assert(nproj->_con < (uint)(TypeFunc::Parms + (res_size ? 1 : 0)), "sane proj");
if (nproj->_con == TypeFunc::Parms) {
nn = res;
} else {
nn = kit.map()->in(nproj->_con);
}
if (nproj->_con == TypeFunc::I_O) {
for (DUIterator j = nproj->outs(); nproj->has_out(j); j++) {
Node* e = nproj->out(j);
if (e->Opcode() == Op_CreateEx) {
e->replace_by(ekit.argument(0));
} else if (e->Opcode() == Op_Catch) {
for (DUIterator k = e->outs(); e->has_out(k); k++) {
CatchProjNode* p = e->out(j)->as_CatchProj();
if (p->is_handler_proj()) {
p->replace_by(ekit.control());
} else {
p->replace_by(kit.control());
}
}
}
}
}
}
NOT_PRODUCT(if (!nn) n->dump(2));
assert(nn != NULL, "don't know what to do with this user");
n->replace_by(nn);
}
Unimplemented();
}
void WarmCallInfo::make_cold() {
......
......@@ -57,6 +57,13 @@ class CallGenerator : public ResourceObj {
// is_trap: Does not return to the caller. (E.g., uncommon trap.)
virtual bool is_trap() const { return false; }
// is_late_inline: supports conversion of call into an inline
virtual bool is_late_inline() const { return false; }
// Replace the call with an inline version of the code
virtual void do_late_inline() { ShouldNotReachHere(); }
virtual CallStaticJavaNode* call_node() const { ShouldNotReachHere(); return NULL; }
// Note: It is possible for a CG to be both inline and virtual.
// (The hashCode intrinsic does a vtable check and an inlined fast path.)
......@@ -92,9 +99,12 @@ class CallGenerator : public ResourceObj {
static CallGenerator* for_osr(ciMethod* m, int osr_bci);
// How to generate vanilla out-of-line call sites:
static CallGenerator* for_direct_call(ciMethod* m); // static, special
static CallGenerator* for_direct_call(ciMethod* m, bool separate_io_projs = false); // static, special
static CallGenerator* for_virtual_call(ciMethod* m, int vtable_index); // virtual, interface
// How to generate a replace a direct call with an inline version
static CallGenerator* for_late_inline(ciMethod* m, CallGenerator* inline_cg);
// How to make a call but defer the decision whether to inline or not.
static CallGenerator* for_warm_call(WarmCallInfo* ci,
CallGenerator* if_cold,
......
......@@ -693,6 +693,84 @@ Node *CallNode::result_cast() {
}
void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj) {
projs->fallthrough_proj = NULL;
projs->fallthrough_catchproj = NULL;
projs->fallthrough_ioproj = NULL;
projs->catchall_ioproj = NULL;
projs->catchall_catchproj = NULL;
projs->fallthrough_memproj = NULL;
projs->catchall_memproj = NULL;
projs->resproj = NULL;
projs->exobj = NULL;
for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
ProjNode *pn = fast_out(i)->as_Proj();
if (pn->outcnt() == 0) continue;
switch (pn->_con) {
case TypeFunc::Control:
{
// For Control (fallthrough) and I_O (catch_all_index) we have CatchProj -> Catch -> Proj
projs->fallthrough_proj = pn;
DUIterator_Fast jmax, j = pn->fast_outs(jmax);
const Node *cn = pn->fast_out(j);
if (cn->is_Catch()) {
ProjNode *cpn = NULL;
for (DUIterator_Fast kmax, k = cn->fast_outs(kmax); k < kmax; k++) {
cpn = cn->fast_out(k)->as_Proj();
assert(cpn->is_CatchProj(), "must be a CatchProjNode");
if (cpn->_con == CatchProjNode::fall_through_index)
projs->fallthrough_catchproj = cpn;
else {
assert(cpn->_con == CatchProjNode::catch_all_index, "must be correct index.");
projs->catchall_catchproj = cpn;
}
}
}
break;
}
case TypeFunc::I_O:
if (pn->_is_io_use)
projs->catchall_ioproj = pn;
else
projs->fallthrough_ioproj = pn;
for (DUIterator j = pn->outs(); pn->has_out(j); j++) {
Node* e = pn->out(j);
if (e->Opcode() == Op_CreateEx && e->in(0)->is_CatchProj()) {
assert(projs->exobj == NULL, "only one");
projs->exobj = e;
}
}
break;
case TypeFunc::Memory:
if (pn->_is_io_use)
projs->catchall_memproj = pn;
else
projs->fallthrough_memproj = pn;
break;
case TypeFunc::Parms:
projs->resproj = pn;
break;
default:
assert(false, "unexpected projection from allocation node.");
}
}
// The resproj may not exist because the result couuld be ignored
// and the exception object may not exist if an exception handler
// swallows the exception but all the other must exist and be found.
assert(projs->fallthrough_proj != NULL, "must be found");
assert(projs->fallthrough_catchproj != NULL, "must be found");
assert(projs->fallthrough_memproj != NULL, "must be found");
assert(projs->fallthrough_ioproj != NULL, "must be found");
assert(projs->catchall_catchproj != NULL, "must be found");
if (separate_io_proj) {
assert(projs->catchall_memproj != NULL, "must be found");
assert(projs->catchall_ioproj != NULL, "must be found");
}
}
//=============================================================================
uint CallJavaNode::size_of() const { return sizeof(*this); }
uint CallJavaNode::cmp( const Node &n ) const {
......
......@@ -470,6 +470,23 @@ public:
#endif
};
// Simple container for the outgoing projections of a call. Useful
// for serious surgery on calls.
class CallProjections : public StackObj {
public:
Node* fallthrough_proj;
Node* fallthrough_catchproj;
Node* fallthrough_memproj;
Node* fallthrough_ioproj;
Node* catchall_catchproj;
Node* catchall_memproj;
Node* catchall_ioproj;
Node* resproj;
Node* exobj;
};
//------------------------------CallNode---------------------------------------
// Call nodes now subsume the function of debug nodes at callsites, so they
// contain the functionality of a full scope chain of debug nodes.
......@@ -521,6 +538,11 @@ public:
// or returns NULL if there is no one.
Node *result_cast();
// Collect all the interesting edges from a call for use in
// replacing the call by something else. Used by macro expansion
// and the late inlining support.
void extract_projections(CallProjections* projs, bool separate_io_proj);
virtual uint match_edge(uint idx) const;
#ifndef PRODUCT
......@@ -529,6 +551,7 @@ public:
#endif
};
//------------------------------CallJavaNode-----------------------------------
// Make a static or dynamic subroutine call node using Java calling
// convention. (The "Java" calling convention is the compiler's calling
......
......@@ -224,6 +224,32 @@ bool Compile::valid_bundle_info(const Node *n) {
}
void Compile::gvn_replace_by(Node* n, Node* nn) {
for (DUIterator_Last imin, i = n->last_outs(imin); i >= imin; ) {
Node* use = n->last_out(i);
bool is_in_table = initial_gvn()->hash_delete(use);
uint uses_found = 0;
for (uint j = 0; j < use->len(); j++) {
if (use->in(j) == n) {
if (j < use->req())
use->set_req(j, nn);
else
use->set_prec(j, nn);
uses_found++;
}
}
if (is_in_table) {
// reinsert into table
initial_gvn()->hash_find_insert(use);
}
record_for_igvn(use);
i -= uses_found; // we deleted 1 or more copies of this edge
}
}
// Identify all nodes that are reachable from below, useful.
// Use breadth-first pass that records state in a Unique_Node_List,
// recursive traversal is slower.
......@@ -554,6 +580,28 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
rethrow_exceptions(kit.transfer_exceptions_into_jvms());
}
if (!failing() && has_stringbuilder()) {
{
// remove useless nodes to make the usage analysis simpler
ResourceMark rm;
PhaseRemoveUseless pru(initial_gvn(), &for_igvn);
}
{
ResourceMark rm;
print_method("Before StringOpts", 3);
PhaseStringOpts pso(initial_gvn(), &for_igvn);
print_method("After StringOpts", 3);
}
// now inline anything that we skipped the first time around
while (_late_inlines.length() > 0) {
CallGenerator* cg = _late_inlines.pop();
cg->do_late_inline();
}
}
assert(_late_inlines.length() == 0, "should have been processed");
print_method("Before RemoveUseless", 3);
// Remove clutter produced by parsing.
......@@ -820,6 +868,7 @@ void Compile::Init(int aliaslevel) {
_fixed_slots = 0;
set_has_split_ifs(false);
set_has_loops(has_method() && method()->has_loops()); // first approximation
set_has_stringbuilder(false);
_deopt_happens = true; // start out assuming the worst
_trap_can_recompile = false; // no traps emitted yet
_major_progress = true; // start out assuming good things will happen
......@@ -2240,6 +2289,30 @@ static void final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc ) {
break;
}
case Op_Proj: {
if (OptimizeStringConcat) {
ProjNode* p = n->as_Proj();
if (p->_is_io_use) {
// Separate projections were used for the exception path which
// are normally removed by a late inline. If it wasn't inlined
// then they will hang around and should just be replaced with
// the original one.
Node* proj = NULL;
// Replace with just one
for (SimpleDUIterator i(p->in(0)); i.has_next(); i.next()) {
Node *use = i.get();
if (use->is_Proj() && p != use && use->as_Proj()->_con == p->_con) {
proj = use;
break;
}
}
assert(p != NULL, "must be found");
p->subsume_by(proj);
}
}
break;
}
case Op_Phi:
if (n->as_Phi()->bottom_type()->isa_narrowoop()) {
// The EncodeP optimization may create Phi with the same edges
......
......@@ -149,6 +149,7 @@ class Compile : public Phase {
bool _has_loops; // True if the method _may_ have some loops
bool _has_split_ifs; // True if the method _may_ have some split-if
bool _has_unsafe_access; // True if the method _may_ produce faults in unsafe loads or stores.
bool _has_stringbuilder; // True StringBuffers or StringBuilders are allocated
uint _trap_hist[trapHistLength]; // Cumulative traps
bool _trap_can_recompile; // Have we emitted a recompiling trap?
uint _decompile_count; // Cumulative decompilation counts.
......@@ -219,6 +220,9 @@ class Compile : public Phase {
Unique_Node_List* _for_igvn; // Initial work-list for next round of Iterative GVN
WarmCallInfo* _warm_calls; // Sorted work-list for heat-based inlining.
GrowableArray<CallGenerator*> _late_inlines; // List of CallGenerators to be revisited after
// main parsing has finished.
// Matching, CFG layout, allocation, code generation
PhaseCFG* _cfg; // Results of CFG finding
bool _select_24_bit_instr; // We selected an instruction with a 24-bit result
......@@ -298,6 +302,8 @@ class Compile : public Phase {
void set_has_split_ifs(bool z) { _has_split_ifs = z; }
bool has_unsafe_access() const { return _has_unsafe_access; }
void set_has_unsafe_access(bool z) { _has_unsafe_access = z; }
bool has_stringbuilder() const { return _has_stringbuilder; }
void set_has_stringbuilder(bool z) { _has_stringbuilder = z; }
void set_trap_count(uint r, uint c) { assert(r < trapHistLength, "oob"); _trap_hist[r] = c; }
uint trap_count(uint r) const { assert(r < trapHistLength, "oob"); return _trap_hist[r]; }
bool trap_can_recompile() const { return _trap_can_recompile; }
......@@ -475,6 +481,7 @@ class Compile : public Phase {
// Decide how to build a call.
// The profile factor is a discount to apply to this site's interp. profile.
CallGenerator* call_generator(ciMethod* call_method, int vtable_index, bool call_is_virtual, JVMState* jvms, bool allow_inline, float profile_factor);
bool should_delay_inlining(ciMethod* call_method, JVMState* jvms);
// Report if there were too many traps at a current method and bci.
// Report if a trap was recorded, and/or PerMethodTrapLimit was exceeded.
......@@ -495,6 +502,11 @@ class Compile : public Phase {
void set_initial_gvn(PhaseGVN *gvn) { _initial_gvn = gvn; }
void set_for_igvn(Unique_Node_List *for_igvn) { _for_igvn = for_igvn; }
// Replace n by nn using initial_gvn, calling hash_delete and
// record_for_igvn as needed.
void gvn_replace_by(Node* n, Node* nn);
void identify_useful_nodes(Unique_Node_List &useful);
void remove_useless_nodes (Unique_Node_List &useful);
......@@ -502,6 +514,9 @@ class Compile : public Phase {
void set_warm_calls(WarmCallInfo* l) { _warm_calls = l; }
WarmCallInfo* pop_warm_call();
// Record this CallGenerator for inlining at the end of parsing.
void add_late_inline(CallGenerator* cg) { _late_inlines.push(cg); }
// Matching, CFG layout, allocation, code generation
PhaseCFG* cfg() { return _cfg; }
bool select_24_bit_instr() const { return _select_24_bit_instr; }
......
......@@ -128,6 +128,12 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
if (allow_inline) {
CallGenerator* cg = CallGenerator::for_inline(call_method, expected_uses);
if (require_inline && cg != NULL && should_delay_inlining(call_method, jvms)) {
// Delay the inlining of this method to give us the
// opportunity to perform some high level optimizations
// first.
return CallGenerator::for_late_inline(call_method, cg);
}
if (cg == NULL) {
// Fall through.
} else if (require_inline || !InlineWarmCalls) {
......@@ -225,8 +231,61 @@ CallGenerator* Compile::call_generator(ciMethod* call_method, int vtable_index,
} else {
// Class Hierarchy Analysis or Type Profile reveals a unique target,
// or it is a static or special call.
return CallGenerator::for_direct_call(call_method);
return CallGenerator::for_direct_call(call_method, should_delay_inlining(call_method, jvms));
}
}
// Return true for methods that shouldn't be inlined early so that
// they are easier to analyze and optimize as intrinsics.
bool Compile::should_delay_inlining(ciMethod* call_method, JVMState* jvms) {
if (has_stringbuilder()) {
if ((call_method->holder() == C->env()->StringBuilder_klass() ||
call_method->holder() == C->env()->StringBuffer_klass()) &&
(jvms->method()->holder() == C->env()->StringBuilder_klass() ||
jvms->method()->holder() == C->env()->StringBuffer_klass())) {
// Delay SB calls only when called from non-SB code
return false;
}
switch (call_method->intrinsic_id()) {
case vmIntrinsics::_StringBuilder_void:
case vmIntrinsics::_StringBuilder_int:
case vmIntrinsics::_StringBuilder_String:
case vmIntrinsics::_StringBuilder_append_char:
case vmIntrinsics::_StringBuilder_append_int:
case vmIntrinsics::_StringBuilder_append_String:
case vmIntrinsics::_StringBuilder_toString:
case vmIntrinsics::_StringBuffer_void:
case vmIntrinsics::_StringBuffer_int:
case vmIntrinsics::_StringBuffer_String:
case vmIntrinsics::_StringBuffer_append_char:
case vmIntrinsics::_StringBuffer_append_int:
case vmIntrinsics::_StringBuffer_append_String:
case vmIntrinsics::_StringBuffer_toString:
case vmIntrinsics::_Integer_toString:
return true;
case vmIntrinsics::_String_String:
{
Node* receiver = jvms->map()->in(jvms->argoff() + 1);
if (receiver->is_Proj() && receiver->in(0)->is_CallStaticJava()) {
CallStaticJavaNode* csj = receiver->in(0)->as_CallStaticJava();
ciMethod* m = csj->method();
if (m != NULL &&
(m->intrinsic_id() == vmIntrinsics::_StringBuffer_toString ||
m->intrinsic_id() == vmIntrinsics::_StringBuilder_toString))
// Delay String.<init>(new SB())
return true;
}
return false;
}
default:
return false;
}
}
return false;
}
......
......@@ -1351,8 +1351,8 @@ void GraphKit::set_all_memory(Node* newmem) {
}
//------------------------------set_all_memory_call----------------------------
void GraphKit::set_all_memory_call(Node* call) {
Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) );
void GraphKit::set_all_memory_call(Node* call, bool separate_io_proj) {
Node* newmem = _gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory, separate_io_proj) );
set_all_memory(newmem);
}
......@@ -1573,7 +1573,7 @@ void GraphKit::set_arguments_for_java_call(CallJavaNode* call) {
//---------------------------set_edges_for_java_call---------------------------
// Connect a newly created call into the current JVMS.
// A return value node (if any) is returned from set_edges_for_java_call.
void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw) {
void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw, bool separate_io_proj) {
// Add the predefined inputs:
call->init_req( TypeFunc::Control, control() );
......@@ -1595,13 +1595,13 @@ void GraphKit::set_edges_for_java_call(CallJavaNode* call, bool must_throw) {
// Re-use the current map to produce the result.
set_control(_gvn.transform(new (C, 1) ProjNode(call, TypeFunc::Control)));
set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O )));
set_all_memory_call(xcall);
set_i_o( _gvn.transform(new (C, 1) ProjNode(call, TypeFunc::I_O , separate_io_proj)));
set_all_memory_call(xcall, separate_io_proj);
//return xcall; // no need, caller already has it
}
Node* GraphKit::set_results_for_java_call(CallJavaNode* call) {
Node* GraphKit::set_results_for_java_call(CallJavaNode* call, bool separate_io_proj) {
if (stopped()) return top(); // maybe the call folded up?
// Capture the return value, if any.
......@@ -1614,8 +1614,15 @@ Node* GraphKit::set_results_for_java_call(CallJavaNode* call) {
// Note: Since any out-of-line call can produce an exception,
// we always insert an I_O projection from the call into the result.
make_slow_call_ex(call, env()->Throwable_klass(), false);
make_slow_call_ex(call, env()->Throwable_klass(), separate_io_proj);
if (separate_io_proj) {
// The caller requested separate projections be used by the fall
// through and exceptional paths, so replace the projections for
// the fall through path.
set_i_o(_gvn.transform( new (C, 1) ProjNode(call, TypeFunc::I_O) ));
set_all_memory(_gvn.transform( new (C, 1) ProjNode(call, TypeFunc::Memory) ));
}
return ret;
}
......@@ -1678,6 +1685,59 @@ void GraphKit::set_predefined_output_for_runtime_call(Node* call,
}
}
// Replace the call with the current state of the kit.
void GraphKit::replace_call(CallNode* call, Node* result) {
JVMState* ejvms = NULL;
if (has_exceptions()) {
ejvms = transfer_exceptions_into_jvms();
}
SafePointNode* final_state = stop();
// Find all the needed outputs of this call
CallProjections callprojs;
call->extract_projections(&callprojs, true);
// Replace all the old call edges with the edges from the inlining result
C->gvn_replace_by(callprojs.fallthrough_catchproj, final_state->in(TypeFunc::Control));
C->gvn_replace_by(callprojs.fallthrough_memproj, final_state->in(TypeFunc::Memory));
C->gvn_replace_by(callprojs.fallthrough_ioproj, final_state->in(TypeFunc::I_O));
// Replace the result with the new result if it exists and is used
if (callprojs.resproj != NULL && result != NULL) {
C->gvn_replace_by(callprojs.resproj, result);
}
if (ejvms == NULL) {
// No exception edges to simply kill off those paths
C->gvn_replace_by(callprojs.catchall_catchproj, C->top());
C->gvn_replace_by(callprojs.catchall_memproj, C->top());
C->gvn_replace_by(callprojs.catchall_ioproj, C->top());
} else {
GraphKit ekit(ejvms);
// Load my combined exception state into the kit, with all phis transformed:
SafePointNode* ex_map = ekit.combine_and_pop_all_exception_states();
Node* ex_oop = ekit.use_exception_state(ex_map);
C->gvn_replace_by(callprojs.catchall_catchproj, ekit.control());
C->gvn_replace_by(callprojs.catchall_memproj, ekit.reset_memory());
C->gvn_replace_by(callprojs.catchall_ioproj, ekit.i_o());
// Replace the old exception object with the newly created one
if (callprojs.exobj != NULL) {
C->gvn_replace_by(callprojs.exobj, ex_oop);
}
}
// Disconnect the call from the graph
call->disconnect_inputs(NULL);
C->gvn_replace_by(call, C->top());
}
//------------------------------increment_counter------------------------------
// for statistics: increment a VM counter by 1
......@@ -3459,4 +3519,3 @@ void GraphKit::g1_write_barrier_post(Node* oop_store,
sync_kit(ideal);
}
#undef __
......@@ -279,6 +279,34 @@ class GraphKit : public Phase {
}
Node* basic_plus_adr(Node* base, Node* ptr, Node* offset);
// Some convenient shortcuts for common nodes
Node* IfTrue(IfNode* iff) { return _gvn.transform(new (C,1) IfTrueNode(iff)); }
Node* IfFalse(IfNode* iff) { return _gvn.transform(new (C,1) IfFalseNode(iff)); }
Node* AddI(Node* l, Node* r) { return _gvn.transform(new (C,3) AddINode(l, r)); }
Node* SubI(Node* l, Node* r) { return _gvn.transform(new (C,3) SubINode(l, r)); }
Node* MulI(Node* l, Node* r) { return _gvn.transform(new (C,3) MulINode(l, r)); }
Node* DivI(Node* ctl, Node* l, Node* r) { return _gvn.transform(new (C,3) DivINode(ctl, l, r)); }
Node* AndI(Node* l, Node* r) { return _gvn.transform(new (C,3) AndINode(l, r)); }
Node* OrI(Node* l, Node* r) { return _gvn.transform(new (C,3) OrINode(l, r)); }
Node* XorI(Node* l, Node* r) { return _gvn.transform(new (C,3) XorINode(l, r)); }
Node* MaxI(Node* l, Node* r) { return _gvn.transform(new (C,3) MaxINode(l, r)); }
Node* MinI(Node* l, Node* r) { return _gvn.transform(new (C,3) MinINode(l, r)); }
Node* LShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) LShiftINode(l, r)); }
Node* RShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) RShiftINode(l, r)); }
Node* URShiftI(Node* l, Node* r) { return _gvn.transform(new (C,3) URShiftINode(l, r)); }
Node* CmpI(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpINode(l, r)); }
Node* CmpL(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpLNode(l, r)); }
Node* CmpP(Node* l, Node* r) { return _gvn.transform(new (C,3) CmpPNode(l, r)); }
Node* Bool(Node* cmp, BoolTest::mask relop) { return _gvn.transform(new (C,2) BoolNode(cmp, relop)); }
Node* AddP(Node* b, Node* a, Node* o) { return _gvn.transform(new (C,4) AddPNode(b, a, o)); }
// Convert between int and long, and size_t.
// (See macros ConvI2X, etc., in type.hpp for ConvI2X, etc.)
Node* ConvI2L(Node* offset);
......@@ -400,7 +428,7 @@ class GraphKit : public Phase {
void set_all_memory(Node* newmem);
// Create a memory projection from the call, then set_all_memory.
void set_all_memory_call(Node* call);
void set_all_memory_call(Node* call, bool separate_io_proj = false);
// Create a LoadNode, reading from the parser's memory state.
// (Note: require_atomic_access is useful only with T_LONG.)
......@@ -543,12 +571,12 @@ class GraphKit : public Phase {
// Transform the call, and update the basics: control, i_o, memory.
// (The next step is usually to call set_results_for_java_call.)
void set_edges_for_java_call(CallJavaNode* call,
bool must_throw = false);
bool must_throw = false, bool separate_io_proj = false);
// Finish up a java call that was started by set_edges_for_java_call.
// Call add_exception on any throw arising from the call.
// Return the call result (transformed).
Node* set_results_for_java_call(CallJavaNode* call);
Node* set_results_for_java_call(CallJavaNode* call, bool separate_io_proj = false);
// Similar to set_edges_for_java_call, but simplified for runtime calls.
void set_predefined_output_for_runtime_call(Node* call) {
......@@ -559,6 +587,11 @@ class GraphKit : public Phase {
const TypePtr* hook_mem);
Node* set_predefined_input_for_runtime_call(SafePointNode* call);
// Replace the call with the current state of the kit. Requires
// that the call was generated with separate io_projs so that
// exceptional control flow can be handled properly.
void replace_call(CallNode* call, Node* result);
// helper functions for statistics
void increment_counter(address counter_addr); // increment a debug counter
void increment_counter(Node* counter_addr); // increment a debug counter
......
......@@ -912,15 +912,29 @@ bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) {
return false;
}
CompileLog* log = C->log();
if (log != NULL) {
Node* klass = alloc->in(AllocateNode::KlassNode);
const TypeKlassPtr* tklass = _igvn.type(klass)->is_klassptr();
log->head("eliminate_allocation type='%d'",
log->identify(tklass->klass()));
JVMState* p = alloc->jvms();
while (p != NULL) {
log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method()));
p = p->caller();
}
log->tail("eliminate_allocation");
}
process_users_of_allocation(alloc);
#ifndef PRODUCT
if (PrintEliminateAllocations) {
if (PrintEliminateAllocations) {
if (alloc->is_AllocateArray())
tty->print_cr("++++ Eliminated: %d AllocateArray", alloc->_idx);
else
tty->print_cr("++++ Eliminated: %d Allocate", alloc->_idx);
}
}
#endif
return true;
......@@ -1639,6 +1653,18 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) {
} // if (!oldbox->is_eliminated())
} // if (alock->is_Lock() && !lock->is_coarsened())
CompileLog* log = C->log();
if (log != NULL) {
log->head("eliminate_lock lock='%d'",
alock->is_Lock());
JVMState* p = alock->jvms();
while (p != NULL) {
log->elem("jvms bci='%d' method='%d'", p->bci(), log->identify(p->method()));
p = p->caller();
}
log->tail("eliminate_lock");
}
#ifndef PRODUCT
if (PrintEliminateLocks) {
if (alock->is_Lock()) {
......
......@@ -1503,6 +1503,8 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
}
}
} else if (tp->base() == Type::InstPtr) {
const TypeInstPtr* tinst = tp->is_instptr();
ciKlass* klass = tinst->klass();
assert( off != Type::OffsetBot ||
// arrays can be cast to Objects
tp->is_oopptr()->klass()->is_java_lang_Object() ||
......@@ -1510,6 +1512,25 @@ const Type *LoadNode::Value( PhaseTransform *phase ) const {
phase->C->has_unsafe_access(),
"Field accesses must be precise" );
// For oop loads, we expect the _type to be precise
if (OptimizeStringConcat && klass == phase->C->env()->String_klass() &&
adr->is_AddP() && off != Type::OffsetBot) {
// For constant Strings treat the fields as compile time constants.
Node* base = adr->in(AddPNode::Base);
if (base->Opcode() == Op_ConP) {
const TypeOopPtr* t = phase->type(base)->isa_oopptr();
ciObject* string = t->const_oop();
ciConstant constant = string->as_instance()->field_value_by_offset(off);
if (constant.basic_type() == T_INT) {
return TypeInt::make(constant.as_int());
} else if (constant.basic_type() == T_ARRAY) {
if (adr->bottom_type()->is_ptr_to_narrowoop()) {
return TypeNarrowOop::make_from_constant(constant.as_object());
} else {
return TypeOopPtr::make_from_constant(constant.as_object());
}
}
}
}
} else if (tp->base() == Type::KlassPtr) {
assert( off != Type::OffsetBot ||
// arrays can be cast to Objects
......
......@@ -661,6 +661,10 @@ public:
return (_flags & Flag_is_Call) != 0;
}
CallNode* isa_Call() const {
return is_Call() ? as_Call() : NULL;
}
CallNode *as_Call() const { // Only for CallNode (not for MachCallNode)
assert((_class_id & ClassMask_Call) == Class_Call, "invalid node class");
return (CallNode*)this;
......@@ -673,6 +677,9 @@ public:
type##Node *as_##type() const { \
assert(is_##type(), "invalid node class"); \
return (type##Node*)this; \
} \
type##Node* isa_##type() const { \
return (is_##type()) ? as_##type() : NULL; \
}
DEFINE_CLASS_QUERY(AbstractLock)
......@@ -1249,6 +1256,24 @@ Node* Node::last_out(DUIterator_Last& i) const {
#undef I_VDUI_ONLY
#undef VDUI_ONLY
// An Iterator that truly follows the iterator pattern. Doesn't
// support deletion but could be made to.
//
// for (SimpleDUIterator i(n); i.has_next(); i.next()) {
// Node* m = i.get();
//
class SimpleDUIterator : public StackObj {
private:
Node* node;
DUIterator_Fast i;
DUIterator_Fast imax;
public:
SimpleDUIterator(Node* n): node(n), i(n->fast_outs(imax)) {}
bool has_next() { return i < imax; }
void next() { i++; }
Node* get() { return node->fast_out(i); }
};
//-----------------------------------------------------------------------------
// Map dense integer indices to Nodes. Uses classic doubling-array trick.
......@@ -1290,6 +1315,12 @@ class Node_List : public Node_Array {
public:
Node_List() : Node_Array(Thread::current()->resource_area()), _cnt(0) {}
Node_List(Arena *a) : Node_Array(a), _cnt(0) {}
bool contains(Node* n) {
for (uint e = 0; e < size(); e++) {
if (at(e) == n) return true;
}
return false;
}
void insert( uint i, Node *n ) { Node_Array::insert(i,n); _cnt++; }
void remove( uint i ) { Node_Array::remove(i); _cnt--; }
void push( Node *b ) { map(_cnt++,b); }
......
......@@ -221,6 +221,14 @@ void Parse::do_new() {
// Push resultant oop onto stack
push(obj);
// Keep track of whether opportunities exist for StringBuilder
// optimizations.
if (OptimizeStringConcat &&
(klass == C->env()->StringBuilder_klass() ||
klass == C->env()->StringBuffer_klass())) {
C->set_has_stringbuilder(true);
}
}
#ifndef PRODUCT
......
......@@ -44,6 +44,7 @@ public:
BlockLayout, // Linear ordering of blocks
Register_Allocation, // Register allocation, duh
LIVE, // Dragon-book LIVE range problem
StringOpts, // StringBuilder related optimizations
Interference_Graph, // Building the IFG
Coalesce, // Coalescing copies
Ideal_Loop, // Find idealized trip-counted loops
......
......@@ -345,7 +345,11 @@ public:
Node *hash_find(const Node *n) { return _table.hash_find(n); }
// Used after parsing to eliminate values that are no longer in program
void remove_useless_nodes(VectorSet &useful) { _table.remove_useless_nodes(useful); }
void remove_useless_nodes(VectorSet &useful) {
_table.remove_useless_nodes(useful);
// this may invalidate cached cons so reset the cache
init_con_caches();
}
virtual ConNode* uncached_makecon(const Type* t); // override from PhaseTransform
......
此差异已折叠。
/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*
*/
class StringConcat;
class PhaseStringOpts : public Phase {
friend class StringConcat;
private:
PhaseGVN* _gvn;
// List of dead nodes to clean up aggressively at the end
Unique_Node_List dead_worklist;
// Memory slices needed for code gen
int char_adr_idx;
int value_field_idx;
int count_field_idx;
int offset_field_idx;
// Integer.sizeTable - used for int to String conversion
ciField* size_table_field;
// A set for use by various stages
VectorSet _visited;
// Collect a list of all SB.toString calls
Node_List collect_toString_calls();
// Examine the use of the SB alloc to see if it can be replace with
// a single string construction.
StringConcat* build_candidate(CallStaticJavaNode* call);
// Replace all the SB calls in concat with an optimization String allocation
void replace_string_concat(StringConcat* concat);
// Load the value of a static field, performing any constant folding.
Node* fetch_static_field(GraphKit& kit, ciField* field);
// Compute the number of characters required to represent the int value
Node* int_stringSize(GraphKit& kit, Node* value);
// Copy the characters representing value into char_array starting at start
void int_getChars(GraphKit& kit, Node* value, Node* char_array, Node* start, Node* end);
// Copy of the contents of the String str into char_array starting at index start.
Node* copy_string(GraphKit& kit, Node* str, Node* char_array, Node* start);
// Clean up any leftover nodes
void record_dead_node(Node* node);
void remove_dead_nodes();
PhaseGVN* gvn() { return _gvn; }
enum {
// max length of constant string copy unrolling in copy_string
unroll_string_copy_length = 6
};
public:
PhaseStringOpts(PhaseGVN* gvn, Unique_Node_List* worklist);
};
......@@ -847,9 +847,6 @@ public:
// Constant pointer to array
static const TypeAryPtr *make( PTR ptr, ciObject* o, const TypeAry *ary, ciKlass* k, bool xk, int offset, int instance_id = InstanceBot);
// Convenience
static const TypeAryPtr *make(ciObject* o);
// Return a 'ptr' version of this type
virtual const Type *cast_to_ptr_type(PTR ptr) const;
......
......@@ -46,7 +46,8 @@ bool Flag::is_unlocker() const {
bool Flag::is_unlocked() const {
if (strcmp(kind, "{diagnostic}") == 0) {
return UnlockDiagnosticVMOptions;
} else if (strcmp(kind, "{experimental}") == 0) {
} else if (strcmp(kind, "{experimental}") == 0 ||
strcmp(kind, "{C2 experimental}") == 0) {
return UnlockExperimentalVMOptions;
} else {
return true;
......@@ -169,6 +170,7 @@ void Flag::print_as_flag(outputStream* st) {
#define C2_PRODUCT_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 product}", DEFAULT },
#define C2_PD_PRODUCT_FLAG_STRUCT(type, name, doc) { #type, XSTR(name), &name, "{C2 pd product}", DEFAULT },
#define C2_DIAGNOSTIC_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 diagnostic}", DEFAULT },
#define C2_EXPERIMENTAL_FLAG_STRUCT(type, name, value, doc) { #type, XSTR(name), &name, "{C2 experimental}", DEFAULT },
#ifdef PRODUCT
#define C2_DEVELOP_FLAG_STRUCT(type, name, value, doc) /* flag is constant */
#define C2_PD_DEVELOP_FLAG_STRUCT(type, name, doc) /* flag is constant */
......@@ -190,7 +192,7 @@ static Flag flagTable[] = {
C1_FLAGS(C1_DEVELOP_FLAG_STRUCT, C1_PD_DEVELOP_FLAG_STRUCT, C1_PRODUCT_FLAG_STRUCT, C1_PD_PRODUCT_FLAG_STRUCT, C1_NOTPRODUCT_FLAG_STRUCT)
#endif
#ifdef COMPILER2
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)
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)
#endif
{0, NULL, NULL}
};
......
......@@ -64,6 +64,7 @@
#define C2_PRODUCT_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name),
#define C2_PD_PRODUCT_FLAG_MEMBER(type, name, doc) FLAG_MEMBER(name),
#define C2_DIAGNOSTIC_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name),
#define C2_EXPERIMENTAL_FLAG_MEMBER(type, name, value, doc) FLAG_MEMBER(name),
#ifdef PRODUCT
#define C2_DEVELOP_FLAG_MEMBER(type, name, value, doc) /* flag is constant */
#define C2_PD_DEVELOP_FLAG_MEMBER(type, name, doc) /* flag is constant */
......@@ -84,7 +85,7 @@ typedef enum {
C1_FLAGS(C1_DEVELOP_FLAG_MEMBER, C1_PD_DEVELOP_FLAG_MEMBER, C1_PRODUCT_FLAG_MEMBER, C1_PD_PRODUCT_FLAG_MEMBER, C1_NOTPRODUCT_FLAG_MEMBER)
#endif
#ifdef COMPILER2
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)
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)
#endif
NUM_CommandLineFlag
} CommandLineFlag;
......@@ -130,6 +131,7 @@ typedef enum {
#define C2_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type),
#define C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE(type, name, doc) FLAG_MEMBER_WITH_TYPE(name,type),
#define C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type),
#define C2_EXPERIMENTAL_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) FLAG_MEMBER_WITH_TYPE(name,type),
#ifdef PRODUCT
#define C2_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, value, doc) /* flag is constant */
#define C2_PD_DEVELOP_FLAG_MEMBER_WITH_TYPE(type, name, doc) /* flag is constant */
......@@ -181,6 +183,7 @@ typedef enum {
C2_PRODUCT_FLAG_MEMBER_WITH_TYPE,
C2_PD_PRODUCT_FLAG_MEMBER_WITH_TYPE,
C2_DIAGNOSTIC_FLAG_MEMBER_WITH_TYPE,
C2_EXPERIMENTAL_FLAG_MEMBER_WITH_TYPE,
C2_NOTPRODUCT_FLAG_MEMBER_WITH_TYPE)
#endif
NUM_CommandLineFlagWithType
......
......@@ -278,6 +278,17 @@ template<class E> class GrowableArray : public GenericGrowableArray {
_len--;
}
// inserts the given element before the element at index i
void insert_before(const int idx, const E& elem) {
check_nesting();
if (_len == _max) grow(_len);
for (int j = _len - 1; j >= idx; j--) {
_data[j + 1] = _data[j];
}
_len++;
_data[idx] = elem;
}
void appendAll(const GrowableArray<E>* l) {
for (int i = 0; i < l->_len; i++) {
raw_at_put_grow(_len, l->_data[i], 0);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册