changeset 53563:5e2f5c15f970 lworld

8216327: [lworld][c1] checkcast for value type should throw NullPointerException with null operand Reviewed-by: thartmann
author iklam
date Wed, 09 Jan 2019 17:03:58 -0800
parents 530ea41023b2
children ca37a22640e7
files src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp src/hotspot/share/c1/c1_Canonicalizer.cpp src/hotspot/share/c1/c1_GraphBuilder.cpp src/hotspot/share/c1/c1_Instruction.hpp src/hotspot/share/c1/c1_LIR.cpp src/hotspot/share/c1/c1_LIR.hpp src/hotspot/share/ci/ciEnv.cpp src/hotspot/share/ci/ciEnv.hpp src/hotspot/share/ci/ciStreams.cpp src/hotspot/share/ci/ciStreams.hpp src/hotspot/share/ci/ciTypeFlow.cpp src/hotspot/share/opto/parseHelper.cpp
diffstat 13 files changed, 58 insertions(+), 40 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/cpu/x86/c1_LIRAssembler_x86.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -1679,20 +1679,22 @@
 
   assert_different_registers(obj, k_RInfo, klass_RInfo);
 
-  __ cmpptr(obj, (int32_t)NULL_WORD);
-  if (op->should_profile()) {
-    Label not_null;
-    __ jccb(Assembler::notEqual, not_null);
-    // Object is null; update MDO and exit
-    Register mdo  = klass_RInfo;
-    __ mov_metadata(mdo, md->constant_encoding());
-    Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset()));
-    int header_bits = BitData::null_seen_byte_constant();
-    __ orb(data_addr, header_bits);
-    __ jmp(*obj_is_null);
-    __ bind(not_null);
-  } else {
-    __ jcc(Assembler::equal, *obj_is_null);
+  if (op->need_null_check()) {
+    __ cmpptr(obj, (int32_t)NULL_WORD);
+    if (op->should_profile()) {
+      Label not_null;
+      __ jccb(Assembler::notEqual, not_null);
+      // Object is null; update MDO and exit
+      Register mdo  = klass_RInfo;
+      __ mov_metadata(mdo, md->constant_encoding());
+      Address data_addr(mdo, md->byte_offset_of_slot(data, DataLayout::flags_offset()));
+      int header_bits = BitData::null_seen_byte_constant();
+      __ orb(data_addr, header_bits);
+      __ jmp(*obj_is_null);
+      __ bind(not_null);
+    } else {
+      __ jcc(Assembler::equal, *obj_is_null);
+    }
   }
 
   if (!k->is_loaded()) {
--- a/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/cpu/x86/c1_LIRGenerator_x86.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -1373,6 +1373,10 @@
       (x->needs_exception_state() ? state_for(x) :
                                     state_for(x, x->state_before(), true /*ignore_xhandler*/));
 
+  if (x->is_never_null()) {
+    __ null_check(obj.result(), new CodeEmitInfo(info_for_exception));
+  }
+
   CodeStub* stub;
   if (x->is_incompatible_class_change_check()) {
     assert(patching_info == NULL, "can't patch this");
@@ -1391,7 +1395,7 @@
   __ checkcast(reg, obj.result(), x->klass(),
                new_register(objectType), new_register(objectType), tmp3,
                x->direct_compare(), info_for_exception, patching_info, stub,
-               x->profiled_method(), x->profiled_bci());
+               x->profiled_method(), x->profiled_bci(), x->is_never_null());
 }
 
 
--- a/src/hotspot/share/c1/c1_Canonicalizer.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/c1/c1_Canonicalizer.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -649,7 +649,8 @@
 void Canonicalizer::do_NewObjectArray (NewObjectArray*  x) {}
 void Canonicalizer::do_NewMultiArray  (NewMultiArray*   x) {}
 void Canonicalizer::do_CheckCast      (CheckCast*       x) {
-  if (x->klass()->is_loaded()) {
+  if (x->klass()->is_loaded() && !x->is_never_null()) {
+    // Don't canonicalize for non-nullable types -- we need to throw NPE.
     Value obj = x->obj();
     ciType* klass = obj->exact_type();
     if (klass == NULL) {
--- a/src/hotspot/share/c1/c1_GraphBuilder.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/c1/c1_GraphBuilder.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -2333,8 +2333,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));
 
--- a/src/hotspot/share/c1/c1_Instruction.hpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/c1/c1_Instruction.hpp	Wed Jan 09 17:03:58 2019 -0800
@@ -1502,10 +1502,11 @@
 
 
 LEAF(CheckCast, TypeCheck)
+  bool _is_never_null;
  public:
   // creation
-  CheckCast(ciKlass* klass, Value obj, ValueStack* state_before)
-  : TypeCheck(klass, obj, objectType, state_before) {}
+  CheckCast(ciKlass* klass, Value obj, ValueStack* state_before, bool never_null = false)
+  : TypeCheck(klass, obj, objectType, state_before), _is_never_null(never_null) {}
 
   void set_incompatible_class_change_check() {
     set_flag(ThrowIncompatibleClassChangeErrorFlag, true);
@@ -1519,6 +1520,9 @@
   bool is_invokespecial_receiver_check() const {
     return check_flag(InvokeSpecialReceiverCheckFlag);
   }
+  bool is_never_null() const {
+    return _is_never_null;
+  }
 
   virtual bool needs_exception_state() const {
     return !is_invokespecial_receiver_check();
--- a/src/hotspot/share/c1/c1_LIR.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/c1/c1_LIR.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -313,7 +313,7 @@
 LIR_OpTypeCheck::LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass,
                                  LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3,
                                  bool fast_check, CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch,
-                                 CodeStub* stub)
+                                 CodeStub* stub, bool need_null_check)
 
   : LIR_Op(code, result, NULL)
   , _object(object)
@@ -329,6 +329,7 @@
   , _profiled_method(NULL)
   , _profiled_bci(-1)
   , _should_profile(false)
+  , _need_null_check(need_null_check)
 {
   if (code == lir_checkcast) {
     assert(info_for_exception != NULL, "checkcast throws exceptions");
@@ -356,6 +357,7 @@
   , _profiled_method(NULL)
   , _profiled_bci(-1)
   , _should_profile(false)
+  , _need_null_check(true)
 {
   if (code == lir_store_check) {
     _stub = new ArrayStoreExceptionStub(object, info_for_exception);
@@ -1389,7 +1391,10 @@
 void LIR_List::checkcast (LIR_Opr result, LIR_Opr object, ciKlass* klass,
                           LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check,
                           CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub,
-                          ciMethod* profiled_method, int profiled_bci) {
+                          ciMethod* profiled_method, int profiled_bci, bool is_never_null) {
+  // If klass is non-nullable,  LIRGenerator::do_CheckCast has already performed null-check
+  // on the object.
+  bool need_null_check = !is_never_null;
   LIR_OpTypeCheck* c = new LIR_OpTypeCheck(lir_checkcast, result, object, klass,
                                            tmp1, tmp2, tmp3, fast_check, info_for_exception, info_for_patch, stub);
   if (profiled_method != NULL) {
--- a/src/hotspot/share/c1/c1_LIR.hpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/c1/c1_LIR.hpp	Wed Jan 09 17:03:58 2019 -0800
@@ -1562,11 +1562,12 @@
   ciMethod*     _profiled_method;
   int           _profiled_bci;
   bool          _should_profile;
+  bool          _need_null_check;
 
 public:
   LIR_OpTypeCheck(LIR_Code code, LIR_Opr result, LIR_Opr object, ciKlass* klass,
                   LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check,
-                  CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub);
+                  CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub, bool need_null_check = true);
   LIR_OpTypeCheck(LIR_Code code, LIR_Opr object, LIR_Opr array,
                   LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, CodeEmitInfo* info_for_exception);
 
@@ -1588,7 +1589,7 @@
   ciMethod* profiled_method() const              { return _profiled_method;   }
   int       profiled_bci() const                 { return _profiled_bci;      }
   bool      should_profile() const               { return _should_profile;    }
-
+  bool      need_null_check() const              { return _need_null_check;   }
   virtual bool is_patching() { return _info_for_patch != NULL; }
   virtual void emit_code(LIR_Assembler* masm);
   virtual LIR_OpTypeCheck* as_OpTypeCheck() { return this; }
@@ -2257,7 +2258,7 @@
   void checkcast (LIR_Opr result, LIR_Opr object, ciKlass* klass,
                   LIR_Opr tmp1, LIR_Opr tmp2, LIR_Opr tmp3, bool fast_check,
                   CodeEmitInfo* info_for_exception, CodeEmitInfo* info_for_patch, CodeStub* stub,
-                  ciMethod* profiled_method, int profiled_bci);
+                  ciMethod* profiled_method, int profiled_bci, bool is_never_null);
   // MethodData* profiling
   void profile_call(ciMethod* method, int bci, ciMethod* callee, LIR_Opr mdo, LIR_Opr recv, LIR_Opr t1, ciKlass* cha_klass) {
     append(new LIR_OpProfileCall(method, bci, callee, mdo, recv, t1, cha_klass));
--- a/src/hotspot/share/ci/ciEnv.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/ci/ciEnv.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -607,20 +607,20 @@
 }
 
 // ------------------------------------------------------------------
-// ciEnv::get_never_null_impl
+// ciEnv::is_klass_never_null_impl
 //
-// Implementation of get_never_null.
-bool ciEnv::get_never_null_impl(const constantPoolHandle& cpool, int index) {
+// Implementation of is_klass_never_null.
+bool ciEnv::is_klass_never_null_impl(const constantPoolHandle& cpool, int index) {
   Symbol* klass_name = cpool->klass_name_at(index);
   return klass_name->is_Q_signature();
 }
 
 // ------------------------------------------------------------------
-// ciEnv::get_never_null
+// ciEnv::is_klass_never_null
 //
 // Get information about nullability from the constant pool.
-bool ciEnv::get_never_null(const constantPoolHandle& cpool, int index) {
-  GUARDED_VM_ENTRY(return get_never_null_impl(cpool, index);)
+bool ciEnv::is_klass_never_null(const constantPoolHandle& cpool, int index) {
+  GUARDED_VM_ENTRY(return is_klass_never_null_impl(cpool, index);)
 }
 
 // ------------------------------------------------------------------
--- a/src/hotspot/share/ci/ciEnv.hpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/ci/ciEnv.hpp	Wed Jan 09 17:03:58 2019 -0800
@@ -132,8 +132,8 @@
   ciMethod*  get_method_by_index(const constantPoolHandle& cpool,
                                  int method_index, Bytecodes::Code bc,
                                  ciInstanceKlass* loading_klass);
-  bool       get_never_null(const constantPoolHandle& cpool,
-                            int klass_index);
+  bool       is_klass_never_null(const constantPoolHandle& cpool,
+                                 int klass_index);
 
   // Implementation methods for loading and constant pool access.
   ciKlass* get_klass_by_name_impl(ciKlass* accessing_klass,
@@ -153,8 +153,8 @@
   ciMethod*  get_method_by_index_impl(const constantPoolHandle& cpool,
                                       int method_index, Bytecodes::Code bc,
                                       ciInstanceKlass* loading_klass);
-  bool       get_never_null_impl(const constantPoolHandle& cpool,
-                                 int klass_index);
+  bool       is_klass_never_null_impl(const constantPoolHandle& cpool,
+                                      int klass_index);
 
   // Helper methods
   bool       check_klass_accessibility(ciKlass* accessing_klass,
--- a/src/hotspot/share/ci/ciStreams.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/ci/ciStreams.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -192,13 +192,13 @@
 }
 
 // ------------------------------------------------------------------
-// ciBytecodeStream::get_never_null
+// ciBytecodeStream::is_klass_never_null
 //
 // Get information about nullability from the constant pool.
-bool ciBytecodeStream::get_never_null() const {
+bool ciBytecodeStream::is_klass_never_null() const {
   VM_ENTRY_MARK;
   constantPoolHandle cpool(_method->get_Method()->constants());
-  return CURRENT_ENV->get_never_null(cpool, get_klass_index());
+  return CURRENT_ENV->is_klass_never_null(cpool, get_klass_index());
 }
 
 // ------------------------------------------------------------------
--- a/src/hotspot/share/ci/ciStreams.hpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/ci/ciStreams.hpp	Wed Jan 09 17:03:58 2019 -0800
@@ -235,7 +235,7 @@
   // or checkcast, get the referenced klass.
   ciKlass* get_klass(bool& will_link);
   int get_klass_index() const;
-  bool get_never_null() const;
+  bool is_klass_never_null() const;
 
   // If this bytecode is one of the ldc variants, get the referenced
   // constant.  Do not attempt to resolve it, since that would require
--- a/src/hotspot/share/ci/ciTypeFlow.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/ci/ciTypeFlow.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -629,7 +629,7 @@
     do_null_assert(klass);
   } else {
     pop_object();
-    if (str->get_never_null()) {
+    if (str->is_klass_never_null()) {
       // Casting to a Q-Type contains a NULL check
       assert(klass->is_valuetype(), "must be a value type");
       push(outer()->mark_as_never_null(klass));
--- a/src/hotspot/share/opto/parseHelper.cpp	Tue Jan 08 17:09:51 2019 +0100
+++ b/src/hotspot/share/opto/parseHelper.cpp	Wed Jan 09 17:03:58 2019 -0800
@@ -68,7 +68,7 @@
 void Parse::do_checkcast() {
   bool will_link;
   ciKlass* klass = iter().get_klass(will_link);
-  bool never_null = iter().get_never_null();
+  bool never_null = iter().is_klass_never_null();
 
   Node *obj = peek();