diff a/src/hotspot/share/opto/parseHelper.cpp b/src/hotspot/share/opto/parseHelper.cpp
--- a/src/hotspot/share/opto/parseHelper.cpp
+++ b/src/hotspot/share/opto/parseHelper.cpp
@@ -21,14 +21,18 @@
  * questions.
  *
  */
 
 #include "precompiled.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciSymbols.hpp"
 #include "compiler/compileLog.hpp"
+#include "oops/flatArrayKlass.hpp"
 #include "oops/objArrayKlass.hpp"
 #include "opto/addnode.hpp"
+#include "opto/castnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/memnode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
@@ -63,11 +67,10 @@
 //=============================================================================
 //------------------------------do_checkcast-----------------------------------
 void Parse::do_checkcast() {
   bool will_link;
   ciKlass* klass = iter().get_klass(will_link);
-
   Node *obj = peek();
 
   // Throw uncommon trap if class is not loaded or the value we are casting
   // _from_ is not loaded, and value is not null.  If the value _is_ null,
   // then the checkcast does nothing.
@@ -136,44 +139,63 @@
   push(res);
 }
 
 //------------------------------array_store_check------------------------------
 // pull array from stack and check that the store is valid
-void Parse::array_store_check() {
-
+Node* Parse::array_store_check(Node*& adr, const Type*& elemtype) {
   // Shorthand access to array store elements without popping them.
   Node *obj = peek(0);
   Node *idx = peek(1);
   Node *ary = peek(2);
 
   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;
+    if (_gvn.type(ary)->is_aryptr()->is_null_free()) {
+      null_check(obj);
+    }
+    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, nullptr, 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();
+  const TypeKlassPtr* tak = _gvn.type(array_klass)->is_klassptr();
 
   // The type of array_klass is usually INexact array-of-oop.  Heroically
   // cast array_klass to EXACT array and uncommon-trap if the cast fails.
   // Make constant out of the inexact array klass, but use it only if the cast
   // succeeds.
   bool always_see_exact_class = false;
-  if (MonomorphicArrayCheck
-      && !too_many_traps(Deoptimization::Reason_array_check)
-      && !tak->klass_is_exact()
-      && tak != TypeInstKlassPtr::OBJECT) {
-      // Regarding the fourth condition in the if-statement from above:
-      //
+  if (MonomorphicArrayCheck && !tak->klass_is_exact()) {
+    // Make a constant out of the inexact array klass
+    const TypeKlassPtr* extak = nullptr;
+    const TypeOopPtr* ary_t = _gvn.type(ary)->is_oopptr();
+    ciKlass* ary_spec = ary_t->speculative_type();
+    Deoptimization::DeoptReason reason = Deoptimization::Reason_none;
+    // Try to cast the array to an exact type from profile data. First
+    // check the speculative type.
+    if (ary_spec != nullptr && !too_many_traps(Deoptimization::Reason_speculate_class_check)) {
+      extak = TypeKlassPtr::make(ary_spec);
+      reason = Deoptimization::Reason_speculate_class_check;
+    } else if (UseArrayLoadStoreProfile) {
+      // No speculative type: check profile data at this bci.
+      reason = Deoptimization::Reason_class_check;
+      if (!too_many_traps(reason)) {
+        ciKlass* array_type = nullptr;
+        ciKlass* element_type = nullptr;
+        ProfilePtrKind element_ptr = ProfileMaybeNull;
+        bool flat_array = true;
+        bool null_free_array = true;
+        method()->array_access_profiled_type(bci(), array_type, element_type, element_ptr, flat_array, null_free_array);
+        if (array_type != nullptr) {
+          extak = TypeKlassPtr::make(array_type);
+        }
+      }
+    } else if (!too_many_traps(Deoptimization::Reason_array_check) && tak != TypeInstKlassPtr::OBJECT) {
       // If the compiler has determined that the type of array 'ary' (represented
       // by 'array_klass') is java/lang/Object, the compiler must not assume that
       // the array 'ary' is monomorphic.
       //
       // If 'ary' were of type java/lang/Object, this arraystore would have to fail,
@@ -188,56 +210,69 @@
       // java/lang/Object is the superclass of all arrays, but it is represented by the VM
       // as an InstanceKlass. The checks generated by gen_checkcast() (see below) expect
       // 'array_klass' to be ObjArrayKlass, which can result in invalid memory accesses.
       //
       // See issue JDK-8057622 for details.
-
-    always_see_exact_class = true;
-    // (If no MDO at all, hope for the best, until a trap actually occurs.)
-
-    // Make a constant out of the inexact array klass
-    const TypeKlassPtr *extak = tak->cast_to_exactness(true);
-
-    if (extak->exact_klass(true) != nullptr) {
+      extak = tak->cast_to_exactness(true);
+      reason = Deoptimization::Reason_array_check;
+    }
+    if (extak != nullptr && extak->exact_klass(true) != nullptr) {
       Node* con = makecon(extak);
-      Node* cmp = _gvn.transform(new CmpPNode( array_klass, con ));
-      Node* bol = _gvn.transform(new BoolNode( cmp, BoolTest::eq ));
-      Node* ctrl= control();
-      { BuildCutout unless(this, bol, PROB_MAX);
-        uncommon_trap(Deoptimization::Reason_array_check,
-                      Deoptimization::Action_maybe_recompile,
-                      extak->exact_klass());
-      }
-      if (stopped()) {          // MUST uncommon-trap?
-        set_control(ctrl);      // Then Don't Do It, just fall into the normal checking
-      } else {                  // Cast array klass to exactness:
-        // Use the exact constant value we know it is.
-        replace_in_map(array_klass,con);
+      Node* cmp = _gvn.transform(new CmpPNode(array_klass, con));
+      Node* bol = _gvn.transform(new BoolNode(cmp, BoolTest::eq));
+      // Only do it if the check does not always pass/fail
+      if (!bol->is_Con()) {
+        always_see_exact_class = true;
+        { BuildCutout unless(this, bol, PROB_MAX);
+          uncommon_trap(reason,
+                        Deoptimization::Action_maybe_recompile,
+                        extak->exact_klass());
+        }
+        // Cast array klass to exactness
+        replace_in_map(array_klass, con);
+        array_klass = con;
+        Node* cast = _gvn.transform(new CheckCastPPNode(control(), ary, extak->as_instance_type()));
+        replace_in_map(ary, cast);
+        ary = cast;
+
+        // Recompute element type and address
+        const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr();
+        elemtype = arytype->elem();
+        adr = array_element_address(ary, idx, T_OBJECT, arytype->size(), control());
+
         CompileLog* log = C->log();
         if (log != nullptr) {
           log->elem("cast_up reason='monomorphic_array' from='%d' to='(exact)'",
                     log->identify(extak->exact_klass()));
         }
-        array_klass = con;      // Use cast value moving forward
       }
     }
   }
 
   // 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
   // LoadKlassNode.
   Node* a_e_klass = _gvn.transform(LoadKlassNode::make(_gvn, always_see_exact_class ? control() : nullptr,
                                                        immutable_memory(), p2, tak));
 
+  // If we statically know that this is an inline type array, use precise element klass for checkcast
+  const TypeAryPtr* arytype = _gvn.type(ary)->is_aryptr();
+  bool null_free = false;
+  if (elemtype->make_ptr()->is_inlinetypeptr()) {
+    // We statically know that this is an inline type array, use precise klass ptr
+    null_free = arytype->is_flat() || !elemtype->make_ptr()->maybe_null();
+    a_e_klass = makecon(TypeKlassPtr::make(elemtype->inline_klass()));
+  }
+
   // 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, nullptr, null_free);
 }
 
 
 //------------------------------do_new-----------------------------------------
 void Parse::do_new() {
@@ -260,10 +295,15 @@
   if (C->needs_clinit_barrier(klass, method())) {
     clinit_barrier(klass, method());
     if (stopped())  return;
   }
 
+  if (klass->is_inlinetype()) {
+    push(InlineTypeNode::make_default(_gvn, klass->as_inline_klass(), /* is_larval */ true));
+    return;
+  }
+
   Node* kls = makecon(TypeKlassPtr::make(klass));
   Node* obj = new_instance(kls);
 
   // Push resultant oop onto stack
   push(obj);
