diff a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
--- a/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
+++ b/src/hotspot/cpu/aarch64/stubGenerator_aarch64.cpp
@@ -326,24 +326,29 @@
 
     // store result depending on type (everything that is not
     // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT)
     // n.b. this assumes Java returns an integral result in r0
     // and a floating result in j_farg0
-    __ ldr(j_rarg2, result);
-    Label is_long, is_float, is_double, exit;
-    __ ldr(j_rarg1, result_type);
-    __ cmp(j_rarg1, (u1)T_OBJECT);
+    // All of j_rargN may be used to return inline type fields so be careful
+    // not to clobber those.
+    // SharedRuntime::generate_buffered_inline_type_adapter() knows the register
+    // assignment of Rresult below.
+    Register Rresult = r14, Rresult_type = r15;
+    __ ldr(Rresult, result);
+    Label is_long, is_float, is_double, check_prim, exit;
+    __ ldr(Rresult_type, result_type);
+    __ cmp(Rresult_type, (u1)T_OBJECT);
+    __ br(Assembler::EQ, check_prim);
+    __ cmp(Rresult_type, (u1)T_LONG);
     __ br(Assembler::EQ, is_long);
-    __ cmp(j_rarg1, (u1)T_LONG);
-    __ br(Assembler::EQ, is_long);
-    __ cmp(j_rarg1, (u1)T_FLOAT);
+    __ cmp(Rresult_type, (u1)T_FLOAT);
     __ br(Assembler::EQ, is_float);
-    __ cmp(j_rarg1, (u1)T_DOUBLE);
+    __ cmp(Rresult_type, (u1)T_DOUBLE);
     __ br(Assembler::EQ, is_double);
 
     // handle T_INT case
-    __ strw(r0, Address(j_rarg2));
+    __ strw(r0, Address(Rresult));
 
     __ BIND(exit);
 
     // pop parameters
     __ sub(esp, rfp, -sp_after_call_off * wordSize);
@@ -391,21 +396,32 @@
     // leave frame and return to caller
     __ leave();
     __ ret(lr);
 
     // handle return types different from T_INT
+    __ BIND(check_prim);
+    if (InlineTypeReturnedAsFields) {
+      // Check for scalarized return value
+      __ tbz(r0, 0, is_long);
+      // Load pack handler address
+      __ andr(rscratch1, r0, -2);
+      __ ldr(rscratch1, Address(rscratch1, InstanceKlass::adr_inlineklass_fixed_block_offset()));
+      __ ldr(rscratch1, Address(rscratch1, InlineKlass::pack_handler_jobject_offset()));
+      __ blr(rscratch1);
+      __ b(exit);
+    }
 
     __ BIND(is_long);
-    __ str(r0, Address(j_rarg2, 0));
+    __ str(r0, Address(Rresult, 0));
     __ br(Assembler::AL, exit);
 
     __ BIND(is_float);
-    __ strs(j_farg0, Address(j_rarg2, 0));
+    __ strs(j_farg0, Address(Rresult, 0));
     __ br(Assembler::AL, exit);
 
     __ BIND(is_double);
-    __ strd(j_farg0, Address(j_rarg2, 0));
+    __ strd(j_farg0, Address(Rresult, 0));
     __ br(Assembler::AL, exit);
 
     return start;
   }
 
@@ -2214,10 +2230,18 @@
     //  if (src->klass() != dst->klass()) return -1;
     __ load_klass(rscratch2, dst);
     __ eor(rscratch2, rscratch2, scratch_src_klass);
     __ cbnz(rscratch2, L_failed);
 
+    // Check for flat inline type array -> return -1
+    __ tst(lh, Klass::_lh_array_tag_flat_value_bit_inplace);
+    __ br(Assembler::NE, L_failed);
+
+    // Check for null-free (non-flat) inline type array -> handle as object array
+    __ tst(lh, Klass::_lh_null_free_array_bit_inplace);
+    __ br(Assembler::NE, L_failed);
+
     //  if (!src->is_Array()) return -1;
     __ tbz(lh, 31, L_failed);  // i.e. (lh >= 0)
 
     // At this point, it is known to be a typeArray (array_tag 0x3).
 #ifdef ASSERT
@@ -8288,10 +8312,138 @@
     //     t0 = sub(Pm_base, Pn_base, t0, len);
     // }
   };
 
 
+  // Call here from the interpreter or compiled code to either load
+  // multiple returned values from the inline type instance being
+  // returned to registers or to store returned values to a newly
+  // allocated inline type instance.
+  address generate_return_value_stub(address destination, const char* name, bool has_res) {
+    // We need to save all registers the calling convention may use so
+    // the runtime calls read or update those registers. This needs to
+    // be in sync with SharedRuntime::java_return_convention().
+    // n.b. aarch64 asserts that frame::arg_reg_save_area_bytes == 0
+    enum layout {
+      j_rarg7_off = 0, j_rarg7_2,    // j_rarg7 is r0
+      j_rarg6_off, j_rarg6_2,
+      j_rarg5_off, j_rarg5_2,
+      j_rarg4_off, j_rarg4_2,
+      j_rarg3_off, j_rarg3_2,
+      j_rarg2_off, j_rarg2_2,
+      j_rarg1_off, j_rarg1_2,
+      j_rarg0_off, j_rarg0_2,
+
+      j_farg7_off, j_farg7_2,
+      j_farg6_off, j_farg6_2,
+      j_farg5_off, j_farg5_2,
+      j_farg4_off, j_farg4_2,
+      j_farg3_off, j_farg3_2,
+      j_farg2_off, j_farg2_2,
+      j_farg1_off, j_farg1_2,
+      j_farg0_off, j_farg0_2,
+
+      rfp_off, rfp_off2,
+      return_off, return_off2,
+
+      framesize // inclusive of return address
+    };
+
+    CodeBuffer code(name, 512, 64);
+    MacroAssembler* masm = new MacroAssembler(&code);
+
+    int frame_size_in_bytes = align_up(framesize*BytesPerInt, 16);
+    assert(frame_size_in_bytes == framesize*BytesPerInt, "misaligned");
+    int frame_size_in_slots = frame_size_in_bytes / BytesPerInt;
+    int frame_size_in_words = frame_size_in_bytes / wordSize;
+
+    OopMapSet* oop_maps = new OopMapSet();
+    OopMap* map = new OopMap(frame_size_in_slots, 0);
+
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg7_off), j_rarg7->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg6_off), j_rarg6->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg5_off), j_rarg5->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg4_off), j_rarg4->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg3_off), j_rarg3->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg2_off), j_rarg2->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg1_off), j_rarg1->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_rarg0_off), j_rarg0->as_VMReg());
+
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg0_off), j_farg0->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg1_off), j_farg1->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg2_off), j_farg2->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg3_off), j_farg3->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg4_off), j_farg4->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg5_off), j_farg5->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg6_off), j_farg6->as_VMReg());
+    map->set_callee_saved(VMRegImpl::stack2reg(j_farg7_off), j_farg7->as_VMReg());
+
+    address start = __ pc();
+
+    __ enter(); // Save FP and LR before call
+
+    __ stpd(j_farg1, j_farg0, Address(__ pre(sp, -2 * wordSize)));
+    __ stpd(j_farg3, j_farg2, Address(__ pre(sp, -2 * wordSize)));
+    __ stpd(j_farg5, j_farg4, Address(__ pre(sp, -2 * wordSize)));
+    __ stpd(j_farg7, j_farg6, Address(__ pre(sp, -2 * wordSize)));
+
+    __ stp(j_rarg1, j_rarg0, Address(__ pre(sp, -2 * wordSize)));
+    __ stp(j_rarg3, j_rarg2, Address(__ pre(sp, -2 * wordSize)));
+    __ stp(j_rarg5, j_rarg4, Address(__ pre(sp, -2 * wordSize)));
+    __ stp(j_rarg7, j_rarg6, Address(__ pre(sp, -2 * wordSize)));
+
+    int frame_complete = __ offset();
+
+    // Set up last_Java_sp and last_Java_fp
+    address the_pc = __ pc();
+    __ set_last_Java_frame(sp, noreg, the_pc, rscratch1);
+
+    // Call runtime
+    __ mov(c_rarg1, r0);
+    __ mov(c_rarg0, rthread);
+
+    __ mov(rscratch1, destination);
+    __ blr(rscratch1);
+
+    oop_maps->add_gc_map(the_pc - start, map);
+
+    __ reset_last_Java_frame(false);
+
+    __ ldp(j_rarg7, j_rarg6, Address(__ post(sp, 2 * wordSize)));
+    __ ldp(j_rarg5, j_rarg4, Address(__ post(sp, 2 * wordSize)));
+    __ ldp(j_rarg3, j_rarg2, Address(__ post(sp, 2 * wordSize)));
+    __ ldp(j_rarg1, j_rarg0, Address(__ post(sp, 2 * wordSize)));
+
+    __ ldpd(j_farg7, j_farg6, Address(__ post(sp, 2 * wordSize)));
+    __ ldpd(j_farg5, j_farg4, Address(__ post(sp, 2 * wordSize)));
+    __ ldpd(j_farg3, j_farg2, Address(__ post(sp, 2 * wordSize)));
+    __ ldpd(j_farg1, j_farg0, Address(__ post(sp, 2 * wordSize)));
+
+    __ leave();
+
+    // check for pending exceptions
+    Label pending;
+    __ ldr(rscratch1, Address(rthread, in_bytes(Thread::pending_exception_offset())));
+    __ cbnz(rscratch1, pending);
+
+    if (has_res) {
+      __ get_vm_result(r0, rthread);
+    }
+
+    __ ret(lr);
+
+    __ bind(pending);
+    __ far_jump(RuntimeAddress(StubRoutines::forward_exception_entry()));
+
+    // -------------
+    // make sure all code is generated
+    masm->flush();
+
+    RuntimeStub* stub = RuntimeStub::new_runtime_stub(name, &code, frame_complete, frame_size_in_words, oop_maps, false);
+    return stub->entry_point();
+  }
+
   // Initialization
   void generate_initial_stubs() {
     // Generate initial stubs and initializes the entry points
 
     // entry points that exist in all platforms Note: This is code
@@ -8343,10 +8495,17 @@
     }
 
     if (vmIntrinsics::is_intrinsic_available(vmIntrinsics::_dcos)) {
       StubRoutines::_dcos = generate_dsin_dcos(/* isCos = */ true);
     }
+
+    if (InlineTypeReturnedAsFields) {
+      StubRoutines::_load_inline_type_fields_in_regs =
+         generate_return_value_stub(CAST_FROM_FN_PTR(address, SharedRuntime::load_inline_type_fields_in_regs), "load_inline_type_fields_in_regs", false);
+      StubRoutines::_store_inline_type_fields_to_buf =
+         generate_return_value_stub(CAST_FROM_FN_PTR(address, SharedRuntime::store_inline_type_fields_to_buf), "store_inline_type_fields_to_buf", true);
+    }
   }
 
   void generate_continuation_stubs() {
     // Continuation stubs:
     StubRoutines::_cont_thaw          = generate_cont_thaw();
