< prev index next >
src/hotspot/share/opto/callnode.cpp
Print this page
@@ -40,10 +40,12 @@
#include "opto/parse.hpp"
#include "opto/regalloc.hpp"
#include "opto/regmask.hpp"
#include "opto/rootnode.hpp"
#include "opto/runtime.hpp"
+#include "opto/valuetypenode.hpp"
+#include "runtime/sharedRuntime.hpp"
// Portions of code courtesy of Clifford Click
// Optimization - Graph Style
@@ -73,11 +75,11 @@
return RegMask::Empty;
}
//------------------------------match------------------------------------------
// Construct projections for incoming parameters, and their RegMask info
-Node *StartNode::match( const ProjNode *proj, const Matcher *match ) {
+Node *StartNode::match(const ProjNode *proj, const Matcher *match, const RegMask* mask) {
switch (proj->_con) {
case TypeFunc::Control:
case TypeFunc::I_O:
case TypeFunc::Memory:
return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj);
@@ -477,10 +479,18 @@
st->print("[%d]", spobj->n_fields());
int ndim = cik->as_array_klass()->dimension() - 1;
while (ndim-- > 0) {
st->print("[]");
}
+ } else if (cik->is_value_array_klass()) {
+ ciKlass* cie = cik->as_value_array_klass()->base_element_klass();
+ cie->print_name_on(st);
+ st->print("[%d]", spobj->n_fields());
+ int ndim = cik->as_array_klass()->dimension() - 1;
+ while (ndim-- > 0) {
+ st->print("[]");
+ }
}
st->print("={");
uint nf = spobj->n_fields();
if (nf > 0) {
uint first_ind = spobj->first_index(mcall->jvms());
@@ -686,49 +696,68 @@
if (_cnt != COUNT_UNKNOWN) st->print(" C=%f",_cnt);
if (jvms() != NULL) jvms()->dump_spec(st);
}
#endif
-const Type *CallNode::bottom_type() const { return tf()->range(); }
+const Type *CallNode::bottom_type() const { return tf()->range_cc(); }
const Type* CallNode::Value(PhaseGVN* phase) const {
- if (phase->type(in(0)) == Type::TOP) return Type::TOP;
- return tf()->range();
+ if (!in(0) || phase->type(in(0)) == Type::TOP) {
+ return Type::TOP;
+ }
+ return tf()->range_cc();
}
//------------------------------calling_convention-----------------------------
-void CallNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const {
+void CallNode::calling_convention(BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt) const {
+ if (_entry_point == StubRoutines::store_value_type_fields_to_buf()) {
+ // The call to that stub is a special case: its inputs are
+ // multiple values returned from a call and so it should follow
+ // the return convention.
+ SharedRuntime::java_return_convention(sig_bt, parm_regs, argcnt);
+ return;
+ }
// Use the standard compiler calling convention
Matcher::calling_convention( sig_bt, parm_regs, argcnt, true );
}
//------------------------------match------------------------------------------
// Construct projections for control, I/O, memory-fields, ..., and
// return result(s) along with their RegMask info
-Node *CallNode::match( const ProjNode *proj, const Matcher *match ) {
- switch (proj->_con) {
+Node *CallNode::match(const ProjNode *proj, const Matcher *match, const RegMask* mask) {
+ uint con = proj->_con;
+ const TypeTuple *range_cc = tf()->range_cc();
+ if (con >= TypeFunc::Parms) {
+ if (is_CallRuntime()) {
+ if (con == TypeFunc::Parms) {
+ uint ideal_reg = range_cc->field_at(TypeFunc::Parms)->ideal_reg();
+ OptoRegPair regs = match->c_return_value(ideal_reg,true);
+ RegMask rm = RegMask(regs.first());
+ if (OptoReg::is_valid(regs.second())) {
+ rm.Insert(regs.second());
+ }
+ return new MachProjNode(this,con,rm,ideal_reg);
+ } else {
+ assert(con == TypeFunc::Parms+1, "only one return value");
+ assert(range_cc->field_at(TypeFunc::Parms+1) == Type::HALF, "");
+ return new MachProjNode(this,con, RegMask::Empty, (uint)OptoReg::Bad);
+ }
+ } else {
+ // The Call may return multiple values (value type fields): we
+ // create one projection per returned values.
+ assert(con <= TypeFunc::Parms+1 || ValueTypeReturnedAsFields, "only for multi value return");
+ uint ideal_reg = range_cc->field_at(con)->ideal_reg();
+ return new MachProjNode(this, con, mask[con-TypeFunc::Parms], ideal_reg);
+ }
+ }
+
+ switch (con) {
case TypeFunc::Control:
case TypeFunc::I_O:
case TypeFunc::Memory:
return new MachProjNode(this,proj->_con,RegMask::Empty,MachProjNode::unmatched_proj);
- case TypeFunc::Parms+1: // For LONG & DOUBLE returns
- assert(tf()->range()->field_at(TypeFunc::Parms+1) == Type::HALF, "");
- // 2nd half of doubles and longs
- return new MachProjNode(this,proj->_con, RegMask::Empty, (uint)OptoReg::Bad);
-
- case TypeFunc::Parms: { // Normal returns
- uint ideal_reg = tf()->range()->field_at(TypeFunc::Parms)->ideal_reg();
- OptoRegPair regs = is_CallRuntime()
- ? match->c_return_value(ideal_reg,true) // Calls into C runtime
- : match-> return_value(ideal_reg,true); // Calls into compiled Java code
- RegMask rm = RegMask(regs.first());
- if( OptoReg::is_valid(regs.second()) )
- rm.Insert( regs.second() );
- return new MachProjNode(this,proj->_con,rm,ideal_reg);
- }
-
case TypeFunc::ReturnAdr:
case TypeFunc::FramePtr:
default:
ShouldNotReachHere();
}
@@ -745,11 +774,11 @@
// instance at the specified offset.
//
bool CallNode::may_modify(const TypeOopPtr *t_oop, PhaseTransform *phase) {
assert((t_oop != NULL), "sanity");
if (is_call_to_arraycopystub() && strcmp(_name, "unsafe_arraycopy") != 0) {
- const TypeTuple* args = _tf->domain();
+ const TypeTuple* args = _tf->domain_sig();
Node* dest = NULL;
// Stubs that can be called once an ArrayCopyNode is expanded have
// different signatures. Look for the second pointer argument,
// that is the destination of the copy.
for (uint i = TypeFunc::Parms, j = 0; i < args->cnt(); i++) {
@@ -794,11 +823,11 @@
if ((inst_t != NULL) && (!inst_t->klass_is_exact() ||
(inst_t->klass() == boxing_klass))) {
return true;
}
}
- const TypeTuple* d = tf()->domain();
+ const TypeTuple* d = tf()->domain_cc();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
const TypeInstPtr* inst_t = d->field_at(i)->isa_instptr();
if ((inst_t != NULL) && (!inst_t->klass_is_exact() ||
(inst_t->klass() == boxing_klass))) {
return true;
@@ -810,20 +839,31 @@
return true;
}
// Does this call have a direct reference to n other than debug information?
bool CallNode::has_non_debug_use(Node *n) {
- const TypeTuple * d = tf()->domain();
+ const TypeTuple * d = tf()->domain_cc();
for (uint i = TypeFunc::Parms; i < d->cnt(); i++) {
Node *arg = in(i);
if (arg == n) {
return true;
}
}
return false;
}
+bool CallNode::has_debug_use(Node *n) {
+ assert(jvms() != NULL, "jvms should not be null");
+ for (uint i = jvms()->debug_start(); i < jvms()->debug_end(); i++) {
+ Node *arg = in(i);
+ if (arg == n) {
+ return true;
+ }
+ }
+ return false;
+}
+
// Returns the unique CheckCastPP of a call
// or 'this' if there are several CheckCastPP or unexpected uses
// or returns NULL if there is no one.
Node *CallNode::result_cast() {
Node *cast = NULL;
@@ -851,20 +891,25 @@
}
return cast;
}
-void CallNode::extract_projections(CallProjections* projs, bool separate_io_proj, bool do_asserts) {
- 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;
+CallProjections* CallNode::extract_projections(bool separate_io_proj, bool do_asserts) {
+ uint max_res = TypeFunc::Parms-1;
+ for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
+ ProjNode *pn = fast_out(i)->as_Proj();
+ max_res = MAX2(max_res, pn->_con);
+ }
+
+ assert(max_res < _tf->range_cc()->cnt(), "result out of bounds");
+
+ uint projs_size = sizeof(CallProjections);
+ if (max_res > TypeFunc::Parms) {
+ projs_size += (max_res-TypeFunc::Parms)*sizeof(Node*);
+ }
+ char* projs_storage = resource_allocate_bytes(projs_size);
+ CallProjections* projs = new(projs_storage)CallProjections(max_res - TypeFunc::Parms + 1);
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) {
@@ -907,14 +952,16 @@
projs->catchall_memproj = pn;
else
projs->fallthrough_memproj = pn;
break;
case TypeFunc::Parms:
- projs->resproj = pn;
+ projs->resproj[0] = pn;
break;
default:
- assert(false, "unexpected projection from allocation node.");
+ assert(pn->_con <= max_res, "unexpected projection from allocation node.");
+ projs->resproj[pn->_con-TypeFunc::Parms] = pn;
+ break;
}
}
// The resproj may not exist because the result could be ignored
// and the exception object may not exist if an exception handler
@@ -927,10 +974,11 @@
assert(!do_asserts || projs->catchall_catchproj != NULL, "must be found");
if (separate_io_proj) {
assert(!do_asserts || projs->catchall_memproj != NULL, "must be found");
assert(!do_asserts || projs->catchall_ioproj != NULL, "must be found");
}
+ return projs;
}
Node *CallNode::Ideal(PhaseGVN *phase, bool can_reshape) {
CallGenerator* cg = generator();
if (can_reshape && cg != NULL && cg->is_mh_late_inline() && !cg->already_attempted()) {
@@ -970,10 +1018,14 @@
#ifdef ASSERT
bool CallJavaNode::validate_symbolic_info() const {
if (method() == NULL) {
return true; // call into runtime or uncommon trap
}
+ Bytecodes::Code bc = jvms()->method()->java_code_at_bci(_bci);
+ if (ACmpOnValues == 3 && (bc == Bytecodes::_if_acmpeq || bc == Bytecodes::_if_acmpne)) {
+ return true;
+ }
ciMethod* symbolic_info = jvms()->method()->get_method_at_bci(_bci);
ciMethod* callee = method();
if (symbolic_info->is_method_handle_intrinsic() && !callee->is_method_handle_intrinsic()) {
assert(override_symbolic_info(), "should be set");
}
@@ -1081,10 +1133,17 @@
}
#endif
//------------------------------calling_convention-----------------------------
void CallRuntimeNode::calling_convention( BasicType* sig_bt, VMRegPair *parm_regs, uint argcnt ) const {
+ if (_entry_point == NULL) {
+ // The call to that stub is a special case: its inputs are
+ // multiple values returned from a call and so it should follow
+ // the return convention.
+ SharedRuntime::java_return_convention(sig_bt, parm_regs, argcnt);
+ return;
+ }
Matcher::c_calling_convention( sig_bt, parm_regs, argcnt );
}
//=============================================================================
//------------------------------calling_convention-----------------------------
@@ -1097,10 +1156,16 @@
st->print("%s", _name);
CallNode::dump_spec(st);
}
#endif
+uint CallLeafNoFPNode::match_edge(uint idx) const {
+ // Null entry point is a special case for which the target is in a
+ // register. Need to match that edge.
+ return entry_point() == NULL && idx == TypeFunc::Parms;
+}
+
//=============================================================================
void SafePointNode::set_local(JVMState* jvms, uint idx, Node *c) {
assert(verify_jvms(jvms), "jvms must match");
int loc = jvms->locoff() + idx;
@@ -1357,18 +1422,21 @@
//=============================================================================
uint AllocateNode::size_of() const { return sizeof(*this); }
AllocateNode::AllocateNode(Compile* C, const TypeFunc *atype,
Node *ctrl, Node *mem, Node *abio,
- Node *size, Node *klass_node, Node *initial_test)
+ Node *size, Node *klass_node,
+ Node* initial_test,
+ ValueTypeBaseNode* value_node)
: CallNode(atype, NULL, TypeRawPtr::BOTTOM)
{
init_class_id(Class_Allocate);
init_flags(Flag_is_macro);
_is_scalar_replaceable = false;
_is_non_escaping = false;
_is_allocation_MemBar_redundant = false;
+ _larval = false;
Node *topnode = C->top();
init_req( TypeFunc::Control , ctrl );
init_req( TypeFunc::I_O , abio );
init_req( TypeFunc::Memory , mem );
@@ -1376,10 +1444,13 @@
init_req( TypeFunc::FramePtr , topnode );
init_req( AllocSize , size);
init_req( KlassNode , klass_node);
init_req( InitialTest , initial_test);
init_req( ALength , topnode);
+ init_req( ValueNode , value_node);
+ // DefaultValue defaults to NULL
+ // RawDefaultValue defaults to NULL
C->add_macro_node(this);
}
void AllocateNode::compute_MemBar_redundancy(ciMethod* initializer)
{
@@ -1396,13 +1467,82 @@
if (analyzer->is_arg_stack(0) || analyzer->is_arg_local(0)) {
_is_allocation_MemBar_redundant = true;
}
}
+Node* AllocateNode::Ideal(PhaseGVN* phase, bool can_reshape) {
+ // Check for unused value type allocation
+ if (can_reshape && in(AllocateNode::ValueNode) != NULL &&
+ outcnt() != 0 && result_cast() == NULL) {
+ // Remove allocation by replacing the projection nodes with its inputs
+ InitializeNode* init = initialization();
+ PhaseIterGVN* igvn = phase->is_IterGVN();
+ CallProjections* projs = extract_projections(true, false);
+ assert(projs->nb_resproj <= 1, "unexpected number of results");
+ if (projs->fallthrough_catchproj != NULL) {
+ igvn->replace_node(projs->fallthrough_catchproj, in(TypeFunc::Control));
+ }
+ if (projs->fallthrough_memproj != NULL) {
+ igvn->replace_node(projs->fallthrough_memproj, in(TypeFunc::Memory));
+ }
+ if (projs->catchall_memproj != NULL) {
+ igvn->replace_node(projs->catchall_memproj, phase->C->top());
+ }
+ if (projs->fallthrough_ioproj != NULL) {
+ igvn->replace_node(projs->fallthrough_ioproj, in(TypeFunc::I_O));
+ }
+ if (projs->catchall_ioproj != NULL) {
+ igvn->replace_node(projs->catchall_ioproj, phase->C->top());
+ }
+ if (projs->catchall_catchproj != NULL) {
+ igvn->replace_node(projs->catchall_catchproj, phase->C->top());
+ }
+ if (projs->resproj[0] != NULL) {
+ igvn->replace_node(projs->resproj[0], phase->C->top());
+ }
+ igvn->replace_node(this, phase->C->top());
+ if (init != NULL) {
+ Node* ctrl_proj = init->proj_out_or_null(TypeFunc::Control);
+ Node* mem_proj = init->proj_out_or_null(TypeFunc::Memory);
+ if (ctrl_proj != NULL) {
+ igvn->replace_node(ctrl_proj, init->in(TypeFunc::Control));
+ }
+ if (mem_proj != NULL) {
+ igvn->replace_node(mem_proj, init->in(TypeFunc::Memory));
+ }
+ }
+ return NULL;
+ }
+
+ return CallNode::Ideal(phase, can_reshape);
+}
+
+Node *AllocateNode::make_ideal_mark(PhaseGVN *phase, Node* obj, Node* control, Node* mem, Node* klass_node) {
+ Node* mark_node = NULL;
+ // For now only enable fast locking for non-array types
+ if ((EnableValhalla || UseBiasedLocking) && Opcode() == Op_Allocate) {
+ if (klass_node == NULL) {
+ Node* k_adr = phase->transform(new AddPNode(obj, obj, phase->MakeConX(oopDesc::klass_offset_in_bytes())));
+ klass_node = phase->transform(LoadKlassNode::make(*phase, NULL, phase->C->immutable_memory(), k_adr, phase->type(k_adr)->is_ptr()));
+ }
+ Node* proto_adr = phase->transform(new AddPNode(klass_node, klass_node, phase->MakeConX(in_bytes(Klass::prototype_header_offset()))));
+ mark_node = LoadNode::make(*phase, control, mem, proto_adr, TypeRawPtr::BOTTOM, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
+ } else {
+ mark_node = phase->MakeConX((intptr_t)markOopDesc::prototype());
+ }
+ mark_node = phase->transform(mark_node);
+ // Avoid returning a constant (old node) here because this method is used by LoadNode::Ideal
+ return new OrXNode(mark_node, phase->MakeConX(_larval ? markOopDesc::larval_state_pattern : 0));
+}
+
+
//=============================================================================
Node* AllocateArrayNode::Ideal(PhaseGVN *phase, bool can_reshape) {
- if (remove_dead_region(phase, can_reshape)) return this;
+ Node* res = SafePointNode::Ideal(phase, can_reshape);
+ if (res != NULL) {
+ return res;
+ }
// Don't bother trying to transform a dead node
if (in(0) && in(0)->is_top()) return NULL;
const Type* type = phase->type(Ideal_length());
if (type->isa_int() && type->is_int()->_hi < 0) {
@@ -2073,11 +2213,12 @@
if (elem == Type::BOTTOM) {
// An array but we don't know what elements are
return true;
}
- dest_t = dest_t->add_offset(Type::OffsetBot)->is_oopptr();
+ dest_t = dest_t->is_aryptr()->with_field_offset(Type::OffsetBot)->add_offset(Type::OffsetBot)->is_oopptr();
+ t_oop = t_oop->is_aryptr()->with_field_offset(Type::OffsetBot);
uint dest_alias = phase->C->get_alias_index(dest_t);
uint t_oop_alias = phase->C->get_alias_index(t_oop);
return dest_alias == t_oop_alias;
}
< prev index next >