Home | History | Annotate | Download | only in arm
      1 /*
      2  * Copyright (C) 2011 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_arm.h"
     18 
     19 #include "base/logging.h"
     20 #include "entrypoints/quick/quick_entrypoints.h"
     21 #include "offsets.h"
     22 #include "thread.h"
     23 #include "utils.h"
     24 
     25 namespace art {
     26 namespace arm {
     27 
     28 const char* kRegisterNames[] = {
     29   "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10",
     30   "fp", "ip", "sp", "lr", "pc"
     31 };
     32 
     33 const char* kConditionNames[] = {
     34   "EQ", "NE", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT",
     35   "LE", "AL",
     36 };
     37 
     38 std::ostream& operator<<(std::ostream& os, const Register& rhs) {
     39   if (rhs >= R0 && rhs <= PC) {
     40     os << kRegisterNames[rhs];
     41   } else {
     42     os << "Register[" << static_cast<int>(rhs) << "]";
     43   }
     44   return os;
     45 }
     46 
     47 
     48 std::ostream& operator<<(std::ostream& os, const SRegister& rhs) {
     49   if (rhs >= S0 && rhs < kNumberOfSRegisters) {
     50     os << "s" << static_cast<int>(rhs);
     51   } else {
     52     os << "SRegister[" << static_cast<int>(rhs) << "]";
     53   }
     54   return os;
     55 }
     56 
     57 
     58 std::ostream& operator<<(std::ostream& os, const DRegister& rhs) {
     59   if (rhs >= D0 && rhs < kNumberOfDRegisters) {
     60     os << "d" << static_cast<int>(rhs);
     61   } else {
     62     os << "DRegister[" << static_cast<int>(rhs) << "]";
     63   }
     64   return os;
     65 }
     66 
     67 std::ostream& operator<<(std::ostream& os, const Condition& rhs) {
     68   if (rhs >= EQ && rhs <= AL) {
     69     os << kConditionNames[rhs];
     70   } else {
     71     os << "Condition[" << static_cast<int>(rhs) << "]";
     72   }
     73   return os;
     74 }
     75 
     76 ShifterOperand::ShifterOperand(uint32_t immed)
     77     : type_(kImmediate), rm_(kNoRegister), rs_(kNoRegister),
     78       is_rotate_(false), is_shift_(false), shift_(kNoShift), rotate_(0), immed_(immed) {
     79   CHECK(immed < (1u << 12) || ArmAssembler::ModifiedImmediate(immed) != kInvalidModifiedImmediate);
     80 }
     81 
     82 
     83 uint32_t ShifterOperand::encodingArm() const {
     84   CHECK(is_valid());
     85   switch (type_) {
     86     case kImmediate:
     87       if (is_rotate_) {
     88         return (rotate_ << kRotateShift) | (immed_ << kImmed8Shift);
     89       } else {
     90         return immed_;
     91       }
     92       break;
     93     case kRegister:
     94       if (is_shift_) {
     95         // Shifted immediate or register.
     96         if (rs_ == kNoRegister) {
     97           // Immediate shift.
     98           return immed_ << kShiftImmShift |
     99                           static_cast<uint32_t>(shift_) << kShiftShift |
    100                           static_cast<uint32_t>(rm_);
    101         } else {
    102           // Register shift.
    103           return static_cast<uint32_t>(rs_) << kShiftRegisterShift |
    104               static_cast<uint32_t>(shift_) << kShiftShift | (1 << 4) |
    105               static_cast<uint32_t>(rm_);
    106         }
    107       } else {
    108         // Simple register
    109         return static_cast<uint32_t>(rm_);
    110       }
    111       break;
    112     default:
    113       // Can't get here.
    114       LOG(FATAL) << "Invalid shifter operand for ARM";
    115       return 0;
    116   }
    117 }
    118 
    119 uint32_t ShifterOperand::encodingThumb() const {
    120   switch (type_) {
    121     case kImmediate:
    122       return immed_;
    123     case kRegister:
    124       if (is_shift_) {
    125         // Shifted immediate or register.
    126         if (rs_ == kNoRegister) {
    127           // Immediate shift.
    128           if (shift_ == RRX) {
    129             // RRX is encoded as an ROR with imm 0.
    130             return ROR << 4 | static_cast<uint32_t>(rm_);
    131           } else {
    132             uint32_t imm3 = immed_ >> 2;
    133             uint32_t imm2 = immed_ & 0b11;
    134 
    135             return imm3 << 12 | imm2 << 6 | shift_ << 4 |
    136                 static_cast<uint32_t>(rm_);
    137           }
    138         } else {
    139           LOG(FATAL) << "No register-shifted register instruction available in thumb";
    140           return 0;
    141         }
    142       } else {
    143         // Simple register
    144         return static_cast<uint32_t>(rm_);
    145       }
    146       break;
    147     default:
    148       // Can't get here.
    149       LOG(FATAL) << "Invalid shifter operand for thumb";
    150       return 0;
    151   }
    152   return 0;
    153 }
    154 
    155 bool ShifterOperand::CanHoldThumb(Register rd, Register rn, Opcode opcode,
    156                                   uint32_t immediate, ShifterOperand* shifter_op) {
    157   shifter_op->type_ = kImmediate;
    158   shifter_op->immed_ = immediate;
    159   shifter_op->is_shift_ = false;
    160   shifter_op->is_rotate_ = false;
    161   switch (opcode) {
    162     case ADD:
    163     case SUB:
    164       if (rn == SP) {
    165         if (rd == SP) {
    166           return immediate < (1 << 9);    // 9 bits allowed.
    167         } else {
    168           return immediate < (1 << 12);   // 12 bits.
    169         }
    170       }
    171       if (immediate < (1 << 12)) {    // Less than (or equal to) 12 bits can always be done.
    172         return true;
    173       }
    174       return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
    175 
    176     case MOV:
    177       // TODO: Support less than or equal to 12bits.
    178       return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
    179     case MVN:
    180     default:
    181       return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate;
    182   }
    183 }
    184 
    185 uint32_t Address::encodingArm() const {
    186   CHECK(IsAbsoluteUint(12, offset_));
    187   uint32_t encoding;
    188   if (is_immed_offset_) {
    189     if (offset_ < 0) {
    190       encoding = (am_ ^ (1 << kUShift)) | -offset_;  // Flip U to adjust sign.
    191     } else {
    192       encoding =  am_ | offset_;
    193     }
    194   } else {
    195     uint32_t imm5 = offset_;
    196     uint32_t shift = shift_;
    197     if (shift == RRX) {
    198       imm5 = 0;
    199       shift = ROR;
    200     }
    201     encoding = am_ | static_cast<uint32_t>(rm_) | shift << 5 | offset_ << 7 | B25;
    202   }
    203   encoding |= static_cast<uint32_t>(rn_) << kRnShift;
    204   return encoding;
    205 }
    206 
    207 
    208 uint32_t Address::encodingThumb(bool is_32bit) const {
    209   uint32_t encoding = 0;
    210   if (is_immed_offset_) {
    211     encoding = static_cast<uint32_t>(rn_) << 16;
    212     // Check for the T3/T4 encoding.
    213     // PUW must Offset for T3
    214     // Convert ARM PU0W to PUW
    215     // The Mode is in ARM encoding format which is:
    216     // |P|U|0|W|
    217     // we need this in thumb2 mode:
    218     // |P|U|W|
    219 
    220     uint32_t am = am_;
    221     int32_t offset = offset_;
    222     if (offset < 0) {
    223       am ^= 1 << kUShift;
    224       offset = -offset;
    225     }
    226     if (offset_ < 0 || (offset >= 0 && offset < 256 &&
    227         am_ != Mode::Offset)) {
    228       // T4 encoding.
    229       uint32_t PUW = am >> 21;   // Move down to bottom of word.
    230       PUW = (PUW >> 1) | (PUW & 1);   // Bits 3, 2 and 0.
    231       // If P is 0 then W must be 1 (Different from ARM).
    232       if ((PUW & 0b100) == 0) {
    233         PUW |= 0b1;
    234       }
    235       encoding |= B11 | PUW << 8 | offset;
    236     } else {
    237       // T3 encoding (also sets op1 to 0b01).
    238       encoding |= B23 | offset_;
    239     }
    240   } else {
    241     // Register offset, possibly shifted.
    242     // Need to choose between encoding T1 (16 bit) or T2.
    243     // Only Offset mode is supported.  Shift must be LSL and the count
    244     // is only 2 bits.
    245     CHECK_EQ(shift_, LSL);
    246     CHECK_LE(offset_, 4);
    247     CHECK_EQ(am_, Offset);
    248     bool is_t2 = is_32bit;
    249     if (ArmAssembler::IsHighRegister(rn_) || ArmAssembler::IsHighRegister(rm_)) {
    250       is_t2 = true;
    251     } else if (offset_ != 0) {
    252       is_t2 = true;
    253     }
    254     if (is_t2) {
    255       encoding = static_cast<uint32_t>(rn_) << 16 | static_cast<uint32_t>(rm_) |
    256           offset_ << 4;
    257     } else {
    258       encoding = static_cast<uint32_t>(rn_) << 3 | static_cast<uint32_t>(rm_) << 6;
    259     }
    260   }
    261   return encoding;
    262 }
    263 
    264 // This is very like the ARM encoding except the offset is 10 bits.
    265 uint32_t Address::encodingThumbLdrdStrd() const {
    266   uint32_t encoding;
    267   uint32_t am = am_;
    268   // If P is 0 then W must be 1 (Different from ARM).
    269   uint32_t PU1W = am_ >> 21;   // Move down to bottom of word.
    270   if ((PU1W & 0b1000) == 0) {
    271     am |= 1 << 21;      // Set W bit.
    272   }
    273   if (offset_ < 0) {
    274     int32_t off = -offset_;
    275     CHECK_LT(off, 1024);
    276     CHECK_EQ((off & 0b11), 0);    // Must be multiple of 4.
    277     encoding = (am ^ (1 << kUShift)) | off >> 2;  // Flip U to adjust sign.
    278   } else {
    279     CHECK_LT(offset_, 1024);
    280     CHECK_EQ((offset_ & 0b11), 0);    // Must be multiple of 4.
    281     encoding =  am | offset_ >> 2;
    282   }
    283   encoding |= static_cast<uint32_t>(rn_) << 16;
    284   return encoding;
    285 }
    286 
    287 // Encoding for ARM addressing mode 3.
    288 uint32_t Address::encoding3() const {
    289   const uint32_t offset_mask = (1 << 12) - 1;
    290   uint32_t encoding = encodingArm();
    291   uint32_t offset = encoding & offset_mask;
    292   CHECK_LT(offset, 256u);
    293   return (encoding & ~offset_mask) | ((offset & 0xf0) << 4) | (offset & 0xf);
    294 }
    295 
    296 // Encoding for vfp load/store addressing.
    297 uint32_t Address::vencoding() const {
    298   const uint32_t offset_mask = (1 << 12) - 1;
    299   uint32_t encoding = encodingArm();
    300   uint32_t offset = encoding & offset_mask;
    301   CHECK(IsAbsoluteUint(10, offset));  // In the range -1020 to +1020.
    302   CHECK_ALIGNED(offset, 2);  // Multiple of 4.
    303   CHECK((am_ == Offset) || (am_ == NegOffset));
    304   uint32_t vencoding = (encoding & (0xf << kRnShift)) | (offset >> 2);
    305   if (am_ == Offset) {
    306     vencoding |= 1 << 23;
    307   }
    308   return vencoding;
    309 }
    310 
    311 
    312 bool Address::CanHoldLoadOffsetArm(LoadOperandType type, int offset) {
    313   switch (type) {
    314     case kLoadSignedByte:
    315     case kLoadSignedHalfword:
    316     case kLoadUnsignedHalfword:
    317     case kLoadWordPair:
    318       return IsAbsoluteUint(8, offset);  // Addressing mode 3.
    319     case kLoadUnsignedByte:
    320     case kLoadWord:
    321       return IsAbsoluteUint(12, offset);  // Addressing mode 2.
    322     case kLoadSWord:
    323     case kLoadDWord:
    324       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
    325     default:
    326       LOG(FATAL) << "UNREACHABLE";
    327       return false;
    328   }
    329 }
    330 
    331 
    332 bool Address::CanHoldStoreOffsetArm(StoreOperandType type, int offset) {
    333   switch (type) {
    334     case kStoreHalfword:
    335     case kStoreWordPair:
    336       return IsAbsoluteUint(8, offset);  // Addressing mode 3.
    337     case kStoreByte:
    338     case kStoreWord:
    339       return IsAbsoluteUint(12, offset);  // Addressing mode 2.
    340     case kStoreSWord:
    341     case kStoreDWord:
    342       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
    343     default:
    344       LOG(FATAL) << "UNREACHABLE";
    345       return false;
    346   }
    347 }
    348 
    349 bool Address::CanHoldLoadOffsetThumb(LoadOperandType type, int offset) {
    350   switch (type) {
    351     case kLoadSignedByte:
    352     case kLoadSignedHalfword:
    353     case kLoadUnsignedHalfword:
    354     case kLoadUnsignedByte:
    355     case kLoadWord:
    356       return IsAbsoluteUint(12, offset);
    357     case kLoadSWord:
    358     case kLoadDWord:
    359       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
    360     case kLoadWordPair:
    361       return IsAbsoluteUint(10, offset);
    362   default:
    363       LOG(FATAL) << "UNREACHABLE";
    364       return false;
    365   }
    366 }
    367 
    368 
    369 bool Address::CanHoldStoreOffsetThumb(StoreOperandType type, int offset) {
    370   switch (type) {
    371     case kStoreHalfword:
    372     case kStoreByte:
    373     case kStoreWord:
    374       return IsAbsoluteUint(12, offset);
    375     case kStoreSWord:
    376     case kStoreDWord:
    377       return IsAbsoluteUint(10, offset);  // VFP addressing mode.
    378     case kStoreWordPair:
    379       return IsAbsoluteUint(10, offset);
    380   default:
    381       LOG(FATAL) << "UNREACHABLE";
    382       return false;
    383   }
    384 }
    385 
    386 void ArmAssembler::Pad(uint32_t bytes) {
    387   AssemblerBuffer::EnsureCapacity ensured(&buffer_);
    388   for (uint32_t i = 0; i < bytes; ++i) {
    389     buffer_.Emit<byte>(0);
    390   }
    391 }
    392 
    393 constexpr size_t kFramePointerSize = 4;
    394 
    395 void ArmAssembler::BuildFrame(size_t frame_size, ManagedRegister method_reg,
    396                               const std::vector<ManagedRegister>& callee_save_regs,
    397                               const ManagedRegisterEntrySpills& entry_spills) {
    398   CHECK_ALIGNED(frame_size, kStackAlignment);
    399   CHECK_EQ(R0, method_reg.AsArm().AsCoreRegister());
    400 
    401   // Push callee saves and link register.
    402   RegList push_list = 1 << LR;
    403   size_t pushed_values = 1;
    404   for (size_t i = 0; i < callee_save_regs.size(); i++) {
    405     Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
    406     push_list |= 1 << reg;
    407     pushed_values++;
    408   }
    409   PushList(push_list);
    410 
    411   // Increase frame to required size.
    412   CHECK_GT(frame_size, pushed_values * kFramePointerSize);  // Must at least have space for Method*.
    413   size_t adjust = frame_size - (pushed_values * kFramePointerSize);
    414   IncreaseFrameSize(adjust);
    415 
    416   // Write out Method*.
    417   StoreToOffset(kStoreWord, R0, SP, 0);
    418 
    419   // Write out entry spills.
    420   for (size_t i = 0; i < entry_spills.size(); ++i) {
    421     Register reg = entry_spills.at(i).AsArm().AsCoreRegister();
    422     StoreToOffset(kStoreWord, reg, SP, frame_size + kFramePointerSize + (i * kFramePointerSize));
    423   }
    424 }
    425 
    426 void ArmAssembler::RemoveFrame(size_t frame_size,
    427                               const std::vector<ManagedRegister>& callee_save_regs) {
    428   CHECK_ALIGNED(frame_size, kStackAlignment);
    429   // Compute callee saves to pop and PC.
    430   RegList pop_list = 1 << PC;
    431   size_t pop_values = 1;
    432   for (size_t i = 0; i < callee_save_regs.size(); i++) {
    433     Register reg = callee_save_regs.at(i).AsArm().AsCoreRegister();
    434     pop_list |= 1 << reg;
    435     pop_values++;
    436   }
    437 
    438   // Decrease frame to start of callee saves.
    439   CHECK_GT(frame_size, pop_values * kFramePointerSize);
    440   size_t adjust = frame_size - (pop_values * kFramePointerSize);
    441   DecreaseFrameSize(adjust);
    442 
    443   // Pop callee saves and PC.
    444   PopList(pop_list);
    445 }
    446 
    447 void ArmAssembler::IncreaseFrameSize(size_t adjust) {
    448   AddConstant(SP, -adjust);
    449 }
    450 
    451 void ArmAssembler::DecreaseFrameSize(size_t adjust) {
    452   AddConstant(SP, adjust);
    453 }
    454 
    455 void ArmAssembler::Store(FrameOffset dest, ManagedRegister msrc, size_t size) {
    456   ArmManagedRegister src = msrc.AsArm();
    457   if (src.IsNoRegister()) {
    458     CHECK_EQ(0u, size);
    459   } else if (src.IsCoreRegister()) {
    460     CHECK_EQ(4u, size);
    461     StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    462   } else if (src.IsRegisterPair()) {
    463     CHECK_EQ(8u, size);
    464     StoreToOffset(kStoreWord, src.AsRegisterPairLow(), SP, dest.Int32Value());
    465     StoreToOffset(kStoreWord, src.AsRegisterPairHigh(),
    466                   SP, dest.Int32Value() + 4);
    467   } else if (src.IsSRegister()) {
    468     StoreSToOffset(src.AsSRegister(), SP, dest.Int32Value());
    469   } else {
    470     CHECK(src.IsDRegister()) << src;
    471     StoreDToOffset(src.AsDRegister(), SP, dest.Int32Value());
    472   }
    473 }
    474 
    475 void ArmAssembler::StoreRef(FrameOffset dest, ManagedRegister msrc) {
    476   ArmManagedRegister src = msrc.AsArm();
    477   CHECK(src.IsCoreRegister()) << src;
    478   StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    479 }
    480 
    481 void ArmAssembler::StoreRawPtr(FrameOffset dest, ManagedRegister msrc) {
    482   ArmManagedRegister src = msrc.AsArm();
    483   CHECK(src.IsCoreRegister()) << src;
    484   StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    485 }
    486 
    487 void ArmAssembler::StoreSpanning(FrameOffset dest, ManagedRegister msrc,
    488                               FrameOffset in_off, ManagedRegister mscratch) {
    489   ArmManagedRegister src = msrc.AsArm();
    490   ArmManagedRegister scratch = mscratch.AsArm();
    491   StoreToOffset(kStoreWord, src.AsCoreRegister(), SP, dest.Int32Value());
    492   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, in_off.Int32Value());
    493   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4);
    494 }
    495 
    496 void ArmAssembler::CopyRef(FrameOffset dest, FrameOffset src,
    497                         ManagedRegister mscratch) {
    498   ArmManagedRegister scratch = mscratch.AsArm();
    499   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    500   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    501 }
    502 
    503 void ArmAssembler::LoadRef(ManagedRegister mdest, ManagedRegister base,
    504                            MemberOffset offs) {
    505   ArmManagedRegister dst = mdest.AsArm();
    506   CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
    507   LoadFromOffset(kLoadWord, dst.AsCoreRegister(),
    508                  base.AsArm().AsCoreRegister(), offs.Int32Value());
    509   if (kPoisonHeapReferences) {
    510     rsb(dst.AsCoreRegister(), dst.AsCoreRegister(), ShifterOperand(0));
    511   }
    512 }
    513 
    514 void ArmAssembler::LoadRef(ManagedRegister mdest, FrameOffset  src) {
    515   ArmManagedRegister dst = mdest.AsArm();
    516   CHECK(dst.IsCoreRegister()) << dst;
    517   LoadFromOffset(kLoadWord, dst.AsCoreRegister(), SP, src.Int32Value());
    518 }
    519 
    520 void ArmAssembler::LoadRawPtr(ManagedRegister mdest, ManagedRegister base,
    521                            Offset offs) {
    522   ArmManagedRegister dst = mdest.AsArm();
    523   CHECK(dst.IsCoreRegister() && dst.IsCoreRegister()) << dst;
    524   LoadFromOffset(kLoadWord, dst.AsCoreRegister(),
    525                  base.AsArm().AsCoreRegister(), offs.Int32Value());
    526 }
    527 
    528 void ArmAssembler::StoreImmediateToFrame(FrameOffset dest, uint32_t imm,
    529                                       ManagedRegister mscratch) {
    530   ArmManagedRegister scratch = mscratch.AsArm();
    531   CHECK(scratch.IsCoreRegister()) << scratch;
    532   LoadImmediate(scratch.AsCoreRegister(), imm);
    533   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    534 }
    535 
    536 void ArmAssembler::StoreImmediateToThread32(ThreadOffset<4> dest, uint32_t imm,
    537                                        ManagedRegister mscratch) {
    538   ArmManagedRegister scratch = mscratch.AsArm();
    539   CHECK(scratch.IsCoreRegister()) << scratch;
    540   LoadImmediate(scratch.AsCoreRegister(), imm);
    541   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), TR, dest.Int32Value());
    542 }
    543 
    544 static void EmitLoad(ArmAssembler* assembler, ManagedRegister m_dst,
    545                      Register src_register, int32_t src_offset, size_t size) {
    546   ArmManagedRegister dst = m_dst.AsArm();
    547   if (dst.IsNoRegister()) {
    548     CHECK_EQ(0u, size) << dst;
    549   } else if (dst.IsCoreRegister()) {
    550     CHECK_EQ(4u, size) << dst;
    551     assembler->LoadFromOffset(kLoadWord, dst.AsCoreRegister(), src_register, src_offset);
    552   } else if (dst.IsRegisterPair()) {
    553     CHECK_EQ(8u, size) << dst;
    554     assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairLow(), src_register, src_offset);
    555     assembler->LoadFromOffset(kLoadWord, dst.AsRegisterPairHigh(), src_register, src_offset + 4);
    556   } else if (dst.IsSRegister()) {
    557     assembler->LoadSFromOffset(dst.AsSRegister(), src_register, src_offset);
    558   } else {
    559     CHECK(dst.IsDRegister()) << dst;
    560     assembler->LoadDFromOffset(dst.AsDRegister(), src_register, src_offset);
    561   }
    562 }
    563 
    564 void ArmAssembler::Load(ManagedRegister m_dst, FrameOffset src, size_t size) {
    565   return EmitLoad(this, m_dst, SP, src.Int32Value(), size);
    566 }
    567 
    568 void ArmAssembler::LoadFromThread32(ManagedRegister m_dst, ThreadOffset<4> src, size_t size) {
    569   return EmitLoad(this, m_dst, TR, src.Int32Value(), size);
    570 }
    571 
    572 void ArmAssembler::LoadRawPtrFromThread32(ManagedRegister m_dst, ThreadOffset<4> offs) {
    573   ArmManagedRegister dst = m_dst.AsArm();
    574   CHECK(dst.IsCoreRegister()) << dst;
    575   LoadFromOffset(kLoadWord, dst.AsCoreRegister(), TR, offs.Int32Value());
    576 }
    577 
    578 void ArmAssembler::CopyRawPtrFromThread32(FrameOffset fr_offs,
    579                                         ThreadOffset<4> thr_offs,
    580                                         ManagedRegister mscratch) {
    581   ArmManagedRegister scratch = mscratch.AsArm();
    582   CHECK(scratch.IsCoreRegister()) << scratch;
    583   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    584                  TR, thr_offs.Int32Value());
    585   StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
    586                 SP, fr_offs.Int32Value());
    587 }
    588 
    589 void ArmAssembler::CopyRawPtrToThread32(ThreadOffset<4> thr_offs,
    590                                       FrameOffset fr_offs,
    591                                       ManagedRegister mscratch) {
    592   ArmManagedRegister scratch = mscratch.AsArm();
    593   CHECK(scratch.IsCoreRegister()) << scratch;
    594   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    595                  SP, fr_offs.Int32Value());
    596   StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
    597                 TR, thr_offs.Int32Value());
    598 }
    599 
    600 void ArmAssembler::StoreStackOffsetToThread32(ThreadOffset<4> thr_offs,
    601                                             FrameOffset fr_offs,
    602                                             ManagedRegister mscratch) {
    603   ArmManagedRegister scratch = mscratch.AsArm();
    604   CHECK(scratch.IsCoreRegister()) << scratch;
    605   AddConstant(scratch.AsCoreRegister(), SP, fr_offs.Int32Value(), AL);
    606   StoreToOffset(kStoreWord, scratch.AsCoreRegister(),
    607                 TR, thr_offs.Int32Value());
    608 }
    609 
    610 void ArmAssembler::StoreStackPointerToThread32(ThreadOffset<4> thr_offs) {
    611   StoreToOffset(kStoreWord, SP, TR, thr_offs.Int32Value());
    612 }
    613 
    614 void ArmAssembler::SignExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
    615   UNIMPLEMENTED(FATAL) << "no sign extension necessary for arm";
    616 }
    617 
    618 void ArmAssembler::ZeroExtend(ManagedRegister /*mreg*/, size_t /*size*/) {
    619   UNIMPLEMENTED(FATAL) << "no zero extension necessary for arm";
    620 }
    621 
    622 void ArmAssembler::Move(ManagedRegister m_dst, ManagedRegister m_src, size_t /*size*/) {
    623   ArmManagedRegister dst = m_dst.AsArm();
    624   ArmManagedRegister src = m_src.AsArm();
    625   if (!dst.Equals(src)) {
    626     if (dst.IsCoreRegister()) {
    627       CHECK(src.IsCoreRegister()) << src;
    628       mov(dst.AsCoreRegister(), ShifterOperand(src.AsCoreRegister()));
    629     } else if (dst.IsDRegister()) {
    630       CHECK(src.IsDRegister()) << src;
    631       vmovd(dst.AsDRegister(), src.AsDRegister());
    632     } else if (dst.IsSRegister()) {
    633       CHECK(src.IsSRegister()) << src;
    634       vmovs(dst.AsSRegister(), src.AsSRegister());
    635     } else {
    636       CHECK(dst.IsRegisterPair()) << dst;
    637       CHECK(src.IsRegisterPair()) << src;
    638       // Ensure that the first move doesn't clobber the input of the second.
    639       if (src.AsRegisterPairHigh() != dst.AsRegisterPairLow()) {
    640         mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow()));
    641         mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh()));
    642       } else {
    643         mov(dst.AsRegisterPairHigh(), ShifterOperand(src.AsRegisterPairHigh()));
    644         mov(dst.AsRegisterPairLow(), ShifterOperand(src.AsRegisterPairLow()));
    645       }
    646     }
    647   }
    648 }
    649 
    650 void ArmAssembler::Copy(FrameOffset dest, FrameOffset src, ManagedRegister mscratch, size_t size) {
    651   ArmManagedRegister scratch = mscratch.AsArm();
    652   CHECK(scratch.IsCoreRegister()) << scratch;
    653   CHECK(size == 4 || size == 8) << size;
    654   if (size == 4) {
    655     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    656     StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    657   } else if (size == 8) {
    658     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value());
    659     StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value());
    660     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP, src.Int32Value() + 4);
    661     StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, dest.Int32Value() + 4);
    662   }
    663 }
    664 
    665 void ArmAssembler::Copy(FrameOffset dest, ManagedRegister src_base, Offset src_offset,
    666                         ManagedRegister mscratch, size_t size) {
    667   Register scratch = mscratch.AsArm().AsCoreRegister();
    668   CHECK_EQ(size, 4u);
    669   LoadFromOffset(kLoadWord, scratch, src_base.AsArm().AsCoreRegister(), src_offset.Int32Value());
    670   StoreToOffset(kStoreWord, scratch, SP, dest.Int32Value());
    671 }
    672 
    673 void ArmAssembler::Copy(ManagedRegister dest_base, Offset dest_offset, FrameOffset src,
    674                         ManagedRegister mscratch, size_t size) {
    675   Register scratch = mscratch.AsArm().AsCoreRegister();
    676   CHECK_EQ(size, 4u);
    677   LoadFromOffset(kLoadWord, scratch, SP, src.Int32Value());
    678   StoreToOffset(kStoreWord, scratch, dest_base.AsArm().AsCoreRegister(), dest_offset.Int32Value());
    679 }
    680 
    681 void ArmAssembler::Copy(FrameOffset /*dst*/, FrameOffset /*src_base*/, Offset /*src_offset*/,
    682                         ManagedRegister /*mscratch*/, size_t /*size*/) {
    683   UNIMPLEMENTED(FATAL);
    684 }
    685 
    686 void ArmAssembler::Copy(ManagedRegister dest, Offset dest_offset,
    687                         ManagedRegister src, Offset src_offset,
    688                         ManagedRegister mscratch, size_t size) {
    689   CHECK_EQ(size, 4u);
    690   Register scratch = mscratch.AsArm().AsCoreRegister();
    691   LoadFromOffset(kLoadWord, scratch, src.AsArm().AsCoreRegister(), src_offset.Int32Value());
    692   StoreToOffset(kStoreWord, scratch, dest.AsArm().AsCoreRegister(), dest_offset.Int32Value());
    693 }
    694 
    695 void ArmAssembler::Copy(FrameOffset /*dst*/, Offset /*dest_offset*/, FrameOffset /*src*/, Offset /*src_offset*/,
    696                         ManagedRegister /*scratch*/, size_t /*size*/) {
    697   UNIMPLEMENTED(FATAL);
    698 }
    699 
    700 void ArmAssembler::CreateHandleScopeEntry(ManagedRegister mout_reg,
    701                                    FrameOffset handle_scope_offset,
    702                                    ManagedRegister min_reg, bool null_allowed) {
    703   ArmManagedRegister out_reg = mout_reg.AsArm();
    704   ArmManagedRegister in_reg = min_reg.AsArm();
    705   CHECK(in_reg.IsNoRegister() || in_reg.IsCoreRegister()) << in_reg;
    706   CHECK(out_reg.IsCoreRegister()) << out_reg;
    707   if (null_allowed) {
    708     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    709     // the address in the handle scope holding the reference.
    710     // e.g. out_reg = (handle == 0) ? 0 : (SP+handle_offset)
    711     if (in_reg.IsNoRegister()) {
    712       LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
    713                      SP, handle_scope_offset.Int32Value());
    714       in_reg = out_reg;
    715     }
    716     cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
    717     if (!out_reg.Equals(in_reg)) {
    718       it(EQ, kItElse);
    719       LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);
    720     } else {
    721       it(NE);
    722     }
    723     AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
    724   } else {
    725     AddConstant(out_reg.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
    726   }
    727 }
    728 
    729 void ArmAssembler::CreateHandleScopeEntry(FrameOffset out_off,
    730                                    FrameOffset handle_scope_offset,
    731                                    ManagedRegister mscratch,
    732                                    bool null_allowed) {
    733   ArmManagedRegister scratch = mscratch.AsArm();
    734   CHECK(scratch.IsCoreRegister()) << scratch;
    735   if (null_allowed) {
    736     LoadFromOffset(kLoadWord, scratch.AsCoreRegister(), SP,
    737                    handle_scope_offset.Int32Value());
    738     // Null values get a handle scope entry value of 0.  Otherwise, the handle scope entry is
    739     // the address in the handle scope holding the reference.
    740     // e.g. scratch = (scratch == 0) ? 0 : (SP+handle_scope_offset)
    741     cmp(scratch.AsCoreRegister(), ShifterOperand(0));
    742     it(NE);
    743     AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), NE);
    744   } else {
    745     AddConstant(scratch.AsCoreRegister(), SP, handle_scope_offset.Int32Value(), AL);
    746   }
    747   StoreToOffset(kStoreWord, scratch.AsCoreRegister(), SP, out_off.Int32Value());
    748 }
    749 
    750 void ArmAssembler::LoadReferenceFromHandleScope(ManagedRegister mout_reg,
    751                                          ManagedRegister min_reg) {
    752   ArmManagedRegister out_reg = mout_reg.AsArm();
    753   ArmManagedRegister in_reg = min_reg.AsArm();
    754   CHECK(out_reg.IsCoreRegister()) << out_reg;
    755   CHECK(in_reg.IsCoreRegister()) << in_reg;
    756   Label null_arg;
    757   if (!out_reg.Equals(in_reg)) {
    758     LoadImmediate(out_reg.AsCoreRegister(), 0, EQ);     // TODO: why EQ?
    759   }
    760   cmp(in_reg.AsCoreRegister(), ShifterOperand(0));
    761   it(NE);
    762   LoadFromOffset(kLoadWord, out_reg.AsCoreRegister(),
    763                  in_reg.AsCoreRegister(), 0, NE);
    764 }
    765 
    766 void ArmAssembler::VerifyObject(ManagedRegister /*src*/, bool /*could_be_null*/) {
    767   // TODO: not validating references.
    768 }
    769 
    770 void ArmAssembler::VerifyObject(FrameOffset /*src*/, bool /*could_be_null*/) {
    771   // TODO: not validating references.
    772 }
    773 
    774 void ArmAssembler::Call(ManagedRegister mbase, Offset offset,
    775                         ManagedRegister mscratch) {
    776   ArmManagedRegister base = mbase.AsArm();
    777   ArmManagedRegister scratch = mscratch.AsArm();
    778   CHECK(base.IsCoreRegister()) << base;
    779   CHECK(scratch.IsCoreRegister()) << scratch;
    780   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    781                  base.AsCoreRegister(), offset.Int32Value());
    782   blx(scratch.AsCoreRegister());
    783   // TODO: place reference map on call.
    784 }
    785 
    786 void ArmAssembler::Call(FrameOffset base, Offset offset,
    787                         ManagedRegister mscratch) {
    788   ArmManagedRegister scratch = mscratch.AsArm();
    789   CHECK(scratch.IsCoreRegister()) << scratch;
    790   // Call *(*(SP + base) + offset)
    791   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    792                  SP, base.Int32Value());
    793   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    794                  scratch.AsCoreRegister(), offset.Int32Value());
    795   blx(scratch.AsCoreRegister());
    796   // TODO: place reference map on call
    797 }
    798 
    799 void ArmAssembler::CallFromThread32(ThreadOffset<4> /*offset*/, ManagedRegister /*scratch*/) {
    800   UNIMPLEMENTED(FATAL);
    801 }
    802 
    803 void ArmAssembler::GetCurrentThread(ManagedRegister tr) {
    804   mov(tr.AsArm().AsCoreRegister(), ShifterOperand(TR));
    805 }
    806 
    807 void ArmAssembler::GetCurrentThread(FrameOffset offset,
    808                                     ManagedRegister /*scratch*/) {
    809   StoreToOffset(kStoreWord, TR, SP, offset.Int32Value(), AL);
    810 }
    811 
    812 void ArmAssembler::ExceptionPoll(ManagedRegister mscratch, size_t stack_adjust) {
    813   ArmManagedRegister scratch = mscratch.AsArm();
    814   ArmExceptionSlowPath* slow = new ArmExceptionSlowPath(scratch, stack_adjust);
    815   buffer_.EnqueueSlowPath(slow);
    816   LoadFromOffset(kLoadWord, scratch.AsCoreRegister(),
    817                  TR, Thread::ExceptionOffset<4>().Int32Value());
    818   cmp(scratch.AsCoreRegister(), ShifterOperand(0));
    819   b(slow->Entry(), NE);
    820 }
    821 
    822 void ArmExceptionSlowPath::Emit(Assembler* sasm) {
    823   ArmAssembler* sp_asm = down_cast<ArmAssembler*>(sasm);
    824 #define __ sp_asm->
    825   __ Bind(&entry_);
    826   if (stack_adjust_ != 0) {  // Fix up the frame.
    827     __ DecreaseFrameSize(stack_adjust_);
    828   }
    829   // Pass exception object as argument.
    830   // Don't care about preserving R0 as this call won't return.
    831   __ mov(R0, ShifterOperand(scratch_.AsCoreRegister()));
    832   // Set up call to Thread::Current()->pDeliverException.
    833   __ LoadFromOffset(kLoadWord, R12, TR, QUICK_ENTRYPOINT_OFFSET(4, pDeliverException).Int32Value());
    834   __ blx(R12);
    835   // Call never returns.
    836   __ bkpt(0);
    837 #undef __
    838 }
    839 
    840 
    841 static int LeadingZeros(uint32_t val) {
    842   uint32_t alt;
    843   int32_t n;
    844   int32_t count;
    845 
    846   count = 16;
    847   n = 32;
    848   do {
    849     alt = val >> count;
    850     if (alt != 0) {
    851       n = n - count;
    852       val = alt;
    853     }
    854     count >>= 1;
    855   } while (count);
    856   return n - val;
    857 }
    858 
    859 
    860 uint32_t ArmAssembler::ModifiedImmediate(uint32_t value) {
    861   int32_t z_leading;
    862   int32_t z_trailing;
    863   uint32_t b0 = value & 0xff;
    864 
    865   /* Note: case of value==0 must use 0:000:0:0000000 encoding */
    866   if (value <= 0xFF)
    867     return b0;  // 0:000:a:bcdefgh.
    868   if (value == ((b0 << 16) | b0))
    869     return (0x1 << 12) | b0; /* 0:001:a:bcdefgh */
    870   if (value == ((b0 << 24) | (b0 << 16) | (b0 << 8) | b0))
    871     return (0x3 << 12) | b0; /* 0:011:a:bcdefgh */
    872   b0 = (value >> 8) & 0xff;
    873   if (value == ((b0 << 24) | (b0 << 8)))
    874     return (0x2 << 12) | b0; /* 0:010:a:bcdefgh */
    875   /* Can we do it with rotation? */
    876   z_leading = LeadingZeros(value);
    877   z_trailing = 32 - LeadingZeros(~value & (value - 1));
    878   /* A run of eight or fewer active bits? */
    879   if ((z_leading + z_trailing) < 24)
    880     return kInvalidModifiedImmediate;  /* No - bail */
    881   /* left-justify the constant, discarding msb (known to be 1) */
    882   value <<= z_leading + 1;
    883   /* Create bcdefgh */
    884   value >>= 25;
    885 
    886   /* Put it all together */
    887   uint32_t v = 8 + z_leading;
    888 
    889   uint32_t i = (v & 0b10000) >> 4;
    890   uint32_t imm3 = (v >> 1) & 0b111;
    891   uint32_t a = v & 1;
    892   return value | i << 26 | imm3 << 12 | a << 7;
    893 }
    894 
    895 }  // namespace arm
    896 }  // namespace art
    897