summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Vladimir Marko <vmarko@google.com> 2023-06-21 09:24:30 +0000
committer VladimĂ­r Marko <vmarko@google.com> 2023-06-27 12:37:04 +0000
commit3c405060efadc453f9bc1cabe376de06e8325868 (patch)
treeb064a88970c826c6c85d60bc33b1694c2efc3b8a
parent2ea3c8b7eabb027f1743caba276073ce08cd831d (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.cc2
-rw-r--r--compiler/jni/quick/calling_convention.cc19
-rw-r--r--compiler/jni/quick/calling_convention.h14
-rw-r--r--compiler/jni/quick/jni_compiler.cc4
-rw-r--r--compiler/jni/quick/riscv64/calling_convention_riscv64.cc19
-rw-r--r--compiler/jni/quick/riscv64/calling_convention_riscv64.h1
-rw-r--r--compiler/utils/riscv64/jni_macro_assembler_riscv64.cc73
-rw-r--r--compiler/utils/riscv64/jni_macro_assembler_riscv64_test.cc234
-rw-r--r--runtime/arch/arm64/asm_support_arm64.S2
-rw-r--r--runtime/arch/riscv64/asm_support_riscv64.S86
-rw-r--r--runtime/arch/riscv64/jni_entrypoints_riscv64.S220
-rw-r--r--runtime/arch/riscv64/quick_entrypoints_riscv64.S2
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