src/share/vm/opto/graphKit.cpp
Index
Unified diffs
Context diffs
Sdiffs
Patch
New
Old
Previous File
Next File
hotspot Cdiff src/share/vm/opto/graphKit.cpp
src/share/vm/opto/graphKit.cpp
Print this page
rev 6139 : 8031755: Type speculation should be used to optimize explicit null checks
Summary: feed profiling data about reference nullness to type speculation.
Reviewed-by:
*** 610,623 ****
// create the stack trace.
// Usual case: Bail to interpreter.
// Reserve the right to recompile if we haven't seen anything yet.
! assert(!Deoptimization::reason_is_speculate(reason), "unsupported");
Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile;
if (treat_throw_as_hot
! && (method()->method_data()->trap_recompiled_at(bci(), NULL)
|| C->too_many_traps(reason))) {
// We cannot afford to take more traps here. Suffer in the interpreter.
if (C->log() != NULL)
C->log()->elem("hot_throw preallocated='0' reason='%s' mcount='%d'",
Deoptimization::trap_reason_name(reason),
--- 610,623 ----
// create the stack trace.
// Usual case: Bail to interpreter.
// Reserve the right to recompile if we haven't seen anything yet.
! ciMethod* m = Deoptimization::reason_is_speculate(reason) ? C->method() : NULL;
Deoptimization::DeoptAction action = Deoptimization::Action_maybe_recompile;
if (treat_throw_as_hot
! && (method()->method_data()->trap_recompiled_at(bci(), m)
|| C->too_many_traps(reason))) {
// We cannot afford to take more traps here. Suffer in the interpreter.
if (C->log() != NULL)
C->log()->elem("hot_throw preallocated='0' reason='%s' mcount='%d'",
Deoptimization::trap_reason_name(reason),
*** 1179,1189 ****
extern int explicit_null_checks_inserted,
explicit_null_checks_elided;
Node* GraphKit::null_check_common(Node* value, BasicType type,
// optional arguments for variations:
bool assert_null,
! Node* *null_control) {
assert(!assert_null || null_control == NULL, "not both at once");
if (stopped()) return top();
if (!GenerateCompilerNullChecks && !assert_null && null_control == NULL) {
// For some performance testing, we may wish to suppress null checking.
value = cast_not_null(value); // Make it appear to be non-null (4962416).
--- 1179,1190 ----
extern int explicit_null_checks_inserted,
explicit_null_checks_elided;
Node* GraphKit::null_check_common(Node* value, BasicType type,
// optional arguments for variations:
bool assert_null,
! Node* *null_control,
! bool speculative) {
assert(!assert_null || null_control == NULL, "not both at once");
if (stopped()) return top();
if (!GenerateCompilerNullChecks && !assert_null && null_control == NULL) {
// For some performance testing, we may wish to suppress null checking.
value = cast_not_null(value); // Make it appear to be non-null (4962416).
*** 1289,1305 ****
//-----------
// Branch to failure if null
float ok_prob = PROB_MAX; // a priori estimate: nulls never happen
Deoptimization::DeoptReason reason;
! if (assert_null)
reason = Deoptimization::Reason_null_assert;
! else if (type == T_OBJECT)
! reason = Deoptimization::Reason_null_check;
! else
reason = Deoptimization::Reason_div0_check;
!
// %%% Since Reason_unhandled is not recorded on a per-bytecode basis,
// ciMethodData::has_trap_at will return a conservative -1 if any
// must-be-null assertion has failed. This could cause performance
// problems for a method after its first do_null_assert failure.
// Consider using 'Reason_class_check' instead?
--- 1290,1306 ----
//-----------
// Branch to failure if null
float ok_prob = PROB_MAX; // a priori estimate: nulls never happen
Deoptimization::DeoptReason reason;
! if (assert_null) {
reason = Deoptimization::Reason_null_assert;
! } else if (type == T_OBJECT) {
! reason = Deoptimization::reason_null_check(speculative);
! } else {
reason = Deoptimization::Reason_div0_check;
! }
// %%% Since Reason_unhandled is not recorded on a per-bytecode basis,
// ciMethodData::has_trap_at will return a conservative -1 if any
// must-be-null assertion has failed. This could cause performance
// problems for a method after its first do_null_assert failure.
// Consider using 'Reason_class_check' instead?
*** 2118,2142 ****
* Record profiling data exact_kls for Node n with the type system so
* that it can propagate it (speculation)
*
* @param n node that the type applies to
* @param exact_kls type from profiling
*
* @return node with improved type
*/
! Node* GraphKit::record_profile_for_speculation(Node* n, ciKlass* exact_kls) {
const Type* current_type = _gvn.type(n);
assert(UseTypeSpeculation, "type speculation must be on");
! const TypeOopPtr* speculative = current_type->speculative();
if (current_type->would_improve_type(exact_kls, jvms()->depth())) {
const TypeKlassPtr* tklass = TypeKlassPtr::make(exact_kls);
const TypeOopPtr* xtype = tklass->as_instance_type();
assert(xtype->klass_is_exact(), "Should be exact");
// record the new speculative type's depth
! speculative = xtype->with_inline_depth(jvms()->depth());
}
if (speculative != current_type->speculative()) {
// Build a type with a speculative type (what we think we know
// about the type but will need a guard when we use it)
--- 2119,2158 ----
* Record profiling data exact_kls for Node n with the type system so
* that it can propagate it (speculation)
*
* @param n node that the type applies to
* @param exact_kls type from profiling
+ * @param maybe_null did profiling see null?
*
* @return node with improved type
*/
! Node* GraphKit::record_profile_for_speculation(Node* n, ciKlass* exact_kls, bool maybe_null) {
const Type* current_type = _gvn.type(n);
assert(UseTypeSpeculation, "type speculation must be on");
! const TypePtr* speculative = current_type->speculative();
+ // Should the klass from the profile be recorded in the speculative type?
if (current_type->would_improve_type(exact_kls, jvms()->depth())) {
const TypeKlassPtr* tklass = TypeKlassPtr::make(exact_kls);
const TypeOopPtr* xtype = tklass->as_instance_type();
assert(xtype->klass_is_exact(), "Should be exact");
+ // Any reason to believe n is not null (from this profiling or a previous one)?
+ const TypePtr* ptr = (maybe_null && current_type->speculative_maybe_null()) ? TypePtr::BOTTOM : TypePtr::NOTNULL;
// record the new speculative type's depth
! speculative = xtype->cast_to_ptr_type(ptr->ptr())->is_ptr();
! speculative = speculative->with_inline_depth(jvms()->depth());
! } else if (current_type->would_improve_ptr(maybe_null)) {
! // Profiling report that null was never seen so we can change the
! // speculative type to non null ptr.
! assert(!maybe_null, "nothing to improve");
! if (speculative == NULL) {
! speculative = TypePtr::NOTNULL;
! } else {
! const TypePtr* ptr = TypePtr::NOTNULL;
! speculative = speculative->cast_to_ptr_type(ptr->ptr())->is_ptr();
! }
}
if (speculative != current_type->speculative()) {
// Build a type with a speculative type (what we think we know
// about the type but will need a guard when we use it)
*** 2165,2175 ****
Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) {
if (!UseTypeSpeculation) {
return n;
}
ciKlass* exact_kls = profile_has_unique_klass();
! return record_profile_for_speculation(n, exact_kls);
}
/**
* Record profiling data from argument profiling at an invoke with the
* type system so that it can propagate it (speculation)
--- 2181,2199 ----
Node* GraphKit::record_profiled_receiver_for_speculation(Node* n) {
if (!UseTypeSpeculation) {
return n;
}
ciKlass* exact_kls = profile_has_unique_klass();
! bool maybe_null = true;
! if (java_bc() == Bytecodes::_checkcast ||
! java_bc() == Bytecodes::_instanceof ||
! java_bc() == Bytecodes::_aastore) {
! ciProfileData* data = method()->method_data()->bci_to_data(bci());
! bool maybe_null = data == NULL ? true : data->as_BitData()->null_seen();
! }
! return record_profile_for_speculation(n, exact_kls, maybe_null);
! return n;
}
/**
* Record profiling data from argument profiling at an invoke with the
* type system so that it can propagate it (speculation)
*** 2185,2197 ****
int nargs = tf->_domain->_cnt - TypeFunc::Parms;
int skip = Bytecodes::has_receiver(bc) ? 1 : 0;
for (int j = skip, i = 0; j < nargs && i < TypeProfileArgsLimit; j++) {
const Type *targ = tf->_domain->field_at(j + TypeFunc::Parms);
if (targ->basic_type() == T_OBJECT || targ->basic_type() == T_ARRAY) {
! ciKlass* better_type = method()->argument_profiled_type(bci(), i);
! if (better_type != NULL) {
! record_profile_for_speculation(argument(j), better_type);
}
i++;
}
}
}
--- 2209,2222 ----
int nargs = tf->_domain->_cnt - TypeFunc::Parms;
int skip = Bytecodes::has_receiver(bc) ? 1 : 0;
for (int j = skip, i = 0; j < nargs && i < TypeProfileArgsLimit; j++) {
const Type *targ = tf->_domain->field_at(j + TypeFunc::Parms);
if (targ->basic_type() == T_OBJECT || targ->basic_type() == T_ARRAY) {
! bool maybe_null = true;
! ciKlass* better_type = NULL;
! if (method()->argument_profiled_type(bci(), i, better_type, maybe_null)) {
! record_profile_for_speculation(argument(j), better_type, maybe_null);
}
i++;
}
}
}
*** 2204,2222 ****
if (!UseTypeSpeculation) {
return;
}
for (int i = 0, j = 0; i < method()->arg_size() ; i++) {
if (_gvn.type(local(i))->isa_oopptr()) {
! ciKlass* better_type = method()->parameter_profiled_type(j);
! if (better_type != NULL) {
! record_profile_for_speculation(local(i), better_type);
}
j++;
}
}
}
void GraphKit::round_double_result(ciMethod* dest_method) {
// A non-strict method may return a double value which has an extended
// exponent, but this must not be visible in a caller which is 'strict'
// If a strict caller invokes a non-strict callee, round a double result
--- 2229,2266 ----
if (!UseTypeSpeculation) {
return;
}
for (int i = 0, j = 0; i < method()->arg_size() ; i++) {
if (_gvn.type(local(i))->isa_oopptr()) {
! bool maybe_null = true;
! ciKlass* better_type = NULL;
! if (method()->parameter_profiled_type(j, better_type, maybe_null)) {
! record_profile_for_speculation(local(i), better_type, maybe_null);
}
j++;
}
}
}
+ /**
+ * Record profiling data from return value profiling at an invoke with
+ * the type system so that it can propagate it (speculation)
+ */
+ void GraphKit::record_profiled_return_for_speculation() {
+ if (!UseTypeSpeculation) {
+ return;
+ }
+ bool maybe_null = true;
+ ciKlass* better_type = NULL;
+ if (method()->return_profiled_type(bci(), better_type, maybe_null)) {
+ // If profiling reports a single type for the return value,
+ // feed it to the type system so it can propagate it as a
+ // speculative type
+ record_profile_for_speculation(stack(sp()-1), better_type, maybe_null);
+ }
+ }
+
void GraphKit::round_double_result(ciMethod* dest_method) {
// A non-strict method may return a double value which has an extended
// exponent, but this must not be visible in a caller which is 'strict'
// If a strict caller invokes a non-strict callee, round a double result
*** 2292,2316 ****
//------------------------------null_check_oop---------------------------------
// Null check oop. Set null-path control into Region in slot 3.
// Make a cast-not-nullness use the other not-null control. Return cast.
Node* GraphKit::null_check_oop(Node* value, Node* *null_control,
! bool never_see_null, bool safe_for_replace) {
// Initial NULL check taken path
(*null_control) = top();
! Node* cast = null_check_common(value, T_OBJECT, false, null_control);
// Generate uncommon_trap:
if (never_see_null && (*null_control) != top()) {
// If we see an unexpected null at a check-cast we record it and force a
// recompile; the offending check-cast will be compiled to handle NULLs.
// If we see more than one offending BCI, then all checkcasts in the
// method will be compiled to handle NULLs.
PreserveJVMState pjvms(this);
set_control(*null_control);
replace_in_map(value, null());
! uncommon_trap(Deoptimization::Reason_null_check,
Deoptimization::Action_make_not_entrant);
(*null_control) = top(); // NULL path is dead
}
if ((*null_control) == top() && safe_for_replace) {
replace_in_map(value, cast);
--- 2336,2363 ----
//------------------------------null_check_oop---------------------------------
// Null check oop. Set null-path control into Region in slot 3.
// Make a cast-not-nullness use the other not-null control. Return cast.
Node* GraphKit::null_check_oop(Node* value, Node* *null_control,
! bool never_see_null,
! bool safe_for_replace,
! bool speculative) {
// Initial NULL check taken path
(*null_control) = top();
! Node* cast = null_check_common(value, T_OBJECT, false, null_control, speculative);
// Generate uncommon_trap:
if (never_see_null && (*null_control) != top()) {
// If we see an unexpected null at a check-cast we record it and force a
// recompile; the offending check-cast will be compiled to handle NULLs.
// If we see more than one offending BCI, then all checkcasts in the
// method will be compiled to handle NULLs.
PreserveJVMState pjvms(this);
set_control(*null_control);
replace_in_map(value, null());
! Deoptimization::DeoptReason reason = Deoptimization::reason_null_check(speculative);
! uncommon_trap(reason,
Deoptimization::Action_make_not_entrant);
(*null_control) = top(); // NULL path is dead
}
if ((*null_control) == top() && safe_for_replace) {
replace_in_map(value, cast);
*** 2730,2753 ****
// Use null_seen information if it is available from the profile.
// If we see an unexpected null at a type check we record it and force a
// recompile; the offending check will be recompiled to handle NULLs.
// If we see several offending BCIs, then all checks in the
// method will be recompiled.
! bool GraphKit::seems_never_null(Node* obj, ciProfileData* data) {
if (UncommonNullCast // Cutout for this technique
&& obj != null() // And not the -Xcomp stupid case?
! && !too_many_traps(Deoptimization::Reason_null_check)
) {
if (data == NULL)
// Edge case: no mature data. Be optimistic here.
return true;
// If the profile has not seen a null, assume it won't happen.
assert(java_bc() == Bytecodes::_checkcast ||
java_bc() == Bytecodes::_instanceof ||
java_bc() == Bytecodes::_aastore, "MDO must collect null_seen bit here");
return !data->as_BitData()->null_seen();
}
return false;
}
//------------------------maybe_cast_profiled_receiver-------------------------
// If the profile has seen exactly one type, narrow to exactly that type.
--- 2777,2806 ----
// Use null_seen information if it is available from the profile.
// If we see an unexpected null at a type check we record it and force a
// recompile; the offending check will be recompiled to handle NULLs.
// If we see several offending BCIs, then all checks in the
// method will be recompiled.
! bool GraphKit::seems_never_null(Node* obj, ciProfileData* data, bool& speculating) {
! speculating = !_gvn.type(obj)->speculative_maybe_null();
! Deoptimization::DeoptReason reason = Deoptimization::reason_null_check(speculating);
if (UncommonNullCast // Cutout for this technique
&& obj != null() // And not the -Xcomp stupid case?
! && !too_many_traps(reason)
) {
+ if (speculating) {
+ return true;
+ }
if (data == NULL)
// Edge case: no mature data. Be optimistic here.
return true;
// If the profile has not seen a null, assume it won't happen.
assert(java_bc() == Bytecodes::_checkcast ||
java_bc() == Bytecodes::_instanceof ||
java_bc() == Bytecodes::_aastore, "MDO must collect null_seen bit here");
return !data->as_BitData()->null_seen();
}
+ speculating = false;
return false;
}
//------------------------maybe_cast_profiled_receiver-------------------------
// If the profile has seen exactly one type, narrow to exactly that type.
*** 2756,2766 ****
ciKlass* require_klass,
ciKlass* spec_klass,
bool safe_for_replace) {
if (!UseTypeProfile || !TypeProfileCasts) return NULL;
! Deoptimization::DeoptReason reason = spec_klass == NULL ? Deoptimization::Reason_class_check : Deoptimization::Reason_speculate_class_check;
// Make sure we haven't already deoptimized from this tactic.
if (too_many_traps(reason))
return NULL;
--- 2809,2819 ----
ciKlass* require_klass,
ciKlass* spec_klass,
bool safe_for_replace) {
if (!UseTypeProfile || !TypeProfileCasts) return NULL;
! Deoptimization::DeoptReason reason = Deoptimization::reason_class_check(spec_klass != NULL);
// Make sure we haven't already deoptimized from this tactic.
if (too_many_traps(reason))
return NULL;
*** 2809,2827 ****
ciKlass* type,
bool not_null) {
// type == NULL if profiling tells us this object is always null
if (type != NULL) {
Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
! Deoptimization::DeoptReason null_reason = Deoptimization::Reason_null_check;
if (!too_many_traps(null_reason) &&
!too_many_traps(class_reason)) {
Node* not_null_obj = NULL;
// not_null is true if we know the object is not null and
// there's no need for a null check
if (!not_null) {
Node* null_ctl = top();
! not_null_obj = null_check_oop(obj, &null_ctl, true, true);
assert(null_ctl->is_top(), "no null control here");
} else {
not_null_obj = obj;
}
--- 2862,2880 ----
ciKlass* type,
bool not_null) {
// type == NULL if profiling tells us this object is always null
if (type != NULL) {
Deoptimization::DeoptReason class_reason = Deoptimization::Reason_speculate_class_check;
! Deoptimization::DeoptReason null_reason = Deoptimization::Reason_speculate_null_check;
if (!too_many_traps(null_reason) &&
!too_many_traps(class_reason)) {
Node* not_null_obj = NULL;
// not_null is true if we know the object is not null and
// there's no need for a null check
if (!not_null) {
Node* null_ctl = top();
! not_null_obj = null_check_oop(obj, &null_ctl, true, true, true);
assert(null_ctl->is_top(), "no null control here");
} else {
not_null_obj = obj;
}
*** 2865,2880 ****
ciProfileData* data = NULL;
if (java_bc() == Bytecodes::_instanceof) { // Only for the bytecode
data = method()->method_data()->bci_to_data(bci());
}
bool never_see_null = (ProfileDynamicTypes // aggressive use of profile
! && seems_never_null(obj, data));
// Null check; get casted pointer; set region slot 3
Node* null_ctl = top();
! Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace);
// If not_null_obj is dead, only null-path is taken
if (stopped()) { // Doing instance-of on a NULL?
set_control(null_ctl);
return intcon(0);
--- 2918,2934 ----
ciProfileData* data = NULL;
if (java_bc() == Bytecodes::_instanceof) { // Only for the bytecode
data = method()->method_data()->bci_to_data(bci());
}
+ bool speculative_not_null = false;
bool never_see_null = (ProfileDynamicTypes // aggressive use of profile
! && seems_never_null(obj, data, speculative_not_null));
// Null check; get casted pointer; set region slot 3
Node* null_ctl = top();
! Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace, speculative_not_null);
// If not_null_obj is dead, only null-path is taken
if (stopped()) { // Doing instance-of on a NULL?
set_control(null_ctl);
return intcon(0);
*** 2993,3008 ****
RegionNode* region = new (C) RegionNode(PATH_LIMIT);
Node* phi = new (C) PhiNode(region, toop);
C->set_has_split_ifs(true); // Has chance for split-if optimization
// Use null-cast information if it is available
bool never_see_null = ((failure_control == NULL) // regular case only
! && seems_never_null(obj, data));
// Null check; get casted pointer; set region slot 3
Node* null_ctl = top();
! Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace);
// If not_null_obj is dead, only null-path is taken
if (stopped()) { // Doing instance-of on a NULL?
set_control(null_ctl);
return null();
--- 3047,3063 ----
RegionNode* region = new (C) RegionNode(PATH_LIMIT);
Node* phi = new (C) PhiNode(region, toop);
C->set_has_split_ifs(true); // Has chance for split-if optimization
// Use null-cast information if it is available
+ bool speculative_not_null = false;
bool never_see_null = ((failure_control == NULL) // regular case only
! && seems_never_null(obj, data, speculative_not_null));
// Null check; get casted pointer; set region slot 3
Node* null_ctl = top();
! Node* not_null_obj = null_check_oop(obj, &null_ctl, never_see_null, safe_for_replace, speculative_not_null);
// If not_null_obj is dead, only null-path is taken
if (stopped()) { // Doing instance-of on a NULL?
set_control(null_ctl);
return null();
src/share/vm/opto/graphKit.cpp
Index
Unified diffs
Context diffs
Sdiffs
Patch
New
Old
Previous File
Next File