diff options
author | Vladimir Marko <vmarko@google.com> | 2021-01-27 09:49:29 +0000 |
---|---|---|
committer | Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com> | 2021-01-27 09:49:29 +0000 |
commit | a19ee52e3ae7edab7af8b22c7bb5dc2a3bf09b0a (patch) | |
tree | c25bc624819d5e09cd0a81123d6cbbe9f29c0b60 /compiler | |
parent | 9093699266d680074b7549b4ad17b162ad89e30f (diff) | |
parent | 853367b85d8e58fa7de8b8b7bc9810da9147fcd8 (diff) |
arm: Fix and improve JNI frame entry/exit. am: 853367b85d
Original change: https://android-review.googlesource.com/c/platform/art/+/1556126
MUST ONLY BE SUBMITTED BY AUTOMERGER
Change-Id: Ib267250f991d39f0625e2ccaa23a6ca004c8560b
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/jni/jni_cfi_test_expected.inc | 13 | ||||
-rw-r--r-- | compiler/utils/arm/jni_macro_assembler_arm_vixl.cc | 62 |
2 files changed, 68 insertions, 7 deletions
diff --git a/compiler/jni/jni_cfi_test_expected.inc b/compiler/jni/jni_cfi_test_expected.inc index 49e5a09c2e5..02b1c7a3522 100644 --- a/compiler/jni/jni_cfi_test_expected.inc +++ b/compiler/jni/jni_cfi_test_expected.inc @@ -15,8 +15,9 @@ static constexpr uint8_t expected_cfi_kThumb2[] = { 0x4E, 0x0E, 0xA0, 0x01, 0x42, 0x0E, 0x80, 0x01, 0x0A, 0x42, 0x0E, 0x5C, 0x44, 0x0E, 0x1C, 0x06, 0x50, 0x06, 0x51, 0x06, 0x52, 0x06, 0x53, 0x06, 0x54, 0x06, 0x55, 0x06, 0x56, 0x06, 0x57, 0x06, 0x58, 0x06, 0x59, 0x06, - 0x5A, 0x06, 0x5B, 0x06, 0x5C, 0x06, 0x5D, 0x06, 0x5E, 0x06, 0x5F, 0x4A, - 0x0B, 0x0E, 0x80, 0x01, + 0x5A, 0x06, 0x5B, 0x06, 0x5C, 0x06, 0x5D, 0x06, 0x5E, 0x06, 0x5F, 0x44, + 0x0E, 0x00, 0xC5, 0xC6, 0xC7, 0xC8, 0xCA, 0xCB, 0xCE, 0x46, 0x0B, 0x0E, + 0x80, 0x01, }; // 0x00000000: push {r5,r6,r7,r8,r10,r11,lr} // 0x00000004: .cfi_def_cfa_offset: 28 @@ -78,6 +79,14 @@ static constexpr uint8_t expected_cfi_kThumb2[] = { // 0x00000020: .cfi_restore_extended: r94 // 0x00000020: .cfi_restore_extended: r95 // 0x00000020: pop {r5,r6,r7,r8,r10,r11,lr} +// 0x00000024: .cfi_def_cfa_offset: 0 +// 0x00000024: .cfi_restore: r5 +// 0x00000024: .cfi_restore: r6 +// 0x00000024: .cfi_restore: r7 +// 0x00000024: .cfi_restore: r8 +// 0x00000024: .cfi_restore: r10 +// 0x00000024: .cfi_restore: r11 +// 0x00000024: .cfi_restore: r14 // 0x00000024: ldr r8, [tr, #48] ; is_gc_marking // 0x00000028: bx lr // 0x0000002a: .cfi_restore_state diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc index 85b253c4c57..38167fbeb0c 100644 --- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc +++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc @@ -98,7 +98,17 @@ void ArmVIXLJNIMacroAssembler::BuildFrame(size_t frame_size, fp_spill_mask |= 1 << reg.AsArm().AsSRegister(); } } - if (core_spill_mask != 0u) { + if (core_spill_mask == (1u << lr.GetCode()) && + fp_spill_mask == 0u && + frame_size == 2 * kFramePointerSize && + !method_reg.IsRegister()) { + // Special case: Only LR to push and one word to skip. Do this with a single + // 16-bit PUSH instruction by arbitrarily pushing r3 (without CFI for r3). + core_spill_mask |= 1u << r3.GetCode(); + ___ Push(RegisterList(core_spill_mask)); + cfi().AdjustCFAOffset(2 * kFramePointerSize); + cfi().RelOffset(DWARFReg(lr), kFramePointerSize); + } else if (core_spill_mask != 0u) { ___ Push(RegisterList(core_spill_mask)); cfi().AdjustCFAOffset(POPCOUNT(core_spill_mask) * kFramePointerSize); cfi().RelOffsetForMany(DWARFReg(r0), 0, core_spill_mask, kFramePointerSize); @@ -131,7 +141,6 @@ void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size, ArrayRef<const ManagedRegister> callee_save_regs, bool may_suspend) { CHECK_ALIGNED(frame_size, kAapcsStackAlignment); - cfi().RememberState(); // Compute callee saves to pop. RegList core_spill_mask = 0u; @@ -144,6 +153,32 @@ void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size, } } + // Pop LR to PC unless we need to emit some read barrier code just before returning. + bool emit_code_before_return = + (kEmitCompilerReadBarrier && kUseBakerReadBarrier) && + (may_suspend || (kIsDebugBuild && emit_run_time_checks_in_debug_mode_)); + if ((core_spill_mask & (1u << lr.GetCode())) != 0u && !emit_code_before_return) { + DCHECK_EQ(core_spill_mask & (1u << pc.GetCode()), 0u); + core_spill_mask ^= (1u << lr.GetCode()) | (1u << pc.GetCode()); + } + + // If there are no FP registers to pop and we pop PC, we can avoid emitting any CFI. + if (fp_spill_mask == 0u && (core_spill_mask & (1u << pc.GetCode())) != 0u) { + if (frame_size == POPCOUNT(core_spill_mask) * kFramePointerSize) { + // Just pop all registers and avoid CFI. + ___ Pop(RegisterList(core_spill_mask)); + return; + } else if (frame_size == 8u && core_spill_mask == (1u << pc.GetCode())) { + // Special case: One word to ignore and one to pop to PC. We are free to clobber the + // caller-save register r3 on return, so use a 16-bit POP instruction and avoid CFI. + ___ Pop(RegisterList((1u << r3.GetCode()) | (1u << pc.GetCode()))); + return; + } + } + + // We shall need to adjust CFI and restore it after the frame exit sequence. + cfi().RememberState(); + // Decrease frame to start of callee saves. size_t pop_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask); CHECK_GE(frame_size, pop_values * kFramePointerSize); @@ -160,9 +195,24 @@ void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size, cfi().RestoreMany(DWARFReg(s0), fp_spill_mask); } - // Pop core callee saves and LR. + // Pop core callee saves. if (core_spill_mask != 0u) { - ___ Pop(RegisterList(core_spill_mask)); + if (IsPowerOfTwo(core_spill_mask) && + core_spill_mask != (1u << pc.GetCode()) && + WhichPowerOf2(core_spill_mask) >= 8) { + // FIXME(vixl): vixl fails to transform a pop with single high register + // to a post-index STR (also known as POP encoding T3) and emits the LDMIA + // (also known as POP encoding T2) which is UNPREDICTABLE for 1 register. + // So we have to explicitly do the transformation here. Bug: 178048807 + vixl32::Register reg(WhichPowerOf2(core_spill_mask)); + ___ Ldr(reg, MemOperand(sp, kFramePointerSize, PostIndex)); + } else { + ___ Pop(RegisterList(core_spill_mask)); + } + if ((core_spill_mask & (1u << pc.GetCode())) == 0u) { + cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(core_spill_mask)); + cfi().RestoreMany(DWARFReg(r0), core_spill_mask); + } } if (kEmitCompilerReadBarrier && kUseBakerReadBarrier) { @@ -193,7 +243,9 @@ void ArmVIXLJNIMacroAssembler::RemoveFrame(size_t frame_size, } // Return to LR. - ___ Bx(vixl32::lr); + if ((core_spill_mask & (1u << pc.GetCode())) == 0u) { + ___ Bx(vixl32::lr); + } // The CFI should be restored for any code that follows the exit block. cfi().RestoreState(); |