--- old/src/hotspot/share/c1/c1_GraphBuilder.cpp 2019-03-06 21:28:04.702453915 -0800 +++ new/src/hotspot/share/c1/c1_GraphBuilder.cpp 2019-03-06 21:28:04.442444781 -0800 @@ -1664,37 +1664,9 @@ 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; @@ -1743,9 +1715,8 @@ state_before = copy_state_for_exception(); } 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()) { + state_before, needs_patching); + if (field->is_never_null()) { load_field->set_never_null(true); } push(type, append(load_field)); --- old/src/hotspot/share/c1/c1_Instruction.hpp 2019-03-06 21:28:05.370477383 -0800 +++ new/src/hotspot/share/c1/c1_Instruction.hpp 2019-03-06 21:28:05.130468951 -0800 @@ -847,24 +847,18 @@ LEAF(LoadField, AccessField) - ciValueKlass* _value_klass; - Value _default_value; public: // creation LoadField(Value obj, int offset, ciField* field, bool is_static, ValueStack* state_before, bool needs_patching, ciValueKlass* value_klass = NULL, Value default_value = NULL ) : AccessField(obj, offset, field, is_static, state_before, needs_patching) - , _value_klass(value_klass), _default_value(default_value) {} ciType* declared_type() const; // generic HASHING2(LoadField, !needs_patching() && !field()->is_volatile(), obj()->subst(), offset()) // cannot be eliminated if needs patching or if volatile - - ciValueKlass* value_klass() const { return _value_klass;} - Value default_value() const { return _default_value; } }; --- old/src/hotspot/share/c1/c1_LIRGenerator.cpp 2019-03-06 21:28:06.094502819 -0800 +++ new/src/hotspot/share/c1/c1_LIRGenerator.cpp 2019-03-06 21:28:05.838493825 -0800 @@ -1543,7 +1543,7 @@ if (x->needs_null_check() && (needs_patching || MacroAssembler::needs_explicit_null_check(x->offset()))) { - if (needs_patching && x->field()->signature()->starts_with("Q", 1)) { + if (needs_patching && x->field()->is_never_null()) { // We are storing a field of type "QT;", but T is not yet loaded, so we don't // know whether this field is flattened or not. Let's deoptimize and recompile. CodeStub* stub = new DeoptimizeStub(new CodeEmitInfo(info), @@ -1868,6 +1868,79 @@ return _barrier_set->resolve(this, decorators, obj); } +Value LIRGenerator::non_nullable_load_field_prolog(LoadField* x, CodeEmitInfo* info) { + ciField* field = x->field(); + ciInstanceKlass* holder = field->holder(); + Value default_value = NULL; + + // Unloaded "QV;" klasses are represented by a ciInstanceKlass + bool field_type_unloaded = field->type()->is_instance_klass() && !field->type()->as_instance_klass()->is_loaded(); + + // Check for edge cases (1), (2) and (3) for getstatic and getfield + bool deopt = false; + bool need_default = false; + if (field->is_static()) { + // (1) holder is unloaded -- no problem: it will be loaded by patching, and field offset will be determined. + + if (field_type_unloaded) { + // (2) field type is unloaded -- problem: we don't know what the default value is. Let's deopt. + // FIXME: consider getting the default value in patching code. + deopt = true; + } else { + need_default = true; + } + + // (3) field is not flattenable -- we don't care: static fields are never flattened. + } else { + if (!holder->is_loaded()) { + // (1) holder is unloaded -- problem: we needed the field offset back in GraphBuilder::access_field() + // FIXME: consider getting field offset in patching code (but only if the field + // type was loaded at compilation time). + deopt = true; + } else if (field_type_unloaded) { + // (2) field type is unloaded -- problem: we don't whether it's flattened or not. Let's deopt + deopt = true; + } else if (!field->is_flattened()) { + // (3) field is not flattenable -- need default value in cases of uninitialized field + need_default = true; + } + } + + assert(!(deopt && need_default), "cannot both be true"); + + if (deopt) { + assert(x->needs_patching(), "must be"); + assert(info != NULL, "must be"); + CodeStub* stub = new DeoptimizeStub(new CodeEmitInfo(info), + Deoptimization::Reason_unloaded, + Deoptimization::Action_make_not_entrant); + __ branch(lir_cond_always, T_ILLEGAL, stub); + } else if (need_default) { + assert(!field_type_unloaded, "must be"); + assert(field->type()->is_valuetype(), "must be"); + ciValueKlass* value_klass = field->type()->as_value_klass(); + assert(value_klass->is_loaded(), "must be"); + + if (field->is_static() && 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 + need_default = false; + } + } + + if (need_default) { + default_value = new Constant(new InstanceConstant(value_klass->default_value_instance())); + } + } + + return default_value; +} + void LIRGenerator::do_LoadField(LoadField* x) { bool needs_patching = x->needs_patching(); bool is_volatile = x->field()->is_volatile(); @@ -1897,37 +1970,25 @@ } #endif + Value default_value = NULL; + if (x->field()->is_never_null()) { + default_value = non_nullable_load_field_prolog(x, info); + } + bool stress_deopt = StressLoopInvariantCodeMotion && info && info->deoptimize_on_exception(); if (x->needs_null_check() && (needs_patching || MacroAssembler::needs_explicit_null_check(x->offset()) || stress_deopt)) { - if (needs_patching && x->field()->signature()->starts_with("Q", 1)) { - // We are loading a field of type "QT;", but class T is not yet loaded. We don't know - // whether this field is flattened or not. Let's deoptimize and recompile. - CodeStub* stub = new DeoptimizeStub(new CodeEmitInfo(info), - Deoptimization::Reason_unloaded, - Deoptimization::Action_make_not_entrant); - __ branch(lir_cond_always, T_ILLEGAL, stub); - } else { - LIR_Opr obj = object.result(); - if (stress_deopt) { - obj = new_register(T_OBJECT); - __ move(LIR_OprFact::oopConst(NULL), obj); - } - // Emit an explicit null check because the offset is too large. - // If the class is not loaded and the object is NULL, we need to deoptimize to throw a - // NoClassDefFoundError in the interpreter instead of an implicit NPE from compiled code. - __ null_check(obj, new CodeEmitInfo(info), /* deoptimize */ needs_patching); - } - } else if (x->value_klass() != NULL && x->default_value() == NULL) { - assert(x->is_static() && !x->value_klass()->is_loaded(), "must be"); - assert(needs_patching, "must be"); - // The value klass was not loaded so we don't know what its default value should be - CodeStub* stub = new DeoptimizeStub(new CodeEmitInfo(info), - Deoptimization::Reason_unloaded, - Deoptimization::Action_make_not_entrant); - __ branch(lir_cond_always, T_ILLEGAL, stub); + LIR_Opr obj = object.result(); + if (stress_deopt) { + obj = new_register(T_OBJECT); + __ move(LIR_OprFact::oopConst(NULL), obj); + } + // Emit an explicit null check because the offset is too large. + // If the class is not loaded and the object is NULL, we need to deoptimize to throw a + // NoClassDefFoundError in the interpreter instead of an implicit NPE from compiled code. + __ null_check(obj, new CodeEmitInfo(info), /* deoptimize */ needs_patching); } DecoratorSet decorators = IN_HEAP; @@ -1943,14 +2004,14 @@ object, LIR_OprFact::intConst(x->offset()), result, info ? new CodeEmitInfo(info) : NULL, info); - if (x->value_klass() != NULL && x->default_value() != NULL) { + if (default_value != NULL) { LabelObj* L_end = new LabelObj(); __ cmp(lir_cond_notEqual, result, LIR_OprFact::oopConst(NULL)); __ branch(lir_cond_notEqual, T_OBJECT, L_end->label()); - LIRItem default_value(x->default_value(), this); - default_value.load_item(); - __ move(default_value.result(), result); + LIRItem dv(default_value, this); + dv.load_item(); + __ move(dv.result(), result); __ branch_destination(L_end->label()); } --- old/src/hotspot/share/c1/c1_LIRGenerator.hpp 2019-03-06 21:28:06.818528254 -0800 +++ new/src/hotspot/share/c1/c1_LIRGenerator.hpp 2019-03-06 21:28:06.562519261 -0800 @@ -266,6 +266,7 @@ void do_update_CRC32C(Intrinsic* x); void do_vectorizedMismatch(Intrinsic* x); + Value non_nullable_load_field_prolog(LoadField* x, CodeEmitInfo* info); void access_flattened_array(bool is_load, LIRItem& array, LIRItem& index, LIRItem& obj_item); bool needs_flattened_array_store_check(StoreIndexed* x); void check_flattened_array(LIRItem& array, CodeStub* slow_path); --- old/src/hotspot/share/ci/ciField.hpp 2019-03-06 21:28:07.454550599 -0800 +++ new/src/hotspot/share/ci/ciField.hpp 2019-03-06 21:28:07.206541886 -0800 @@ -202,6 +202,12 @@ klass_name == ciSymbol::java_lang_Long_LongCache())); } + bool is_never_null() const { + // Cannot use (type()->basic_type() == T_VALUETYPE) -- if the class is not loaded, + // type() is an unloaded ciInstanceKlass! + return signature()->char_at(0) == 'Q'; + } + // Debugging output void print(); void print_name_on(outputStream* st); --- old/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedValueTypeField.java 2019-03-06 21:28:08.086572802 -0800 +++ new/test/hotspot/jtreg/compiler/valhalla/valuetypes/TestUnloadedValueTypeField.java 2019-03-06 21:28:07.834563949 -0800 @@ -27,24 +27,33 @@ * @summary Test the handling of fields of unloaded value classes. * @compile -XDallowWithFieldOperator hack/GetUnresolvedValueFieldWrongSignature.java * @compile -XDallowWithFieldOperator TestUnloadedValueTypeField.java - * @run main/othervm -XX:+EnableValhalla -Xcomp + * @run main/othervm -XX:+EnableValhalla -Xcomp -XX:+Inline * -XX:CompileCommand=compileonly,TestUnloadedValueTypeField::test1 + * -XX:CompileCommand=print,TestUnloadedValueTypeField::test1 * -XX:CompileCommand=compileonly,TestUnloadedValueTypeField::test2 * -XX:CompileCommand=compileonly,GetUnresolvedValueFieldWrongSignature::test3 * -XX:CompileCommand=compileonly,TestUnloadedValueTypeField::test4 * -XX:CompileCommand=compileonly,TestUnloadedValueTypeField::test5 + * -XX:CompileCommand=compileonly,TestUnloadedValueTypeField::test11 + * -XX:CompileCommand=compileonly,TestUnloadedValueTypeField::test12 * TestUnloadedValueTypeField */ import jdk.test.lib.Asserts; public class TestUnloadedValueTypeField { + static final int WARMUP_LOOPS = 10000; static public void main(String[] args) { + // instance fields test1_verifier(); test2_verifier(); test3_verifier(); test4_verifier(); test5_verifier(); + + // static fields + test11_verifier(); + test12_verifier(); } // Test case 1: @@ -85,13 +94,13 @@ } static void test1_verifier() { - for (int i=0; i<10000; i++) { + for (int i=0; i