diff a/src/hotspot/share/ci/ciTypeFlow.cpp b/src/hotspot/share/ci/ciTypeFlow.cpp
--- a/src/hotspot/share/ci/ciTypeFlow.cpp
+++ b/src/hotspot/share/ci/ciTypeFlow.cpp
@@ -23,10 +23,11 @@
  */
 
 #include "precompiled.hpp"
 #include "ci/ciConstant.hpp"
 #include "ci/ciField.hpp"
+#include "ci/ciInlineKlass.hpp"
 #include "ci/ciMethod.hpp"
 #include "ci/ciMethodData.hpp"
 #include "ci/ciObjArrayKlass.hpp"
 #include "ci/ciStreams.hpp"
 #include "ci/ciTypeArrayKlass.hpp"
@@ -272,13 +273,23 @@
   assert(t1 != t2, "checked in caller");
   if (t1->equals(top_type())) {
     return t2;
   } else if (t2->equals(top_type())) {
     return t1;
-  } else if (t1->is_primitive_type() || t2->is_primitive_type()) {
+  }
+  // Unwrap after saving nullness information and handling top meets
+  bool null_free1 = t1->is_null_free();
+  bool null_free2 = t2->is_null_free();
+  if (t1->unwrap() == t2->unwrap() && null_free1 == null_free2) {
+    return t1;
+  }
+  t1 = t1->unwrap();
+  t2 = t2->unwrap();
+
+  if (t1->is_primitive_type() || t2->is_primitive_type()) {
     // Special case null_type.  null_type meet any reference type T
-    // is T.  null_type meet null_type is null_type.
+    // is T. null_type meet null_type is null_type.
     if (t1->equals(null_type())) {
       if (!t2->is_primitive_type() || t2->equals(null_type())) {
         return t2;
       }
     } else if (t2->equals(null_type())) {
@@ -288,54 +299,61 @@
     }
 
     // At least one of the two types is a non-top primitive type.
     // The other type is not equal to it.  Fall to bottom.
     return bottom_type();
-  } else {
-    // Both types are non-top non-primitive types.  That is,
-    // both types are either instanceKlasses or arrayKlasses.
-    ciKlass* object_klass = analyzer->env()->Object_klass();
-    ciKlass* k1 = t1->as_klass();
-    ciKlass* k2 = t2->as_klass();
-    if (k1->equals(object_klass) || k2->equals(object_klass)) {
-      return object_klass;
-    } else if (!k1->is_loaded() || !k2->is_loaded()) {
-      // Unloaded classes fall to java.lang.Object at a merge.
-      return object_klass;
-    } else if (k1->is_interface() != k2->is_interface()) {
-      // When an interface meets a non-interface, we get Object;
-      // This is what the verifier does.
-      return object_klass;
-    } else if (k1->is_array_klass() || k2->is_array_klass()) {
-      // When an array meets a non-array, we get Object.
-      // When objArray meets typeArray, we also get Object.
-      // And when typeArray meets different typeArray, we again get Object.
-      // But when objArray meets objArray, we look carefully at element types.
-      if (k1->is_obj_array_klass() && k2->is_obj_array_klass()) {
-        // Meet the element types, then construct the corresponding array type.
-        ciKlass* elem1 = k1->as_obj_array_klass()->element_klass();
-        ciKlass* elem2 = k2->as_obj_array_klass()->element_klass();
-        ciKlass* elem  = type_meet_internal(elem1, elem2, analyzer)->as_klass();
-        // Do an easy shortcut if one type is a super of the other.
-        if (elem == elem1) {
-          assert(k1 == ciObjArrayKlass::make(elem), "shortcut is OK");
-          return k1;
-        } else if (elem == elem2) {
-          assert(k2 == ciObjArrayKlass::make(elem), "shortcut is OK");
-          return k2;
-        } else {
-          return ciObjArrayKlass::make(elem);
-        }
+  }
+
+  // Both types are non-top non-primitive types.  That is,
+  // both types are either instanceKlasses or arrayKlasses.
+  ciKlass* object_klass = analyzer->env()->Object_klass();
+  ciKlass* k1 = t1->as_klass();
+  ciKlass* k2 = t2->as_klass();
+  if (k1->equals(object_klass) || k2->equals(object_klass)) {
+    return object_klass;
+  } else if (!k1->is_loaded() || !k2->is_loaded()) {
+    // Unloaded classes fall to java.lang.Object at a merge.
+    return object_klass;
+  } else if (k1->is_interface() != k2->is_interface()) {
+    // When an interface meets a non-interface, we get Object;
+    // This is what the verifier does.
+    return object_klass;
+  } else if (k1->is_array_klass() || k2->is_array_klass()) {
+    // When an array meets a non-array, we get Object.
+    // When (obj/flat)Array meets typeArray, we also get Object.
+    // And when typeArray meets different typeArray, we again get Object.
+    // But when (obj/flat)Array meets (obj/flat)Array, we look carefully at element types.
+    if ((k1->is_obj_array_klass() || k1->is_flat_array_klass()) &&
+        (k2->is_obj_array_klass() || k2->is_flat_array_klass())) {
+      ciType* elem1 = k1->as_array_klass()->element_klass();
+      ciType* elem2 = k2->as_array_klass()->element_klass();
+      ciType* elem = elem1;
+      if (elem1 != elem2) {
+        elem = type_meet_internal(elem1, elem2, analyzer)->as_klass();
+      }
+      // Do an easy shortcut if one type is a super of the other.
+      if (elem == elem1 && !elem->is_inlinetype()) {
+        assert(k1 == ciArrayKlass::make(elem), "shortcut is OK");
+        return k1;
+      } else if (elem == elem2 && !elem->is_inlinetype()) {
+        assert(k2 == ciArrayKlass::make(elem), "shortcut is OK");
+        return k2;
       } else {
-        return object_klass;
+        return ciArrayKlass::make(elem);
       }
     } else {
-      // Must be two plain old instance klasses.
-      assert(k1->is_instance_klass(), "previous cases handle non-instances");
-      assert(k2->is_instance_klass(), "previous cases handle non-instances");
-      return k1->least_common_ancestor(k2);
+      return object_klass;
+    }
+  } else {
+    // Must be two plain old instance klasses.
+    assert(k1->is_instance_klass(), "previous cases handle non-instances");
+    assert(k2->is_instance_klass(), "previous cases handle non-instances");
+    ciType* result = k1->least_common_ancestor(k2);
+    if (null_free1 && null_free2 && result->is_inlinetype()) {
+      result = analyzer->mark_as_null_free(result);
     }
+    return result;
   }
 }
 
 
 // ------------------------------------------------------------------
@@ -393,11 +411,16 @@
     // even if it were possible for an OSR entry point to be at bci zero.
   }
   // "Push" the method signature into the first few locals.
   state->set_stack_size(-max_locals());
   if (!method()->is_static()) {
-    state->push(method()->holder());
+    ciType* holder = method()->holder();
+    if (holder->is_inlinetype()) {
+      // The receiver is null-free
+      holder = mark_as_null_free(holder);
+    }
+    state->push(holder);
     assert(state->tos() == state->local(0), "");
   }
   for (ciSignatureStream str(method()->signature());
        !str.at_return_type();
        str.next()) {
@@ -543,16 +566,16 @@
     }
   }
 }
 
 // ------------------------------------------------------------------
-// ciTypeFlow::StateVector::do_aaload
-void ciTypeFlow::StateVector::do_aaload(ciBytecodeStream* str) {
+// ciTypeFlow::StateVector::do_aload
+void ciTypeFlow::StateVector::do_aload(ciBytecodeStream* str) {
   pop_int();
-  ciObjArrayKlass* array_klass = pop_objArray();
+  ciArrayKlass* array_klass = pop_objOrFlatArray();
   if (array_klass == nullptr) {
-    // Did aaload on a null reference; push a null and ignore the exception.
+    // Did aload on a null reference; push a null and ignore the exception.
     // This instruction will never continue normally.  All we have to do
     // is report a value that will meet correctly with any downstream
     // reference types on paths that will truly be executed.  This null type
     // meets with any reference type to yield that same reference type.
     // (The compiler will generate an unconditional exception here.)
@@ -584,18 +607,24 @@
 // ciTypeFlow::StateVector::do_checkcast
 void ciTypeFlow::StateVector::do_checkcast(ciBytecodeStream* str) {
   bool will_link;
   ciKlass* klass = str->get_klass(will_link);
   if (!will_link) {
-    // VM's interpreter will not load 'klass' if object is null.
+    // VM's interpreter will not load 'klass' if object is nullptr.
     // Type flow after this block may still be needed in two situations:
     // 1) C2 uses do_null_assert() and continues compilation for later blocks
     // 2) C2 does an OSR compile in a later block (see bug 4778368).
     pop_object();
     do_null_assert(klass);
   } else {
-    pop_object();
+    ciType* type = pop_value();
+    type = type->unwrap();
+    if (type->is_loaded() && klass->is_loaded() &&
+        type != klass && type->is_subtype_of(klass)) {
+      // Useless cast, propagate more precise type of object
+      klass = type->as_klass();
+    }
     push_object(klass);
   }
 }
 
 // ------------------------------------------------------------------
@@ -613,11 +642,20 @@
   ciField* field = str->get_field(will_link);
   if (!will_link) {
     trap(str, field->holder(), str->get_field_holder_index());
   } else {
     ciType* field_type = field->type();
-    if (!field_type->is_loaded()) {
+    if (field->is_static() && field->is_null_free() &&
+        !field_type->as_instance_klass()->is_initialized()) {
+      // Deoptimize if we load from a static field with an uninitialized inline type
+      // because we need to throw an exception if initialization of the type failed.
+      trap(str, field_type->as_klass(),
+           Deoptimization::make_trap_request
+           (Deoptimization::Reason_unloaded,
+            Deoptimization::Action_reinterpret));
+      return;
+    } else if (!field_type->is_loaded()) {
       // Normally, we need the field's type to be loaded if we are to
       // do anything interesting with its value.
       // We used to do this:  trap(str, str->get_field_signature_index());
       //
       // There is one good reason not to trap here.  Execution can
@@ -634,10 +672,13 @@
       // here can make an OSR entry point unreachable, triggering the
       // assert on non_osr_block in ciTypeFlow::get_start_state.
       // (See bug 4379915.)
       do_null_assert(field_type->as_klass());
     } else {
+      if (field->is_null_free()) {
+        field_type = outer()->mark_as_null_free(field_type);
+      }
       push_translate(field_type);
     }
   }
 }
 
@@ -699,11 +740,21 @@
         // does not need to be loaded!  The compiler must assume that
         // the value of the unloaded class reference is null; if the code
         // ever sees a non-null value, loading has occurred.
         //
         // See do_getstatic() for similar explanation, as well as bug 4684993.
-        do_null_assert(return_type->as_klass());
+        if (InlineTypeReturnedAsFields) {
+          // Return might be in scalarized form but we can't handle it because we
+          // don't know the type. This can happen due to a missing preload attribute.
+          // TODO 8284443 Use PhaseMacroExpand::expand_mh_intrinsic_return for this
+          trap(str, nullptr,
+               Deoptimization::make_trap_request
+               (Deoptimization::Reason_uninitialized,
+                Deoptimization::Action_reinterpret));
+        } else {
+          do_null_assert(return_type->as_klass());
+        }
       } else {
         push_translate(return_type);
       }
     }
   }
@@ -737,11 +788,15 @@
       ciObject* obj = con.as_object();
       if (obj->is_null_object()) {
         push_null();
       } else {
         assert(obj->is_instance() || obj->is_array(), "must be java_mirror of klass");
-        push_object(obj->klass());
+        ciType* type = obj->klass();
+        if (type->is_inlinetype()) {
+          type = outer()->mark_as_null_free(type);
+        }
+        push(type);
       }
     } else {
       assert(basic_type == con.basic_type() || con.basic_type() == T_OBJECT,
              "not a boxed form: %s vs %s", type2name(basic_type), type2name(con.basic_type()));
       push_translate(ciType::make(basic_type));
@@ -884,17 +939,17 @@
     tty->print_cr(">> Interpreting bytecode %d:%s", str->cur_bci(),
                   Bytecodes::name(str->cur_bc()));
   }
 
   switch(str->cur_bc()) {
-  case Bytecodes::_aaload: do_aaload(str);                       break;
+  case Bytecodes::_aaload: do_aload(str);                           break;
 
   case Bytecodes::_aastore:
     {
       pop_object();
       pop_int();
-      pop_objArray();
+      pop_objOrFlatArray();
       break;
     }
   case Bytecodes::_aconst_null:
     {
       push_null();
@@ -912,11 +967,11 @@
       bool will_link;
       ciKlass* element_klass = str->get_klass(will_link);
       if (!will_link) {
         trap(str, element_klass, str->get_klass_index());
       } else {
-        push_object(ciObjArrayKlass::make(element_klass));
+        push_object(ciArrayKlass::make(element_klass));
       }
       break;
     }
   case Bytecodes::_areturn:
   case Bytecodes::_ifnonnull:
@@ -1471,10 +1526,11 @@
       ciType* value2 = pop_value();
       push(value1);
       push(value2);
       break;
     }
+
   case Bytecodes::_wide:
   default:
     {
       // The iterator should skip this.
       ShouldNotReachHere();
@@ -1491,11 +1547,11 @@
 
 #ifndef PRODUCT
 // ------------------------------------------------------------------
 // ciTypeFlow::StateVector::print_cell_on
 void ciTypeFlow::StateVector::print_cell_on(outputStream* st, Cell c) const {
-  ciType* type = type_at(c);
+  ciType* type = type_at(c)->unwrap();
   if (type == top_type()) {
     st->print("top");
   } else if (type == bottom_type()) {
     st->print("bottom");
   } else if (type == null_type()) {
@@ -1754,13 +1810,16 @@
           _successors->append_if_missing(block);
         }
         break;
       }
 
-      case Bytecodes::_athrow:     case Bytecodes::_ireturn:
-      case Bytecodes::_lreturn:    case Bytecodes::_freturn:
-      case Bytecodes::_dreturn:    case Bytecodes::_areturn:
+      case Bytecodes::_athrow:
+      case Bytecodes::_ireturn:
+      case Bytecodes::_lreturn:
+      case Bytecodes::_freturn:
+      case Bytecodes::_dreturn:
+      case Bytecodes::_areturn:
       case Bytecodes::_return:
         _successors =
           new (arena) GrowableArray<Block*>(arena, 1, 0, nullptr);
         // No successors
         break;
@@ -3149,10 +3208,15 @@
     // Record the first failure reason.
     _failure_reason = reason;
   }
 }
 
+ciType* ciTypeFlow::mark_as_null_free(ciType* type) {
+  // Wrap the type to carry the information that it is null-free
+  return env()->make_null_free_wrapper(type);
+}
+
 #ifndef PRODUCT
 void ciTypeFlow::print() const       { print_on(tty); }
 
 // ------------------------------------------------------------------
 // ciTypeFlow::print_on
