Home | History | Annotate | Download | only in arm
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include "jni_macro_assembler_arm.h"
     18 
     19 #include <algorithm>
     20 
     21 #include "assembler_thumb2.h"
     22 #include "base/arena_allocator.h"
     23 #include "base/bit_utils.h"
     24 #include "base/logging.h"
     25 #include "entrypoints/quick/quick_entrypoints.h"
     26 #include "offsets.h"
     27 #include "thread.h"
     28 
     29 namespace art {
     30 namespace arm {
     31 
     32 constexpr size_t kFramePointerSize = static_cast<size_t>(kArmPointerSize);
     33 
     34 // Slowpath entered when Thread::Current()->_exception is non-null
     35 class ArmExceptionSlowPath FINAL : public SlowPath {
     36  public:
     37   ArmExceptionSlowPath(ArmManagedRegister scratch, size_t stack_adjust)
     38       : scratch_(scratch), stack_adjust_(stack_adjust) {
     39   }
     40   void Emit(Assembler *sp_asm) OVERRIDE;
     41  private:
     42   const ArmManagedRegister scratch_;
     43   const size_t stack_adjust_;
     44 };
     45 
     46 ArmJNIMacroAssembler::ArmJNIMacroAssembler(ArenaAllocator* arena, InstructionSet isa) {
     47   switch (isa) {
     48     case kArm:
     49     case kThumb2:
     50       asm_.reset(new (arena) Thumb2Assembler(arena));
     51       break;
     52 
     53     default:
     54       LOG(FATAL) << isa;
     55       UNREACHABLE();
     56   }
     57 }
     58 
     59 ArmJNIMacroAssembler::~ArmJNIMacroAssembler() {
     60 }
     61 
     62 size_t ArmJNIMacroAssembler::CodeSize() const {
     63   return asm_->CodeSize();
     64 }
     65 
     66 DebugFrameOpCodeWriterForAssembler& ArmJNIMacroAssembler::cfi() {
     67   return asm_->cfi();
     68 }
     69 
     70 void ArmJNIMacroAssembler::FinalizeCode() {
     71   asm_->FinalizeCode();
     72 }
     73 
     74 void ArmJNIMacroAssembler::FinalizeInstructions(const MemoryRegion& region) {
     75   asm_->FinalizeInstructions(region);
     76 }
     77 
     78 static dwarf::Reg DWARFReg(Register reg) {
     79   return dwarf::Reg::ArmCore(static_cast<int>(reg));
     80 }
     81 
     82 static dwarf::Reg DWARFReg(SRegister reg) {
     83   return dwarf::Reg::ArmFp(static_cast<int>(reg));
     84 }
     85 
     86 #define __ asm_->
     87 
     88 void ArmJNIMacroAssembler::BuildFrame(size_t frame_size,
     89                                       ManagedRegister method_reg,
     90                                       ArrayRef<const ManagedRegister> callee_save_regs,
     91                                       const ManagedRegisterEntrySpills& entry_spills) {
     92   CHECK_EQ(CodeSize(), 0U);  // Nothing emitted yet
     93   CHECK_ALIGNED(frame_size, kStackAlignment);
     94   CHECK_EQ(R0, method_reg.AsArm().AsCoreRegister());
     95 
     96   // Push callee saves and link register.
     97   RegList core_spill_mask = 1 << LR;
     98   uint32_t fp_spill_mask = 0;
     99   for (const ManagedRegister& reg : callee_save_regs) {
    100     if (reg.AsArm().IsCoreRegister()) {
    101       core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
    102     } else {
    103       fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
    104     }
    105   }
    106   __ PushList(core_spill_mask);
    107   cfi().AdjustCFAOffset(POPCOUNT(core_spill_mask) * kFramePointerSize);
    108   cfi().RelOffsetForMany(DWARFReg(Register(0)), 0, core_spill_mask, kFramePointerSize);
    109   if (fp_spill_mask != 0) {
    110     __ vpushs(SRegister(CTZ(fp_spill_mask)), POPCOUNT(fp_spill_mask));
    111     cfi().AdjustCFAOffset(POPCOUNT(fp_spill_mask) * kFramePointerSize);
    112     cfi().RelOffsetForMany(DWARFReg(SRegister(0)), 0, fp_spill_mask, kFramePointerSize);
    113   }
    114 
    115   // Increase frame to required size.
    116   int pushed_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
    117   CHECK_GT(frame_size, pushed_values * kFramePointerSize);  // Must at least have space for Method*.
    118   IncreaseFrameSize(frame_size - pushed_values * kFramePointerSize);  // handles CFI as well.
    119 
    120   // Write out Method*.
    121   __ StoreToOffset(kStoreWord, R0, SP, 0);
    122 
    123   // Write out entry spills.
    124   int32_t offset = frame_size + kFramePointerSize;
    125   for (size_t i = 0; i < entry_spills.size(); ++i) {
    126     ArmManagedRegister reg = entry_spills.at(i).AsArm();
    127     if (reg.IsNoRegister()) {
    128       // only increment stack offset.
    129       ManagedRegisterSpill spill = entry_spills.at(i);
    130       offset += spill.getSize();
    131     } else if (reg.IsCoreRegister()) {
    132       __ StoreToOffset(kStoreWord, reg.AsCoreRegister(), SP, offset);
    133       offset += 4;
    134     } else if (reg.IsSRegister()) {
    135       __ StoreSToOffset(reg.AsSRegister(), SP, offset);
    136       offset += 4;
    137     } else if (reg.IsDRegister()) {
    138       __ StoreDToOffset(reg.AsDRegister(), SP, offset);
    139       offset += 8;
    140     }
    141   }
    142 }
    143 
    144 void ArmJNIMacroAssembler::RemoveFrame(size_t frame_size,
    145                                        ArrayRef<const ManagedRegister> callee_save_regs) {
    146   CHECK_ALIGNED(frame_size, kStackAlignment);
    147   cfi().RememberState();
    148 
    149   // Compute callee saves to pop and PC.
    150   RegList core_spill_mask = 1 << PC;
    151   uint32_t fp_spill_mask = 0;
    152   for (const ManagedRegister& reg : callee_save_regs) {
    153     if (reg.AsArm().IsCoreRegister()) {
    154       core_spill_mask |= 1 << reg.AsArm().AsCoreRegister();
    155     } else {
    156       fp_spill_mask |= 1 << reg.AsArm().AsSRegister();
    157     }
    158   }
    159 
    160   // Decrease frame to start of callee saves.
    161   int pop_values = POPCOUNT(core_spill_mask) + POPCOUNT(fp_spill_mask);
    162   CHECK_GT(frame_size, pop_values * kFramePointerSize);
    163   DecreaseFrameSize(frame_size - (pop_values * kFramePointerSize));  // handles CFI as well.
    164 
    165   if (fp_spill_mask != 0) {
    166     __ vpops(SRegister(CTZ(fp_spill_mask)), POPCOUNT(fp_spill_mask));
    167     cfi().AdjustCFAOffset(-kFramePointerSize * POPCOUNT(fp_spill_mask));
    168     cfi().RestoreMany(DWARFReg(SRegister(0)), fp_spill_mask);
    169   }
    170 
    171   // Pop callee saves and PC.
    172   __ PopList(core_spill_mask);
    173 
    174   // The CFI should be restored for any code that follows the exit block.
    175   cfi().RestoreState();
    176   cfi().DefCFAOffset(frame_size);
    177 }
    178 
    179 void ArmJNIMacroAssembler::IncreaseFrameSize(size_t adjust) {
    180   __ AddConstant(SP, -adjust);
    181   cfi().AdjustCFAOffset(adjust);
    182 }
    183 
    184 static void DecreaseFrameSizeImpl(ArmAssembler* assembler, size_t adjust) {
    185   assembler->AddConstant(SP, adjust);
    186   assembler->cfi().AdjustCFAOffset(-adjust);
    187 }
    188 
    189 void ArmJNIMacroAssembler::DecreaseFrameSize(size_t adjust) {
    190   DecreaseFrameSizeImpl(asm_.get(), adjust);
    191 }
    192 
    193 void ArmJNIMacroAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
    194   ArmManagedRegister src = msrc.AsArm();
    195   if (src.IsNoRegister()) {
    196     CHECK_EQ(0u, size);
    197   } else if (src.IsCoreRegister()) {
    198     CHECK_EQ(4u, size);
    199     __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    200   } else if (src.IsRegisterPair()) {
    201     CHECK_EQ(8u, size);
    202     __ StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
    203     __ StoreToOffset(kStoreWord, src.AsRegisterPairHigh(), SP, dest.Int32Value() + 4);
    204   } else if (src.IsSRegister()) {
    205     __ StoreSToOffset(src.AsSRegister(), SP, dest.Int32Value());
    206   } else {
    207     CHECK(src.IsDRegister()) << src;
    208     __ StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value());
    209   }
    210 }
    211 
    212 void ArmJNIMacroAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
    213   ArmManagedRegister src = msrc.AsArm();
    214   CHECK(src.IsCoreRegister()) << src;
    215   __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    216 }
    217 
    218 void ArmJNIMacroAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
    219   ArmManagedRegister src = msrc.AsArm();
    220   CHECK(src.IsCoreRegister()) << src;
    221   __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    222 }
    223 
    224 void ArmJNIMacroAssembler::StoreSpanning(FrameOffset dest,
    225                                          ManagedRegister msrc,
    226                                          FrameOffset in_off,
    227                                          ManagedRegister mscratch) {
    228   ArmManagedRegister src = msrc.AsArm();
    229   ArmManagedRegister scratch = mscratch.AsArm();
    230   __ StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    231   __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
    232   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + sizeof(uint32_t));
    233 }
    234 
    235 void ArmJNIMacroAssembler::CopyRef(FrameOffset dest, FrameOffset src, ManagedRegister mscratch) {
    236   ArmManagedRegister scratch = mscratch.AsArm();
    237   __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    238   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    239 }
    240 
    241 void ArmJNIMacroAssembler::LoadRef(ManagedRegister mdest,
    242                                    ManagedRegister mbase,
    243                                    MemberOffset offs,
    244                                    bool unpoison_reference) {
    245   ArmManagedRegister base = mbase.AsArm();
    246   ArmManagedRegister dst = mdest.AsArm();
    247   CHECK(base.IsCoreRegister()) << base;
    248   CHECK(dst.IsCoreRegister()) << dst;
    249   __ LoadFromOffset(kLoadWord,
    250                     dst.AsCoreRegister(),
    251                     base.AsCoreRegister(),
    252                     offs.Int32Value());
    253   if (unpoison_reference) {
    254     __ MaybeUnpoisonHeapReference(dst.AsCoreRegister());
    255   }
    256 }
    257 
    258 void ArmJNIMacroAssembler::LoadRef(ManagedRegister mdest, FrameOffset  src) {
    259   ArmManagedRegister dst = mdest.AsArm();
    260   CHECK(dst.IsCoreRegister()) << dst;
    261   __ LoadFromOffset(kLoadWord, dst.AsCoreRegister(), SP, src.Int32Value());
    262 }
    263 
    264 void ArmJNIMacroAssembler::LoadRawPtr(ManagedRegister mdest,
    265                                       ManagedRegister mbase,
    266                                       Offset offs) {
    267   ArmManagedRegister base = mbase.AsArm();
    268   ArmManagedRegister dst = mdest.AsArm();
    269   CHECK(base.IsCoreRegister()) << base;
    270   CHECK(dst.IsCoreRegister()) << dst;
    271   __ LoadFromOffset(kLoadWord,
    272                     dst.AsCoreRegister(),
    273                     base.AsCoreRegister(),
    274                     offs.Int32Value());
    275 }
    276 
    277 void ArmJNIMacroAssembler::StoreImmediateToFrame(FrameOffset dest,
    278                                                  uint32_t imm,
    279                                                  ManagedRegister mscratch) {
    280   ArmManagedRegister scratch = mscratch.AsArm();
    281   CHECK(scratch.IsCoreRegister()) << scratch;
    282   __ LoadImmediate(scratch.AsCoreRegister(), imm);
    283   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    284 }
    285 
    286 static void EmitLoad(ArmAssembler* assembler,
    287                      ManagedRegister m_dst,
    288                      Register src_register,
    289                      int32_t src_offset,
    290                      size_t size) {
    291   ArmManagedRegister dst = m_dst.AsArm();
    292   if (dst.IsNoRegister()) {
    293     CHECK_EQ(0u, size) << dst;
    294   } else if (dst.IsCoreRegister()) {
    295     CHECK_EQ(4u, size) << dst;
    296     assembler->LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
    297   } else if (dst.IsRegisterPair()) {
    298     CHECK_EQ(8u, size) << dst;
    299     assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset);
    300     assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4);
    301   } else if (dst.IsSRegister()) {
    302     assembler->LoadSFromOffset(dst.AsSRegister(), src_register, src_offset);
    303   } else {
    304     CHECK(dst.IsDRegister()) << dst;
    305     assembler->LoadDFromOffset(dst.AsDRegister(), src_register, src_offset);
    306   }
    307 }
    308 
    309 void ArmJNIMacroAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
    310   EmitLoad(asm_.get(), m_dst, SP, src.Int32Value(), size);
    311 }
    312 
    313 void ArmJNIMacroAssembler::LoadFromThread(ManagedRegister m_dst, ThreadOffset32 src, size_t size) {
    314   EmitLoad(asm_.get(), m_dst, TR, src.Int32Value(), size);
    315 }
    316 
    317 void ArmJNIMacroAssembler::LoadRawPtrFromThread(ManagedRegister m_dst, ThreadOffset32 offs) {
    318   ArmManagedRegister dst = m_dst.AsArm();
    319   CHECK(dst.IsCoreRegister()) << dst;
    320   __ LoadFromOffset(kLoadWord, dst.AsCoreRegister(), TR, offs.Int32Value());
    321 }
    322 
    323 void ArmJNIMacroAssembler::CopyRawPtrFromThread(FrameOffset fr_offs,
    324                                                 ThreadOffset32 thr_offs,
    325                                                 ManagedRegister mscratch) {
    326   ArmManagedRegister scratch = mscratch.AsArm();
    327   CHECK(scratch.IsCoreRegister()) << scratch;
    328   __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), TR, thr_offs.Int32Value());
    329   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
    330 }
    331 
    332 void ArmJNIMacroAssembler::CopyRawPtrToThread(ThreadOffset32 thr_offs,
    333                                               FrameOffset fr_offs,
    334                                               ManagedRegister mscratch) {
    335   ArmManagedRegister scratch = mscratch.AsArm();
    336   CHECK(scratch.IsCoreRegister()) << scratch;
    337   __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, fr_offs.Int32Value());
    338   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, thr_offs.Int32Value());
    339 }
    340 
    341 void ArmJNIMacroAssembler::StoreStackOffsetToThread(ThreadOffset32 thr_offs,
    342                                                     FrameOffset fr_offs,
    343                                                     ManagedRegister mscratch) {
    344   ArmManagedRegister scratch = mscratch.AsArm();
    345   CHECK(scratch.IsCoreRegister()) << scratch;
    346   __ AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value(), AL);
    347   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, thr_offs.Int32Value());
    348 }
    349 
    350 void ArmJNIMacroAssembler::StoreStackPointerToThread(ThreadOffset32 thr_offs) {
    351   __ StoreToOffset(kStoreWord, SP, TR, thr_offs.Int32Value());
    352 }
    353 
    354 void ArmJNIMacroAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
    355   UNIMPLEMENTED(FATAL) << "no sign extension necessary for arm";
    356 }
    357 
    358 void ArmJNIMacroAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
    359   UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm";
    360 }
    361 
    362 void ArmJNIMacroAssembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t /*size*/) {
    363   ArmManagedRegister dst = m_dst.AsArm();
    364   ArmManagedRegister src = m_src.AsArm();
    365   if (!dst.Equals(src)) {
    366     if (dst.IsCoreRegister()) {
    367       CHECK(src.IsCoreRegister()) << src;
    368       __ mov(dst.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
    369     } else if (dst.IsDRegister()) {
    370       if (src.IsDRegister()) {
    371         __ vmovd(dst.AsDRegister(), src.AsDRegister());
    372       } else {
    373         // VMOV Dn, Rlo, Rhi (Dn = {Rlo, Rhi})
    374         CHECK(src.IsRegisterPair()) << src;
    375         __ vmovdrr(dst.AsDRegister(), src.AsRegisterPairLow(), src.AsRegisterPairHigh());
    376       }
    377     } else if (dst.IsSRegister()) {
    378       if (src.IsSRegister()) {
    379         __ vmovs(dst.AsSRegister(), src.AsSRegister());
    380       } else {
    381         // VMOV Sn, Rn  (Sn = Rn)
    382         CHECK(src.IsCoreRegister()) << src;
    383         __ vmovsr(dst.AsSRegister(), src.AsCoreRegister());
    384       }
    385     } else {
    386       CHECK(dst.IsRegisterPair()) << dst;
    387       CHECK(src.IsRegisterPair()) << src;
    388       // Ensure that the first move doesn't clobber the input of the second.
    389       if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) {
    390         __ mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow()));
    391         __ mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh()));
    392       } else {
    393         __ mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh()));
    394         __ mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow()));
    395       }
    396     }
    397   }
    398 }
    399 
    400 void ArmJNIMacroAssembler::Copy(FrameOffset dest,
    401                                 FrameOffset src,
    402                                 ManagedRegister mscratch,
    403                                 size_t size) {
    404   ArmManagedRegister scratch = mscratch.AsArm();
    405   CHECK(scratch.IsCoreRegister()) << scratch;
    406   CHECK(size == 4 || size == 8) << size;
    407   if (size == 4) {
    408     __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    409     __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    410   } else if (size == 8) {
    411     __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    412     __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    413     __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4);
    414     __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4);
    415   }
    416 }
    417 
    418 void ArmJNIMacroAssembler::Copy(FrameOffset dest,
    419                                 ManagedRegister src_base,
    420                                 Offset src_offset,
    421                                 ManagedRegister mscratch,
    422                                 size_t size) {
    423   Register scratch = mscratch.AsArm().AsCoreRegister();
    424   CHECK_EQ(size, 4u);
    425   __ LoadFromOffset(kLoadWord, scratch, src_base.AsArm().AsCoreRegister(), src_offset.Int32Value());
    426   __ StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
    427 }
    428 
    429 void ArmJNIMacroAssembler::Copy(ManagedRegister dest_base,
    430                                 Offset dest_offset,
    431                                 FrameOffset src,
    432                                 ManagedRegister mscratch,
    433                                 size_t size) {
    434   Register scratch = mscratch.AsArm().AsCoreRegister();
    435   CHECK_EQ(size, 4u);
    436   __ LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
    437   __ StoreToOffset(kStoreWord,
    438                    scratch,
    439                    dest_base.AsArm().AsCoreRegister(),
    440                    dest_offset.Int32Value());
    441 }
    442 
    443 void ArmJNIMacroAssembler::Copy(FrameOffset /*dst*/,
    444                                 FrameOffset /*src_base*/,
    445                                 Offset /*src_offset*/,
    446                                 ManagedRegister /*mscratch*/,
    447                                 size_t /*size*/) {
    448   UNIMPLEMENTED(FATAL);
    449 }
    450 
    451 void ArmJNIMacroAssembler::Copy(ManagedRegister dest,
    452                                 Offset dest_offset,
    453                                 ManagedRegister src,
    454                                 Offset src_offset,
    455                                 ManagedRegister mscratch,
    456                                 size_t size) {
    457   CHECK_EQ(size, 4u);
    458   Register scratch = mscratch.AsArm().AsCoreRegister();
    459   __ LoadFromOffset(kLoadWord, scratch, src.AsArm().AsCoreRegister(), src_offset.Int32Value());
    460   __ StoreToOffset(kStoreWord, scratch, dest.AsArm().AsCoreRegister(), dest_offset.Int32Value());
    461 }
    462 
    463 void ArmJNIMacroAssembler::Copy(FrameOffset /*dst*/,
    464                                 Offset /*dest_offset*/,
    465                                 FrameOffset /*src*/,
    466                                 Offset /*src_offset*/,
    467                                 ManagedRegister /*scratch*/,
    468                                 size_t /*size*/) {
    469   UNIMPLEMENTED(FATAL);
    470 }
    471 
    472 void ArmJNIMacroAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
    473                                                   FrameOffset handle_scope_offset,
    474                                                   ManagedRegister min_reg,
    475                                                   bool null_allowed) {
    476   ArmManagedRegister out_reg = mout_reg.AsArm();
    477   ArmManagedRegister in_reg = min_reg.AsArm();
    478   CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
    479   CHECK(out_reg.IsCoreRegister()) << out_reg;
    480   if (null_allowed) {
    481     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    482     // the address in the handle scope holding the reference.
    483     // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
    484     if (in_reg.IsNoRegister()) {
    485       __ LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
    486       in_reg = out_reg;
    487     }
    488     __ cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
    489     if (!out_reg.Equals(in_reg)) {
    490       __ it(EQ, kItElse);
    491       __ LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
    492     } else {
    493       __ it(NE);
    494     }
    495     __ AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
    496   } else {
    497     __ AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
    498   }
    499 }
    500 
    501 void ArmJNIMacroAssembler::CreateHandleScopeEntry(FrameOffset out_off,
    502                                                   FrameOffset handle_scope_offset,
    503                                                   ManagedRegister mscratch,
    504                                                   bool null_allowed) {
    505   ArmManagedRegister scratch = mscratch.AsArm();
    506   CHECK(scratch.IsCoreRegister()) << scratch;
    507   if (null_allowed) {
    508     __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value());
    509     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    510     // the address in the handle scope holding the reference.
    511     // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
    512     __ cmp(scratch.AsCoreRegister(), ShifterOperand(0));
    513     __ it(NE);
    514     __ AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
    515   } else {
    516     __ AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
    517   }
    518   __ StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
    519 }
    520 
    521 void ArmJNIMacroAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
    522                                                         ManagedRegister min_reg) {
    523   ArmManagedRegister out_reg = mout_reg.AsArm();
    524   ArmManagedRegister in_reg = min_reg.AsArm();
    525   CHECK(out_reg.IsCoreRegister()) << out_reg;
    526   CHECK(in_reg.IsCoreRegister()) << in_reg;
    527   Label null_arg;
    528   if (!out_reg.Equals(in_reg)) {
    529     __ LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);     // TODO: why EQ?
    530   }
    531   __ cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
    532   __ it(NE);
    533   __ LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(), in_reg.AsCoreRegister(), 0, NE);
    534 }
    535 
    536 void ArmJNIMacroAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
    537   // TODO: not validating references.
    538 }
    539 
    540 void ArmJNIMacroAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
    541   // TODO: not validating references.
    542 }
    543 
    544 void ArmJNIMacroAssembler::Call(ManagedRegister mbase,
    545                                 Offset offset,
    546                                 ManagedRegister mscratch) {
    547   ArmManagedRegister base = mbase.AsArm();
    548   ArmManagedRegister scratch = mscratch.AsArm();
    549   CHECK(base.IsCoreRegister()) << base;
    550   CHECK(scratch.IsCoreRegister()) << scratch;
    551   __ LoadFromOffset(kLoadWord,
    552                     scratch.AsCoreRegister(),
    553                     base.AsCoreRegister(),
    554                     offset.Int32Value());
    555   __ blx(scratch.AsCoreRegister());
    556   // TODO: place reference map on call.
    557 }
    558 
    559 void ArmJNIMacroAssembler::Call(FrameOffset base, Offset offset, ManagedRegister mscratch) {
    560   ArmManagedRegister scratch = mscratch.AsArm();
    561   CHECK(scratch.IsCoreRegister()) << scratch;
    562   // Call *(*(SP + base) + offset)
    563   __ LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, base.Int32Value());
    564   __ LoadFromOffset(kLoadWord,
    565                     scratch.AsCoreRegister(),
    566                     scratch.AsCoreRegister(),
    567                     offset.Int32Value());
    568   __ blx(scratch.AsCoreRegister());
    569   // TODO: place reference map on call
    570 }
    571 
    572 void ArmJNIMacroAssembler::CallFromThread(ThreadOffset32 offset ATTRIBUTE_UNUSED,
    573                                           ManagedRegister scratch ATTRIBUTE_UNUSED) {
    574   UNIMPLEMENTED(FATAL);
    575 }
    576 
    577 void ArmJNIMacroAssembler::GetCurrentThread(ManagedRegister tr) {
    578   __ mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR));
    579 }
    580 
    581 void ArmJNIMacroAssembler::GetCurrentThread(FrameOffset offset, ManagedRegister /*scratch*/) {
    582   __ StoreToOffset(kStoreWord, TR, SP, offset.Int32Value(), AL);
    583 }
    584 
    585 void ArmJNIMacroAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
    586   ArmManagedRegister scratch = mscratch.AsArm();
    587   ArmExceptionSlowPath* slow = new (__ GetArena()) ArmExceptionSlowPath(scratch, stack_adjust);
    588   __ GetBuffer()->EnqueueSlowPath(slow);
    589   __ LoadFromOffset(kLoadWord,
    590                     scratch.AsCoreRegister(),
    591                     TR,
    592                     Thread::ExceptionOffset<kArmPointerSize>().Int32Value());
    593   __ cmp(scratch.AsCoreRegister(), ShifterOperand(0));
    594   __ b(slow->Entry(), NE);
    595 }
    596 
    597 std::unique_ptr<JNIMacroLabel> ArmJNIMacroAssembler::CreateLabel() {
    598   return std::unique_ptr<JNIMacroLabel>(new ArmJNIMacroLabel());
    599 }
    600 
    601 void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label) {
    602   CHECK(label != nullptr);
    603   __ b(ArmJNIMacroLabel::Cast(label)->AsArm());
    604 }
    605 
    606 void ArmJNIMacroAssembler::Jump(JNIMacroLabel* label,
    607                                 JNIMacroUnaryCondition condition,
    608                                 ManagedRegister test) {
    609   CHECK(label != nullptr);
    610 
    611   arm::Condition arm_cond;
    612   switch (condition) {
    613     case JNIMacroUnaryCondition::kZero:
    614       arm_cond = EQ;
    615       break;
    616     case JNIMacroUnaryCondition::kNotZero:
    617       arm_cond = NE;
    618       break;
    619     default:
    620       LOG(FATAL) << "Not implemented condition: " << static_cast<int>(condition);
    621       UNREACHABLE();
    622   }
    623   __ cmp(test.AsArm().AsCoreRegister(), ShifterOperand(0));
    624   __ b(ArmJNIMacroLabel::Cast(label)->AsArm(), arm_cond);
    625 }
    626 
    627 void ArmJNIMacroAssembler::Bind(JNIMacroLabel* label) {
    628   CHECK(label != nullptr);
    629   __ Bind(ArmJNIMacroLabel::Cast(label)->AsArm());
    630 }
    631 
    632 #undef __
    633 
    634 void ArmExceptionSlowPath::Emit(Assembler* sasm) {
    635   ArmAssembler* sp_asm = down_cast<ArmAssembler*>(sasm);
    636 #define __ sp_asm->
    637   __ Bind(&entry_);
    638   if (stack_adjust_ != 0) {  // Fix up the frame.
    639     DecreaseFrameSizeImpl(sp_asm, stack_adjust_);
    640   }
    641   // Pass exception object as argument.
    642   // Don't care about preserving R0 as this call won't return.
    643   __ mov(R0, ShifterOperand(scratch_.AsCoreRegister()));
    644   // Set up call to Thread::Current()->pDeliverException.
    645   __ LoadFromOffset(kLoadWord,
    646                     R12,
    647                     TR,
    648                     QUICK_ENTRYPOINT_OFFSET(kArmPointerSize, pDeliverException).Int32Value());
    649   __ blx(R12);
    650 #undef __
    651 }
    652 
    653 void ArmJNIMacroAssembler::MemoryBarrier(ManagedRegister mscratch) {
    654   CHECK_EQ(mscratch.AsArm().AsCoreRegister(), R12);
    655   asm_->dmb(SY);
    656 }
    657 
    658 }  // namespace arm
    659 }  // namespace art
    660