diff options
author | 2023-06-21 09:24:30 +0000 | |
---|---|---|
committer | 2023-06-27 12:37:04 +0000 | |
commit | 3c405060efadc453f9bc1cabe376de06e8325868 (patch) | |
tree | b064a88970c826c6c85d60bc33b1694c2efc3b8a | |
parent | 2ea3c8b7eabb027f1743caba276073ce08cd831d (diff) |
riscv64: Implement JNI compiler for normal native.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: run-gtests.sh
Bug: 283082089
Change-Id: I2d6e8d029a74004076b6d514205a147ce1145f03
-rw-r--r-- | compiler/jni/jni_compiler_test.cc | 2 | ||||
-rw-r--r-- | compiler/jni/quick/calling_convention.cc | 19 | ||||
-rw-r--r-- | compiler/jni/quick/calling_convention.h | 14 | ||||
-rw-r--r-- | compiler/jni/quick/jni_compiler.cc | 4 | ||||
-rw-r--r-- | compiler/jni/quick/riscv64/calling_convention_riscv64.cc | 19 | ||||
-rw-r--r-- | compiler/jni/quick/riscv64/calling_convention_riscv64.h | 1 | ||||
-rw-r--r-- | compiler/utils/riscv64/jni_macro_assembler_riscv64.cc | 73 | ||||
-rw-r--r-- | compiler/utils/riscv64/jni_macro_assembler_riscv64_test.cc | 234 | ||||
-rw-r--r-- | runtime/arch/arm64/asm_support_arm64.S | 2 | ||||
-rw-r--r-- | runtime/arch/riscv64/asm_support_riscv64.S | 86 | ||||
-rw-r--r-- | runtime/arch/riscv64/jni_entrypoints_riscv64.S | 220 | ||||
-rw-r--r-- | runtime/arch/riscv64/quick_entrypoints_riscv64.S | 2 |
12 files changed, 554 insertions, 122 deletions
diff --git a/compiler/jni/jni_compiler_test.cc b/compiler/jni/jni_compiler_test.cc index 18f86528b7..40989b2999 100644 --- a/compiler/jni/jni_compiler_test.cc +++ b/compiler/jni/jni_compiler_test.cc @@ -446,14 +446,12 @@ LockWord JniCompilerTest::GetLockWord(jobject obj) { // 1) synchronized keyword # define JNI_TEST_NORMAL_ONLY(TestName) \ TEST_F(JniCompilerTest, TestName ## NormalCompiler) { \ - TEST_DISABLED_FOR_RISCV64(); \ ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("Normal JNI with compiler"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \ TestName ## Impl(); \ } \ TEST_F(JniCompilerTest, TestName ## NormalGeneric) { \ - TEST_DISABLED_FOR_RISCV64(); \ ScopedCheckHandleScope top_handle_scope_check; \ SCOPED_TRACE("Normal JNI with generic"); \ gCurrentJni = static_cast<uint32_t>(JniKind::kNormal); \ diff --git a/compiler/jni/quick/calling_convention.cc b/compiler/jni/quick/calling_convention.cc index ade6781c61..459beb0c67 100644 --- a/compiler/jni/quick/calling_convention.cc +++ b/compiler/jni/quick/calling_convention.cc @@ -124,7 +124,7 @@ bool ManagedRuntimeCallingConvention::IsCurrentArgPossiblyNull() { } size_t ManagedRuntimeCallingConvention::CurrentParamSize() { - return ParamSize(itr_args_); + return ParamSize(itr_args_, /*reference_size=*/ sizeof(mirror::HeapReference<mirror::Object>)); } bool ManagedRuntimeCallingConvention::IsCurrentParamAReference() { @@ -204,7 +204,7 @@ bool JniCallingConvention::HasNext() { if (IsCurrentArgExtraForJni()) { return true; } else { - unsigned int arg_pos = GetIteratorPositionWithinShorty(); + size_t arg_pos = GetIteratorPositionWithinShorty(); return arg_pos < NumArgs(); } } @@ -236,7 +236,7 @@ bool JniCallingConvention::IsCurrentParamAReference() { &return_value)) { return return_value; } else { - int arg_pos = GetIteratorPositionWithinShorty(); + size_t arg_pos = GetIteratorPositionWithinShorty(); return IsParamAReference(arg_pos); } } @@ -258,7 +258,7 @@ bool JniCallingConvention::IsCurrentParamAFloatOrDouble() { &return_value)) { return return_value; } else { - int arg_pos = GetIteratorPositionWithinShorty(); + size_t arg_pos = GetIteratorPositionWithinShorty(); return IsParamAFloatOrDouble(arg_pos); } } @@ -272,7 +272,7 @@ bool JniCallingConvention::IsCurrentParamADouble() { &return_value)) { return return_value; } else { - int arg_pos = GetIteratorPositionWithinShorty(); + size_t arg_pos = GetIteratorPositionWithinShorty(); return IsParamADouble(arg_pos); } } @@ -286,7 +286,7 @@ bool JniCallingConvention::IsCurrentParamALong() { &return_value)) { return return_value; } else { - int arg_pos = GetIteratorPositionWithinShorty(); + size_t arg_pos = GetIteratorPositionWithinShorty(); return IsParamALong(arg_pos); } } @@ -295,8 +295,9 @@ size_t JniCallingConvention::CurrentParamSize() const { if (IsCurrentArgExtraForJni()) { return static_cast<size_t>(frame_pointer_size_); // JNIEnv or jobject/jclass } else { - int arg_pos = GetIteratorPositionWithinShorty(); - return ParamSize(arg_pos); + size_t arg_pos = GetIteratorPositionWithinShorty(); + // References are converted to `jobject` for the native call. Pass `frame_pointer_size_`. + return ParamSize(arg_pos, /*reference_size=*/ static_cast<size_t>(frame_pointer_size_)); } } @@ -321,7 +322,7 @@ bool JniCallingConvention::HasSelfClass() const { } } -unsigned int JniCallingConvention::GetIteratorPositionWithinShorty() const { +size_t JniCallingConvention::GetIteratorPositionWithinShorty() const { // We need to subtract out the extra JNI arguments if we want to use this iterator position // with the inherited CallingConvention member functions, which rely on scanning the shorty. // Note that our shorty does *not* include the JNIEnv, jclass/jobject parameters. diff --git a/compiler/jni/quick/calling_convention.h b/compiler/jni/quick/calling_convention.h index 0187b14256..2657e943e6 100644 --- a/compiler/jni/quick/calling_convention.h +++ b/compiler/jni/quick/calling_convention.h @@ -178,14 +178,18 @@ class CallingConvention : public DeletableArenaObject<kArenaAllocCallingConventi size_t NumReferenceArgs() const { return num_ref_args_; } - size_t ParamSize(unsigned int param) const { + size_t ParamSize(size_t param, size_t reference_size) const { DCHECK_LT(param, NumArgs()); if (IsStatic()) { param++; // 0th argument must skip return value at start of the shorty } else if (param == 0) { - return sizeof(mirror::HeapReference<mirror::Object>); // this argument + return reference_size; // this argument } - size_t result = Primitive::ComponentSize(Primitive::GetType(shorty_[param])); + Primitive::Type type = Primitive::GetType(shorty_[param]); + if (type == Primitive::kPrimNot) { + return reference_size; + } + size_t result = Primitive::ComponentSize(type); if (result >= 1 && result < 4) { result = 4; } @@ -344,7 +348,7 @@ class JniCallingConvention : public CallingConvention { return IsCurrentParamALong() || IsCurrentParamADouble(); } bool IsCurrentParamJniEnv(); - size_t CurrentParamSize() const; + virtual size_t CurrentParamSize() const; virtual bool IsCurrentParamInRegister() = 0; virtual bool IsCurrentParamOnStack() = 0; virtual ManagedRegister CurrentParamRegister() = 0; @@ -432,7 +436,7 @@ class JniCallingConvention : public CallingConvention { bool HasSelfClass() const; // Returns the position of itr_args_, fixed up by removing the offset of extra JNI arguments. - unsigned int GetIteratorPositionWithinShorty() const; + size_t GetIteratorPositionWithinShorty() const; // Is the current argument (at the iterator) an extra argument for JNI? bool IsCurrentArgExtraForJni() const; diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 1e3852a9ce..03d784bec1 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -387,8 +387,8 @@ static JniCompiledMethod ArtJniCompileMethodInternal(const CompilerOptions& comp DCHECK(main_jni_conv->HasNext()); static_assert(kObjectReferenceSize == 4u); bool is_reference = mr_conv->IsCurrentParamAReference(); - size_t src_size = (!is_reference && mr_conv->IsCurrentParamALongOrDouble()) ? 8u : 4u; - size_t dest_size = is_reference ? kRawPointerSize : src_size; + size_t src_size = mr_conv->CurrentParamSize(); + size_t dest_size = main_jni_conv->CurrentParamSize(); src_args.push_back(mr_conv->IsCurrentParamInRegister() ? ArgumentLocation(mr_conv->CurrentParamRegister(), src_size) : ArgumentLocation(mr_conv->CurrentParamStackOffset(), src_size)); diff --git a/compiler/jni/quick/riscv64/calling_convention_riscv64.cc b/compiler/jni/quick/riscv64/calling_convention_riscv64.cc index 31718a13ee..b083fec14a 100644 --- a/compiler/jni/quick/riscv64/calling_convention_riscv64.cc +++ b/compiler/jni/quick/riscv64/calling_convention_riscv64.cc @@ -315,6 +315,25 @@ uint32_t Riscv64JniCallingConvention::FpSpillMask() const { return is_critical_native_ ? 0u : kFpCalleeSpillMask; } +size_t Riscv64JniCallingConvention::CurrentParamSize() const { + if (IsCurrentArgExtraForJni()) { + return static_cast<size_t>(frame_pointer_size_); // JNIEnv or jobject/jclass + } else { + size_t arg_pos = GetIteratorPositionWithinShorty(); + DCHECK_LT(arg_pos, NumArgs()); + if (IsStatic()) { + ++arg_pos; // 0th argument must skip return value at start of the shorty + } else if (arg_pos == 0) { + return static_cast<size_t>(kRiscv64PointerSize); // this argument + } + // The riscv64 native calling convention specifies that integers narrower than XLEN (64) + // bits are "widened according to the sign of their type up to 32 bits, then sign-extended + // to XLEN bits." Thus, everything other than `float` (which has the high 32 bits undefined) + // is passed as 64 bits, whether in register, or on the stack. + return (GetShorty()[arg_pos] == 'F') ? 4u : static_cast<size_t>(kRiscv64PointerSize); + } +} + bool Riscv64JniCallingConvention::IsCurrentParamInRegister() { // FP args use FPRs, then GPRs and only then the stack. if (itr_float_and_doubles_ < kMaxFloatOrDoubleArgumentRegisters) { diff --git a/compiler/jni/quick/riscv64/calling_convention_riscv64.h b/compiler/jni/quick/riscv64/calling_convention_riscv64.h index 336d2bf374..5add183f72 100644 --- a/compiler/jni/quick/riscv64/calling_convention_riscv64.h +++ b/compiler/jni/quick/riscv64/calling_convention_riscv64.h @@ -65,6 +65,7 @@ class Riscv64JniCallingConvention final : public JniCallingConvention { ArrayRef<const ManagedRegister> ArgumentScratchRegisters() const override; uint32_t CoreSpillMask() const override; uint32_t FpSpillMask() const override; + size_t CurrentParamSize() const override; bool IsCurrentParamInRegister() override; bool IsCurrentParamOnStack() override; ManagedRegister CurrentParamRegister() override; diff --git a/compiler/utils/riscv64/jni_macro_assembler_riscv64.cc b/compiler/utils/riscv64/jni_macro_assembler_riscv64.cc index e3d542441a..a05e9b49f4 100644 --- a/compiler/utils/riscv64/jni_macro_assembler_riscv64.cc +++ b/compiler/utils/riscv64/jni_macro_assembler_riscv64.cc @@ -209,6 +209,12 @@ void Riscv64JNIMacroAssembler::Load(ManagedRegister m_dest, Riscv64ManagedRegister dest = m_dest.AsRiscv64(); if (dest.IsXRegister()) { if (size == 4u) { + // The riscv64 native calling convention specifies that integers narrower than XLEN (64) + // bits are "widened according to the sign of their type up to 32 bits, then sign-extended + // to XLEN bits." The managed ABI already passes integral values this way in registers + // and correctly widened to 32 bits on the stack. The `Load()` must sign-extend narrower + // types here to pass integral values correctly to the native call. + // For `float` args, the upper 32 bits are undefined, so this is fine for them as well. __ Loadw(dest.AsXRegister(), base.AsXRegister(), offs.Int32Value()); } else { CHECK_EQ(8u, size); @@ -263,7 +269,9 @@ void Riscv64JNIMacroAssembler::MoveArguments(ArrayRef<ArgumentLocation> dests, DCHECK_EQ(src.GetSize(), kObjectReferenceSize); DCHECK_EQ(dest.GetSize(), static_cast<size_t>(kRiscv64PointerSize)); } else { - DCHECK_EQ(src.GetSize(), dest.GetSize()); + DCHECK(src.GetSize() == 4u || src.GetSize() == 8u) << src.GetSize(); + DCHECK(dest.GetSize() == 4u || dest.GetSize() == 8u) << dest.GetSize(); + DCHECK_LE(src.GetSize(), dest.GetSize()); } if (dest.IsRegister()) { if (src.IsRegister() && src.GetRegister().Equals(dest.GetRegister())) { @@ -320,7 +328,7 @@ void Riscv64JNIMacroAssembler::MoveArguments(ArrayRef<ArgumentLocation> dests, } src_regs &= ~get_mask(src.GetRegister()); // Allow clobbering source register. } else { - Load(dest.GetRegister(), src.GetFrameOffset(), dest.GetSize()); + Load(dest.GetRegister(), src.GetFrameOffset(), src.GetSize()); // No `jobject` conversion needed. There are enough arg registers in managed ABI // to hold all references that yield a register arg `jobject` in native ABI. DCHECK_EQ(ref, kInvalidReferenceOffset); @@ -419,16 +427,67 @@ void Riscv64JNIMacroAssembler::CallFromThread(ThreadOffset64 offset) { void Riscv64JNIMacroAssembler::TryToTransitionFromRunnableToNative( JNIMacroLabel* label, ArrayRef<const ManagedRegister> scratch_regs) { - // TODO(riscv64): Implement this. - UNUSED(label, scratch_regs); + constexpr uint32_t kNativeStateValue = Thread::StoredThreadStateValue(ThreadState::kNative); + constexpr uint32_t kRunnableStateValue = Thread::StoredThreadStateValue(ThreadState::kRunnable); + constexpr ThreadOffset64 thread_flags_offset = Thread::ThreadFlagsOffset<kRiscv64PointerSize>(); + constexpr ThreadOffset64 thread_held_mutex_mutator_lock_offset = + Thread::HeldMutexOffset<kRiscv64PointerSize>(kMutatorLock); + + DCHECK_GE(scratch_regs.size(), 2u); + XRegister scratch = scratch_regs[0].AsRiscv64().AsXRegister(); + XRegister scratch2 = scratch_regs[1].AsRiscv64().AsXRegister(); + + // CAS release, old_value = kRunnableStateValue, new_value = kNativeStateValue, no flags. + Riscv64Label retry; + __ Bind(&retry); + static_assert(thread_flags_offset.Int32Value() == 0); // LR/SC require exact address. + __ LrW(scratch, TR, /*aqrl=*/ 0u); + __ Li(scratch2, kNativeStateValue); + // If any flags are set, go to the slow path. + static_assert(kRunnableStateValue == 0u); + __ Bnez(scratch, Riscv64JNIMacroLabel::Cast(label)->AsRiscv64()); + __ ScW(scratch, scratch2, TR, /*aqrl=*/ 1u); // Release. + __ Bnez(scratch, &retry); + + // Clear `self->tlsPtr_.held_mutexes[kMutatorLock]`. + __ Stored(Zero, TR, thread_held_mutex_mutator_lock_offset.Int32Value()); } void Riscv64JNIMacroAssembler::TryToTransitionFromNativeToRunnable( JNIMacroLabel* label, ArrayRef<const ManagedRegister> scratch_regs, ManagedRegister return_reg) { - // TODO(riscv64): Implement this. - UNUSED(label, scratch_regs, return_reg); + constexpr uint32_t kNativeStateValue = Thread::StoredThreadStateValue(ThreadState::kNative); + constexpr uint32_t kRunnableStateValue = Thread::StoredThreadStateValue(ThreadState::kRunnable); + constexpr ThreadOffset64 thread_flags_offset = Thread::ThreadFlagsOffset<kRiscv64PointerSize>(); + constexpr ThreadOffset64 thread_held_mutex_mutator_lock_offset = + Thread::HeldMutexOffset<kRiscv64PointerSize>(kMutatorLock); + constexpr ThreadOffset64 thread_mutator_lock_offset = + Thread::MutatorLockOffset<kRiscv64PointerSize>(); + + DCHECK_GE(scratch_regs.size(), 2u); + DCHECK(!scratch_regs[0].AsRiscv64().Overlaps(return_reg.AsRiscv64())); + XRegister scratch = scratch_regs[0].AsRiscv64().AsXRegister(); + DCHECK(!scratch_regs[1].AsRiscv64().Overlaps(return_reg.AsRiscv64())); + XRegister scratch2 = scratch_regs[1].AsRiscv64().AsXRegister(); + + // CAS acquire, old_value = kNativeStateValue, new_value = kRunnableStateValue, no flags. + Riscv64Label retry; + __ Bind(&retry); + static_assert(thread_flags_offset.Int32Value() == 0); // LR/SC require exact address. + __ LrW(scratch, TR, /*aqrl=*/ 2u); // Acquire. + __ Li(scratch2, kNativeStateValue); + // If any flags are set, or the state is not Native, go to the slow path. + // (While the thread can theoretically transition between different Suspended states, + // it would be very unexpected to see a state other than Native at this point.) + __ Bne(scratch, scratch2, Riscv64JNIMacroLabel::Cast(label)->AsRiscv64()); + static_assert(kRunnableStateValue == 0u); + __ ScW(scratch, Zero, TR, /*aqrl=*/ 0u); + __ Bnez(scratch, &retry); + + // Set `self->tlsPtr_.held_mutexes[kMutatorLock]` to the mutator lock. + __ Loadd(scratch, TR, thread_mutator_lock_offset.Int32Value()); + __ Stored(scratch, TR, thread_held_mutex_mutator_lock_offset.Int32Value()); } void Riscv64JNIMacroAssembler::SuspendCheck(JNIMacroLabel* label) { @@ -451,7 +510,7 @@ void Riscv64JNIMacroAssembler::DeliverPendingException() { __ Loadd(RA, TR, QUICK_ENTRYPOINT_OFFSET(kRiscv64PointerSize, pDeliverException).Int32Value()); __ Jalr(RA); // Call should never return. - __ Ebreak(); + __ Unimp(); } std::unique_ptr<JNIMacroLabel> Riscv64JNIMacroAssembler::CreateLabel() { diff --git a/compiler/utils/riscv64/jni_macro_assembler_riscv64_test.cc b/compiler/utils/riscv64/jni_macro_assembler_riscv64_test.cc index aab26164f3..8aa41565d8 100644 --- a/compiler/utils/riscv64/jni_macro_assembler_riscv64_test.cc +++ b/compiler/utils/riscv64/jni_macro_assembler_riscv64_test.cc @@ -296,6 +296,8 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { static constexpr FrameOffset kInvalidReferenceOffset = JNIMacroAssembler<kArmPointerSize>::kInvalidReferenceOffset; static constexpr size_t kNativePointerSize = static_cast<size_t>(kRiscv64PointerSize); + static constexpr size_t kFloatSize = 4u; + static constexpr size_t kXlenInBytes = 8u; // Used for integral args and `double`. // Normal or @FastNative static with parameters "LIJIJILJI". // Note: This shall not spill references to the stack. The JNI compiler spills @@ -303,14 +305,14 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { ArgumentLocation move_dests1[] = { ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kNativePointerSize), // `jclass` ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kNativePointerSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kXlenInBytes), ArgumentLocation(FrameOffset(0), kNativePointerSize), - ArgumentLocation(FrameOffset(8), 2 * kVRegSize), - ArgumentLocation(FrameOffset(16), kVRegSize), + ArgumentLocation(FrameOffset(8), kXlenInBytes), + ArgumentLocation(FrameOffset(16), kXlenInBytes), }; ArgumentLocation move_srcs1[] = { ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A0), kNativePointerSize), // `jclass` @@ -346,7 +348,7 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { "ld t5, 76(sp)\n" "sd t5, 8(sp)\n" "lw t5, 84(sp)\n" - "sw t5, 16(sp)\n" + "sd t5, 16(sp)\n" "mv a7, a6\n" "mv a6, a5\n" "mv a5, a4\n" @@ -358,20 +360,39 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { "2:\n" "mv a1, a0\n"; + // Normal or @FastNative static with parameters "LIJIJILJI" - spill references. + ArgumentLocation move_dests1_spill_refs[] = { + ArgumentLocation(FrameOffset(40), kVRegSize), + ArgumentLocation(FrameOffset(72), kVRegSize), + }; + ArgumentLocation move_srcs1_spill_refs[] = { + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kVRegSize), + }; + FrameOffset move_refs1_spill_refs[] { + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + }; + __ MoveArguments(ArrayRef<ArgumentLocation>(move_dests1_spill_refs), + ArrayRef<ArgumentLocation>(move_srcs1_spill_refs), + ArrayRef<FrameOffset>(move_refs1_spill_refs)); + expected += "sw a1, 40(sp)\n" + "sw a7, 72(sp)\n"; + // Normal or @FastNative with parameters "LLIJIJIJLI" (first is `this`). // Note: This shall not spill references to the stack. The JNI compiler spills // references in an separate initial pass before moving arguments and creating `jobject`s. ArgumentLocation move_dests2[] = { ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kNativePointerSize), ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kNativePointerSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kVRegSize), - ArgumentLocation(FrameOffset(0), 2 * kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kXlenInBytes), + ArgumentLocation(FrameOffset(0), kXlenInBytes), ArgumentLocation(FrameOffset(8), kNativePointerSize), - ArgumentLocation(FrameOffset(16), kVRegSize), + ArgumentLocation(FrameOffset(16), kXlenInBytes), }; ArgumentLocation move_srcs2[] = { ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kVRegSize), @@ -413,27 +434,27 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { "2:\n" "sd t5, 8(sp)\n" "lw t5, 88(sp)\n" - "sw t5, 16(sp)\n"; + "sd t5, 16(sp)\n"; // Normal or @FastNative static with parameters "FDFDFDFDFDIJIJIJL". ArgumentLocation move_dests3[] = { ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kNativePointerSize), // `jclass` - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA0), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA1), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA2), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA3), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA4), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA5), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA6), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA7), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), 2 * kVRegSize), - ArgumentLocation(FrameOffset(0), kVRegSize), - ArgumentLocation(FrameOffset(8), 2 * kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA0), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA1), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA2), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA3), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA4), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA5), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA6), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA7), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kXlenInBytes), + ArgumentLocation(FrameOffset(0), kXlenInBytes), + ArgumentLocation(FrameOffset(8), kXlenInBytes), ArgumentLocation(FrameOffset(16), kNativePointerSize), }; ArgumentLocation move_srcs3[] = { @@ -480,7 +501,7 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { ArrayRef<ArgumentLocation>(move_srcs3), ArrayRef<FrameOffset>(move_refs3)); // FP args in FA0-FA7 do not move. - expected += "sw a5, 0(sp)\n" + expected += "sd a5, 0(sp)\n" "sd a6, 8(sp)\n" "beqz a7, 1f\n" "addi a7, sp, 88\n" @@ -496,24 +517,24 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { // @CriticalNative with parameters "DFDFDFDFIDJIJFDIIJ". ArgumentLocation move_dests4[] = { - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA0), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA1), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA2), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA3), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA4), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA5), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA6), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA7), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A0), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), 2 * kVRegSize), - ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kVRegSize), - ArgumentLocation(FrameOffset(0), kVRegSize), - ArgumentLocation(FrameOffset(8), 2 * kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA0), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA1), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA2), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA3), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA4), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA5), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA6), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA7), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A0), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kFloatSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kXlenInBytes), + ArgumentLocation(FrameOffset(0), kXlenInBytes), + ArgumentLocation(FrameOffset(8), kXlenInBytes), }; ArgumentLocation move_srcs4[] = { ArgumentLocation(Riscv64ManagedRegister::FromFRegister(FA0), 2 * kVRegSize), @@ -559,7 +580,7 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { ArrayRef<ArgumentLocation>(move_srcs4), ArrayRef<FrameOffset>(move_refs4)); // FP args in FA0-FA7 and integral args in A2-A4 do not move. - expected += "sw a6, 0(sp)\n" + expected += "sd a6, 0(sp)\n" "sd a7, 8(sp)\n" "mv a0, a1\n" "ld a1, 92(sp)\n" @@ -567,6 +588,59 @@ TEST_F(JniMacroAssemblerRiscv64Test, MoveArguments) { "mv a7, a5\n" "lw a5, 112(sp)\n"; + // @CriticalNative with parameters "JIJIJIJIJI". + ArgumentLocation move_dests5[] = { + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A0), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kXlenInBytes), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), kXlenInBytes), + ArgumentLocation(FrameOffset(0), kXlenInBytes), + ArgumentLocation(FrameOffset(8), kXlenInBytes), + }; + ArgumentLocation move_srcs5[] = { + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A1), 2 * kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A2), kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A3), 2 * kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A4), kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A5), 2 * kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A6), kVRegSize), + ArgumentLocation(Riscv64ManagedRegister::FromXRegister(A7), 2 * kVRegSize), + ArgumentLocation(FrameOffset(84), kVRegSize), + ArgumentLocation(FrameOffset(88), 2 * kVRegSize), + ArgumentLocation(FrameOffset(96), kVRegSize), + }; + FrameOffset move_refs5[] { + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + FrameOffset(kInvalidReferenceOffset), + }; + __ MoveArguments(ArrayRef<ArgumentLocation>(move_dests5), + ArrayRef<ArgumentLocation>(move_srcs5), + ArrayRef<FrameOffset>(move_refs5)); + expected += "ld t5, 88(sp)\n" + "sd t5, 0(sp)\n" + "lw t5, 96(sp)\n" + "sd t5, 8(sp)\n" + "mv a0, a1\n" + "mv a1, a2\n" + "mv a2, a3\n" + "mv a3, a4\n" + "mv a4, a5\n" + "mv a5, a6\n" + "mv a6, a7\n" + "lw a7, 84(sp)\n"; + DriverStr(expected, "MoveArguments"); } @@ -668,6 +742,60 @@ TEST_F(JniMacroAssemblerRiscv64Test, Call) { DriverStr(expected, "Call"); } +TEST_F(JniMacroAssemblerRiscv64Test, Transitions) { + std::string expected; + + constexpr uint32_t kNativeStateValue = Thread::StoredThreadStateValue(ThreadState::kNative); + constexpr uint32_t kRunnableStateValue = Thread::StoredThreadStateValue(ThreadState::kRunnable); + static_assert(kRunnableStateValue == 0u); + constexpr ThreadOffset64 thread_flags_offset = Thread::ThreadFlagsOffset<kRiscv64PointerSize>(); + static_assert(thread_flags_offset.SizeValue() == 0u); + constexpr size_t thread_held_mutex_mutator_lock_offset = + Thread::HeldMutexOffset<kRiscv64PointerSize>(kMutatorLock).SizeValue(); + constexpr size_t thread_mutator_lock_offset = + Thread::MutatorLockOffset<kRiscv64PointerSize>().SizeValue(); + + std::unique_ptr<JNIMacroLabel> slow_path = __ CreateLabel(); + std::unique_ptr<JNIMacroLabel> resume = __ CreateLabel(); + + const ManagedRegister raw_scratch_regs[] = { AsManaged(T0), AsManaged(T1) }; + const ArrayRef<const ManagedRegister> scratch_regs(raw_scratch_regs); + + __ TryToTransitionFromRunnableToNative(slow_path.get(), scratch_regs); + expected += "1:\n" + "lr.w t0, (s1)\n" + "li t1, " + std::to_string(kNativeStateValue) + "\n" + "bnez t0, 4f\n" + "sc.w.rl t0, t1, (s1)\n" + "bnez t0, 1b\n" + "addi t6, s1, 0x7f8\n" + "sd x0, " + std::to_string(thread_held_mutex_mutator_lock_offset - 0x7f8u) + "(t6)\n"; + + __ TryToTransitionFromNativeToRunnable(slow_path.get(), scratch_regs, AsManaged(A0)); + expected += "2:\n" + "lr.w.aq t0, (s1)\n" + "li t1, " + std::to_string(kNativeStateValue) + "\n" + "bne t0, t1, 4f\n" + "sc.w t0, x0, (s1)\n" + "bnez t0, 2b\n" + "ld t0, " + std::to_string(thread_mutator_lock_offset) + "(s1)\n" + "addi t6, s1, 0x7f8\n" + "sd t0, " + std::to_string(thread_held_mutex_mutator_lock_offset - 0x7f8u) + "(t6)\n"; + + __ Bind(resume.get()); + expected += "3:\n"; + + expected += EmitRet(); + + __ Bind(slow_path.get()); + expected += "4:\n"; + + __ Jump(resume.get()); + expected += "j 3b"; + + DriverStr(expected, "SuspendCheck"); +} + TEST_F(JniMacroAssemblerRiscv64Test, SuspendCheck) { std::string expected; @@ -716,7 +844,7 @@ TEST_F(JniMacroAssemblerRiscv64Test, Exception) { expected += "ld a0, " + std::to_string(exception_offset.Int32Value()) + "(s1)\n" "ld ra, " + std::to_string(deliver_offset.Int32Value()) + "(s1)\n" "jalr ra\n" - "ebreak\n"; + "unimp\n"; DriverStr(expected, "Exception"); } diff --git a/runtime/arch/arm64/asm_support_arm64.S b/runtime/arch/arm64/asm_support_arm64.S index 4ef6ce07a9..86d8649df4 100644 --- a/runtime/arch/arm64/asm_support_arm64.S +++ b/runtime/arch/arm64/asm_support_arm64.S @@ -419,7 +419,7 @@ stxr w10, w11, [x8] cbnz w10, 1b // If the store failed, retry. ret -2: // w10: original lock word, w9: thread id, w11: w10 ^ w11 +2: // w10: original lock word, w9: thread id, w11: w10 ^ w9 // Check lock word state and thread id together, tst w11, #(LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) b.ne \slow_lock diff --git a/runtime/arch/riscv64/asm_support_riscv64.S b/runtime/arch/riscv64/asm_support_riscv64.S index fa7f05d4b7..55f917219f 100644 --- a/runtime/arch/riscv64/asm_support_riscv64.S +++ b/runtime/arch/riscv64/asm_support_riscv64.S @@ -583,4 +583,90 @@ DELIVER_PENDING_EXCEPTION .endm +// Macro to emit a single LUI to load the given value while checking that the low 12 bits are zero. +.macro LUI_VALUE reg, value + .if (\value & 0xfff) != 0 + .error "Cannot use LUI to materialize a value with some of the low 12 bits set." + .endif + lui \reg, (\value) >> 12 +.endm + + +// Locking is needed for both managed code and JNI stubs. +.macro LOCK_OBJECT_FAST_PATH obj, slow_lock, can_be_null + // Use scratch registers T1-T6 as temporaries. + // Note: T0 is used as the argument register for `art_jni_lock_object` and passed as `obj`. + lw t2, THREAD_ID_OFFSET(xSELF) + .if \can_be_null + beqz \obj, \slow_lock + .endif + addi t1, \obj, MIRROR_OBJECT_LOCK_WORD_OFFSET // Exclusive load/store has no offset. +1: + // Note: The LR/SC sequence must be at most 16 instructions, so we cannot have the + // recursive locking in a slow-path as on other architectures. + lr.w.aq t3, (t1) // Acquire needed only in most common case. + LUI_VALUE t5, LOCK_WORD_GC_STATE_MASK_SHIFTED // Prepare mask for testing non-gc bits. + xor t4, t3, t2 // Prepare the value to store if unlocked + // (thread id, count of 0 and preserved read barrier bits), + // or prepare to compare thread id for recursive lock check + // (lock_word.ThreadId() ^ self->ThreadId()). + or t6, t5, t3 // Test the non-gc bits. + beq t6, t5, 2f // Check if unlocked. + // Check lock word state and thread id together, + LUI_VALUE \ + t5, 0xffffffff ^ (LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) + or t6, t5, t4 + bne t6, t5, \slow_lock + LUI_VALUE t4, LOCK_WORD_THIN_LOCK_COUNT_ONE // Increment the recursive lock count. + addw t4, t3, t4 + LUI_VALUE t5, LOCK_WORD_THIN_LOCK_COUNT_MASK_SHIFTED // Test the new thin lock count. + and t5, t4, t5 + beqz t5, \slow_lock // Zero as the new count indicates overflow, go slow path. +2: + // Store the prepared value: + // - if unlocked, original lock word plus thread id, + // - if already locked, original lock word plus incremented lock count. + sc.w t3, t4, (t1) + bnez t3, 1b // If the store failed, retry. + ret +.endm + +// Unlocking is needed for both managed code and JNI stubs. +.macro UNLOCK_OBJECT_FAST_PATH obj, slow_unlock, can_be_null + // Use scratch registers T1-T6 as temporaries. + // Note: T0 is used as the argument register for `art_jni_unlock_object` and passed as `obj`. + lw t2, THREAD_ID_OFFSET(xSELF) + .if \can_be_null + beqz \obj, \slow_unlock + .endif + addi t1, \obj, MIRROR_OBJECT_LOCK_WORD_OFFSET // Exclusive load/store has no offset. +1: + // Note: Without read barriers, we could do plain LW here but there is no store-release + // other than SC on riscv64, so we do this with LR/SC for all cofigurations. + // Note: The LR/SC sequence must be at most 16 instructions, so we cannot have the + // recursive unlocking in a slow-path as on other architectures. + lr.w t3, (t1) + LUI_VALUE t5, LOCK_WORD_GC_STATE_MASK_SHIFTED // Prepare mask for testing non-gc bits. + xor t4, t3, t2 // Prepare the value to store if simply locked + // (mostly 0s, and preserved read barrier bits), + // or prepare to compare thread id for recursive lock check + // (lock_word.ThreadId() ^ self->ThreadId()). + or t6, t5, t4 // Test the non-gc bits. + beq t6, t5, 2f // Simply locked by this thread? + // Check lock word state and thread id together. + LUI_VALUE \ + t5, 0xffffffff ^ (LOCK_WORD_STATE_MASK_SHIFTED | LOCK_WORD_THIN_LOCK_OWNER_MASK_SHIFTED) + or t6, t5, t4 + bne t6, t5, \slow_unlock + LUI_VALUE t4, LOCK_WORD_THIN_LOCK_COUNT_ONE // Decrement the recursive lock count. + subw t4, t3, t4 +2: + // Store the prepared value: + // - if simply locked, original lock word with removed thread id, + // - if recursively locked, original lock word plus decremented lock count. + sc.w.rl t3, t4, (t1) // Need to use atomic instructions for read barrier. + bnez t3, 1b // If the store failed, retry. + ret +.endm + #endif // ART_RUNTIME_ARCH_RISCV64_ASM_SUPPORT_RISCV64_S_ diff --git a/runtime/arch/riscv64/jni_entrypoints_riscv64.S b/runtime/arch/riscv64/jni_entrypoints_riscv64.S index 3bed1a29a6..92289be935 100644 --- a/runtime/arch/riscv64/jni_entrypoints_riscv64.S +++ b/runtime/arch/riscv64/jni_entrypoints_riscv64.S @@ -16,15 +16,6 @@ #include "asm_support_riscv64.S" -UNDEFINED art_jni_method_start -UNDEFINED art_jni_method_end -UNDEFINED art_jni_read_barrier -UNDEFINED art_jni_method_entry_hook -UNDEFINED art_jni_lock_object_no_inline -UNDEFINED art_jni_lock_object -UNDEFINED art_jni_unlock_object_no_inline -UNDEFINED art_jni_unlock_object - // 8 argument GPRS: a0 - a7 and 8 argument FPRs: fa0 - fa7 #define ALL_ARGS_SIZE (8 * (8 + 8)) @@ -35,52 +26,95 @@ UNDEFINED art_jni_unlock_object INCREASE_FRAME (ALL_ARGS_SIZE + \extra_space) // Argument GPRs a0 - a7. - SAVE_GPR a0, (8*0) - SAVE_GPR a1, (8*1) - SAVE_GPR a2, (8*2) - SAVE_GPR a3, (8*3) - SAVE_GPR a4, (8*4) - SAVE_GPR a5, (8*5) - SAVE_GPR a6, (8*6) - SAVE_GPR a7, (8*7) + sd a0, (8*0)(sp) + sd a1, (8*1)(sp) + sd a2, (8*2)(sp) + sd a3, (8*3)(sp) + sd a4, (8*4)(sp) + sd a5, (8*5)(sp) + sd a6, (8*6)(sp) + sd a7, (8*7)(sp) // Argument FPRs fa0 - fa7. - SAVE_FPR fa0, (8*8) - SAVE_FPR fa1, (8*9) - SAVE_FPR fa2, (8*10) - SAVE_FPR fa3, (8*11) - SAVE_FPR fa4, (8*12) - SAVE_FPR fa5, (8*13) - SAVE_FPR fa6, (8*14) - SAVE_FPR fa7, (8*15) + fsd fa0, (8*8)(sp) + fsd fa1, (8*9)(sp) + fsd fa2, (8*10)(sp) + fsd fa3, (8*11)(sp) + fsd fa4, (8*12)(sp) + fsd fa5, (8*13)(sp) + fsd fa6, (8*14)(sp) + fsd fa7, (8*15)(sp) .endm .macro RESTORE_ALL_ARGS_DECREASE_FRAME extra_space // Argument GPRs a0 - a7. - RESTORE_GPR a0, (8*0) - RESTORE_GPR a1, (8*1) - RESTORE_GPR a2, (8*2) - RESTORE_GPR a3, (8*3) - RESTORE_GPR a4, (8*4) - RESTORE_GPR a5, (8*5) - RESTORE_GPR a6, (8*6) - RESTORE_GPR a7, (8*7) + ld a0, (8*0)(sp) + ld a1, (8*1)(sp) + ld a2, (8*2)(sp) + ld a3, (8*3)(sp) + ld a4, (8*4)(sp) + ld a5, (8*5)(sp) + ld a6, (8*6)(sp) + ld a7, (8*7)(sp) // Argument FPRs fa0 - fa7. - RESTORE_FPR fa0, (8*8) - RESTORE_FPR fa1, (8*9) - RESTORE_FPR fa2, (8*10) - RESTORE_FPR fa3, (8*11) - RESTORE_FPR fa4, (8*12) - RESTORE_FPR fa5, (8*13) - RESTORE_FPR fa6, (8*14) - RESTORE_FPR fa7, (8*15) + fld fa0, (8*8)(sp) + fld fa1, (8*9)(sp) + fld fa2, (8*10)(sp) + fld fa3, (8*11)(sp) + fld fa4, (8*12)(sp) + fld fa5, (8*13)(sp) + fld fa6, (8*14)(sp) + fld fa7, (8*15)(sp) DECREASE_FRAME (ALL_ARGS_SIZE + \extra_space) .endm +.macro JNI_SAVE_MANAGED_ARGS_TRAMPOLINE name, cxx_name, arg1 = "none" + .extern \cxx_name +ENTRY \name + // Save args and RA. + SAVE_ALL_ARGS_INCREASE_FRAME /*padding*/ 8 + /*RA*/ 8 + SAVE_GPR ra, (ALL_ARGS_SIZE + /*padding*/ 8) + // Call `cxx_name()`. + .ifnc \arg1, none + mv a0, \arg1 + .endif + call \cxx_name + // Restore RA and args and return. + RESTORE_GPR ra, (ALL_ARGS_SIZE + /*padding*/ 8) + RESTORE_ALL_ARGS_DECREASE_FRAME /*padding*/ 8 + /*RA*/ 8 + ret +END \name +.endm + + +.macro JNI_SAVE_RETURN_VALUE_TRAMPOLINE name, cxx_name, arg1, arg2 = "none" + .extern \cxx_name +ENTRY \name + // Save return registers and return address. + INCREASE_FRAME 32 + sd a0, 0(sp) + fsd fa0, 8(sp) + SAVE_GPR ra, 24 + // Call `cxx_name()`. + mv a0, \arg1 + .ifnc \arg2, none + mv a1, \arg2 + .endif + call \cxx_name + // Restore result registers and return. + ld a0, 0(sp) + fld fa0, 8(sp) + RESTORE_GPR ra, 24 + DECREASE_FRAME 32 + ret +END \name +.endm + + // JNI dlsym lookup stub. .extern artFindNativeMethod .extern artFindNativeMethodRunnable @@ -413,3 +447,107 @@ ENTRY art_jni_dlsym_lookup_critical_stub DELIVER_PENDING_EXCEPTION_FRAME_READY END art_jni_dlsym_lookup_critical_stub + + /* + * Read barrier for the method's declaring class needed by JNI stub for static methods. + * (We're using a pointer to the declaring class in `ArtMethod` as `jclass`.) + */ +// The method argument is already in a0 for call to `artJniReadBarrier(ArtMethod*)`. +JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_read_barrier, artJniReadBarrier + + /* + * Trampoline to `artJniMethodStart()` that preserves all managed arguments. + */ +JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_start, artJniMethodStart, xSELF + + /* + * Trampoline to `artJniMethodEntryHook` that preserves all managed arguments. + */ +JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_method_entry_hook, artJniMethodEntryHook, xSELF + + /* + * Trampoline to `artJniMonitoredMethodStart()` that preserves all managed arguments. + */ +JNI_SAVE_MANAGED_ARGS_TRAMPOLINE art_jni_monitored_method_start, artJniMonitoredMethodStart, xSELF + + /* + * Trampoline to `artJniMethodEnd()` that preserves all return registers. + */ +JNI_SAVE_RETURN_VALUE_TRAMPOLINE art_jni_method_end, artJniMethodEnd, xSELF + + /* + * Trampoline to `artJniMonitoredMethodEnd()` that preserves all return registers. + */ +JNI_SAVE_RETURN_VALUE_TRAMPOLINE art_jni_monitored_method_end, artJniMonitoredMethodEnd, xSELF + + /* + * Entry from JNI stub that tries to lock the object in a fast path and + * calls `artLockObjectFromCode()` (the same as for managed code) for the + * difficult cases, may block for GC. + * Custom calling convention: + * T0 holds the non-null object to lock. + * Callee-save registers have been saved and can be used as temporaries. + * All argument registers need to be preserved. + */ +ENTRY art_jni_lock_object + LOCK_OBJECT_FAST_PATH t0, art_jni_lock_object_no_inline, /*can_be_null*/ 0 +END art_jni_lock_object + + /* + * Entry from JNI stub that calls `artLockObjectFromCode()` + * (the same as for managed code), may block for GC. + * Custom calling convention: + * T0 holds the non-null object to lock. + * Callee-save registers have been saved and can be used as temporaries. + * All argument registers need to be preserved. + */ + .extern artLockObjectFromCode +ENTRY art_jni_lock_object_no_inline + // This is also the slow path for art_jni_lock_object. + // Save args and RA. + SAVE_ALL_ARGS_INCREASE_FRAME /*padding*/ 8 + /*RA*/ 8 + SAVE_GPR ra, (ALL_ARGS_SIZE + /*padding*/ 8) + // Call `artLockObjectFromCode()`. + mv a0, t0 // Pass the object to lock. + mv a1, xSELF // Pass Thread::Current(). + call artLockObjectFromCode // (Object* obj, Thread*) + // Restore return address. + RESTORE_GPR ra, (ALL_ARGS_SIZE + /*padding*/ 8) + // Check result. + bnez a0, 1f + // Restore register args a0-a7, fa0-fa7 and return. + RESTORE_ALL_ARGS_DECREASE_FRAME /*padding*/ 8 + /*RA*/ 8 + ret + .cfi_adjust_cfa_offset (ALL_ARGS_SIZE + /*padding*/ 8 + /*RA*/ 8) +1: + // All args are irrelevant when throwing an exception. Remove the spill area. + DECREASE_FRAME (ALL_ARGS_SIZE + /*padding*/ 8 + /*RA*/ 8) + // Make a tail call to `artDeliverPendingExceptionFromCode()`. + // Rely on the JNI transition frame constructed in the JNI stub. + mv a0, xSELF // Pass Thread::Current(). + tail artDeliverPendingExceptionFromCode // (Thread*) +END art_jni_lock_object_no_inline + + /* + * Entry from JNI stub that tries to unlock the object in a fast path and calls + * `artJniUnlockObject()` for the difficult cases. Note that failure to unlock + * is fatal, so we do not need to check for exceptions in the slow path. + * Custom calling convention: + * T0 holds the non-null object to unlock. + * Callee-save registers have been saved and can be used as temporaries. + * Return registers a0 and fa0 need to be preserved. + */ +ENTRY art_jni_unlock_object + UNLOCK_OBJECT_FAST_PATH t0, art_jni_unlock_object_no_inline, /*can_be_null*/ 0 +END art_jni_unlock_object + + /* + * Entry from JNI stub that calls `artJniUnlockObject()`. Note that failure to + * unlock is fatal, so we do not need to check for exceptions. + * Custom calling convention: + * T0 holds the non-null object to unlock. + * Callee-save registers have been saved and can be used as temporaries. + * Return registers a0 and fa0 need to be preserved. + */ + // This is also the slow path for art_jni_unlock_object. +JNI_SAVE_RETURN_VALUE_TRAMPOLINE art_jni_unlock_object_no_inline, artJniUnlockObject, t0, xSELF diff --git a/runtime/arch/riscv64/quick_entrypoints_riscv64.S b/runtime/arch/riscv64/quick_entrypoints_riscv64.S index e9eb3130bc..41408d833e 100644 --- a/runtime/arch/riscv64/quick_entrypoints_riscv64.S +++ b/runtime/arch/riscv64/quick_entrypoints_riscv64.S @@ -795,6 +795,4 @@ UNDEFINED art_quick_throw_null_pointer_exception UNDEFINED art_quick_throw_stack_overflow UNDEFINED art_quick_throw_string_bounds UNDEFINED art_quick_update_inline_cache -UNDEFINED art_jni_monitored_method_start -UNDEFINED art_jni_monitored_method_end UNDEFINED art_quick_indexof |