--- old/src/hotspot/share/opto/parseHelper.cpp 2019-03-11 14:26:55.470354473 +0100 +++ new/src/hotspot/share/opto/parseHelper.cpp 2019-03-11 14:26:55.258354476 +0100 @@ -23,15 +23,18 @@ */ #include "precompiled.hpp" +#include "ci/ciValueKlass.hpp" #include "classfile/systemDictionary.hpp" #include "compiler/compileLog.hpp" #include "oops/objArrayKlass.hpp" +#include "oops/valueArrayKlass.hpp" #include "opto/addnode.hpp" #include "opto/memnode.hpp" #include "opto/mulnode.hpp" #include "opto/parse.hpp" #include "opto/rootnode.hpp" #include "opto/runtime.hpp" +#include "opto/valuetypenode.hpp" #include "runtime/sharedRuntime.hpp" //------------------------------make_dtrace_method_entry_exit ---------------- @@ -65,6 +68,7 @@ void Parse::do_checkcast() { bool will_link; ciKlass* klass = iter().get_klass(will_link); + bool never_null = iter().is_klass_never_null(); Node *obj = peek(); @@ -92,7 +96,10 @@ return; } - Node *res = gen_checkcast(obj, makecon(TypeKlassPtr::make(klass)) ); + Node* res = gen_checkcast(obj, makecon(TypeKlassPtr::make(klass)), NULL, never_null); + if (stopped()) { + return; + } // Pop from stack AFTER gen_checkcast because it can uncommon trap and // the debug info has to be correct. @@ -137,26 +144,27 @@ //------------------------------array_store_check------------------------------ // pull array from stack and check that the store is valid -void Parse::array_store_check() { - +Node* Parse::array_store_check() { // Shorthand access to array store elements without popping them. Node *obj = peek(0); Node *idx = peek(1); Node *ary = peek(2); + const TypeAryPtr* ary_t = _gvn.type(ary)->is_aryptr(); + const Type* elemtype = ary_t->elem(); + const TypeOopPtr* elemptr = elemtype->make_oopptr(); + bool is_value_array = elemtype->isa_valuetype() != NULL || (elemptr != NULL && elemptr->is_valuetypeptr()); + if (_gvn.type(obj) == TypePtr::NULL_PTR) { // There's never a type check on null values. // This cutout lets us avoid the uncommon_trap(Reason_array_check) // below, which turns into a performance liability if the // gen_checkcast folds up completely. - return; + return obj; } // Extract the array klass type - int klass_offset = oopDesc::klass_offset_in_bytes(); - Node* p = basic_plus_adr( ary, ary, klass_offset ); - // p's type is array-of-OOPS plus klass_offset - Node* array_klass = _gvn.transform(LoadKlassNode::make(_gvn, NULL, immutable_memory(), p, TypeInstPtr::KLASS)); + Node* array_klass = load_object_klass(ary); // Get the array klass const TypeKlassPtr *tak = _gvn.type(array_klass)->is_klassptr(); @@ -221,7 +229,8 @@ // Come here for polymorphic array klasses // Extract the array element class - int element_klass_offset = in_bytes(ObjArrayKlass::element_klass_offset()); + int element_klass_offset = in_bytes(ArrayKlass::element_klass_offset()); + Node *p2 = basic_plus_adr(array_klass, array_klass, element_klass_offset); // We are allowed to use the constant type only if cast succeeded. If always_see_exact_class is true, // we must set a control edge from the IfTrue node created by the uncommon_trap above to the @@ -229,48 +238,64 @@ Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, always_see_exact_class ? control() : NULL, immutable_memory(), p2, tak)); + // Handle value type arrays + if (is_value_array) { + // We statically know that this is a value type array, use precise klass ptr + ciValueKlass* vk = elemtype->isa_valuetype() ? elemtype->is_valuetype()->value_klass() : + elemptr->value_klass(); + a_e_klass = makecon(TypeKlassPtr::make(vk)); + } + // Check (the hard way) and throw if not a subklass. - // Result is ignored, we just need the CFG effects. - gen_checkcast(obj, a_e_klass); + return gen_checkcast(obj, a_e_klass); } void Parse::emit_guard_for_new(ciInstanceKlass* klass) { - // Emit guarded new - // if (klass->_init_thread != current_thread || - // klass->_init_state != being_initialized) - // uncommon_trap - Node* cur_thread = _gvn.transform( new ThreadLocalNode() ); - Node* merge = new RegionNode(3); - _gvn.set_type(merge, Type::CONTROL); - Node* kls = makecon(TypeKlassPtr::make(klass)); + if ((!klass->is_initialized() && !klass->is_being_initialized()) || + klass->is_abstract() || klass->is_interface() || + klass->name() == ciSymbol::java_lang_Class() || + iter().is_unresolved_klass()) { + uncommon_trap(Deoptimization::Reason_uninitialized, + Deoptimization::Action_reinterpret, + klass); + } if (klass->is_being_initialized()) { + // Emit guarded new + // if (klass->_init_thread != current_thread || + // klass->_init_state != being_initialized) + // uncommon_trap + Node* cur_thread = _gvn.transform( new ThreadLocalNode() ); + Node* merge = new RegionNode(3); + _gvn.set_type(merge, Type::CONTROL); + Node* kls = makecon(TypeKlassPtr::make(klass)); + + Node* init_thread_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_thread_offset())); + Node* adr_node = basic_plus_adr(kls, kls, init_thread_offset); + Node* init_thread = make_load(NULL, adr_node, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); + Node *tst = Bool( CmpP( init_thread, cur_thread), BoolTest::eq); + IfNode* iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN); + set_control(IfTrue(iff)); + merge->set_req(1, IfFalse(iff)); + + Node* init_state_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_state_offset())); + adr_node = basic_plus_adr(kls, kls, init_state_offset); + // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler + // can generate code to load it as unsigned byte. + Node* init_state = make_load(NULL, adr_node, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); + Node* being_init = _gvn.intcon(InstanceKlass::being_initialized); + tst = Bool( CmpI( init_state, being_init), BoolTest::eq); + iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN); + set_control(IfTrue(iff)); + merge->set_req(2, IfFalse(iff)); + + PreserveJVMState pjvms(this); + record_for_igvn(merge); + set_control(merge); - Node* init_thread_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_thread_offset())); - Node* adr_node = basic_plus_adr(kls, kls, init_thread_offset); - Node* init_thread = make_load(NULL, adr_node, TypeRawPtr::BOTTOM, T_ADDRESS, MemNode::unordered); - Node *tst = Bool( CmpP( init_thread, cur_thread), BoolTest::eq); - IfNode* iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN); - set_control(IfTrue(iff)); - merge->set_req(1, IfFalse(iff)); - - Node* init_state_offset = _gvn.MakeConX(in_bytes(InstanceKlass::init_state_offset())); - adr_node = basic_plus_adr(kls, kls, init_state_offset); - // Use T_BOOLEAN for InstanceKlass::_init_state so the compiler - // can generate code to load it as unsigned byte. - Node* init_state = make_load(NULL, adr_node, TypeInt::UBYTE, T_BOOLEAN, MemNode::unordered); - Node* being_init = _gvn.intcon(InstanceKlass::being_initialized); - tst = Bool( CmpI( init_state, being_init), BoolTest::eq); - iff = create_and_map_if(control(), tst, PROB_ALWAYS, COUNT_UNKNOWN); - set_control(IfTrue(iff)); - merge->set_req(2, IfFalse(iff)); - - PreserveJVMState pjvms(this); - record_for_igvn(merge); - set_control(merge); - - uncommon_trap(Deoptimization::Reason_uninitialized, - Deoptimization::Action_reinterpret, - klass); + uncommon_trap(Deoptimization::Reason_uninitialized, + Deoptimization::Action_reinterpret, + klass); + } } @@ -283,18 +308,8 @@ assert(will_link, "_new: typeflow responsibility"); // Should initialize, or throw an InstantiationError? - if ((!klass->is_initialized() && !klass->is_being_initialized()) || - klass->is_abstract() || klass->is_interface() || - klass->name() == ciSymbol::java_lang_Class() || - iter().is_unresolved_klass()) { - uncommon_trap(Deoptimization::Reason_uninitialized, - Deoptimization::Action_reinterpret, - klass); - return; - } - if (klass->is_being_initialized()) { - emit_guard_for_new(klass); - } + emit_guard_for_new(klass); + if (stopped()) return; Node* kls = makecon(TypeKlassPtr::make(klass)); Node* obj = new_instance(kls); @@ -316,6 +331,63 @@ } } +//------------------------------do_defaultvalue--------------------------------- +void Parse::do_defaultvalue() { + bool will_link; + ciValueKlass* vk = iter().get_klass(will_link)->as_value_klass(); + assert(will_link, "defaultvalue: typeflow responsibility"); + + // Should initialize, or throw an InstantiationError? + emit_guard_for_new(vk); + if (stopped()) return; + + // Always scalarize default value because it's not NULL by definition + push(ValueTypeNode::make_default(_gvn, vk)); +} + +//------------------------------do_withfield------------------------------------ +void Parse::do_withfield() { + bool will_link; + ciField* field = iter().get_field(will_link); + assert(will_link, "withfield: typeflow responsibility"); + BasicType bt = field->layout_type(); + Node* val = type2size[bt] == 1 ? pop() : pop_pair(); + ciValueKlass* holder_klass = field->holder()->as_value_klass(); + Node* holder = pop(); + + if (!holder->is_ValueType()) { + // Null check and scalarize value type holder + inc_sp(2); + holder = null_check(holder); + dec_sp(2); + if (stopped()) return; + holder = ValueTypeNode::make_from_oop(this, holder, holder_klass); + } + if (!val->is_ValueType() && field->is_flattenable()) { + // Null check and scalarize value type field value + inc_sp(2); + val = null_check(val); + dec_sp(2); + if (stopped()) return; + val = ValueTypeNode::make_from_oop(this, val, gvn().type(val)->value_klass()); + } else if (val->is_ValueType() && !field->is_flattenable()) { + // Non-flattenable field should not be scalarized + val = ValueTypePtrNode::make_from_value_type(this, val->as_ValueType()); + } + + // Clone the value type node and set the new field value + ValueTypeNode* new_vt = holder->clone()->as_ValueType(); + new_vt->set_oop(_gvn.zerocon(T_VALUETYPE)); + gvn().set_type(new_vt, new_vt->bottom_type()); + new_vt->set_field_value_by_offset(field->offset(), val); + + if (holder_klass->is_scalarizable()) { + push(_gvn.transform(new_vt)); + } else { + push(new_vt->allocate(this)->get_oop()); + } +} + #ifndef PRODUCT //------------------------------dump_map_adr_mem------------------------------- // Debug dump of the mapping from address types to MergeMemNode indices.