diff a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp
--- a/src/hotspot/share/opto/doCall.cpp
+++ b/src/hotspot/share/opto/doCall.cpp
@@ -36,10 +36,11 @@
 #include "logging/logStream.hpp"
 #include "opto/addnode.hpp"
 #include "opto/callGenerator.hpp"
 #include "opto/castnode.hpp"
 #include "opto/cfgnode.hpp"
+#include "opto/inlinetypenode.hpp"
 #include "opto/mulnode.hpp"
 #include "opto/parse.hpp"
 #include "opto/rootnode.hpp"
 #include "opto/runtime.hpp"
 #include "opto/subnode.hpp"
@@ -575,10 +576,29 @@
   // Try to get the most accurate receiver type
   ciMethod* callee             = orig_callee;
   int       vtable_index       = Method::invalid_vtable_index;
   bool      call_does_dispatch = false;
 
+  // Detect the call to the object or abstract class constructor at the end of a value constructor to know when we are done initializing the larval
+  if (orig_callee->is_object_constructor() && (orig_callee->holder()->is_abstract() || orig_callee->holder()->is_java_lang_Object()) && stack(sp() - nargs)->is_InlineType()) {
+    assert(method()->is_object_constructor() && (method()->holder()->is_inlinetype() || method()->holder()->is_abstract()), "Unexpected caller");
+    InlineTypeNode* receiver = stack(sp() - nargs)->as_InlineType();
+    // TODO 8325106 re-enable the assert and add the same check for the receiver in the caller map
+    //assert(receiver->is_larval(), "must be larval");
+    InlineTypeNode* clone = receiver->clone_if_required(&_gvn, _map);
+    clone->set_is_larval(false);
+    clone = _gvn.transform(clone)->as_InlineType();
+    replace_in_map(receiver, clone);
+
+    if (_caller->has_method()) {
+      // Get receiver from the caller map and update it in the exit map now that we are done initializing it
+      Node* receiver_in_caller = _caller->map()->argument(_caller, 0);
+      assert(receiver_in_caller->bottom_type()->inline_klass() == receiver->bottom_type()->inline_klass(), "Receiver type mismatch");
+      _exits.map()->replace_edge(receiver_in_caller, clone, &_gvn);
+    }
+  }
+
   // Speculative type of the receiver if any
   ciKlass* speculative_receiver_type = nullptr;
   if (is_virtual_or_interface) {
     Node* receiver_node             = stack(sp() - nargs);
     const TypeOopPtr* receiver_type = _gvn.type(receiver_node)->isa_oopptr();
@@ -595,11 +615,11 @@
     speculative_receiver_type = receiver_type != nullptr ? receiver_type->speculative_type() : nullptr;
   }
 
   // Additional receiver subtype checks for interface calls via invokespecial or invokeinterface.
   ciKlass* receiver_constraint = nullptr;
-  if (iter().cur_bc_raw() == Bytecodes::_invokespecial && !orig_callee->is_object_initializer()) {
+  if (iter().cur_bc_raw() == Bytecodes::_invokespecial && !orig_callee->is_object_constructor()) {
     ciInstanceKlass* calling_klass = method()->holder();
     ciInstanceKlass* sender_klass = calling_klass;
     if (sender_klass->is_interface()) {
       receiver_constraint = sender_klass;
     }
@@ -725,11 +745,11 @@
       // Be careful here with return types.
       if (ctype != rtype) {
         BasicType rt = rtype->basic_type();
         BasicType ct = ctype->basic_type();
         if (ct == T_VOID) {
-          // It's OK for a method  to return a value that is discarded.
+          // It's OK for a method to return a value that is discarded.
           // The discarding does not require any special action from the caller.
           // The Java code knows this, at VerifyType.isNullConversion.
           pop_node(rt);  // whatever it was, pop it
         } else if (rt == T_INT || is_subword_type(rt)) {
           // Nothing.  These cases are handled in lambda form bytecode.
@@ -783,10 +803,25 @@
     }
     BasicType ct = ctype->basic_type();
     if (is_reference_type(ct)) {
       record_profiled_return_for_speculation();
     }
+    if (rtype->is_inlinetype() && !peek()->is_InlineType()) {
+      Node* retnode = pop();
+      retnode = InlineTypeNode::make_from_oop(this, retnode, rtype->as_inline_klass(), !gvn().type(retnode)->maybe_null());
+      push_node(T_OBJECT, retnode);
+    }
+
+    // Did we inline a value class constructor from another value class constructor?
+    if (_caller->has_method() && cg->is_inline() && cg->method()->is_object_constructor() && cg->method()->holder()->is_inlinetype() &&
+        _method->is_object_constructor() && _method->holder()->is_inlinetype() && receiver == _caller->map()->argument(_caller, 0)) {
+      // Update the receiver in the exit map because the constructor call updated it.
+      // MethodLiveness::BasicBlock::compute_gen_kill_single ensures that the receiver in local(0) is still live.
+      assert(local(0)->is_InlineType(), "Unexpected receiver");
+      assert(receiver->bottom_type()->inline_klass() == local(0)->bottom_type()->inline_klass(), "Receiver type mismatch");
+      _exits.map()->replace_edge(receiver, local(0), &_gvn);
+    }
   }
 
   // Restart record of parsing work after possible inlining of call
 #ifndef PRODUCT
   parse_histogram()->set_initial_state(bc());
