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