Mark jclass in JNI stubs in a slow path.
Test: m test-art-host-gtest
Test: testrunner.py --host --gcstress
Bug: 172332525
Change-Id: Id791a16135c8a947c317ed56ac88dac81cadfd2d
diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc
index 2fd9abd..a4be7ef 100644
--- a/compiler/jni/quick/jni_compiler.cc
+++ b/compiler/jni/quick/jni_compiler.cc
@@ -258,54 +258,20 @@
current_frame_size += main_out_arg_size;
}
- // 4. Call the read barrier for the declaring class in the method for a static call.
- // Skip this for @CriticalNative because we're not passing a `jclass` to the native method.
- // Note that we always have outgoing param space available for at least two params.
+ // 4. Check if we need to go to the slow path to emit the read barrier for the declaring class
+ // in the method for a static call.
+ // Skip this for @CriticalNative because we're not passing a `jclass` to the native method.
+ std::unique_ptr<JNIMacroLabel> jclass_read_barrier_slow_path;
+ std::unique_ptr<JNIMacroLabel> jclass_read_barrier_return;
if (kUseReadBarrier && is_static && !is_critical_native) {
- const bool kReadBarrierFastPath = true; // Always true after Mips codegen was removed.
- std::unique_ptr<JNIMacroLabel> skip_cold_path_label;
- if (kReadBarrierFastPath) {
- skip_cold_path_label = __ CreateLabel();
- // Fast path for supported targets.
- //
- // Check if gc_is_marking is set -- if it's not, we don't need
- // a read barrier so skip it.
- // Jump over the slow path if gc is marking is false.
- __ TestGcMarking(skip_cold_path_label.get(), JNIMacroUnaryCondition::kZero);
- }
+ jclass_read_barrier_slow_path = __ CreateLabel();
+ jclass_read_barrier_return = __ CreateLabel();
- // Construct slow path for read barrier:
- //
- // Call into the runtime's ReadBarrierJni and have it fix up
- // the object address if it was moved.
- //
- // TODO: Move this to an actual slow path, so that the fast path is a branch-not-taken.
+ // Check if gc_is_marking is set -- if it's not, we don't need a read barrier.
+ __ TestGcMarking(jclass_read_barrier_slow_path.get(), JNIMacroUnaryCondition::kNotZero);
- ThreadOffset<kPointerSize> read_barrier = QUICK_ENTRYPOINT_OFFSET(kPointerSize,
- pReadBarrierJni);
- main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
- // Pass the pointer to the method's declaring class as the first argument.
- DCHECK_EQ(ArtMethod::DeclaringClassOffset().SizeValue(), 0u);
- SetNativeParameter(jni_asm.get(), main_jni_conv.get(), method_register);
- main_jni_conv->Next();
- // Pass the current thread as the second argument and call.
- if (main_jni_conv->IsCurrentParamInRegister()) {
- __ GetCurrentThread(main_jni_conv->CurrentParamRegister());
- __ Call(main_jni_conv->CurrentParamRegister(), Offset(read_barrier));
- } else {
- __ GetCurrentThread(main_jni_conv->CurrentParamStackOffset());
- __ CallFromThread(read_barrier);
- }
- if (is_synchronized) {
- // Reload the method pointer in the slow path because it is needed below.
- __ Load(method_register,
- FrameOffset(current_out_arg_size + mr_conv->MethodStackOffset().SizeValue()),
- static_cast<size_t>(kPointerSize));
- }
-
- if (kReadBarrierFastPath) {
- __ Bind(skip_cold_path_label.get());
- }
+ // If marking, the slow path returns after the check.
+ __ Bind(jclass_read_barrier_return.get());
}
// 5. Call into appropriate JniMethodStart passing Thread* so that transition out of Runnable
@@ -628,7 +594,63 @@
DCHECK_EQ(jni_asm->cfi().GetCurrentCFAOffset(), static_cast<int>(current_frame_size));
}
- // 17. Finalize code generation
+ // 17. Read barrier slow path for the declaring class in the method for a static call.
+ // Skip this for @CriticalNative because we're not passing a `jclass` to the native method.
+ if (kUseReadBarrier && is_static && !is_critical_native) {
+ __ Bind(jclass_read_barrier_slow_path.get());
+
+ // We do the marking check after adjusting for outgoing arguments. That ensures that
+ // we have space available for at least two params in case we need to pass the read
+ // barrier parameters on stack (only x86). But that means we must adjust the CFI
+ // offset accordingly as it does not include the outgoing args after `RemoveFrame().
+ if (main_out_arg_size != 0) {
+ // Note: The DW_CFA_def_cfa_offset emitted by `RemoveFrame()` above
+ // is useless when it is immediatelly overridden here but avoiding
+ // it adds a lot of code complexity for minimal gain.
+ jni_asm->cfi().AdjustCFAOffset(main_out_arg_size);
+ }
+
+ // We enter the slow path with the method register unclobbered.
+ method_register = mr_conv->MethodRegister();
+
+ // Construct slow path for read barrier:
+ //
+ // Call into the runtime's ReadBarrierJni and have it fix up
+ // the object address if it was moved.
+
+ ThreadOffset<kPointerSize> read_barrier = QUICK_ENTRYPOINT_OFFSET(kPointerSize,
+ pReadBarrierJni);
+ main_jni_conv->ResetIterator(FrameOffset(main_out_arg_size));
+ // Pass the pointer to the method's declaring class as the first argument.
+ DCHECK_EQ(ArtMethod::DeclaringClassOffset().SizeValue(), 0u);
+ SetNativeParameter(jni_asm.get(), main_jni_conv.get(), method_register);
+ main_jni_conv->Next();
+ // Pass the current thread as the second argument and call.
+ if (main_jni_conv->IsCurrentParamInRegister()) {
+ __ GetCurrentThread(main_jni_conv->CurrentParamRegister());
+ __ Call(main_jni_conv->CurrentParamRegister(), Offset(read_barrier));
+ } else {
+ __ GetCurrentThread(main_jni_conv->CurrentParamStackOffset());
+ __ CallFromThread(read_barrier);
+ }
+ if (is_synchronized) {
+ // Reload the method pointer in the slow path because it is needed
+ // as an argument for the `JniMethodStartSynchronized`.
+ __ Load(method_register,
+ FrameOffset(main_out_arg_size + mr_conv->MethodStackOffset().SizeValue()),
+ static_cast<size_t>(kPointerSize));
+ }
+
+ // Return to main path.
+ __ Jump(jclass_read_barrier_return.get());
+
+ // Undo the CFI offset adjustment at the start of the slow path.
+ if (main_out_arg_size != 0) {
+ jni_asm->cfi().AdjustCFAOffset(-main_out_arg_size);
+ }
+ }
+
+ // 18. Finalize code generation
__ FinalizeCode();
size_t cs = __ CodeSize();
std::vector<uint8_t> managed_code(cs);