--- old/src/hotspot/share/c1/c1_GraphBuilder.cpp 2019-03-11 14:25:06.694355977 +0100 +++ new/src/hotspot/share/c1/c1_GraphBuilder.cpp 2019-03-11 14:25:06.318355983 +0100 @@ -33,6 +33,7 @@ #include "ci/ciKlass.hpp" #include "ci/ciMemberName.hpp" #include "ci/ciUtilities.inline.hpp" +#include "ci/ciValueKlass.hpp" #include "compiler/compileBroker.hpp" #include "interpreter/bytecode.hpp" #include "jfr/jfrEvents.hpp" @@ -648,6 +649,17 @@ } } + // Record this newly allocated object + void new_instance(NewValueTypeInstance* object) { + int index = _newobjects.length(); + _newobjects.append(object); + if (_fields.at_grow(index, NULL) == NULL) { + _fields.at_put(index, new FieldBuffer()); + } else { + _fields.at(index)->kill(); + } + } + void store_value(Value value) { int index = _newobjects.find(value); if (index != -1) { @@ -979,7 +991,19 @@ (array->as_NewArray() && array->as_NewArray()->length() && array->as_NewArray()->length()->type()->is_constant())) { length = append(new ArrayLength(array, state_before)); } - push(as_ValueType(type), append(new LoadIndexed(array, index, length, type, state_before))); + + if (array->is_loaded_flattened_array()) { + ciType* array_type = array->declared_type(); + ciValueKlass* elem_klass = array_type->as_value_array_klass()->element_klass()->as_value_klass(); + NewValueTypeInstance* new_instance = new NewValueTypeInstance(elem_klass, state_before, false); + _memory->new_instance(new_instance); + apush(append_split(new_instance)); + LoadIndexed* load_indexed = new LoadIndexed(array, index, length, type, state_before); + load_indexed->set_vt(new_instance); + append(load_indexed); + } else { + push(as_ValueType(type), append(new LoadIndexed(array, index, length, type, state_before))); + } } @@ -1008,6 +1032,7 @@ } else if (type == T_BYTE) { check_boolean = true; } + StoreIndexed* result = new StoreIndexed(array, index, length, type, value, state_before, check_boolean); append(result); _memory->store_value(value); @@ -1619,15 +1644,57 @@ } } +void GraphBuilder::copy_value_content(ciValueKlass* vk, Value src, int src_off, Value dest, int dest_off, + ValueStack* state_before, bool needs_patching) { + for (int i = 0; i < vk->nof_nonstatic_fields(); i++) { + ciField* inner_field = vk->nonstatic_field_at(i); + assert(!inner_field->is_flattened(), "the iteration over nested fields is handled by the loop itself"); + int off = inner_field->offset() - vk->first_field_offset(); + LoadField* load = new LoadField(src, src_off + off, inner_field, false, state_before, needs_patching); + Value replacement = append(load); + StoreField* store = new StoreField(dest, dest_off + off, inner_field, replacement, false, state_before, needs_patching); + append(store); + } +} + void GraphBuilder::access_field(Bytecodes::Code code) { bool will_link; ciField* field = stream()->get_field(will_link); ciInstanceKlass* holder = field->holder(); BasicType field_type = field->type()->basic_type(); ValueType* type = as_ValueType(field_type); + + // Null check and deopt for getting static value field + ciValueKlass* value_klass = NULL; + Value default_value = NULL; + bool needs_deopt = false; + if (code == Bytecodes::_getstatic && !field->is_static_constant() && + field->layout_type() == T_VALUETYPE && field->is_flattenable()) { + value_klass = field->type()->as_value_klass(); + if (holder->is_loaded()) { + ciInstance* mirror = field->holder()->java_mirror(); + ciObject* val = mirror->field_value(field).as_object(); + if (val->is_null_object()) { + // This is a non-nullable static field, but it's not initialized. + // We need to do a null check, and replace it with the default value. + } else { + // No need to perform null check on this static field + value_klass = NULL; + } + } + if (value_klass != NULL) { + if (value_klass->is_loaded()) { + default_value = new Constant(new InstanceConstant(value_klass->default_value_instance())); + } else { + needs_deopt = true; + } + } + } + // call will_link again to determine if the field is valid. const bool needs_patching = !holder->is_loaded() || !field->will_link(method(), code) || + needs_deopt || PatchALot; ValueStack* state_before = NULL; @@ -1675,8 +1742,13 @@ if (state_before == NULL) { state_before = copy_state_for_exception(); } - push(type, append(new LoadField(append(obj), offset, field, true, - state_before, needs_patching))); + LoadField* load_field = new LoadField(append(obj), offset, field, true, + state_before, needs_patching, + value_klass, default_value); + if (field->layout_type() == T_VALUETYPE && field->is_flattenable()) { + load_field->set_never_null(true); + } + push(type, append(load_field)); } break; } @@ -1697,7 +1769,7 @@ Value constant = NULL; obj = apop(); ObjectType* obj_type = obj->type()->as_ObjectType(); - if (field->is_constant() && obj_type->is_constant() && !PatchALot) { + if (field->is_constant() && !field->is_flattened() && obj_type->is_constant() && !PatchALot) { ciObject* const_oop = obj_type->constant_value(); if (!const_oop->is_null_object() && const_oop->is_loaded()) { ciConstant field_value = field->constant_value_of(const_oop); @@ -1720,13 +1792,28 @@ if (state_before == NULL) { state_before = copy_state_for_exception(); } - LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching); - Value replacement = !needs_patching ? _memory->load(load) : load; - if (replacement != load) { - assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked"); - push(type, replacement); - } else { - push(type, append(load)); + + if (!field->is_flattened()) { + LoadField* load = new LoadField(obj, offset, field, false, state_before, needs_patching); + Value replacement = !needs_patching ? _memory->load(load) : load; + if (replacement != load) { + assert(replacement->is_linked() || !replacement->can_be_linked(), "should already by linked"); + push(type, replacement); + } else { + push(type, append(load)); + } + } else { // flattened field, not optimized solution: re-instantiate the flattened value + assert(field->type()->is_valuetype(), "Sanity check"); + ciValueKlass* value_klass = field->type()->as_value_klass(); + int flattening_offset = field->offset() - value_klass->first_field_offset(); + assert(field->type()->is_valuetype(), "Sanity check"); + scope()->set_wrote_final(); + scope()->set_wrote_fields(); + NewValueTypeInstance* new_instance = new NewValueTypeInstance(value_klass, state_before, false); + _memory->new_instance(new_instance); + apush(append_split(new_instance)); + copy_value_content(value_klass, obj, field->offset() , new_instance, value_klass->first_field_offset(), + state_before, needs_patching); } } break; @@ -1741,10 +1828,19 @@ Value mask = append(new Constant(new IntConstant(1))); val = append(new LogicOp(Bytecodes::_iand, val, mask)); } - StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching); - if (!needs_patching) store = _memory->store(store); - if (store != NULL) { - append(store); + + if (!field->is_flattened()) { + StoreField* store = new StoreField(obj, offset, field, val, false, state_before, needs_patching); + if (!needs_patching) store = _memory->store(store); + if (store != NULL) { + append(store); + } + } else { + assert(field->type()->is_valuetype(), "Sanity check"); + ciValueKlass* value_klass = field->type()->as_value_klass(); + int flattening_offset = field->offset() - value_klass->first_field_offset(); + copy_value_content(value_klass, val, value_klass->first_field_offset(), obj, field->offset(), + state_before, needs_patching); } break; } @@ -1754,6 +1850,69 @@ } } +// Baseline version of withfield, allocate every time +void GraphBuilder::withfield(int field_index) +{ + bool will_link; + ciField* field_modify = stream()->get_field(will_link); + ciInstanceKlass* holder = field_modify->holder(); + assert(holder->is_valuetype(), "must be a value klass"); + BasicType field_type = field_modify->type()->basic_type(); + ValueType* type = as_ValueType(field_type); + + // call will_link again to determine if the field is valid. + const bool needs_patching = !holder->is_loaded() || + !field_modify->will_link(method(), Bytecodes::_withfield) || + PatchALot; + + + scope()->set_wrote_final(); + scope()->set_wrote_fields(); + + const int offset = !needs_patching ? field_modify->offset() : -1; + Value val = pop(type); + Value obj = apop(); + + ValueStack* state_before = copy_state_for_exception(); + + NewValueTypeInstance* new_instance = new NewValueTypeInstance(holder->as_value_klass(), state_before, false); + _memory->new_instance(new_instance); + apush(append_split(new_instance)); + + for (int i = 0; i < holder->nof_nonstatic_fields(); i++) { + ciField* field = holder->nonstatic_field_at(i); + int off = field->offset(); + + if (field->offset() != offset) { + if (field->is_flattened()) { + assert(field->type()->is_valuetype(), "Sanity check"); + assert(field->type()->is_valuetype(), "Only value types can be flattened"); + ciValueKlass* vk = field->type()->as_value_klass(); + copy_value_content(vk, obj, off, new_instance, vk->first_field_offset(), state_before, needs_patching); + } else { + // Only load those fields who are not modified + LoadField* load = new LoadField(obj, off, field, false, state_before, needs_patching); + Value replacement = append(load); + StoreField* store = new StoreField(new_instance, off, field, replacement, false, state_before, needs_patching); + append(store); + } + } + } + + // Field to modify + if (field_modify->type()->basic_type() == T_BOOLEAN) { + Value mask = append(new Constant(new IntConstant(1))); + val = append(new LogicOp(Bytecodes::_iand, val, mask)); + } + if (field_modify->is_flattened()) { + assert(field_modify->type()->is_valuetype(), "Only value types can be flattened"); + ciValueKlass* vk = field_modify->type()->as_value_klass(); + copy_value_content(vk, val, vk->first_field_offset(), new_instance, field_modify->offset(), state_before, needs_patching); + } else { + StoreField* store = new StoreField(new_instance, offset, field_modify, val, false, state_before, needs_patching); + append(store); + } +} Dependencies* GraphBuilder::dependency_recorder() const { assert(DeoptC1, "need debug information"); @@ -2111,7 +2270,8 @@ } } - Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target, state_before); + Invoke* result = new Invoke(code, result_type, recv, args, vtable_index, target, state_before, + declared_signature->returns_never_null()); // push result append_split(result); @@ -2133,11 +2293,22 @@ bool will_link; ciKlass* klass = stream()->get_klass(will_link); assert(klass->is_instance_klass(), "must be an instance klass"); + assert(!klass->is_valuetype(), "must not be a value klass"); NewInstance* new_instance = new NewInstance(klass->as_instance_klass(), state_before, stream()->is_unresolved_klass()); _memory->new_instance(new_instance); apush(append_split(new_instance)); } +void GraphBuilder::new_value_type_instance(int klass_index) { + ValueStack* state_before = copy_state_exhandling(); + bool will_link; + ciKlass* klass = stream()->get_klass(will_link); + assert(klass->is_valuetype(), "must be a value klass"); + NewValueTypeInstance* new_instance = new NewValueTypeInstance(klass->as_value_klass(), + state_before, stream()->is_unresolved_klass()); + _memory->new_instance(new_instance); + apush(append_split(new_instance)); +} void GraphBuilder::new_type_array() { ValueStack* state_before = copy_state_exhandling(); @@ -2150,6 +2321,9 @@ ciKlass* klass = stream()->get_klass(will_link); ValueStack* state_before = !klass->is_loaded() || PatchALot ? copy_state_before() : copy_state_exhandling(); NewArray* n = new NewObjectArray(klass, ipop(), state_before); + if (stream()->is_klass_never_null()) { + n->set_never_null(true); + } apush(append_split(n)); } @@ -2174,8 +2348,9 @@ void GraphBuilder::check_cast(int klass_index) { bool will_link; ciKlass* klass = stream()->get_klass(will_link); + bool never_null = stream()->is_klass_never_null(); ValueStack* state_before = !klass->is_loaded() || PatchALot ? copy_state_before() : copy_state_for_exception(); - CheckCast* c = new CheckCast(klass, apop(), state_before); + CheckCast* c = new CheckCast(klass, apop(), state_before, never_null); apush(append_split(c)); c->set_direct_compare(direct_compare(klass)); @@ -2214,9 +2389,28 @@ void GraphBuilder::monitorenter(Value x, int bci) { + bool maybe_valuetype = false; + if (bci == InvocationEntryBci) { + // Called by GraphBuilder::inline_sync_entry. +#ifdef ASSERT + ciType* obj_type = x->declared_type(); + assert(obj_type == NULL || !obj_type->is_valuetype(), "valuetypes cannot have synchronized methods"); +#endif + } else { + // We are compiling a monitorenter bytecode + if (EnableValhalla) { + ciType* obj_type = x->declared_type(); + if (obj_type == NULL || obj_type->is_valuetype() || obj_type->as_klass()->is_java_lang_Object()) { + // If we're (possibly) locking on a valuetype, check for markOopDesc::always_locked_pattern + // and throw IMSE. (obj_type is null for Phi nodes, so let's just be conservative). + maybe_valuetype = true; + } + } + } + // save state before locking in case of deoptimization after a NullPointerException ValueStack* state_before = copy_state_for_exception_with_bci(bci); - append_with_bci(new MonitorEnter(x, state()->lock(x), state_before), bci); + append_with_bci(new MonitorEnter(x, state()->lock(x), state_before, maybe_valuetype), bci); kill_all(); } @@ -2872,6 +3066,8 @@ case Bytecodes::_ifnonnull : if_null(objectType, If::neq); break; case Bytecodes::_goto_w : _goto(s.cur_bci(), s.get_far_dest()); break; case Bytecodes::_jsr_w : jsr(s.get_far_dest()); break; + case Bytecodes::_defaultvalue : new_value_type_instance(s.get_index_u2()); break; + case Bytecodes::_withfield : withfield(s.get_index_u2()); break; case Bytecodes::_breakpoint : BAILOUT_("concurrent setting of breakpoint", NULL); default : ShouldNotReachHere(); break; } @@ -3157,7 +3353,8 @@ int idx = 0; if (!method()->is_static()) { // we should always see the receiver - state->store_local(idx, new Local(method()->holder(), objectType, idx, true)); + state->store_local(idx, new Local(method()->holder(), objectType, idx, + /*receiver*/ true, /*never_null*/ method()->holder()->is_value_array_klass())); idx = 1; } @@ -3167,9 +3364,9 @@ ciType* type = sig->type_at(i); BasicType basic_type = type->basic_type(); // don't allow T_ARRAY to propagate into locals types - if (basic_type == T_ARRAY) basic_type = T_OBJECT; + if (basic_type == T_ARRAY || basic_type == T_VALUETYPE) basic_type = T_OBJECT; ValueType* vt = as_ValueType(basic_type); - state->store_local(idx, new Local(type, vt, idx, false)); + state->store_local(idx, new Local(type, vt, idx, false, sig->is_never_null_at(i))); idx += type->size(); }