Home | History | Annotate | Download | only in arm64
      1 /*
      2  * Copyright (C) 2014 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 "assembler_arm64.h"
     18 #include "base/logging.h"
     19 #include "entrypoints/quick/quick_entrypoints.h"
     20 #include "offsets.h"
     21 #include "thread.h"
     22 
     23 using namespace vixl;  // NOLINT(build/namespaces)
     24 
     25 namespace art {
     26 namespace arm64 {
     27 
     28 #ifdef ___
     29 #error "ARM64 Assembler macro already defined."
     30 #else
     31 #define ___   vixl_masm_->
     32 #endif
     33 
     34 void Arm64Assembler::FinalizeCode() {
     35   for (const std::unique_ptr<Arm64Exception>& exception : exception_blocks_) {
     36     EmitExceptionPoll(exception.get());
     37   }
     38   ___ FinalizeCode();
     39 }
     40 
     41 size_t Arm64Assembler::CodeSize() const {
     42   return vixl_masm_->BufferCapacity() - vixl_masm_->RemainingBufferSpace();
     43 }
     44 
     45 const uint8_t* Arm64Assembler::CodeBufferBaseAddress() const {
     46   return vixl_masm_->GetStartAddress<uint8_t*>();
     47 }
     48 
     49 void Arm64Assembler::FinalizeInstructions(const MemoryRegion& region) {
     50   // Copy the instructions from the buffer.
     51   MemoryRegion from(vixl_masm_->GetStartAddress<void*>(), CodeSize());
     52   region.CopyFrom(0, from);
     53 }
     54 
     55 void Arm64Assembler::GetCurrentThread(ManagedRegister tr) {
     56   ___ Mov(reg_x(tr.AsArm64().AsXRegister()), reg_x(TR));
     57 }
     58 
     59 void Arm64Assembler::GetCurrentThread(FrameOffset offset, ManagedRegister /* scratch */) {
     60   StoreToOffset(TR, SP, offset.Int32Value());
     61 }
     62 
     63 // See Arm64 PCS Section 5.2.2.1.
     64 void Arm64Assembler::IncreaseFrameSize(size_t adjust) {
     65   CHECK_ALIGNED(adjust, kStackAlignment);
     66   AddConstant(SP, -adjust);
     67   cfi().AdjustCFAOffset(adjust);
     68 }
     69 
     70 // See Arm64 PCS Section 5.2.2.1.
     71 void Arm64Assembler::DecreaseFrameSize(size_t adjust) {
     72   CHECK_ALIGNED(adjust, kStackAlignment);
     73   AddConstant(SP, adjust);
     74   cfi().AdjustCFAOffset(-adjust);
     75 }
     76 
     77 void Arm64Assembler::AddConstant(XRegister rd, int32_t value, Condition cond) {
     78   AddConstant(rd, rd, value, cond);
     79 }
     80 
     81 void Arm64Assembler::AddConstant(XRegister rd, XRegister rn, int32_t value,
     82                                  Condition cond) {
     83   if ((cond == al) || (cond == nv)) {
     84     // VIXL macro-assembler handles all variants.
     85     ___ Add(reg_x(rd), reg_x(rn), value);
     86   } else {
     87     // temp = rd + value
     88     // rd = cond ? temp : rn
     89     vixl::UseScratchRegisterScope temps(vixl_masm_);
     90     temps.Exclude(reg_x(rd), reg_x(rn));
     91     vixl::Register temp = temps.AcquireX();
     92     ___ Add(temp, reg_x(rn), value);
     93     ___ Csel(reg_x(rd), temp, reg_x(rd), cond);
     94   }
     95 }
     96 
     97 void Arm64Assembler::StoreWToOffset(StoreOperandType type, WRegister source,
     98                                     XRegister base, int32_t offset) {
     99   switch (type) {
    100     case kStoreByte:
    101       ___ Strb(reg_w(source), MEM_OP(reg_x(base), offset));
    102       break;
    103     case kStoreHalfword:
    104       ___ Strh(reg_w(source), MEM_OP(reg_x(base), offset));
    105       break;
    106     case kStoreWord:
    107       ___ Str(reg_w(source), MEM_OP(reg_x(base), offset));
    108       break;
    109     default:
    110       LOG(FATAL) << "UNREACHABLE";
    111   }
    112 }
    113 
    114 void Arm64Assembler::StoreToOffset(XRegister source, XRegister base, int32_t offset) {
    115   CHECK_NE(source, SP);
    116   ___ Str(reg_x(source), MEM_OP(reg_x(base), offset));
    117 }
    118 
    119 void Arm64Assembler::StoreSToOffset(SRegister source, XRegister base, int32_t offset) {
    120   ___ Str(reg_s(source), MEM_OP(reg_x(base), offset));
    121 }
    122 
    123 void Arm64Assembler::StoreDToOffset(DRegister source, XRegister base, int32_t offset) {
    124   ___ Str(reg_d(source), MEM_OP(reg_x(base), offset));
    125 }
    126 
    127 void Arm64Assembler::Store(FrameOffset offs, ManagedRegister m_src, size_t size) {
    128   Arm64ManagedRegister src = m_src.AsArm64();
    129   if (src.IsNoRegister()) {
    130     CHECK_EQ(0u, size);
    131   } else if (src.IsWRegister()) {
    132     CHECK_EQ(4u, size);
    133     StoreWToOffset(kStoreWord, src.AsWRegister(), SP, offs.Int32Value());
    134   } else if (src.IsXRegister()) {
    135     CHECK_EQ(8u, size);
    136     StoreToOffset(src.AsXRegister(), SP, offs.Int32Value());
    137   } else if (src.IsSRegister()) {
    138     StoreSToOffset(src.AsSRegister(), SP, offs.Int32Value());
    139   } else {
    140     CHECK(src.IsDRegister()) << src;
    141     StoreDToOffset(src.AsDRegister(), SP, offs.Int32Value());
    142   }
    143 }
    144 
    145 void Arm64Assembler::StoreRef(FrameOffset offs, ManagedRegister m_src) {
    146   Arm64ManagedRegister src = m_src.AsArm64();
    147   CHECK(src.IsXRegister()) << src;
    148   StoreWToOffset(kStoreWord, src.AsOverlappingWRegister(), SP,
    149                  offs.Int32Value());
    150 }
    151 
    152 void Arm64Assembler::StoreRawPtr(FrameOffset offs, ManagedRegister m_src) {
    153   Arm64ManagedRegister src = m_src.AsArm64();
    154   CHECK(src.IsXRegister()) << src;
    155   StoreToOffset(src.AsXRegister(), SP, offs.Int32Value());
    156 }
    157 
    158 void Arm64Assembler::StoreImmediateToFrame(FrameOffset offs, uint32_t imm,
    159                                            ManagedRegister m_scratch) {
    160   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    161   CHECK(scratch.IsXRegister()) << scratch;
    162   LoadImmediate(scratch.AsXRegister(), imm);
    163   StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP,
    164                  offs.Int32Value());
    165 }
    166 
    167 void Arm64Assembler::StoreImmediateToThread64(ThreadOffset<8> offs, uint32_t imm,
    168                                             ManagedRegister m_scratch) {
    169   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    170   CHECK(scratch.IsXRegister()) << scratch;
    171   LoadImmediate(scratch.AsXRegister(), imm);
    172   StoreToOffset(scratch.AsXRegister(), TR, offs.Int32Value());
    173 }
    174 
    175 void Arm64Assembler::StoreStackOffsetToThread64(ThreadOffset<8> tr_offs,
    176                                               FrameOffset fr_offs,
    177                                               ManagedRegister m_scratch) {
    178   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    179   CHECK(scratch.IsXRegister()) << scratch;
    180   AddConstant(scratch.AsXRegister(), SP, fr_offs.Int32Value());
    181   StoreToOffset(scratch.AsXRegister(), TR, tr_offs.Int32Value());
    182 }
    183 
    184 void Arm64Assembler::StoreStackPointerToThread64(ThreadOffset<8> tr_offs) {
    185   vixl::UseScratchRegisterScope temps(vixl_masm_);
    186   vixl::Register temp = temps.AcquireX();
    187   ___ Mov(temp, reg_x(SP));
    188   ___ Str(temp, MEM_OP(reg_x(TR), tr_offs.Int32Value()));
    189 }
    190 
    191 void Arm64Assembler::StoreSpanning(FrameOffset dest_off, ManagedRegister m_source,
    192                                    FrameOffset in_off, ManagedRegister m_scratch) {
    193   Arm64ManagedRegister source = m_source.AsArm64();
    194   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    195   StoreToOffset(source.AsXRegister(), SP, dest_off.Int32Value());
    196   LoadFromOffset(scratch.AsXRegister(), SP, in_off.Int32Value());
    197   StoreToOffset(scratch.AsXRegister(), SP, dest_off.Int32Value() + 8);
    198 }
    199 
    200 // Load routines.
    201 void Arm64Assembler::LoadImmediate(XRegister dest, int32_t value,
    202                                    Condition cond) {
    203   if ((cond == al) || (cond == nv)) {
    204     ___ Mov(reg_x(dest), value);
    205   } else {
    206     // temp = value
    207     // rd = cond ? temp : rd
    208     if (value != 0) {
    209       vixl::UseScratchRegisterScope temps(vixl_masm_);
    210       temps.Exclude(reg_x(dest));
    211       vixl::Register temp = temps.AcquireX();
    212       ___ Mov(temp, value);
    213       ___ Csel(reg_x(dest), temp, reg_x(dest), cond);
    214     } else {
    215       ___ Csel(reg_x(dest), reg_x(XZR), reg_x(dest), cond);
    216     }
    217   }
    218 }
    219 
    220 void Arm64Assembler::LoadWFromOffset(LoadOperandType type, WRegister dest,
    221                                      XRegister base, int32_t offset) {
    222   switch (type) {
    223     case kLoadSignedByte:
    224       ___ Ldrsb(reg_w(dest), MEM_OP(reg_x(base), offset));
    225       break;
    226     case kLoadSignedHalfword:
    227       ___ Ldrsh(reg_w(dest), MEM_OP(reg_x(base), offset));
    228       break;
    229     case kLoadUnsignedByte:
    230       ___ Ldrb(reg_w(dest), MEM_OP(reg_x(base), offset));
    231       break;
    232     case kLoadUnsignedHalfword:
    233       ___ Ldrh(reg_w(dest), MEM_OP(reg_x(base), offset));
    234       break;
    235     case kLoadWord:
    236       ___ Ldr(reg_w(dest), MEM_OP(reg_x(base), offset));
    237       break;
    238     default:
    239         LOG(FATAL) << "UNREACHABLE";
    240   }
    241 }
    242 
    243 // Note: We can extend this member by adding load type info - see
    244 // sign extended A64 load variants.
    245 void Arm64Assembler::LoadFromOffset(XRegister dest, XRegister base,
    246                                     int32_t offset) {
    247   CHECK_NE(dest, SP);
    248   ___ Ldr(reg_x(dest), MEM_OP(reg_x(base), offset));
    249 }
    250 
    251 void Arm64Assembler::LoadSFromOffset(SRegister dest, XRegister base,
    252                                      int32_t offset) {
    253   ___ Ldr(reg_s(dest), MEM_OP(reg_x(base), offset));
    254 }
    255 
    256 void Arm64Assembler::LoadDFromOffset(DRegister dest, XRegister base,
    257                                      int32_t offset) {
    258   ___ Ldr(reg_d(dest), MEM_OP(reg_x(base), offset));
    259 }
    260 
    261 void Arm64Assembler::Load(Arm64ManagedRegister dest, XRegister base,
    262                           int32_t offset, size_t size) {
    263   if (dest.IsNoRegister()) {
    264     CHECK_EQ(0u, size) << dest;
    265   } else if (dest.IsWRegister()) {
    266     CHECK_EQ(4u, size) << dest;
    267     ___ Ldr(reg_w(dest.AsWRegister()), MEM_OP(reg_x(base), offset));
    268   } else if (dest.IsXRegister()) {
    269     CHECK_NE(dest.AsXRegister(), SP) << dest;
    270     if (size == 4u) {
    271       ___ Ldr(reg_w(dest.AsOverlappingWRegister()), MEM_OP(reg_x(base), offset));
    272     } else {
    273       CHECK_EQ(8u, size) << dest;
    274       ___ Ldr(reg_x(dest.AsXRegister()), MEM_OP(reg_x(base), offset));
    275     }
    276   } else if (dest.IsSRegister()) {
    277     ___ Ldr(reg_s(dest.AsSRegister()), MEM_OP(reg_x(base), offset));
    278   } else {
    279     CHECK(dest.IsDRegister()) << dest;
    280     ___ Ldr(reg_d(dest.AsDRegister()), MEM_OP(reg_x(base), offset));
    281   }
    282 }
    283 
    284 void Arm64Assembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
    285   return Load(m_dst.AsArm64(), SP, src.Int32Value(), size);
    286 }
    287 
    288 void Arm64Assembler::LoadFromThread64(ManagedRegister m_dst, ThreadOffset<8> src, size_t size) {
    289   return Load(m_dst.AsArm64(), TR, src.Int32Value(), size);
    290 }
    291 
    292 void Arm64Assembler::LoadRef(ManagedRegister m_dst, FrameOffset offs) {
    293   Arm64ManagedRegister dst = m_dst.AsArm64();
    294   CHECK(dst.IsXRegister()) << dst;
    295   LoadWFromOffset(kLoadWord, dst.AsOverlappingWRegister(), SP, offs.Int32Value());
    296 }
    297 
    298 void Arm64Assembler::LoadRef(ManagedRegister m_dst, ManagedRegister m_base, MemberOffset offs,
    299                              bool unpoison_reference) {
    300   Arm64ManagedRegister dst = m_dst.AsArm64();
    301   Arm64ManagedRegister base = m_base.AsArm64();
    302   CHECK(dst.IsXRegister() && base.IsXRegister());
    303   LoadWFromOffset(kLoadWord, dst.AsOverlappingWRegister(), base.AsXRegister(),
    304                   offs.Int32Value());
    305   if (unpoison_reference) {
    306     WRegister ref_reg = dst.AsOverlappingWRegister();
    307     MaybeUnpoisonHeapReference(reg_w(ref_reg));
    308   }
    309 }
    310 
    311 void Arm64Assembler::LoadRawPtr(ManagedRegister m_dst, ManagedRegister m_base, Offset offs) {
    312   Arm64ManagedRegister dst = m_dst.AsArm64();
    313   Arm64ManagedRegister base = m_base.AsArm64();
    314   CHECK(dst.IsXRegister() && base.IsXRegister());
    315   // Remove dst and base form the temp list - higher level API uses IP1, IP0.
    316   vixl::UseScratchRegisterScope temps(vixl_masm_);
    317   temps.Exclude(reg_x(dst.AsXRegister()), reg_x(base.AsXRegister()));
    318   ___ Ldr(reg_x(dst.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value()));
    319 }
    320 
    321 void Arm64Assembler::LoadRawPtrFromThread64(ManagedRegister m_dst, ThreadOffset<8> offs) {
    322   Arm64ManagedRegister dst = m_dst.AsArm64();
    323   CHECK(dst.IsXRegister()) << dst;
    324   LoadFromOffset(dst.AsXRegister(), TR, offs.Int32Value());
    325 }
    326 
    327 // Copying routines.
    328 void Arm64Assembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t size) {
    329   Arm64ManagedRegister dst = m_dst.AsArm64();
    330   Arm64ManagedRegister src = m_src.AsArm64();
    331   if (!dst.Equals(src)) {
    332     if (dst.IsXRegister()) {
    333       if (size == 4) {
    334         CHECK(src.IsWRegister());
    335         ___ Mov(reg_w(dst.AsOverlappingWRegister()), reg_w(src.AsWRegister()));
    336       } else {
    337         if (src.IsXRegister()) {
    338           ___ Mov(reg_x(dst.AsXRegister()), reg_x(src.AsXRegister()));
    339         } else {
    340           ___ Mov(reg_x(dst.AsXRegister()), reg_x(src.AsOverlappingXRegister()));
    341         }
    342       }
    343     } else if (dst.IsWRegister()) {
    344       CHECK(src.IsWRegister()) << src;
    345       ___ Mov(reg_w(dst.AsWRegister()), reg_w(src.AsWRegister()));
    346     } else if (dst.IsSRegister()) {
    347       CHECK(src.IsSRegister()) << src;
    348       ___ Fmov(reg_s(dst.AsSRegister()), reg_s(src.AsSRegister()));
    349     } else {
    350       CHECK(dst.IsDRegister()) << dst;
    351       CHECK(src.IsDRegister()) << src;
    352       ___ Fmov(reg_d(dst.AsDRegister()), reg_d(src.AsDRegister()));
    353     }
    354   }
    355 }
    356 
    357 void Arm64Assembler::CopyRawPtrFromThread64(FrameOffset fr_offs,
    358                                           ThreadOffset<8> tr_offs,
    359                                           ManagedRegister m_scratch) {
    360   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    361   CHECK(scratch.IsXRegister()) << scratch;
    362   LoadFromOffset(scratch.AsXRegister(), TR, tr_offs.Int32Value());
    363   StoreToOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value());
    364 }
    365 
    366 void Arm64Assembler::CopyRawPtrToThread64(ThreadOffset<8> tr_offs,
    367                                         FrameOffset fr_offs,
    368                                         ManagedRegister m_scratch) {
    369   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    370   CHECK(scratch.IsXRegister()) << scratch;
    371   LoadFromOffset(scratch.AsXRegister(), SP, fr_offs.Int32Value());
    372   StoreToOffset(scratch.AsXRegister(), TR, tr_offs.Int32Value());
    373 }
    374 
    375 void Arm64Assembler::CopyRef(FrameOffset dest, FrameOffset src,
    376                              ManagedRegister m_scratch) {
    377   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    378   CHECK(scratch.IsXRegister()) << scratch;
    379   LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(),
    380                   SP, src.Int32Value());
    381   StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(),
    382                  SP, dest.Int32Value());
    383 }
    384 
    385 void Arm64Assembler::Copy(FrameOffset dest, FrameOffset src,
    386                           ManagedRegister m_scratch, size_t size) {
    387   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    388   CHECK(scratch.IsXRegister()) << scratch;
    389   CHECK(size == 4 || size == 8) << size;
    390   if (size == 4) {
    391     LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP, src.Int32Value());
    392     StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), SP, dest.Int32Value());
    393   } else if (size == 8) {
    394     LoadFromOffset(scratch.AsXRegister(), SP, src.Int32Value());
    395     StoreToOffset(scratch.AsXRegister(), SP, dest.Int32Value());
    396   } else {
    397     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
    398   }
    399 }
    400 
    401 void Arm64Assembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
    402                           ManagedRegister m_scratch, size_t size) {
    403   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    404   Arm64ManagedRegister base = src_base.AsArm64();
    405   CHECK(base.IsXRegister()) << base;
    406   CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch;
    407   CHECK(size == 4 || size == 8) << size;
    408   if (size == 4) {
    409     LoadWFromOffset(kLoadWord, scratch.AsWRegister(), base.AsXRegister(),
    410                    src_offset.Int32Value());
    411     StoreWToOffset(kStoreWord, scratch.AsWRegister(), SP, dest.Int32Value());
    412   } else if (size == 8) {
    413     LoadFromOffset(scratch.AsXRegister(), base.AsXRegister(), src_offset.Int32Value());
    414     StoreToOffset(scratch.AsXRegister(), SP, dest.Int32Value());
    415   } else {
    416     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
    417   }
    418 }
    419 
    420 void Arm64Assembler::Copy(ManagedRegister m_dest_base, Offset dest_offs, FrameOffset src,
    421                           ManagedRegister m_scratch, size_t size) {
    422   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    423   Arm64ManagedRegister base = m_dest_base.AsArm64();
    424   CHECK(base.IsXRegister()) << base;
    425   CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch;
    426   CHECK(size == 4 || size == 8) << size;
    427   if (size == 4) {
    428     LoadWFromOffset(kLoadWord, scratch.AsWRegister(), SP, src.Int32Value());
    429     StoreWToOffset(kStoreWord, scratch.AsWRegister(), base.AsXRegister(),
    430                    dest_offs.Int32Value());
    431   } else if (size == 8) {
    432     LoadFromOffset(scratch.AsXRegister(), SP, src.Int32Value());
    433     StoreToOffset(scratch.AsXRegister(), base.AsXRegister(), dest_offs.Int32Value());
    434   } else {
    435     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
    436   }
    437 }
    438 
    439 void Arm64Assembler::Copy(FrameOffset /*dst*/, FrameOffset /*src_base*/, Offset /*src_offset*/,
    440                           ManagedRegister /*mscratch*/, size_t /*size*/) {
    441   UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant";
    442 }
    443 
    444 void Arm64Assembler::Copy(ManagedRegister m_dest, Offset dest_offset,
    445                           ManagedRegister m_src, Offset src_offset,
    446                           ManagedRegister m_scratch, size_t size) {
    447   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    448   Arm64ManagedRegister src = m_src.AsArm64();
    449   Arm64ManagedRegister dest = m_dest.AsArm64();
    450   CHECK(dest.IsXRegister()) << dest;
    451   CHECK(src.IsXRegister()) << src;
    452   CHECK(scratch.IsXRegister() || scratch.IsWRegister()) << scratch;
    453   CHECK(size == 4 || size == 8) << size;
    454   if (size == 4) {
    455     if (scratch.IsWRegister()) {
    456       LoadWFromOffset(kLoadWord, scratch.AsWRegister(), src.AsXRegister(),
    457                     src_offset.Int32Value());
    458       StoreWToOffset(kStoreWord, scratch.AsWRegister(), dest.AsXRegister(),
    459                    dest_offset.Int32Value());
    460     } else {
    461       LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), src.AsXRegister(),
    462                     src_offset.Int32Value());
    463       StoreWToOffset(kStoreWord, scratch.AsOverlappingWRegister(), dest.AsXRegister(),
    464                    dest_offset.Int32Value());
    465     }
    466   } else if (size == 8) {
    467     LoadFromOffset(scratch.AsXRegister(), src.AsXRegister(), src_offset.Int32Value());
    468     StoreToOffset(scratch.AsXRegister(), dest.AsXRegister(), dest_offset.Int32Value());
    469   } else {
    470     UNIMPLEMENTED(FATAL) << "We only support Copy() of size 4 and 8";
    471   }
    472 }
    473 
    474 void Arm64Assembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/,
    475                           FrameOffset /*src*/, Offset /*src_offset*/,
    476                           ManagedRegister /*scratch*/, size_t /*size*/) {
    477   UNIMPLEMENTED(FATAL) << "Unimplemented Copy() variant";
    478 }
    479 
    480 void Arm64Assembler::MemoryBarrier(ManagedRegister m_scratch ATTRIBUTE_UNUSED) {
    481   // TODO: Should we check that m_scratch is IP? - see arm.
    482   ___ Dmb(vixl::InnerShareable, vixl::BarrierAll);
    483 }
    484 
    485 void Arm64Assembler::SignExtend(ManagedRegister mreg, size_t size) {
    486   Arm64ManagedRegister reg = mreg.AsArm64();
    487   CHECK(size == 1 || size == 2) << size;
    488   CHECK(reg.IsWRegister()) << reg;
    489   if (size == 1) {
    490     ___ Sxtb(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister()));
    491   } else {
    492     ___ Sxth(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister()));
    493   }
    494 }
    495 
    496 void Arm64Assembler::ZeroExtend(ManagedRegister mreg, size_t size) {
    497   Arm64ManagedRegister reg = mreg.AsArm64();
    498   CHECK(size == 1 || size == 2) << size;
    499   CHECK(reg.IsWRegister()) << reg;
    500   if (size == 1) {
    501     ___ Uxtb(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister()));
    502   } else {
    503     ___ Uxth(reg_w(reg.AsWRegister()), reg_w(reg.AsWRegister()));
    504   }
    505 }
    506 
    507 void Arm64Assembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
    508   // TODO: not validating references.
    509 }
    510 
    511 void Arm64Assembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
    512   // TODO: not validating references.
    513 }
    514 
    515 void Arm64Assembler::Call(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) {
    516   Arm64ManagedRegister base = m_base.AsArm64();
    517   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    518   CHECK(base.IsXRegister()) << base;
    519   CHECK(scratch.IsXRegister()) << scratch;
    520   LoadFromOffset(scratch.AsXRegister(), base.AsXRegister(), offs.Int32Value());
    521   ___ Blr(reg_x(scratch.AsXRegister()));
    522 }
    523 
    524 void Arm64Assembler::JumpTo(ManagedRegister m_base, Offset offs, ManagedRegister m_scratch) {
    525   Arm64ManagedRegister base = m_base.AsArm64();
    526   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    527   CHECK(base.IsXRegister()) << base;
    528   CHECK(scratch.IsXRegister()) << scratch;
    529   // Remove base and scratch form the temp list - higher level API uses IP1, IP0.
    530   vixl::UseScratchRegisterScope temps(vixl_masm_);
    531   temps.Exclude(reg_x(base.AsXRegister()), reg_x(scratch.AsXRegister()));
    532   ___ Ldr(reg_x(scratch.AsXRegister()), MEM_OP(reg_x(base.AsXRegister()), offs.Int32Value()));
    533   ___ Br(reg_x(scratch.AsXRegister()));
    534 }
    535 
    536 void Arm64Assembler::Call(FrameOffset base, Offset offs, ManagedRegister m_scratch) {
    537   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    538   CHECK(scratch.IsXRegister()) << scratch;
    539   // Call *(*(SP + base) + offset)
    540   LoadFromOffset(scratch.AsXRegister(), SP, base.Int32Value());
    541   LoadFromOffset(scratch.AsXRegister(), scratch.AsXRegister(), offs.Int32Value());
    542   ___ Blr(reg_x(scratch.AsXRegister()));
    543 }
    544 
    545 void Arm64Assembler::CallFromThread64(ThreadOffset<8> /*offset*/, ManagedRegister /*scratch*/) {
    546   UNIMPLEMENTED(FATAL) << "Unimplemented Call() variant";
    547 }
    548 
    549 void Arm64Assembler::CreateHandleScopeEntry(
    550     ManagedRegister m_out_reg, FrameOffset handle_scope_offs, ManagedRegister m_in_reg,
    551     bool null_allowed) {
    552   Arm64ManagedRegister out_reg = m_out_reg.AsArm64();
    553   Arm64ManagedRegister in_reg = m_in_reg.AsArm64();
    554   // For now we only hold stale handle scope entries in x registers.
    555   CHECK(in_reg.IsNoRegister() || in_reg.IsXRegister()) << in_reg;
    556   CHECK(out_reg.IsXRegister()) << out_reg;
    557   if (null_allowed) {
    558     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    559     // the address in the handle scope holding the reference.
    560     // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
    561     if (in_reg.IsNoRegister()) {
    562       LoadWFromOffset(kLoadWord, out_reg.AsOverlappingWRegister(), SP,
    563                       handle_scope_offs.Int32Value());
    564       in_reg = out_reg;
    565     }
    566     ___ Cmp(reg_w(in_reg.AsOverlappingWRegister()), 0);
    567     if (!out_reg.Equals(in_reg)) {
    568       LoadImmediate(out_reg.AsXRegister(), 0, eq);
    569     }
    570     AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), ne);
    571   } else {
    572     AddConstant(out_reg.AsXRegister(), SP, handle_scope_offs.Int32Value(), al);
    573   }
    574 }
    575 
    576 void Arm64Assembler::CreateHandleScopeEntry(FrameOffset out_off, FrameOffset handle_scope_offset,
    577                                             ManagedRegister m_scratch, bool null_allowed) {
    578   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    579   CHECK(scratch.IsXRegister()) << scratch;
    580   if (null_allowed) {
    581     LoadWFromOffset(kLoadWord, scratch.AsOverlappingWRegister(), SP,
    582                     handle_scope_offset.Int32Value());
    583     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    584     // the address in the handle scope holding the reference.
    585     // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
    586     ___ Cmp(reg_w(scratch.AsOverlappingWRegister()), 0);
    587     // Move this logic in add constants with flags.
    588     AddConstant(scratch.AsXRegister(), SP, handle_scope_offset.Int32Value(), ne);
    589   } else {
    590     AddConstant(scratch.AsXRegister(), SP, handle_scope_offset.Int32Value(), al);
    591   }
    592   StoreToOffset(scratch.AsXRegister(), SP, out_off.Int32Value());
    593 }
    594 
    595 void Arm64Assembler::LoadReferenceFromHandleScope(ManagedRegister m_out_reg,
    596                                                   ManagedRegister m_in_reg) {
    597   Arm64ManagedRegister out_reg = m_out_reg.AsArm64();
    598   Arm64ManagedRegister in_reg = m_in_reg.AsArm64();
    599   CHECK(out_reg.IsXRegister()) << out_reg;
    600   CHECK(in_reg.IsXRegister()) << in_reg;
    601   vixl::Label exit;
    602   if (!out_reg.Equals(in_reg)) {
    603     // FIXME: Who sets the flags here?
    604     LoadImmediate(out_reg.AsXRegister(), 0, eq);
    605   }
    606   ___ Cbz(reg_x(in_reg.AsXRegister()), &exit);
    607   LoadFromOffset(out_reg.AsXRegister(), in_reg.AsXRegister(), 0);
    608   ___ Bind(&exit);
    609 }
    610 
    611 void Arm64Assembler::ExceptionPoll(ManagedRegister m_scratch, size_t stack_adjust) {
    612   CHECK_ALIGNED(stack_adjust, kStackAlignment);
    613   Arm64ManagedRegister scratch = m_scratch.AsArm64();
    614   exception_blocks_.emplace_back(new Arm64Exception(scratch, stack_adjust));
    615   LoadFromOffset(scratch.AsXRegister(), TR, Thread::ExceptionOffset<8>().Int32Value());
    616   ___ Cbnz(reg_x(scratch.AsXRegister()), exception_blocks_.back()->Entry());
    617 }
    618 
    619 void Arm64Assembler::EmitExceptionPoll(Arm64Exception *exception) {
    620   vixl::UseScratchRegisterScope temps(vixl_masm_);
    621   temps.Exclude(reg_x(exception->scratch_.AsXRegister()));
    622   vixl::Register temp = temps.AcquireX();
    623 
    624   // Bind exception poll entry.
    625   ___ Bind(exception->Entry());
    626   if (exception->stack_adjust_ != 0) {  // Fix up the frame.
    627     DecreaseFrameSize(exception->stack_adjust_);
    628   }
    629   // Pass exception object as argument.
    630   // Don't care about preserving X0 as this won't return.
    631   ___ Mov(reg_x(X0), reg_x(exception->scratch_.AsXRegister()));
    632   ___ Ldr(temp, MEM_OP(reg_x(TR), QUICK_ENTRYPOINT_OFFSET(8, pDeliverException).Int32Value()));
    633 
    634   ___ Blr(temp);
    635   // Call should never return.
    636   ___ Brk();
    637 }
    638 
    639 static inline dwarf::Reg DWARFReg(CPURegister reg) {
    640   if (reg.IsFPRegister()) {
    641     return dwarf::Reg::Arm64Fp(reg.code());
    642   } else {
    643     DCHECK_LT(reg.code(), 31u);  // X0 - X30.
    644     return dwarf::Reg::Arm64Core(reg.code());
    645   }
    646 }
    647 
    648 void Arm64Assembler::SpillRegisters(vixl::CPURegList registers, int offset) {
    649   int size = registers.RegisterSizeInBytes();
    650   const Register sp = vixl_masm_->StackPointer();
    651   while (registers.Count() >= 2) {
    652     const CPURegister& dst0 = registers.PopLowestIndex();
    653     const CPURegister& dst1 = registers.PopLowestIndex();
    654     ___ Stp(dst0, dst1, MemOperand(sp, offset));
    655     cfi_.RelOffset(DWARFReg(dst0), offset);
    656     cfi_.RelOffset(DWARFReg(dst1), offset + size);
    657     offset += 2 * size;
    658   }
    659   if (!registers.IsEmpty()) {
    660     const CPURegister& dst0 = registers.PopLowestIndex();
    661     ___ Str(dst0, MemOperand(sp, offset));
    662     cfi_.RelOffset(DWARFReg(dst0), offset);
    663   }
    664   DCHECK(registers.IsEmpty());
    665 }
    666 
    667 void Arm64Assembler::UnspillRegisters(vixl::CPURegList registers, int offset) {
    668   int size = registers.RegisterSizeInBytes();
    669   const Register sp = vixl_masm_->StackPointer();
    670   while (registers.Count() >= 2) {
    671     const CPURegister& dst0 = registers.PopLowestIndex();
    672     const CPURegister& dst1 = registers.PopLowestIndex();
    673     ___ Ldp(dst0, dst1, MemOperand(sp, offset));
    674     cfi_.Restore(DWARFReg(dst0));
    675     cfi_.Restore(DWARFReg(dst1));
    676     offset += 2 * size;
    677   }
    678   if (!registers.IsEmpty()) {
    679     const CPURegister& dst0 = registers.PopLowestIndex();
    680     ___ Ldr(dst0, MemOperand(sp, offset));
    681     cfi_.Restore(DWARFReg(dst0));
    682   }
    683   DCHECK(registers.IsEmpty());
    684 }
    685 
    686 void Arm64Assembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
    687                                 const std::vector<ManagedRegister>& callee_save_regs,
    688                                 const ManagedRegisterEntrySpills& entry_spills) {
    689   // Setup VIXL CPURegList for callee-saves.
    690   CPURegList core_reg_list(CPURegister::kRegister, kXRegSize, 0);
    691   CPURegList fp_reg_list(CPURegister::kFPRegister, kDRegSize, 0);
    692   for (auto r : callee_save_regs) {
    693     Arm64ManagedRegister reg = r.AsArm64();
    694     if (reg.IsXRegister()) {
    695       core_reg_list.Combine(reg_x(reg.AsXRegister()).code());
    696     } else {
    697       DCHECK(reg.IsDRegister());
    698       fp_reg_list.Combine(reg_d(reg.AsDRegister()).code());
    699     }
    700   }
    701   size_t core_reg_size = core_reg_list.TotalSizeInBytes();
    702   size_t fp_reg_size = fp_reg_list.TotalSizeInBytes();
    703 
    704   // Increase frame to required size.
    705   DCHECK_ALIGNED(frame_size, kStackAlignment);
    706   DCHECK_GE(frame_size, core_reg_size + fp_reg_size + kArm64PointerSize);
    707   IncreaseFrameSize(frame_size);
    708 
    709   // Save callee-saves.
    710   SpillRegisters(core_reg_list, frame_size - core_reg_size);
    711   SpillRegisters(fp_reg_list, frame_size - core_reg_size - fp_reg_size);
    712 
    713   DCHECK(core_reg_list.IncludesAliasOf(reg_x(TR)));
    714 
    715   // Write ArtMethod*
    716   DCHECK(X0 == method_reg.AsArm64().AsXRegister());
    717   StoreToOffset(X0, SP, 0);
    718 
    719   // Write out entry spills
    720   int32_t offset = frame_size + kArm64PointerSize;
    721   for (size_t i = 0; i < entry_spills.size(); ++i) {
    722     Arm64ManagedRegister reg = entry_spills.at(i).AsArm64();
    723     if (reg.IsNoRegister()) {
    724       // only increment stack offset.
    725       ManagedRegisterSpill spill = entry_spills.at(i);
    726       offset += spill.getSize();
    727     } else if (reg.IsXRegister()) {
    728       StoreToOffset(reg.AsXRegister(), SP, offset);
    729       offset += 8;
    730     } else if (reg.IsWRegister()) {
    731       StoreWToOffset(kStoreWord, reg.AsWRegister(), SP, offset);
    732       offset += 4;
    733     } else if (reg.IsDRegister()) {
    734       StoreDToOffset(reg.AsDRegister(), SP, offset);
    735       offset += 8;
    736     } else if (reg.IsSRegister()) {
    737       StoreSToOffset(reg.AsSRegister(), SP, offset);
    738       offset += 4;
    739     }
    740   }
    741 }
    742 
    743 void Arm64Assembler::RemoveFrame(size_t frame_size,
    744                                  const std::vector<ManagedRegister>& callee_save_regs) {
    745   // Setup VIXL CPURegList for callee-saves.
    746   CPURegList core_reg_list(CPURegister::kRegister, kXRegSize, 0);
    747   CPURegList fp_reg_list(CPURegister::kFPRegister, kDRegSize, 0);
    748   for (auto r : callee_save_regs) {
    749     Arm64ManagedRegister reg = r.AsArm64();
    750     if (reg.IsXRegister()) {
    751       core_reg_list.Combine(reg_x(reg.AsXRegister()).code());
    752     } else {
    753       DCHECK(reg.IsDRegister());
    754       fp_reg_list.Combine(reg_d(reg.AsDRegister()).code());
    755     }
    756   }
    757   size_t core_reg_size = core_reg_list.TotalSizeInBytes();
    758   size_t fp_reg_size = fp_reg_list.TotalSizeInBytes();
    759 
    760   // For now we only check that the size of the frame is large enough to hold spills and method
    761   // reference.
    762   DCHECK_GE(frame_size, core_reg_size + fp_reg_size + kArm64PointerSize);
    763   DCHECK_ALIGNED(frame_size, kStackAlignment);
    764 
    765   DCHECK(core_reg_list.IncludesAliasOf(reg_x(TR)));
    766 
    767   cfi_.RememberState();
    768 
    769   // Restore callee-saves.
    770   UnspillRegisters(core_reg_list, frame_size - core_reg_size);
    771   UnspillRegisters(fp_reg_list, frame_size - core_reg_size - fp_reg_size);
    772 
    773   // Decrease frame size to start of callee saved regs.
    774   DecreaseFrameSize(frame_size);
    775 
    776   // Pop callee saved and return to LR.
    777   ___ Ret();
    778 
    779   // The CFI should be restored for any code that follows the exit block.
    780   cfi_.RestoreState();
    781   cfi_.DefCFAOffset(frame_size);
    782 }
    783 
    784 void Arm64Assembler::PoisonHeapReference(vixl::Register reg) {
    785   DCHECK(reg.IsW());
    786   // reg = -reg.
    787   ___ Neg(reg, vixl::Operand(reg));
    788 }
    789 
    790 void Arm64Assembler::UnpoisonHeapReference(vixl::Register reg) {
    791   DCHECK(reg.IsW());
    792   // reg = -reg.
    793   ___ Neg(reg, vixl::Operand(reg));
    794 }
    795 
    796 void Arm64Assembler::MaybeUnpoisonHeapReference(vixl::Register reg) {
    797   if (kPoisonHeapReferences) {
    798     UnpoisonHeapReference(reg);
    799   }
    800 }
    801 
    802 #undef ___
    803 
    804 }  // namespace arm64
    805 }  // namespace art
    806