macro-assembler-aarch32.cc revision 15985a2fcc72ce0ec5e19c410b444ceec899c11f
1// Copyright 2015, VIXL authors 2// All rights reserved. 3// 4// Redistribution and use in source and binary forms, with or without 5// modification, are permitted provided that the following conditions are met: 6// 7// * Redistributions of source code must retain the above copyright notice, 8// this list of conditions and the following disclaimer. 9// * Redistributions in binary form must reproduce the above copyright 10// notice, this list of conditions and the following disclaimer in the 11// documentation and/or other materials provided with the distribution. 12// * Neither the name of ARM Limited nor the names of its contributors may 13// be used to endorse or promote products derived from this software 14// without specific prior written permission. 15// 16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS CONTRIBUTORS "AS IS" AND 17// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 20// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26// POSSIBILITY OF SUCH DAMAGE. 27 28#include "aarch32/macro-assembler-aarch32.h" 29 30#define STRINGIFY(x) #x 31#define TOSTRING(x) STRINGIFY(x) 32 33#define CONTEXT_SCOPE \ 34 ContextScope context(this, __FILE__ ":" TOSTRING(__LINE__)) 35 36namespace vixl { 37namespace aarch32 { 38 39void UseScratchRegisterScope::Open(MacroAssembler* masm) { 40 VIXL_ASSERT((available_ == NULL) && (available_vfp_ == NULL)); 41 available_ = masm->GetScratchRegisterList(); 42 old_available_ = available_->GetList(); 43 available_vfp_ = masm->GetScratchVRegisterList(); 44 old_available_vfp_ = available_vfp_->GetList(); 45} 46 47 48void UseScratchRegisterScope::Close() { 49 if (available_ != NULL) { 50 available_->SetList(old_available_); 51 available_ = NULL; 52 } 53 if (available_vfp_ != NULL) { 54 available_vfp_->SetList(old_available_vfp_); 55 available_vfp_ = NULL; 56 } 57} 58 59 60bool UseScratchRegisterScope::IsAvailable(const Register& reg) const { 61 VIXL_ASSERT(available_ != NULL); 62 VIXL_ASSERT(reg.IsValid()); 63 return available_->Includes(reg); 64} 65 66 67bool UseScratchRegisterScope::IsAvailable(const VRegister& reg) const { 68 VIXL_ASSERT(available_vfp_ != NULL); 69 VIXL_ASSERT(reg.IsValid()); 70 return available_vfp_->IncludesAllOf(reg); 71} 72 73 74Register UseScratchRegisterScope::Acquire() { 75 VIXL_ASSERT(available_ != NULL); 76 VIXL_CHECK(!available_->IsEmpty()); 77 Register reg = available_->GetFirstAvailableRegister(); 78 available_->Remove(reg); 79 return reg; 80} 81 82 83VRegister UseScratchRegisterScope::AcquireV(unsigned size_in_bits) { 84 switch (size_in_bits) { 85 case kSRegSizeInBits: 86 return AcquireS(); 87 case kDRegSizeInBits: 88 return AcquireD(); 89 case kQRegSizeInBits: 90 return AcquireQ(); 91 default: 92 VIXL_UNREACHABLE(); 93 return NoVReg; 94 } 95} 96 97 98QRegister UseScratchRegisterScope::AcquireQ() { 99 VIXL_ASSERT(available_vfp_ != NULL); 100 VIXL_CHECK(!available_vfp_->IsEmpty()); 101 QRegister reg = available_vfp_->GetFirstAvailableQRegister(); 102 available_vfp_->Remove(reg); 103 return reg; 104} 105 106 107DRegister UseScratchRegisterScope::AcquireD() { 108 VIXL_ASSERT(available_vfp_ != NULL); 109 VIXL_CHECK(!available_vfp_->IsEmpty()); 110 DRegister reg = available_vfp_->GetFirstAvailableDRegister(); 111 available_vfp_->Remove(reg); 112 return reg; 113} 114 115 116SRegister UseScratchRegisterScope::AcquireS() { 117 VIXL_ASSERT(available_vfp_ != NULL); 118 VIXL_CHECK(!available_vfp_->IsEmpty()); 119 SRegister reg = available_vfp_->GetFirstAvailableSRegister(); 120 available_vfp_->Remove(reg); 121 return reg; 122} 123 124 125void UseScratchRegisterScope::Release(const Register& reg) { 126 VIXL_ASSERT(available_ != NULL); 127 VIXL_ASSERT(reg.IsValid()); 128 VIXL_ASSERT(!available_->Includes(reg)); 129 available_->Combine(reg); 130} 131 132 133void UseScratchRegisterScope::Release(const VRegister& reg) { 134 VIXL_ASSERT(available_vfp_ != NULL); 135 VIXL_ASSERT(reg.IsValid()); 136 VIXL_ASSERT(!available_vfp_->IncludesAliasOf(reg)); 137 available_vfp_->Combine(reg); 138} 139 140 141void UseScratchRegisterScope::Include(const RegisterList& list) { 142 VIXL_ASSERT(available_ != NULL); 143 RegisterList excluded_registers(sp, lr, pc); 144 uint32_t mask = list.GetList() & ~excluded_registers.GetList(); 145 available_->SetList(available_->GetList() | mask); 146} 147 148 149void UseScratchRegisterScope::Include(const VRegisterList& list) { 150 VIXL_ASSERT(available_vfp_ != NULL); 151 available_vfp_->SetList(available_vfp_->GetList() | list.GetList()); 152} 153 154 155void UseScratchRegisterScope::Exclude(const RegisterList& list) { 156 VIXL_ASSERT(available_ != NULL); 157 available_->SetList(available_->GetList() & ~list.GetList()); 158} 159 160 161void UseScratchRegisterScope::Exclude(const VRegisterList& list) { 162 VIXL_ASSERT(available_vfp_ != NULL); 163 available_vfp_->SetList(available_vfp_->GetList() & ~list.GetList()); 164} 165 166 167void UseScratchRegisterScope::Exclude(const Operand& operand) { 168 if (operand.IsImmediateShiftedRegister()) { 169 Exclude(operand.GetBaseRegister()); 170 } else if (operand.IsRegisterShiftedRegister()) { 171 Exclude(operand.GetBaseRegister(), operand.GetShiftRegister()); 172 } else { 173 VIXL_ASSERT(operand.IsImmediate()); 174 } 175} 176 177 178void UseScratchRegisterScope::ExcludeAll() { 179 if (available_ != NULL) { 180 available_->SetList(0); 181 } 182 if (available_vfp_ != NULL) { 183 available_vfp_->SetList(0); 184 } 185} 186 187 188void VeneerPoolManager::AddLabel(Label* label) { 189 if (last_label_reference_offset_ != 0) { 190 // If the pool grows faster than the instruction stream, we must adjust 191 // the checkpoint to compensate. The veneer pool entries take 32 bits, so 192 // this can only occur when two consecutive 16-bit instructions add veneer 193 // pool entries. 194 // This is typically the case for cbz and cbnz (other forward branches 195 // have a 32 bit variant which is always used). 196 if (last_label_reference_offset_ + 2 * k16BitT32InstructionSizeInBytes == 197 static_cast<uint32_t>(masm_->GetCursorOffset())) { 198 // We found two 16 bit forward branches generated one after the other. 199 // That means that the pool will grow by one 32-bit branch when 200 // the cursor offset will move forward by only one 16-bit branch. 201 // Update the cbz/cbnz checkpoint to manage the difference. 202 near_checkpoint_ -= 203 k32BitT32InstructionSizeInBytes - k16BitT32InstructionSizeInBytes; 204 } 205 } 206 Label::ForwardReference& back = label->GetBackForwardRef(); 207 VIXL_ASSERT(back.GetMaxForwardDistance() >= kCbzCbnzRange); 208 if (!label->IsInVeneerPool()) { 209 if (back.GetMaxForwardDistance() == kCbzCbnzRange) { 210 near_labels_.push_back(label); 211 label->SetVeneerPoolManager(this, true); 212 } else { 213 far_labels_.push_back(label); 214 label->SetVeneerPoolManager(this, false); 215 } 216 } else if (back.GetMaxForwardDistance() == kCbzCbnzRange) { 217 if (!label->IsNear()) { 218 far_labels_.remove(label); 219 near_labels_.push_back(label); 220 label->SetVeneerPoolManager(this, true); 221 } 222 } 223 224 back.SetIsBranch(); 225 last_label_reference_offset_ = back.GetLocation(); 226 label->UpdateCheckpoint(); 227 Label::Offset tmp = label->GetCheckpoint(); 228 if (label->IsNear()) { 229 if (near_checkpoint_ > tmp) near_checkpoint_ = tmp; 230 } else { 231 if (far_checkpoint_ > tmp) far_checkpoint_ = tmp; 232 } 233 // Always compute the global checkpoint as, adding veneers shorten the 234 // literals' checkpoint. 235 masm_->ComputeCheckpoint(); 236} 237 238 239void VeneerPoolManager::RemoveLabel(Label* label) { 240 label->ClearVeneerPoolManager(); 241 std::list<Label*>& list = label->IsNear() ? near_labels_ : far_labels_; 242 Label::Offset* checkpoint_reference = 243 label->IsNear() ? &near_checkpoint_ : &far_checkpoint_; 244 if (label->GetCheckpoint() == *checkpoint_reference) { 245 // We have to compute checkpoint again. 246 *checkpoint_reference = Label::kMaxOffset; 247 for (std::list<Label*>::iterator it = list.begin(); it != list.end();) { 248 if (*it == label) { 249 it = list.erase(it); 250 } else { 251 *checkpoint_reference = 252 std::min(*checkpoint_reference, (*it)->GetCheckpoint()); 253 ++it; 254 } 255 } 256 masm_->ComputeCheckpoint(); 257 } else { 258 // We only have to remove the label from the list. 259 list.remove(label); 260 } 261} 262 263 264void VeneerPoolManager::EmitLabel(Label* label, Label::Offset emitted_target) { 265 // Define the veneer. 266 Label veneer; 267 masm_->Bind(&veneer); 268 Label::Offset label_checkpoint = Label::kMaxOffset; 269 // Check all uses of this label. 270 for (Label::ForwardRefList::iterator ref = label->GetFirstForwardRef(); 271 ref != label->GetEndForwardRef();) { 272 if (ref->IsBranch()) { 273 if (ref->GetCheckpoint() <= emitted_target) { 274 // Use the veneer. 275 masm_->EncodeLabelFor(*ref, &veneer); 276 ref = label->Erase(ref); 277 } else { 278 // Don't use the veneer => update checkpoint. 279 label_checkpoint = std::min(label_checkpoint, ref->GetCheckpoint()); 280 ++ref; 281 } 282 } else { 283 ++ref; 284 } 285 } 286 label->SetCheckpoint(label_checkpoint); 287 if (label->IsNear()) { 288 near_checkpoint_ = std::min(near_checkpoint_, label_checkpoint); 289 } else { 290 far_checkpoint_ = std::min(far_checkpoint_, label_checkpoint); 291 } 292 // Generate the veneer. 293 masm_->B(label); 294} 295 296 297void VeneerPoolManager::Emit(Label::Offset target) { 298 VIXL_ASSERT(!IsBlocked()); 299 // Sort labels (regarding their checkpoint) to avoid that a veneer 300 // becomes out of range. Near labels are always sorted as it holds only one 301 // range. 302 far_labels_.sort(Label::CompareLabels); 303 // To avoid too many veneers, generate veneers which will be necessary soon. 304 static const size_t kVeneerEmissionMargin = 1 * KBytes; 305 // To avoid too many veneers, use generated veneers for other not too far 306 // uses. 307 static const size_t kVeneerEmittedMargin = 2 * KBytes; 308 Label::Offset emitted_target = target + kVeneerEmittedMargin; 309 target += kVeneerEmissionMargin; 310 // Reset the checkpoints. They will be computed again in the loop. 311 near_checkpoint_ = Label::kMaxOffset; 312 far_checkpoint_ = Label::kMaxOffset; 313 for (std::list<Label*>::iterator it = near_labels_.begin(); 314 it != near_labels_.end();) { 315 Label* label = *it; 316 // Move the label from the near list to the far list as it will be needed in 317 // the far list (as the veneer will generate a far branch). 318 // The label is pushed at the end of the list. The list remains sorted as 319 // we use an unconditional jump which has the biggest range. However, it 320 // wouldn't be a problem if the items at the end of the list were not 321 // sorted as they won't be used by this generation (their range will be 322 // greater than kVeneerEmittedMargin). 323 it = near_labels_.erase(it); 324 far_labels_.push_back(label); 325 label->SetVeneerPoolManager(this, false); 326 EmitLabel(label, emitted_target); 327 } 328 for (std::list<Label*>::iterator it = far_labels_.begin(); 329 it != far_labels_.end();) { 330 // The labels are sorted. As soon as a veneer is not needed, we can stop. 331 if ((*it)->GetCheckpoint() > target) { 332 far_checkpoint_ = std::min(far_checkpoint_, (*it)->GetCheckpoint()); 333 break; 334 } 335 // Even if we no longer have use of this label, we can keep it in the list 336 // as the next "B" would add it back. 337 EmitLabel(*it, emitted_target); 338 ++it; 339 } 340#ifdef VIXL_DEBUG 341 for (std::list<Label*>::iterator it = near_labels_.begin(); 342 it != near_labels_.end(); 343 ++it) { 344 VIXL_ASSERT((*it)->GetCheckpoint() >= near_checkpoint_); 345 } 346 for (std::list<Label*>::iterator it = far_labels_.begin(); 347 it != far_labels_.end(); 348 ++it) { 349 VIXL_ASSERT((*it)->GetCheckpoint() >= far_checkpoint_); 350 } 351#endif 352 masm_->ComputeCheckpoint(); 353} 354 355 356// We use a subclass to access the protected `ExactAssemblyScope` constructor 357// giving us control over the pools, and make the constructor private to limit 358// usage to code paths emitting pools. 359class ExactAssemblyScopeWithoutPoolsCheck : public ExactAssemblyScope { 360 private: 361 ExactAssemblyScopeWithoutPoolsCheck(MacroAssembler* masm, 362 size_t size, 363 SizePolicy size_policy = kExactSize) 364 : ExactAssemblyScope(masm, 365 size, 366 size_policy, 367 ExactAssemblyScope::kIgnorePools) {} 368 369 friend void MacroAssembler::EmitLiteralPool(LiteralPool* const literal_pool, 370 EmitOption option); 371 372 // TODO: `PerformEnsureEmit` is `private`, so we have to make the 373 // `MacroAssembler` a friend. 374 friend class MacroAssembler; 375}; 376 377 378void MacroAssembler::PerformEnsureEmit(Label::Offset target, uint32_t size) { 379 EmitOption option = kBranchRequired; 380 Label after_pools; 381 if (target > veneer_pool_manager_.GetCheckpoint()) { 382 { 383 ExactAssemblyScopeWithoutPoolsCheck 384 guard(this, 385 kMaxInstructionSizeInBytes, 386 ExactAssemblyScope::kMaximumSize); 387 b(&after_pools); 388 } 389 veneer_pool_manager_.Emit(target); 390 option = kNoBranchRequired; 391 } 392 // Check if the macro-assembler's internal literal pool should be emitted 393 // to avoid any overflow. If we already generated the veneers, we can 394 // emit the pool (the branch is already done). 395 VIXL_ASSERT(GetCursorOffset() <= literal_pool_manager_.GetCheckpoint()); 396 if ((target > literal_pool_manager_.GetCheckpoint()) || 397 (option == kNoBranchRequired)) { 398 // We will generate the literal pool. Generate all the veneers which 399 // would become out of range. 400 size_t literal_pool_size = literal_pool_manager_.GetLiteralPoolSize(); 401 VIXL_ASSERT(IsInt32(literal_pool_size)); 402 Label::Offset veneers_target = 403 target + static_cast<Label::Offset>(literal_pool_size); 404 VIXL_ASSERT(veneers_target >= 0); 405 if (veneers_target > veneer_pool_manager_.GetCheckpoint()) { 406 veneer_pool_manager_.Emit(veneers_target); 407 } 408 EmitLiteralPool(option); 409 } 410 BindHelper(&after_pools); 411 if (GetBuffer()->IsManaged()) { 412 bool grow_requested; 413 GetBuffer()->EnsureSpaceFor(size, &grow_requested); 414 if (grow_requested) ComputeCheckpoint(); 415 } 416} 417 418 419void MacroAssembler::ComputeCheckpoint() { 420 checkpoint_ = veneer_pool_manager_.GetCheckpoint(); 421 if (literal_pool_manager_.GetCheckpoint() != Label::kMaxOffset) { 422 size_t veneer_max_size = veneer_pool_manager_.GetMaxSize(); 423 VIXL_ASSERT(IsInt32(veneer_max_size)); 424 // We must be able to generate the pool and a branch over the pool. 425 Label::Offset tmp = literal_pool_manager_.GetCheckpoint() - 426 static_cast<Label::Offset>(veneer_max_size + 427 kMaxInstructionSizeInBytes); 428 VIXL_ASSERT(tmp >= 0); 429 checkpoint_ = std::min(checkpoint_, tmp); 430 } 431 size_t buffer_size = GetBuffer()->GetCapacity(); 432 VIXL_ASSERT(IsInt32(buffer_size)); 433 Label::Offset buffer_checkpoint = static_cast<Label::Offset>(buffer_size); 434 checkpoint_ = std::min(checkpoint_, buffer_checkpoint); 435} 436 437 438void MacroAssembler::EmitLiteralPool(LiteralPool* const literal_pool, 439 EmitOption option) { 440 if (literal_pool->GetSize() > 0) { 441#ifdef VIXL_DEBUG 442 for (LiteralPool::RawLiteralListIterator literal_it = 443 literal_pool->GetFirst(); 444 literal_it != literal_pool->GetEnd(); 445 literal_it++) { 446 RawLiteral* literal = *literal_it; 447 VIXL_ASSERT(GetCursorOffset() < literal->GetCheckpoint()); 448 } 449#endif 450 Label after_literal; 451 if (option == kBranchRequired) { 452 GetBuffer()->EnsureSpaceFor(kMaxInstructionSizeInBytes); 453 VIXL_ASSERT(!AllowAssembler()); 454 { 455 ExactAssemblyScopeWithoutPoolsCheck 456 guard(this, 457 kMaxInstructionSizeInBytes, 458 ExactAssemblyScope::kMaximumSize); 459 b(&after_literal); 460 } 461 } 462 GetBuffer()->Align(); 463 GetBuffer()->EnsureSpaceFor(literal_pool->GetSize()); 464 for (LiteralPool::RawLiteralListIterator it = literal_pool->GetFirst(); 465 it != literal_pool->GetEnd(); 466 it++) { 467 PlaceHelper(*it); 468 GetBuffer()->Align(); 469 } 470 if (option == kBranchRequired) BindHelper(&after_literal); 471 literal_pool->Clear(); 472 } 473} 474 475 476void MacroAssembler::Switch(Register reg, JumpTableBase* table) { 477 // 32-bit table A32: 478 // adr ip, table 479 // add ip, r1, lsl 2 480 // ldr ip, [ip] 481 // jmp: add pc, pc, ip, lsl 2 482 // table: 483 // .int (case_0 - (jmp + 8)) >> 2 484 // .int (case_1 - (jmp + 8)) >> 2 485 // .int (case_2 - (jmp + 8)) >> 2 486 487 // 16-bit table T32: 488 // adr ip, table 489 // jmp: tbh ip, r1 490 // table: 491 // .short (case_0 - (jmp + 4)) >> 1 492 // .short (case_1 - (jmp + 4)) >> 1 493 // .short (case_2 - (jmp + 4)) >> 1 494 // case_0: 495 // ... 496 // b end_switch 497 // case_1: 498 // ... 499 // b end_switch 500 // ... 501 // end_switch: 502 Label jump_table; 503 UseScratchRegisterScope temps(this); 504 Register scratch = temps.Acquire(); 505 int table_size = AlignUp(table->GetTableSizeInBytes(), 4); 506 507 // Jump to default if reg is not in [0, table->GetLength()[ 508 Cmp(reg, table->GetLength()); 509 B(ge, table->GetDefaultLabel()); 510 511 Adr(scratch, &jump_table); 512 if (IsUsingA32()) { 513 Add(scratch, scratch, Operand(reg, LSL, table->GetOffsetShift())); 514 switch (table->GetOffsetShift()) { 515 case 0: 516 Ldrb(scratch, MemOperand(scratch)); 517 break; 518 case 1: 519 Ldrh(scratch, MemOperand(scratch)); 520 break; 521 case 2: 522 Ldr(scratch, MemOperand(scratch)); 523 break; 524 default: 525 VIXL_ABORT_WITH_MSG("Unsupported jump table size.\n"); 526 } 527 // Emit whatever needs to be emitted if we want to 528 // correctly record the position of the branch instruction 529 uint32_t branch_location = GetCursorOffset(); 530 table->SetBranchLocation(branch_location + GetArchitectureStatePCOffset()); 531 ExactAssemblyScope scope(this, 532 table_size + kA32InstructionSizeInBytes, 533 ExactAssemblyScope::kMaximumSize); 534 add(pc, pc, Operand(scratch, LSL, 2)); 535 VIXL_ASSERT((GetCursorOffset() - branch_location) == 4); 536 bind(&jump_table); 537 GenerateSwitchTable(table, table_size); 538 } else { 539 // Thumb mode - We have tbb and tbh to do this for 8 or 16bit offsets. 540 // But for 32bit offsets, we use the same coding as for A32 541 if (table->GetOffsetShift() == 2) { 542 // 32bit offsets 543 Add(scratch, scratch, Operand(reg, LSL, 2)); 544 Ldr(scratch, MemOperand(scratch)); 545 // Cannot use add pc, pc, r lsl 1 as this is unpredictable in T32, 546 // so let's do the shift before 547 Lsl(scratch, scratch, 1); 548 // Emit whatever needs to be emitted if we want to 549 // correctly record the position of the branch instruction 550 uint32_t branch_location = GetCursorOffset(); 551 table->SetBranchLocation(branch_location + 552 GetArchitectureStatePCOffset()); 553 ExactAssemblyScope scope(this, 554 table_size + kMaxInstructionSizeInBytes, 555 ExactAssemblyScope::kMaximumSize); 556 add(pc, pc, scratch); 557 // add pc, pc, rm fits in 16bit T2 (except for rm = sp) 558 VIXL_ASSERT((GetCursorOffset() - branch_location) == 2); 559 bind(&jump_table); 560 GenerateSwitchTable(table, table_size); 561 } else { 562 VIXL_ASSERT((table->GetOffsetShift() == 0) || 563 (table->GetOffsetShift() == 1)); 564 // Emit whatever needs to be emitted if we want to 565 // correctly record the position of the branch instruction 566 uint32_t branch_location = GetCursorOffset(); 567 table->SetBranchLocation(branch_location + 568 GetArchitectureStatePCOffset()); 569 ExactAssemblyScope scope(this, 570 table_size + kMaxInstructionSizeInBytes, 571 ExactAssemblyScope::kMaximumSize); 572 if (table->GetOffsetShift() == 0) { 573 // 8bit offsets 574 tbb(scratch, reg); 575 } else { 576 // 16bit offsets 577 tbh(scratch, reg); 578 } 579 // tbb/tbh is a 32bit instruction 580 VIXL_ASSERT((GetCursorOffset() - branch_location) == 4); 581 bind(&jump_table); 582 GenerateSwitchTable(table, table_size); 583 } 584 } 585} 586 587 588void MacroAssembler::GenerateSwitchTable(JumpTableBase* table, int table_size) { 589 table->BindTable(GetCursorOffset()); 590 for (int i = 0; i < table_size / 4; i++) { 591 GetBuffer()->Emit32(0); 592 } 593} 594 595 596// switch/case/default : case 597// case_index is assumed to be < table->GetLength() 598// which is checked in JumpTable::Link and Table::SetPresenceBit 599void MacroAssembler::Case(JumpTableBase* table, int case_index) { 600 table->Link(this, case_index, GetCursorOffset()); 601 table->SetPresenceBitForCase(case_index); 602} 603 604// switch/case/default : default 605void MacroAssembler::Default(JumpTableBase* table) { 606 Bind(table->GetDefaultLabel()); 607} 608 609// switch/case/default : break 610void MacroAssembler::Break(JumpTableBase* table) { B(table->GetEndLabel()); } 611 612// switch/case/default : finalize 613// Manage the default path, mosstly. All empty offsets in the jumptable 614// will point to default. 615// All values not in [0, table->GetLength()[ are already pointing here anyway. 616void MacroAssembler::EndSwitch(JumpTableBase* table) { table->Finalize(this); } 617 618void MacroAssembler::HandleOutOfBoundsImmediate(Condition cond, 619 Register tmp, 620 uint32_t imm) { 621 if (IsUintN(16, imm)) { 622 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 623 mov(cond, tmp, imm & 0xffff); 624 return; 625 } 626 if (IsUsingT32()) { 627 if (ImmediateT32::IsImmediateT32(~imm)) { 628 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 629 mvn(cond, tmp, ~imm); 630 return; 631 } 632 } else { 633 if (ImmediateA32::IsImmediateA32(~imm)) { 634 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 635 mvn(cond, tmp, ~imm); 636 return; 637 } 638 } 639 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 640 mov(cond, tmp, imm & 0xffff); 641 movt(cond, tmp, imm >> 16); 642} 643 644 645void MacroAssembler::PadToMinimumBranchRange(Label* label) { 646 const Label::ForwardReference* last_reference = label->GetForwardRefBack(); 647 if ((last_reference != NULL) && last_reference->IsUsingT32()) { 648 uint32_t location = last_reference->GetLocation(); 649 if (location + k16BitT32InstructionSizeInBytes == 650 static_cast<uint32_t>(GetCursorOffset())) { 651 uint16_t* instr_ptr = buffer_.GetOffsetAddress<uint16_t*>(location); 652 if ((instr_ptr[0] & kCbzCbnzMask) == kCbzCbnzValue) { 653 VIXL_ASSERT(!InITBlock()); 654 // A Cbz or a Cbnz can't jump immediately after the instruction. If the 655 // target is immediately after the Cbz or Cbnz, we insert a nop to 656 // avoid that. 657 EmitT32_16(k16BitT32NopOpcode); 658 } 659 } 660 } 661} 662 663 664MemOperand MacroAssembler::MemOperandComputationHelper( 665 Condition cond, 666 Register scratch, 667 Register base, 668 uint32_t offset, 669 uint32_t extra_offset_mask) { 670 VIXL_ASSERT(!AliasesAvailableScratchRegister(scratch)); 671 VIXL_ASSERT(!AliasesAvailableScratchRegister(base)); 672 VIXL_ASSERT(allow_macro_instructions_); 673 VIXL_ASSERT(OutsideITBlock()); 674 675 // Check for the simple pass-through case. 676 if ((offset & extra_offset_mask) == offset) return MemOperand(base, offset); 677 678 // If the base register is pc, the offset must be adjusted to account for the 679 // Align(pc, 4) in the original offset calculation. This alignment does not 680 // occur when the pc is read in 'add'. 681 if (base.IsPC() && !IsMultiple<4>(GetCursorOffset())) { 682 VIXL_ASSERT(IsMultiple<2>(GetCursorOffset())); 683 offset -= 2; 684 } 685 686 MacroEmissionCheckScope guard(this); 687 ITScope it_scope(this, &cond); 688 689 uint32_t load_store_offset = offset & extra_offset_mask; 690 uint32_t add_sub_offset = offset & ~extra_offset_mask; 691 692 add(cond, scratch, base, add_sub_offset); 693 694 return MemOperand(scratch, load_store_offset); 695} 696 697 698uint32_t MacroAssembler::GetOffsetMask(InstructionType type, 699 AddrMode addrmode) { 700 switch (type) { 701 case kLdr: 702 case kLdrb: 703 case kStr: 704 case kStrb: 705 if (IsUsingA32() || (addrmode == Offset)) { 706 return 0xfff; 707 } else { 708 return 0xff; 709 } 710 case kLdrsb: 711 case kLdrh: 712 case kLdrsh: 713 case kStrh: 714 if (IsUsingT32() && (addrmode == Offset)) { 715 return 0xfff; 716 } else { 717 return 0xff; 718 } 719 case kVldr: 720 case kVstr: 721 return 0x3fc; 722 case kLdrd: 723 case kStrd: 724 if (IsUsingA32()) { 725 return 0xff; 726 } else { 727 return 0x3fc; 728 } 729 default: 730 VIXL_UNREACHABLE(); 731 return 0; 732 } 733} 734 735 736HARDFLOAT void PrintfTrampolineRRRR( 737 const char* format, uint32_t a, uint32_t b, uint32_t c, uint32_t d) { 738 printf(format, a, b, c, d); 739} 740 741 742HARDFLOAT void PrintfTrampolineRRRD( 743 const char* format, uint32_t a, uint32_t b, uint32_t c, double d) { 744 printf(format, a, b, c, d); 745} 746 747 748HARDFLOAT void PrintfTrampolineRRDR( 749 const char* format, uint32_t a, uint32_t b, double c, uint32_t d) { 750 printf(format, a, b, c, d); 751} 752 753 754HARDFLOAT void PrintfTrampolineRRDD( 755 const char* format, uint32_t a, uint32_t b, double c, double d) { 756 printf(format, a, b, c, d); 757} 758 759 760HARDFLOAT void PrintfTrampolineRDRR( 761 const char* format, uint32_t a, double b, uint32_t c, uint32_t d) { 762 printf(format, a, b, c, d); 763} 764 765 766HARDFLOAT void PrintfTrampolineRDRD( 767 const char* format, uint32_t a, double b, uint32_t c, double d) { 768 printf(format, a, b, c, d); 769} 770 771 772HARDFLOAT void PrintfTrampolineRDDR( 773 const char* format, uint32_t a, double b, double c, uint32_t d) { 774 printf(format, a, b, c, d); 775} 776 777 778HARDFLOAT void PrintfTrampolineRDDD( 779 const char* format, uint32_t a, double b, double c, double d) { 780 printf(format, a, b, c, d); 781} 782 783 784HARDFLOAT void PrintfTrampolineDRRR( 785 const char* format, double a, uint32_t b, uint32_t c, uint32_t d) { 786 printf(format, a, b, c, d); 787} 788 789 790HARDFLOAT void PrintfTrampolineDRRD( 791 const char* format, double a, uint32_t b, uint32_t c, double d) { 792 printf(format, a, b, c, d); 793} 794 795 796HARDFLOAT void PrintfTrampolineDRDR( 797 const char* format, double a, uint32_t b, double c, uint32_t d) { 798 printf(format, a, b, c, d); 799} 800 801 802HARDFLOAT void PrintfTrampolineDRDD( 803 const char* format, double a, uint32_t b, double c, double d) { 804 printf(format, a, b, c, d); 805} 806 807 808HARDFLOAT void PrintfTrampolineDDRR( 809 const char* format, double a, double b, uint32_t c, uint32_t d) { 810 printf(format, a, b, c, d); 811} 812 813 814HARDFLOAT void PrintfTrampolineDDRD( 815 const char* format, double a, double b, uint32_t c, double d) { 816 printf(format, a, b, c, d); 817} 818 819 820HARDFLOAT void PrintfTrampolineDDDR( 821 const char* format, double a, double b, double c, uint32_t d) { 822 printf(format, a, b, c, d); 823} 824 825 826HARDFLOAT void PrintfTrampolineDDDD( 827 const char* format, double a, double b, double c, double d) { 828 printf(format, a, b, c, d); 829} 830 831 832void MacroAssembler::Printf(const char* format, 833 CPURegister reg1, 834 CPURegister reg2, 835 CPURegister reg3, 836 CPURegister reg4) { 837 if (generate_simulator_code_) { 838 PushRegister(reg4); 839 PushRegister(reg3); 840 PushRegister(reg2); 841 PushRegister(reg1); 842 Push(RegisterList(r0, r1)); 843 StringLiteral* format_literal = 844 new StringLiteral(format, RawLiteral::kDeletedOnPlacementByPool); 845 Adr(r0, format_literal); 846 uint32_t args = (reg4.GetType() << 12) | (reg3.GetType() << 8) | 847 (reg2.GetType() << 4) | reg1.GetType(); 848 Mov(r1, args); 849 Hvc(kPrintfCode); 850 Pop(RegisterList(r0, r1)); 851 int size = reg4.GetRegSizeInBytes() + reg3.GetRegSizeInBytes() + 852 reg2.GetRegSizeInBytes() + reg1.GetRegSizeInBytes(); 853 Drop(size); 854 } else { 855 // Generate on a native platform => 32 bit environment. 856 // Preserve core registers r0-r3, r12, r14 857 const uint32_t saved_registers_mask = 858 kCallerSavedRegistersMask | (1 << r5.GetCode()); 859 Push(RegisterList(saved_registers_mask)); 860 // Push VFP registers. 861 Vpush(Untyped64, DRegisterList(d0, 8)); 862 if (Has32DRegs()) Vpush(Untyped64, DRegisterList(d16, 16)); 863 // Search one register which has been saved and which doesn't need to be 864 // printed. 865 RegisterList available_registers(kCallerSavedRegistersMask); 866 if (reg1.GetType() == CPURegister::kRRegister) { 867 available_registers.Remove(Register(reg1.GetCode())); 868 } 869 if (reg2.GetType() == CPURegister::kRRegister) { 870 available_registers.Remove(Register(reg2.GetCode())); 871 } 872 if (reg3.GetType() == CPURegister::kRRegister) { 873 available_registers.Remove(Register(reg3.GetCode())); 874 } 875 if (reg4.GetType() == CPURegister::kRRegister) { 876 available_registers.Remove(Register(reg4.GetCode())); 877 } 878 Register tmp = available_registers.GetFirstAvailableRegister(); 879 VIXL_ASSERT(tmp.GetType() == CPURegister::kRRegister); 880 // Push the flags. 881 Mrs(tmp, APSR); 882 Push(tmp); 883 Vmrs(RegisterOrAPSR_nzcv(tmp.GetCode()), FPSCR); 884 Push(tmp); 885 // Push the registers to print on the stack. 886 PushRegister(reg4); 887 PushRegister(reg3); 888 PushRegister(reg2); 889 PushRegister(reg1); 890 int core_count = 1; 891 int vfp_count = 0; 892 uint32_t printf_type = 0; 893 // Pop the registers to print and store them into r1-r3 and/or d0-d3. 894 // Reg4 may stay into the stack if all the register to print are core 895 // registers. 896 PreparePrintfArgument(reg1, &core_count, &vfp_count, &printf_type); 897 PreparePrintfArgument(reg2, &core_count, &vfp_count, &printf_type); 898 PreparePrintfArgument(reg3, &core_count, &vfp_count, &printf_type); 899 PreparePrintfArgument(reg4, &core_count, &vfp_count, &printf_type); 900 // Ensure that the stack is aligned on 8 bytes. 901 And(r5, sp, 0x7); 902 if (core_count == 5) { 903 // One 32 bit argument (reg4) has been left on the stack => align the 904 // stack 905 // before the argument. 906 Pop(r0); 907 Sub(sp, sp, r5); 908 Push(r0); 909 } else { 910 Sub(sp, sp, r5); 911 } 912 // Select the right trampoline depending on the arguments. 913 uintptr_t address; 914 switch (printf_type) { 915 case 0: 916 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRR); 917 break; 918 case 1: 919 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRRR); 920 break; 921 case 2: 922 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDRR); 923 break; 924 case 3: 925 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDRR); 926 break; 927 case 4: 928 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRDR); 929 break; 930 case 5: 931 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRDR); 932 break; 933 case 6: 934 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDDR); 935 break; 936 case 7: 937 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDDR); 938 break; 939 case 8: 940 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRD); 941 break; 942 case 9: 943 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRRD); 944 break; 945 case 10: 946 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDRD); 947 break; 948 case 11: 949 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDRD); 950 break; 951 case 12: 952 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRDD); 953 break; 954 case 13: 955 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDRDD); 956 break; 957 case 14: 958 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRDDD); 959 break; 960 case 15: 961 address = reinterpret_cast<uintptr_t>(PrintfTrampolineDDDD); 962 break; 963 default: 964 VIXL_UNREACHABLE(); 965 address = reinterpret_cast<uintptr_t>(PrintfTrampolineRRRR); 966 break; 967 } 968 StringLiteral* format_literal = 969 new StringLiteral(format, RawLiteral::kDeletedOnPlacementByPool); 970 Adr(r0, format_literal); 971 Mov(ip, Operand::From(address)); 972 Blx(ip); 973 // If register reg4 was left on the stack => skip it. 974 if (core_count == 5) Drop(kRegSizeInBytes); 975 // Restore the stack as it was before alignment. 976 Add(sp, sp, r5); 977 // Restore the flags. 978 Pop(tmp); 979 Vmsr(FPSCR, tmp); 980 Pop(tmp); 981 Msr(APSR_nzcvqg, tmp); 982 // Restore the regsisters. 983 if (Has32DRegs()) Vpop(Untyped64, DRegisterList(d16, 16)); 984 Vpop(Untyped64, DRegisterList(d0, 8)); 985 Pop(RegisterList(saved_registers_mask)); 986 } 987} 988 989 990void MacroAssembler::PushRegister(CPURegister reg) { 991 switch (reg.GetType()) { 992 case CPURegister::kNoRegister: 993 break; 994 case CPURegister::kRRegister: 995 Push(Register(reg.GetCode())); 996 break; 997 case CPURegister::kSRegister: 998 Vpush(Untyped32, SRegisterList(SRegister(reg.GetCode()))); 999 break; 1000 case CPURegister::kDRegister: 1001 Vpush(Untyped64, DRegisterList(DRegister(reg.GetCode()))); 1002 break; 1003 case CPURegister::kQRegister: 1004 VIXL_UNIMPLEMENTED(); 1005 break; 1006 } 1007} 1008 1009 1010void MacroAssembler::PreparePrintfArgument(CPURegister reg, 1011 int* core_count, 1012 int* vfp_count, 1013 uint32_t* printf_type) { 1014 switch (reg.GetType()) { 1015 case CPURegister::kNoRegister: 1016 break; 1017 case CPURegister::kRRegister: 1018 VIXL_ASSERT(*core_count <= 4); 1019 if (*core_count < 4) Pop(Register(*core_count)); 1020 *core_count += 1; 1021 break; 1022 case CPURegister::kSRegister: 1023 VIXL_ASSERT(*vfp_count < 4); 1024 *printf_type |= 1 << (*core_count + *vfp_count - 1); 1025 Vpop(Untyped32, SRegisterList(SRegister(*vfp_count * 2))); 1026 Vcvt(F64, F32, DRegister(*vfp_count), SRegister(*vfp_count * 2)); 1027 *vfp_count += 1; 1028 break; 1029 case CPURegister::kDRegister: 1030 VIXL_ASSERT(*vfp_count < 4); 1031 *printf_type |= 1 << (*core_count + *vfp_count - 1); 1032 Vpop(Untyped64, DRegisterList(DRegister(*vfp_count))); 1033 *vfp_count += 1; 1034 break; 1035 case CPURegister::kQRegister: 1036 VIXL_UNIMPLEMENTED(); 1037 break; 1038 } 1039} 1040 1041 1042void MacroAssembler::Delegate(InstructionType type, 1043 InstructionCondROp instruction, 1044 Condition cond, 1045 Register rn, 1046 const Operand& operand) { 1047 // movt, sxtb16, teq, uxtb16 1048 VIXL_ASSERT((type == kMovt) || (type == kSxtb16) || (type == kTeq) || 1049 (type == kUxtb16)); 1050 1051 if (type == kMovt) { 1052 VIXL_ABORT_WITH_MSG("`Movt` expects a 16-bit immediate."); 1053 } 1054 1055 // This delegate only supports teq with immediates. 1056 CONTEXT_SCOPE; 1057 if ((type == kTeq) && operand.IsImmediate()) { 1058 UseScratchRegisterScope temps(this); 1059 Register scratch = temps.Acquire(); 1060 HandleOutOfBoundsImmediate(cond, scratch, operand.GetImmediate()); 1061 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1062 teq(cond, rn, scratch); 1063 return; 1064 } 1065 Assembler::Delegate(type, instruction, cond, rn, operand); 1066} 1067 1068 1069void MacroAssembler::Delegate(InstructionType type, 1070 InstructionCondSizeROp instruction, 1071 Condition cond, 1072 EncodingSize size, 1073 Register rn, 1074 const Operand& operand) { 1075 // cmn cmp mov movs mvn mvns sxtb sxth tst uxtb uxth 1076 CONTEXT_SCOPE; 1077 VIXL_ASSERT(size.IsBest()); 1078 VIXL_ASSERT((type == kCmn) || (type == kCmp) || (type == kMov) || 1079 (type == kMovs) || (type == kMvn) || (type == kMvns) || 1080 (type == kSxtb) || (type == kSxth) || (type == kTst) || 1081 (type == kUxtb) || (type == kUxth)); 1082 if (IsUsingT32() && operand.IsRegisterShiftedRegister()) { 1083 VIXL_ASSERT((type != kMov) || (type != kMovs)); 1084 InstructionCondRROp shiftop = NULL; 1085 switch (operand.GetShift().GetType()) { 1086 case LSL: 1087 shiftop = &Assembler::lsl; 1088 break; 1089 case LSR: 1090 shiftop = &Assembler::lsr; 1091 break; 1092 case ASR: 1093 shiftop = &Assembler::asr; 1094 break; 1095 case RRX: 1096 // A RegisterShiftedRegister operand cannot have a shift of type RRX. 1097 VIXL_UNREACHABLE(); 1098 break; 1099 case ROR: 1100 shiftop = &Assembler::ror; 1101 break; 1102 default: 1103 VIXL_UNREACHABLE(); 1104 } 1105 if (shiftop != NULL) { 1106 UseScratchRegisterScope temps(this); 1107 Register scratch = temps.Acquire(); 1108 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 1109 (this->*shiftop)(cond, 1110 scratch, 1111 operand.GetBaseRegister(), 1112 operand.GetShiftRegister()); 1113 (this->*instruction)(cond, size, rn, scratch); 1114 return; 1115 } 1116 } 1117 if (operand.IsImmediate()) { 1118 uint32_t imm = operand.GetImmediate(); 1119 switch (type) { 1120 case kMov: 1121 case kMovs: 1122 if (!rn.IsPC()) { 1123 // Immediate is too large, but not using PC, so handle with mov{t}. 1124 HandleOutOfBoundsImmediate(cond, rn, imm); 1125 if (type == kMovs) { 1126 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1127 tst(cond, rn, rn); 1128 } 1129 return; 1130 } else if (type == kMov) { 1131 VIXL_ASSERT(IsUsingA32() || cond.Is(al)); 1132 // Immediate is too large and using PC, so handle using a temporary 1133 // register. 1134 UseScratchRegisterScope temps(this); 1135 Register scratch = temps.Acquire(); 1136 HandleOutOfBoundsImmediate(al, scratch, imm); 1137 EnsureEmitFor(kMaxInstructionSizeInBytes); 1138 bx(cond, scratch); 1139 return; 1140 } 1141 break; 1142 case kCmn: 1143 case kCmp: 1144 if (IsUsingA32() || !rn.IsPC()) { 1145 UseScratchRegisterScope temps(this); 1146 Register scratch = temps.Acquire(); 1147 HandleOutOfBoundsImmediate(cond, scratch, imm); 1148 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1149 (this->*instruction)(cond, size, rn, scratch); 1150 return; 1151 } 1152 break; 1153 case kMvn: 1154 case kMvns: 1155 if (!rn.IsPC()) { 1156 UseScratchRegisterScope temps(this); 1157 Register scratch = temps.Acquire(); 1158 HandleOutOfBoundsImmediate(cond, scratch, imm); 1159 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1160 (this->*instruction)(cond, size, rn, scratch); 1161 return; 1162 } 1163 break; 1164 case kTst: 1165 if (IsUsingA32() || !rn.IsPC()) { 1166 UseScratchRegisterScope temps(this); 1167 Register scratch = temps.Acquire(); 1168 HandleOutOfBoundsImmediate(cond, scratch, imm); 1169 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1170 (this->*instruction)(cond, size, rn, scratch); 1171 return; 1172 } 1173 break; 1174 default: // kSxtb, Sxth, Uxtb, Uxth 1175 break; 1176 } 1177 } 1178 Assembler::Delegate(type, instruction, cond, size, rn, operand); 1179} 1180 1181 1182void MacroAssembler::Delegate(InstructionType type, 1183 InstructionCondRROp instruction, 1184 Condition cond, 1185 Register rd, 1186 Register rn, 1187 const Operand& operand) { 1188 // orn orns pkhbt pkhtb rsc rscs sxtab sxtab16 sxtah uxtab uxtab16 uxtah 1189 1190 if ((type == kSxtab) || (type == kSxtab16) || (type == kSxtah) || 1191 (type == kUxtab) || (type == kUxtab16) || (type == kUxtah) || 1192 (type == kPkhbt) || (type == kPkhtb)) { 1193 UnimplementedDelegate(type); 1194 return; 1195 } 1196 1197 // This delegate only handles the following instructions. 1198 VIXL_ASSERT((type == kOrn) || (type == kOrns) || (type == kRsc) || 1199 (type == kRscs)); 1200 CONTEXT_SCOPE; 1201 1202 // T32 does not support register shifted register operands, emulate it. 1203 if (IsUsingT32() && operand.IsRegisterShiftedRegister()) { 1204 InstructionCondRROp shiftop = NULL; 1205 switch (operand.GetShift().GetType()) { 1206 case LSL: 1207 shiftop = &Assembler::lsl; 1208 break; 1209 case LSR: 1210 shiftop = &Assembler::lsr; 1211 break; 1212 case ASR: 1213 shiftop = &Assembler::asr; 1214 break; 1215 case RRX: 1216 // A RegisterShiftedRegister operand cannot have a shift of type RRX. 1217 VIXL_UNREACHABLE(); 1218 break; 1219 case ROR: 1220 shiftop = &Assembler::ror; 1221 break; 1222 default: 1223 VIXL_UNREACHABLE(); 1224 } 1225 if (shiftop != NULL) { 1226 UseScratchRegisterScope temps(this); 1227 Register rm = operand.GetBaseRegister(); 1228 Register rs = operand.GetShiftRegister(); 1229 // Try to use rd as a scratch register. We can do this if it aliases rs or 1230 // rm (because we read them in the first instruction), but not rn. 1231 if (!rd.Is(rn)) temps.Include(rd); 1232 Register scratch = temps.Acquire(); 1233 // TODO: The scope length was measured empirically. We should analyse the 1234 // worst-case size and add targetted tests. 1235 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1236 (this->*shiftop)(cond, scratch, rm, rs); 1237 (this->*instruction)(cond, rd, rn, scratch); 1238 return; 1239 } 1240 } 1241 1242 // T32 does not have a Rsc instruction, negate the lhs input and turn it into 1243 // an Adc. Adc and Rsc are equivalent using a bitwise NOT: 1244 // adc rd, rn, operand <-> rsc rd, NOT(rn), operand 1245 if (IsUsingT32() && ((type == kRsc) || (type == kRscs))) { 1246 // The RegisterShiftRegister case should have been handled above. 1247 VIXL_ASSERT(!operand.IsRegisterShiftedRegister()); 1248 UseScratchRegisterScope temps(this); 1249 // Try to use rd as a scratch register. We can do this if it aliases rn 1250 // (because we read it in the first instruction), but not rm. 1251 temps.Include(rd); 1252 temps.Exclude(operand); 1253 Register negated_rn = temps.Acquire(); 1254 { 1255 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1256 mvn(cond, negated_rn, rn); 1257 } 1258 if (type == kRsc) { 1259 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1260 adc(cond, rd, negated_rn, operand); 1261 return; 1262 } 1263 // TODO: We shouldn't have to specify how much space the next instruction 1264 // needs. 1265 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1266 adcs(cond, rd, negated_rn, operand); 1267 return; 1268 } 1269 1270 // A32 does not have a Orn instruction, negate the rhs input and turn it into 1271 // a Orr. 1272 if (IsUsingA32() && ((type == kOrn) || (type == kOrns))) { 1273 // TODO: orn r0, r1, imm -> orr r0, r1, neg(imm) if doable 1274 // mvn r0, r2 1275 // orr r0, r1, r0 1276 Register scratch; 1277 UseScratchRegisterScope temps(this); 1278 // Try to use rd as a scratch register. We can do this if it aliases rs or 1279 // rm (because we read them in the first instruction), but not rn. 1280 if (!rd.Is(rn)) temps.Include(rd); 1281 scratch = temps.Acquire(); 1282 { 1283 // TODO: We shouldn't have to specify how much space the next instruction 1284 // needs. 1285 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1286 mvn(cond, scratch, operand); 1287 } 1288 if (type == kOrns) { 1289 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1290 orrs(cond, rd, rn, scratch); 1291 return; 1292 } 1293 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1294 orr(cond, rd, rn, scratch); 1295 return; 1296 } 1297 if (operand.IsImmediate()) { 1298 int32_t imm = operand.GetSignedImmediate(); 1299 1300 // If the immediate can be encoded when inverted, turn Orn into Orr. 1301 // Otherwise rely on HandleOutOfBoundsImmediate to generate a series of 1302 // mov. 1303 if (IsUsingT32() && ((type == kOrn) || (type == kOrns)) && 1304 ImmediateT32::IsImmediateT32(~imm)) { 1305 VIXL_ASSERT((type == kOrn) || (type == kOrns)); 1306 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1307 switch (type) { 1308 case kOrn: 1309 orr(cond, rd, rn, ~imm); 1310 return; 1311 case kOrns: 1312 orrs(cond, rd, rn, ~imm); 1313 return; 1314 default: 1315 VIXL_UNREACHABLE(); 1316 break; 1317 } 1318 } else { 1319 UseScratchRegisterScope temps(this); 1320 // Allow using the destination as a scratch register if possible. 1321 if (!rd.Is(rn)) temps.Include(rd); 1322 Register scratch = temps.Acquire(); 1323 HandleOutOfBoundsImmediate(cond, scratch, imm); 1324 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1325 (this->*instruction)(cond, rd, rn, scratch); 1326 return; 1327 } 1328 } 1329 Assembler::Delegate(type, instruction, cond, rd, rn, operand); 1330} 1331 1332 1333void MacroAssembler::Delegate(InstructionType type, 1334 InstructionCondSizeRL instruction, 1335 Condition cond, 1336 EncodingSize size, 1337 Register rd, 1338 Label* label) { 1339 VIXL_ASSERT((type == kLdr) || (type == kAdr)); 1340 1341 CONTEXT_SCOPE; 1342 VIXL_ASSERT(size.IsBest()); 1343 1344 if ((type == kLdr) && label->IsBound()) { 1345 CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes); 1346 UseScratchRegisterScope temps(this); 1347 temps.Include(rd); 1348 uint32_t mask = GetOffsetMask(type, Offset); 1349 ldr(rd, MemOperandComputationHelper(cond, temps.Acquire(), label, mask)); 1350 return; 1351 } 1352 1353 Assembler::Delegate(type, instruction, cond, size, rd, label); 1354} 1355 1356 1357void MacroAssembler::Delegate(InstructionType type, 1358 InstructionCondSizeRROp instruction, 1359 Condition cond, 1360 EncodingSize size, 1361 Register rd, 1362 Register rn, 1363 const Operand& operand) { 1364 // adc adcs add adds and_ ands asr asrs bic bics eor eors lsl lsls lsr lsrs 1365 // orr orrs ror rors rsb rsbs sbc sbcs sub subs 1366 1367 VIXL_ASSERT( 1368 (type == kAdc) || (type == kAdcs) || (type == kAdd) || (type == kAdds) || 1369 (type == kAnd) || (type == kAnds) || (type == kAsr) || (type == kAsrs) || 1370 (type == kBic) || (type == kBics) || (type == kEor) || (type == kEors) || 1371 (type == kLsl) || (type == kLsls) || (type == kLsr) || (type == kLsrs) || 1372 (type == kOrr) || (type == kOrrs) || (type == kRor) || (type == kRors) || 1373 (type == kRsb) || (type == kRsbs) || (type == kSbc) || (type == kSbcs) || 1374 (type == kSub) || (type == kSubs)); 1375 1376 CONTEXT_SCOPE; 1377 VIXL_ASSERT(size.IsBest()); 1378 if (IsUsingT32() && operand.IsRegisterShiftedRegister()) { 1379 InstructionCondRROp shiftop = NULL; 1380 switch (operand.GetShift().GetType()) { 1381 case LSL: 1382 shiftop = &Assembler::lsl; 1383 break; 1384 case LSR: 1385 shiftop = &Assembler::lsr; 1386 break; 1387 case ASR: 1388 shiftop = &Assembler::asr; 1389 break; 1390 case RRX: 1391 // A RegisterShiftedRegister operand cannot have a shift of type RRX. 1392 VIXL_UNREACHABLE(); 1393 break; 1394 case ROR: 1395 shiftop = &Assembler::ror; 1396 break; 1397 default: 1398 VIXL_UNREACHABLE(); 1399 } 1400 if (shiftop != NULL) { 1401 UseScratchRegisterScope temps(this); 1402 Register rm = operand.GetBaseRegister(); 1403 Register rs = operand.GetShiftRegister(); 1404 // Try to use rd as a scratch register. We can do this if it aliases rs or 1405 // rm (because we read them in the first instruction), but not rn. 1406 if (!rd.Is(rn)) temps.Include(rd); 1407 Register scratch = temps.Acquire(); 1408 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 1409 (this->*shiftop)(cond, scratch, rm, rs); 1410 (this->*instruction)(cond, size, rd, rn, scratch); 1411 return; 1412 } 1413 } 1414 if (operand.IsImmediate()) { 1415 int32_t imm = operand.GetSignedImmediate(); 1416 if (ImmediateT32::IsImmediateT32(~imm)) { 1417 if (IsUsingT32()) { 1418 switch (type) { 1419 case kOrr: 1420 orn(cond, rd, rn, ~imm); 1421 return; 1422 case kOrrs: 1423 orns(cond, rd, rn, ~imm); 1424 return; 1425 default: 1426 break; 1427 } 1428 } 1429 } 1430 if (imm < 0) { 1431 InstructionCondSizeRROp asmcb = NULL; 1432 // Add and sub are equivalent using an arithmetic negation: 1433 // add rd, rn, #imm <-> sub rd, rn, - #imm 1434 // Add and sub with carry are equivalent using a bitwise NOT: 1435 // adc rd, rn, #imm <-> sbc rd, rn, NOT #imm 1436 switch (type) { 1437 case kAdd: 1438 asmcb = &Assembler::sub; 1439 imm = -imm; 1440 break; 1441 case kAdds: 1442 asmcb = &Assembler::subs; 1443 imm = -imm; 1444 break; 1445 case kSub: 1446 asmcb = &Assembler::add; 1447 imm = -imm; 1448 break; 1449 case kSubs: 1450 asmcb = &Assembler::adds; 1451 imm = -imm; 1452 break; 1453 case kAdc: 1454 asmcb = &Assembler::sbc; 1455 imm = ~imm; 1456 break; 1457 case kAdcs: 1458 asmcb = &Assembler::sbcs; 1459 imm = ~imm; 1460 break; 1461 case kSbc: 1462 asmcb = &Assembler::adc; 1463 imm = ~imm; 1464 break; 1465 case kSbcs: 1466 asmcb = &Assembler::adcs; 1467 imm = ~imm; 1468 break; 1469 default: 1470 break; 1471 } 1472 if (asmcb != NULL) { 1473 CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes); 1474 (this->*asmcb)(cond, size, rd, rn, Operand(imm)); 1475 return; 1476 } 1477 } 1478 UseScratchRegisterScope temps(this); 1479 // Allow using the destination as a scratch register if possible. 1480 if (!rd.Is(rn)) temps.Include(rd); 1481 1482 if (rn.IsPC()) { 1483 // If we're reading the PC, we need to do it in the first instruction, 1484 // otherwise we'll read the wrong value. We rely on this to handle the 1485 // long-range PC-relative MemOperands which can result from user-managed 1486 // literals. 1487 1488 // Only handle negative offsets. The correct way to handle positive 1489 // offsets isn't clear; does the user want the offset from the start of 1490 // the macro, or from the end (to allow a certain amount of space)? 1491 bool offset_is_negative_or_zero = (imm <= 0); 1492 switch (type) { 1493 case kAdd: 1494 case kAdds: 1495 offset_is_negative_or_zero = (imm <= 0); 1496 break; 1497 case kSub: 1498 case kSubs: 1499 offset_is_negative_or_zero = (imm >= 0); 1500 break; 1501 case kAdc: 1502 case kAdcs: 1503 offset_is_negative_or_zero = (imm < 0); 1504 break; 1505 case kSbc: 1506 case kSbcs: 1507 offset_is_negative_or_zero = (imm > 0); 1508 break; 1509 default: 1510 break; 1511 } 1512 if (offset_is_negative_or_zero) { 1513 { 1514 rn = temps.Acquire(); 1515 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1516 mov(cond, rn, pc); 1517 } 1518 // Recurse rather than falling through, to try to get the immediate into 1519 // a single instruction. 1520 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1521 (this->*instruction)(cond, size, rd, rn, operand); 1522 return; 1523 } 1524 } else { 1525 Register scratch = temps.Acquire(); 1526 // TODO: The scope length was measured empirically. We should analyse the 1527 // worst-case size and add targetted tests. 1528 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1529 mov(cond, scratch, operand.GetImmediate()); 1530 (this->*instruction)(cond, size, rd, rn, scratch); 1531 return; 1532 } 1533 } 1534 Assembler::Delegate(type, instruction, cond, size, rd, rn, operand); 1535} 1536 1537 1538void MacroAssembler::Delegate(InstructionType type, 1539 InstructionRL instruction, 1540 Register rn, 1541 Label* label) { 1542 // cbz cbnz 1543 VIXL_ASSERT((type == kCbz) || (type == kCbnz)); 1544 1545 CONTEXT_SCOPE; 1546 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 1547 if (IsUsingA32()) { 1548 if (type == kCbz) { 1549 VIXL_ABORT_WITH_MSG("Cbz is only available for T32.\n"); 1550 } else { 1551 VIXL_ABORT_WITH_MSG("Cbnz is only available for T32.\n"); 1552 } 1553 } else if (rn.IsLow()) { 1554 switch (type) { 1555 case kCbnz: { 1556 Label done; 1557 cbz(rn, &done); 1558 b(label); 1559 Bind(&done); 1560 return; 1561 } 1562 case kCbz: { 1563 Label done; 1564 cbnz(rn, &done); 1565 b(label); 1566 Bind(&done); 1567 return; 1568 } 1569 default: 1570 break; 1571 } 1572 } 1573 Assembler::Delegate(type, instruction, rn, label); 1574} 1575 1576 1577template <typename T> 1578static inline bool IsI64BitPattern(T imm) { 1579 for (T mask = 0xff << ((sizeof(T) - 1) * 8); mask != 0; mask >>= 8) { 1580 if (((imm & mask) != mask) && ((imm & mask) != 0)) return false; 1581 } 1582 return true; 1583} 1584 1585 1586template <typename T> 1587static inline bool IsI8BitPattern(T imm) { 1588 uint8_t imm8 = imm & 0xff; 1589 for (unsigned rep = sizeof(T) - 1; rep > 0; rep--) { 1590 imm >>= 8; 1591 if ((imm & 0xff) != imm8) return false; 1592 } 1593 return true; 1594} 1595 1596 1597static inline bool CanBeInverted(uint32_t imm32) { 1598 uint32_t fill8 = 0; 1599 1600 if ((imm32 & 0xffffff00) == 0xffffff00) { 1601 // 11111111 11111111 11111111 abcdefgh 1602 return true; 1603 } 1604 if (((imm32 & 0xff) == 0) || ((imm32 & 0xff) == 0xff)) { 1605 fill8 = imm32 & 0xff; 1606 imm32 >>= 8; 1607 if ((imm32 >> 8) == 0xffff) { 1608 // 11111111 11111111 abcdefgh 00000000 1609 // or 11111111 11111111 abcdefgh 11111111 1610 return true; 1611 } 1612 if ((imm32 & 0xff) == fill8) { 1613 imm32 >>= 8; 1614 if ((imm32 >> 8) == 0xff) { 1615 // 11111111 abcdefgh 00000000 00000000 1616 // or 11111111 abcdefgh 11111111 11111111 1617 return true; 1618 } 1619 if ((fill8 == 0xff) && ((imm32 & 0xff) == 0xff)) { 1620 // abcdefgh 11111111 11111111 11111111 1621 return true; 1622 } 1623 } 1624 } 1625 return false; 1626} 1627 1628 1629template <typename RES, typename T> 1630static inline RES replicate(T imm) { 1631 VIXL_ASSERT((sizeof(RES) > sizeof(T)) && 1632 (((sizeof(RES) / sizeof(T)) * sizeof(T)) == sizeof(RES))); 1633 RES res = imm; 1634 for (unsigned i = sizeof(RES) / sizeof(T) - 1; i > 0; i--) { 1635 res = (res << (sizeof(T) * 8)) | imm; 1636 } 1637 return res; 1638} 1639 1640 1641void MacroAssembler::Delegate(InstructionType type, 1642 InstructionCondDtSSop instruction, 1643 Condition cond, 1644 DataType dt, 1645 SRegister rd, 1646 const SOperand& operand) { 1647 CONTEXT_SCOPE; 1648 if (type == kVmov) { 1649 if (operand.IsImmediate() && dt.Is(F32)) { 1650 const NeonImmediate& neon_imm = operand.GetNeonImmediate(); 1651 if (neon_imm.CanConvert<float>()) { 1652 // movw ip, imm16 1653 // movk ip, imm16 1654 // vmov s0, ip 1655 UseScratchRegisterScope temps(this); 1656 Register scratch = temps.Acquire(); 1657 float f = neon_imm.GetImmediate<float>(); 1658 // TODO: The scope length was measured empirically. We should analyse 1659 // the 1660 // worst-case size and add targetted tests. 1661 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1662 mov(cond, scratch, FloatToRawbits(f)); 1663 vmov(cond, rd, scratch); 1664 return; 1665 } 1666 } 1667 } 1668 Assembler::Delegate(type, instruction, cond, dt, rd, operand); 1669} 1670 1671 1672void MacroAssembler::Delegate(InstructionType type, 1673 InstructionCondDtDDop instruction, 1674 Condition cond, 1675 DataType dt, 1676 DRegister rd, 1677 const DOperand& operand) { 1678 CONTEXT_SCOPE; 1679 if (type == kVmov) { 1680 if (operand.IsImmediate()) { 1681 const NeonImmediate& neon_imm = operand.GetNeonImmediate(); 1682 switch (dt.GetValue()) { 1683 case I32: 1684 if (neon_imm.CanConvert<uint32_t>()) { 1685 uint32_t imm = neon_imm.GetImmediate<uint32_t>(); 1686 // vmov.i32 d0, 0xabababab will translate into vmov.i8 d0, 0xab 1687 if (IsI8BitPattern(imm)) { 1688 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1689 vmov(cond, I8, rd, imm & 0xff); 1690 return; 1691 } 1692 // vmov.i32 d0, 0xff0000ff will translate into 1693 // vmov.i64 d0, 0xff0000ffff0000ff 1694 if (IsI64BitPattern(imm)) { 1695 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1696 vmov(cond, I64, rd, replicate<uint64_t>(imm)); 1697 return; 1698 } 1699 // vmov.i32 d0, 0xffab0000 will translate into 1700 // vmvn.i32 d0, 0x0054ffff 1701 if (cond.Is(al) && CanBeInverted(imm)) { 1702 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1703 vmvn(I32, rd, ~imm); 1704 return; 1705 } 1706 } 1707 break; 1708 case I16: 1709 if (neon_imm.CanConvert<uint16_t>()) { 1710 uint16_t imm = neon_imm.GetImmediate<uint16_t>(); 1711 // vmov.i16 d0, 0xabab will translate into vmov.i8 d0, 0xab 1712 if (IsI8BitPattern(imm)) { 1713 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1714 vmov(cond, I8, rd, imm & 0xff); 1715 return; 1716 } 1717 } 1718 break; 1719 case I64: 1720 if (neon_imm.CanConvert<uint64_t>()) { 1721 uint64_t imm = neon_imm.GetImmediate<uint64_t>(); 1722 // vmov.i64 d0, -1 will translate into vmov.i8 d0, 0xff 1723 if (IsI8BitPattern(imm)) { 1724 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1725 vmov(cond, I8, rd, imm & 0xff); 1726 return; 1727 } 1728 // mov ip, lo(imm64) 1729 // vdup d0, ip 1730 // vdup is prefered to 'vmov d0[0]' as d0[1] does not need to be 1731 // preserved 1732 { 1733 UseScratchRegisterScope temps(this); 1734 Register scratch = temps.Acquire(); 1735 { 1736 // TODO: The scope length was measured empirically. We should 1737 // analyse the 1738 // worst-case size and add targetted tests. 1739 CodeBufferCheckScope scope(this, 1740 2 * kMaxInstructionSizeInBytes); 1741 mov(cond, scratch, static_cast<uint32_t>(imm & 0xffffffff)); 1742 } 1743 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1744 vdup(cond, Untyped32, rd, scratch); 1745 } 1746 // mov ip, hi(imm64) 1747 // vmov d0[1], ip 1748 { 1749 UseScratchRegisterScope temps(this); 1750 Register scratch = temps.Acquire(); 1751 { 1752 // TODO: The scope length was measured empirically. We should 1753 // analyse the 1754 // worst-case size and add targetted tests. 1755 CodeBufferCheckScope scope(this, 1756 2 * kMaxInstructionSizeInBytes); 1757 mov(cond, scratch, static_cast<uint32_t>(imm >> 32)); 1758 } 1759 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1760 vmov(cond, Untyped32, DRegisterLane(rd, 1), scratch); 1761 } 1762 return; 1763 } 1764 break; 1765 default: 1766 break; 1767 } 1768 VIXL_ASSERT(!dt.Is(I8)); // I8 cases should have been handled already. 1769 if ((dt.Is(I16) || dt.Is(I32)) && neon_imm.CanConvert<uint32_t>()) { 1770 // mov ip, imm32 1771 // vdup.16 d0, ip 1772 UseScratchRegisterScope temps(this); 1773 Register scratch = temps.Acquire(); 1774 { 1775 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 1776 mov(cond, scratch, neon_imm.GetImmediate<uint32_t>()); 1777 } 1778 DataTypeValue vdup_dt = Untyped32; 1779 switch (dt.GetValue()) { 1780 case I16: 1781 vdup_dt = Untyped16; 1782 break; 1783 case I32: 1784 vdup_dt = Untyped32; 1785 break; 1786 default: 1787 VIXL_UNREACHABLE(); 1788 } 1789 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1790 vdup(cond, vdup_dt, rd, scratch); 1791 return; 1792 } 1793 if (dt.Is(F32) && neon_imm.CanConvert<float>()) { 1794 float f = neon_imm.GetImmediate<float>(); 1795 // Punt to vmov.i32 1796 // TODO: The scope length was guessed based on the double case below. We 1797 // should analyse the worst-case size and add targetted tests. 1798 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1799 vmov(cond, I32, rd, FloatToRawbits(f)); 1800 return; 1801 } 1802 if (dt.Is(F64) && neon_imm.CanConvert<double>()) { 1803 // Punt to vmov.i64 1804 double d = neon_imm.GetImmediate<double>(); 1805 // TODO: The scope length was measured empirically. We should analyse 1806 // the 1807 // worst-case size and add targetted tests. 1808 CodeBufferCheckScope scope(this, 6 * kMaxInstructionSizeInBytes); 1809 vmov(cond, I64, rd, DoubleToRawbits(d)); 1810 return; 1811 } 1812 } 1813 } 1814 Assembler::Delegate(type, instruction, cond, dt, rd, operand); 1815} 1816 1817 1818void MacroAssembler::Delegate(InstructionType type, 1819 InstructionCondDtQQop instruction, 1820 Condition cond, 1821 DataType dt, 1822 QRegister rd, 1823 const QOperand& operand) { 1824 CONTEXT_SCOPE; 1825 if (type == kVmov) { 1826 if (operand.IsImmediate()) { 1827 const NeonImmediate& neon_imm = operand.GetNeonImmediate(); 1828 switch (dt.GetValue()) { 1829 case I32: 1830 if (neon_imm.CanConvert<uint32_t>()) { 1831 uint32_t imm = neon_imm.GetImmediate<uint32_t>(); 1832 // vmov.i32 d0, 0xabababab will translate into vmov.i8 d0, 0xab 1833 if (IsI8BitPattern(imm)) { 1834 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1835 vmov(cond, I8, rd, imm & 0xff); 1836 return; 1837 } 1838 // vmov.i32 d0, 0xff0000ff will translate into 1839 // vmov.i64 d0, 0xff0000ffff0000ff 1840 if (IsI64BitPattern(imm)) { 1841 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1842 vmov(cond, I64, rd, replicate<uint64_t>(imm)); 1843 return; 1844 } 1845 // vmov.i32 d0, 0xffab0000 will translate into 1846 // vmvn.i32 d0, 0x0054ffff 1847 if (CanBeInverted(imm)) { 1848 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1849 vmvn(cond, I32, rd, ~imm); 1850 return; 1851 } 1852 } 1853 break; 1854 case I16: 1855 if (neon_imm.CanConvert<uint16_t>()) { 1856 uint16_t imm = neon_imm.GetImmediate<uint16_t>(); 1857 // vmov.i16 d0, 0xabab will translate into vmov.i8 d0, 0xab 1858 if (IsI8BitPattern(imm)) { 1859 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1860 vmov(cond, I8, rd, imm & 0xff); 1861 return; 1862 } 1863 } 1864 break; 1865 case I64: 1866 if (neon_imm.CanConvert<uint64_t>()) { 1867 uint64_t imm = neon_imm.GetImmediate<uint64_t>(); 1868 // vmov.i64 d0, -1 will translate into vmov.i8 d0, 0xff 1869 if (IsI8BitPattern(imm)) { 1870 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1871 vmov(cond, I8, rd, imm & 0xff); 1872 return; 1873 } 1874 // mov ip, lo(imm64) 1875 // vdup q0, ip 1876 // vdup is prefered to 'vmov d0[0]' as d0[1-3] don't need to be 1877 // preserved 1878 { 1879 UseScratchRegisterScope temps(this); 1880 Register scratch = temps.Acquire(); 1881 { 1882 CodeBufferCheckScope scope(this, 1883 2 * kMaxInstructionSizeInBytes); 1884 mov(cond, scratch, static_cast<uint32_t>(imm & 0xffffffff)); 1885 } 1886 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1887 vdup(cond, Untyped32, rd, scratch); 1888 } 1889 // mov ip, hi(imm64) 1890 // vmov.i32 d0[1], ip 1891 // vmov d1, d0 1892 { 1893 UseScratchRegisterScope temps(this); 1894 Register scratch = temps.Acquire(); 1895 { 1896 CodeBufferCheckScope scope(this, 1897 2 * kMaxInstructionSizeInBytes); 1898 mov(cond, scratch, static_cast<uint32_t>(imm >> 32)); 1899 } 1900 { 1901 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1902 vmov(cond, 1903 Untyped32, 1904 DRegisterLane(rd.GetLowDRegister(), 1), 1905 scratch); 1906 } 1907 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1908 vmov(cond, F64, rd.GetHighDRegister(), rd.GetLowDRegister()); 1909 } 1910 return; 1911 } 1912 break; 1913 default: 1914 break; 1915 } 1916 VIXL_ASSERT(!dt.Is(I8)); // I8 cases should have been handled already. 1917 if ((dt.Is(I16) || dt.Is(I32)) && neon_imm.CanConvert<uint32_t>()) { 1918 // mov ip, imm32 1919 // vdup.16 d0, ip 1920 UseScratchRegisterScope temps(this); 1921 Register scratch = temps.Acquire(); 1922 { 1923 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 1924 mov(cond, scratch, neon_imm.GetImmediate<uint32_t>()); 1925 } 1926 DataTypeValue vdup_dt = Untyped32; 1927 switch (dt.GetValue()) { 1928 case I16: 1929 vdup_dt = Untyped16; 1930 break; 1931 case I32: 1932 vdup_dt = Untyped32; 1933 break; 1934 default: 1935 VIXL_UNREACHABLE(); 1936 } 1937 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 1938 vdup(cond, vdup_dt, rd, scratch); 1939 return; 1940 } 1941 if (dt.Is(F32) && neon_imm.CanConvert<float>()) { 1942 // Punt to vmov.i64 1943 float f = neon_imm.GetImmediate<float>(); 1944 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 1945 vmov(cond, I32, rd, FloatToRawbits(f)); 1946 return; 1947 } 1948 if (dt.Is(F64) && neon_imm.CanConvert<double>()) { 1949 // Use vmov to create the double in the low D register, then duplicate 1950 // it into the high D register. 1951 double d = neon_imm.GetImmediate<double>(); 1952 CodeBufferCheckScope scope(this, 7 * kMaxInstructionSizeInBytes); 1953 vmov(cond, F64, rd.GetLowDRegister(), d); 1954 vmov(cond, F64, rd.GetHighDRegister(), rd.GetLowDRegister()); 1955 return; 1956 } 1957 } 1958 } 1959 Assembler::Delegate(type, instruction, cond, dt, rd, operand); 1960} 1961 1962 1963void MacroAssembler::Delegate(InstructionType type, 1964 InstructionCondRL instruction, 1965 Condition cond, 1966 Register rt, 1967 Label* label) { 1968 VIXL_ASSERT((type == kLdrb) || (type == kLdrh) || (type == kLdrsb) || 1969 (type == kLdrsh)); 1970 1971 CONTEXT_SCOPE; 1972 1973 if (label->IsBound()) { 1974 CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes); 1975 UseScratchRegisterScope temps(this); 1976 temps.Include(rt); 1977 Register scratch = temps.Acquire(); 1978 uint32_t mask = GetOffsetMask(type, Offset); 1979 switch (type) { 1980 case kLdrb: 1981 ldrb(rt, MemOperandComputationHelper(cond, scratch, label, mask)); 1982 return; 1983 case kLdrh: 1984 ldrh(rt, MemOperandComputationHelper(cond, scratch, label, mask)); 1985 return; 1986 case kLdrsb: 1987 ldrsb(rt, MemOperandComputationHelper(cond, scratch, label, mask)); 1988 return; 1989 case kLdrsh: 1990 ldrsh(rt, MemOperandComputationHelper(cond, scratch, label, mask)); 1991 return; 1992 default: 1993 VIXL_UNREACHABLE(); 1994 } 1995 return; 1996 } 1997 1998 Assembler::Delegate(type, instruction, cond, rt, label); 1999} 2000 2001 2002void MacroAssembler::Delegate(InstructionType type, 2003 InstructionCondRRL instruction, 2004 Condition cond, 2005 Register rt, 2006 Register rt2, 2007 Label* label) { 2008 VIXL_ASSERT(type == kLdrd); 2009 2010 CONTEXT_SCOPE; 2011 2012 if (label->IsBound()) { 2013 CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes); 2014 UseScratchRegisterScope temps(this); 2015 temps.Include(rt, rt2); 2016 Register scratch = temps.Acquire(); 2017 uint32_t mask = GetOffsetMask(type, Offset); 2018 ldrd(rt, rt2, MemOperandComputationHelper(cond, scratch, label, mask)); 2019 return; 2020 } 2021 2022 Assembler::Delegate(type, instruction, cond, rt, rt2, label); 2023} 2024 2025 2026void MacroAssembler::Delegate(InstructionType type, 2027 InstructionCondSizeRMop instruction, 2028 Condition cond, 2029 EncodingSize size, 2030 Register rd, 2031 const MemOperand& operand) { 2032 // ldr ldrb ldrh ldrsb ldrsh str strb strh 2033 CONTEXT_SCOPE; 2034 VIXL_ASSERT(size.IsBest()); 2035 VIXL_ASSERT((type == kLdr) || (type == kLdrb) || (type == kLdrh) || 2036 (type == kLdrsb) || (type == kLdrsh) || (type == kStr) || 2037 (type == kStrb) || (type == kStrh)); 2038 if (operand.IsImmediate()) { 2039 const Register& rn = operand.GetBaseRegister(); 2040 AddrMode addrmode = operand.GetAddrMode(); 2041 int32_t offset = operand.GetOffsetImmediate(); 2042 uint32_t mask = GetOffsetMask(type, addrmode); 2043 bool negative; 2044 // Try to maximize the offset use by the MemOperand (load_store_offset). 2045 // Add or subtract the part which can't be used by the MemOperand 2046 // (add_sub_offset). 2047 int32_t add_sub_offset; 2048 int32_t load_store_offset; 2049 load_store_offset = offset & mask; 2050 if (offset >= 0) { 2051 negative = false; 2052 add_sub_offset = offset & ~mask; 2053 } else { 2054 negative = true; 2055 add_sub_offset = -offset & ~mask; 2056 if (load_store_offset > 0) add_sub_offset += mask + 1; 2057 } 2058 switch (addrmode) { 2059 case PreIndex: 2060 // Avoid the unpredictable case 'str r0, [r0, imm]!' 2061 if (!rn.Is(rd)) { 2062 // Pre-Indexed case: 2063 // ldr r0, [r1, 12345]! will translate into 2064 // add r1, r1, 12345 2065 // ldr r0, [r1] 2066 { 2067 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2068 if (negative) { 2069 sub(cond, rn, rn, add_sub_offset); 2070 } else { 2071 add(cond, rn, rn, add_sub_offset); 2072 } 2073 } 2074 { 2075 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2076 (this->*instruction)(cond, 2077 size, 2078 rd, 2079 MemOperand(rn, load_store_offset, PreIndex)); 2080 } 2081 return; 2082 } 2083 break; 2084 case Offset: { 2085 UseScratchRegisterScope temps(this); 2086 // Allow using the destination as a scratch register if possible. 2087 if ((type != kStr) && (type != kStrb) && (type != kStrh) && 2088 !rd.Is(rn)) { 2089 temps.Include(rd); 2090 } 2091 Register scratch = temps.Acquire(); 2092 // Offset case: 2093 // ldr r0, [r1, 12345] will translate into 2094 // add r0, r1, 12345 2095 // ldr r0, [r0] 2096 { 2097 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2098 if (negative) { 2099 sub(cond, scratch, rn, add_sub_offset); 2100 } else { 2101 add(cond, scratch, rn, add_sub_offset); 2102 } 2103 } 2104 { 2105 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2106 (this->*instruction)(cond, 2107 size, 2108 rd, 2109 MemOperand(scratch, load_store_offset)); 2110 } 2111 return; 2112 } 2113 case PostIndex: 2114 // Avoid the unpredictable case 'ldr r0, [r0], imm' 2115 if (!rn.Is(rd)) { 2116 // Post-indexed case: 2117 // ldr r0. [r1], imm32 will translate into 2118 // ldr r0, [r1] 2119 // movw ip. imm32 & 0xffffffff 2120 // movt ip, imm32 >> 16 2121 // add r1, r1, ip 2122 { 2123 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2124 (this->*instruction)(cond, 2125 size, 2126 rd, 2127 MemOperand(rn, load_store_offset, PostIndex)); 2128 } 2129 { 2130 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2131 if (negative) { 2132 sub(cond, rn, rn, add_sub_offset); 2133 } else { 2134 add(cond, rn, rn, add_sub_offset); 2135 } 2136 } 2137 return; 2138 } 2139 break; 2140 } 2141 } else if (operand.IsPlainRegister()) { 2142 const Register& rn = operand.GetBaseRegister(); 2143 AddrMode addrmode = operand.GetAddrMode(); 2144 const Register& rm = operand.GetOffsetRegister(); 2145 if (rm.IsPC()) { 2146 VIXL_ABORT_WITH_MSG( 2147 "The MacroAssembler does not convert loads and stores with a PC " 2148 "offset register.\n"); 2149 } 2150 if (rn.IsPC()) { 2151 if (addrmode == Offset) { 2152 if (IsUsingT32()) { 2153 VIXL_ABORT_WITH_MSG( 2154 "The MacroAssembler does not convert loads and stores with a PC " 2155 "base register for T32.\n"); 2156 } 2157 } else { 2158 VIXL_ABORT_WITH_MSG( 2159 "The MacroAssembler does not convert loads and stores with a PC " 2160 "base register in pre-index or post-index mode.\n"); 2161 } 2162 } 2163 switch (addrmode) { 2164 case PreIndex: 2165 // Avoid the unpredictable case 'str r0, [r0, imm]!' 2166 if (!rn.Is(rd)) { 2167 // Pre-Indexed case: 2168 // ldr r0, [r1, r2]! will translate into 2169 // add r1, r1, r2 2170 // ldr r0, [r1] 2171 { 2172 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2173 if (operand.GetSign().IsPlus()) { 2174 add(cond, rn, rn, rm); 2175 } else { 2176 sub(cond, rn, rn, rm); 2177 } 2178 } 2179 { 2180 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2181 (this->*instruction)(cond, size, rd, MemOperand(rn, Offset)); 2182 } 2183 return; 2184 } 2185 break; 2186 case Offset: { 2187 UseScratchRegisterScope temps(this); 2188 // Allow using the destination as a scratch register if this is not a 2189 // store. 2190 // Avoid using PC as a temporary as this has side-effects. 2191 if ((type != kStr) && (type != kStrb) && (type != kStrh) && 2192 !rd.IsPC()) { 2193 temps.Include(rd); 2194 } 2195 Register scratch = temps.Acquire(); 2196 // Offset case: 2197 // ldr r0, [r1, r2] will translate into 2198 // add r0, r1, r2 2199 // ldr r0, [r0] 2200 { 2201 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2202 if (operand.GetSign().IsPlus()) { 2203 add(cond, scratch, rn, rm); 2204 } else { 2205 sub(cond, scratch, rn, rm); 2206 } 2207 } 2208 { 2209 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2210 (this->*instruction)(cond, size, rd, MemOperand(scratch, Offset)); 2211 } 2212 return; 2213 } 2214 case PostIndex: 2215 // Avoid the unpredictable case 'ldr r0, [r0], imm' 2216 if (!rn.Is(rd)) { 2217 // Post-indexed case: 2218 // ldr r0. [r1], r2 will translate into 2219 // ldr r0, [r1] 2220 // add r1, r1, r2 2221 { 2222 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2223 (this->*instruction)(cond, size, rd, MemOperand(rn, Offset)); 2224 } 2225 { 2226 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2227 if (operand.GetSign().IsPlus()) { 2228 add(cond, rn, rn, rm); 2229 } else { 2230 sub(cond, rn, rn, rm); 2231 } 2232 } 2233 return; 2234 } 2235 break; 2236 } 2237 } 2238 Assembler::Delegate(type, instruction, cond, size, rd, operand); 2239} 2240 2241 2242void MacroAssembler::Delegate(InstructionType type, 2243 InstructionCondRRMop instruction, 2244 Condition cond, 2245 Register rt, 2246 Register rt2, 2247 const MemOperand& operand) { 2248 // ldaexd, ldrd, ldrexd, stlex, stlexb, stlexh, strd, strex, strexb, strexh 2249 2250 if ((type == kLdaexd) || (type == kLdrexd) || (type == kStlex) || 2251 (type == kStlexb) || (type == kStlexh) || (type == kStrex) || 2252 (type == kStrexb) || (type == kStrexh)) { 2253 UnimplementedDelegate(type); 2254 return; 2255 } 2256 2257 VIXL_ASSERT((type == kLdrd) || (type == kStrd)); 2258 2259 CONTEXT_SCOPE; 2260 2261 // TODO: Should we allow these cases? 2262 if (IsUsingA32()) { 2263 // The first register needs to be even. 2264 if ((rt.GetCode() & 1) != 0) { 2265 UnimplementedDelegate(type); 2266 return; 2267 } 2268 // Registers need to be adjacent. 2269 if (((rt.GetCode() + 1) % kNumberOfRegisters) != rt2.GetCode()) { 2270 UnimplementedDelegate(type); 2271 return; 2272 } 2273 // LDRD lr, pc [...] is not allowed. 2274 if (rt.Is(lr)) { 2275 UnimplementedDelegate(type); 2276 return; 2277 } 2278 } 2279 2280 if (operand.IsImmediate()) { 2281 const Register& rn = operand.GetBaseRegister(); 2282 AddrMode addrmode = operand.GetAddrMode(); 2283 int32_t offset = operand.GetOffsetImmediate(); 2284 switch (addrmode) { 2285 case PreIndex: { 2286 // Allow using the destinations as a scratch registers if possible. 2287 UseScratchRegisterScope temps(this); 2288 if (type == kLdrd) { 2289 if (!rt.Is(rn)) temps.Include(rt); 2290 if (!rt2.Is(rn)) temps.Include(rt2); 2291 } 2292 2293 // Pre-Indexed case: 2294 // ldrd r0, r1, [r2, 12345]! will translate into 2295 // add r2, 12345 2296 // ldrd r0, r1, [r2] 2297 { 2298 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2299 add(cond, rn, rn, offset); 2300 } 2301 { 2302 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2303 (this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset)); 2304 } 2305 return; 2306 } 2307 case Offset: { 2308 UseScratchRegisterScope temps(this); 2309 // Allow using the destinations as a scratch registers if possible. 2310 if (type == kLdrd) { 2311 if (!rt.Is(rn)) temps.Include(rt); 2312 if (!rt2.Is(rn)) temps.Include(rt2); 2313 } 2314 Register scratch = temps.Acquire(); 2315 // Offset case: 2316 // ldrd r0, r1, [r2, 12345] will translate into 2317 // add r0, r2, 12345 2318 // ldrd r0, r1, [r0] 2319 { 2320 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2321 add(cond, scratch, rn, offset); 2322 } 2323 { 2324 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2325 (this->*instruction)(cond, rt, rt2, MemOperand(scratch, Offset)); 2326 } 2327 return; 2328 } 2329 case PostIndex: 2330 // Avoid the unpredictable case 'ldrd r0, r1, [r0], imm' 2331 if (!rn.Is(rt) && !rn.Is(rt2)) { 2332 // Post-indexed case: 2333 // ldrd r0, r1, [r2], imm32 will translate into 2334 // ldrd r0, r1, [r2] 2335 // movw ip. imm32 & 0xffffffff 2336 // movt ip, imm32 >> 16 2337 // add r2, ip 2338 { 2339 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2340 (this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset)); 2341 } 2342 { 2343 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2344 add(cond, rn, rn, offset); 2345 } 2346 return; 2347 } 2348 break; 2349 } 2350 } 2351 if (operand.IsPlainRegister()) { 2352 const Register& rn = operand.GetBaseRegister(); 2353 const Register& rm = operand.GetOffsetRegister(); 2354 AddrMode addrmode = operand.GetAddrMode(); 2355 switch (addrmode) { 2356 case PreIndex: 2357 // ldrd r0, r1, [r2, r3]! will translate into 2358 // add r2, r3 2359 // ldrd r0, r1, [r2] 2360 { 2361 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2362 if (operand.GetSign().IsPlus()) { 2363 add(cond, rn, rn, rm); 2364 } else { 2365 sub(cond, rn, rn, rm); 2366 } 2367 } 2368 { 2369 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2370 (this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset)); 2371 } 2372 return; 2373 case PostIndex: 2374 // ldrd r0, r1, [r2], r3 will translate into 2375 // ldrd r0, r1, [r2] 2376 // add r2, r3 2377 { 2378 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2379 (this->*instruction)(cond, rt, rt2, MemOperand(rn, Offset)); 2380 } 2381 { 2382 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2383 if (operand.GetSign().IsPlus()) { 2384 add(cond, rn, rn, rm); 2385 } else { 2386 sub(cond, rn, rn, rm); 2387 } 2388 } 2389 return; 2390 case Offset: { 2391 UseScratchRegisterScope temps(this); 2392 // Allow using the destinations as a scratch registers if possible. 2393 if (type == kLdrd) { 2394 if (!rt.Is(rn)) temps.Include(rt); 2395 if (!rt2.Is(rn)) temps.Include(rt2); 2396 } 2397 Register scratch = temps.Acquire(); 2398 // Offset case: 2399 // ldrd r0, r1, [r2, r3] will translate into 2400 // add r0, r2, r3 2401 // ldrd r0, r1, [r0] 2402 { 2403 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2404 if (operand.GetSign().IsPlus()) { 2405 add(cond, scratch, rn, rm); 2406 } else { 2407 sub(cond, scratch, rn, rm); 2408 } 2409 } 2410 { 2411 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2412 (this->*instruction)(cond, rt, rt2, MemOperand(scratch, Offset)); 2413 } 2414 return; 2415 } 2416 } 2417 } 2418 Assembler::Delegate(type, instruction, cond, rt, rt2, operand); 2419} 2420 2421 2422void MacroAssembler::Delegate(InstructionType type, 2423 InstructionCondDtSMop instruction, 2424 Condition cond, 2425 DataType dt, 2426 SRegister rd, 2427 const MemOperand& operand) { 2428 // vldr.32 vstr.32 2429 CONTEXT_SCOPE; 2430 if (operand.IsImmediate()) { 2431 const Register& rn = operand.GetBaseRegister(); 2432 AddrMode addrmode = operand.GetAddrMode(); 2433 int32_t offset = operand.GetOffsetImmediate(); 2434 VIXL_ASSERT(((offset > 0) && operand.GetSign().IsPlus()) || 2435 ((offset < 0) && operand.GetSign().IsMinus()) || (offset == 0)); 2436 if (rn.IsPC()) { 2437 VIXL_ABORT_WITH_MSG( 2438 "The MacroAssembler does not convert vldr or vstr with a PC base " 2439 "register.\n"); 2440 } 2441 switch (addrmode) { 2442 case PreIndex: 2443 // Pre-Indexed case: 2444 // vldr.32 s0, [r1, 12345]! will translate into 2445 // add r1, 12345 2446 // vldr.32 s0, [r1] 2447 if (offset != 0) { 2448 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2449 add(cond, rn, rn, offset); 2450 } 2451 { 2452 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2453 (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset)); 2454 } 2455 return; 2456 case Offset: { 2457 UseScratchRegisterScope temps(this); 2458 Register scratch = temps.Acquire(); 2459 // Offset case: 2460 // vldr.32 s0, [r1, 12345] will translate into 2461 // add ip, r1, 12345 2462 // vldr.32 s0, [ip] 2463 { 2464 VIXL_ASSERT(offset != 0); 2465 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2466 add(cond, scratch, rn, offset); 2467 } 2468 { 2469 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2470 (this->*instruction)(cond, dt, rd, MemOperand(scratch, Offset)); 2471 } 2472 return; 2473 } 2474 case PostIndex: 2475 // Post-indexed case: 2476 // vldr.32 s0, [r1], imm32 will translate into 2477 // vldr.32 s0, [r1] 2478 // movw ip. imm32 & 0xffffffff 2479 // movt ip, imm32 >> 16 2480 // add r1, ip 2481 { 2482 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2483 (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset)); 2484 } 2485 if (offset != 0) { 2486 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2487 add(cond, rn, rn, offset); 2488 } 2489 return; 2490 } 2491 } 2492 Assembler::Delegate(type, instruction, cond, dt, rd, operand); 2493} 2494 2495 2496void MacroAssembler::Delegate(InstructionType type, 2497 InstructionCondDtDMop instruction, 2498 Condition cond, 2499 DataType dt, 2500 DRegister rd, 2501 const MemOperand& operand) { 2502 // vldr.64 vstr.64 2503 CONTEXT_SCOPE; 2504 if (operand.IsImmediate()) { 2505 const Register& rn = operand.GetBaseRegister(); 2506 AddrMode addrmode = operand.GetAddrMode(); 2507 int32_t offset = operand.GetOffsetImmediate(); 2508 VIXL_ASSERT(((offset > 0) && operand.GetSign().IsPlus()) || 2509 ((offset < 0) && operand.GetSign().IsMinus()) || (offset == 0)); 2510 if (rn.IsPC()) { 2511 VIXL_ABORT_WITH_MSG( 2512 "The MacroAssembler does not convert vldr or vstr with a PC base " 2513 "register.\n"); 2514 } 2515 switch (addrmode) { 2516 case PreIndex: 2517 // Pre-Indexed case: 2518 // vldr.64 d0, [r1, 12345]! will translate into 2519 // add r1, 12345 2520 // vldr.64 d0, [r1] 2521 if (offset != 0) { 2522 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2523 add(cond, rn, rn, offset); 2524 } 2525 { 2526 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2527 (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset)); 2528 } 2529 return; 2530 case Offset: { 2531 UseScratchRegisterScope temps(this); 2532 Register scratch = temps.Acquire(); 2533 // Offset case: 2534 // vldr.64 d0, [r1, 12345] will translate into 2535 // add ip, r1, 12345 2536 // vldr.32 s0, [ip] 2537 { 2538 VIXL_ASSERT(offset != 0); 2539 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2540 add(cond, scratch, rn, offset); 2541 } 2542 { 2543 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2544 (this->*instruction)(cond, dt, rd, MemOperand(scratch, Offset)); 2545 } 2546 return; 2547 } 2548 case PostIndex: 2549 // Post-indexed case: 2550 // vldr.64 d0. [r1], imm32 will translate into 2551 // vldr.64 d0, [r1] 2552 // movw ip. imm32 & 0xffffffff 2553 // movt ip, imm32 >> 16 2554 // add r1, ip 2555 { 2556 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2557 (this->*instruction)(cond, dt, rd, MemOperand(rn, Offset)); 2558 } 2559 if (offset != 0) { 2560 CodeBufferCheckScope scope(this, 3 * kMaxInstructionSizeInBytes); 2561 add(cond, rn, rn, offset); 2562 } 2563 return; 2564 } 2565 } 2566 Assembler::Delegate(type, instruction, cond, dt, rd, operand); 2567} 2568 2569 2570void MacroAssembler::Delegate(InstructionType type, 2571 InstructionCondMsrOp instruction, 2572 Condition cond, 2573 MaskedSpecialRegister spec_reg, 2574 const Operand& operand) { 2575 USE(type); 2576 VIXL_ASSERT(type == kMsr); 2577 if (operand.IsImmediate()) { 2578 UseScratchRegisterScope temps(this); 2579 Register scratch = temps.Acquire(); 2580 { 2581 CodeBufferCheckScope scope(this, 2 * kMaxInstructionSizeInBytes); 2582 mov(cond, scratch, operand); 2583 } 2584 CodeBufferCheckScope scope(this, kMaxInstructionSizeInBytes); 2585 msr(cond, spec_reg, scratch); 2586 return; 2587 } 2588 Assembler::Delegate(type, instruction, cond, spec_reg, operand); 2589} 2590 2591 2592void MacroAssembler::Delegate(InstructionType type, 2593 InstructionCondDtDL instruction, 2594 Condition cond, 2595 DataType dt, 2596 DRegister rd, 2597 Label* label) { 2598 VIXL_ASSERT(type == kVldr); 2599 2600 CONTEXT_SCOPE; 2601 2602 if (label->IsBound()) { 2603 CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes); 2604 UseScratchRegisterScope temps(this); 2605 Register scratch = temps.Acquire(); 2606 uint32_t mask = GetOffsetMask(type, Offset); 2607 vldr(dt, rd, MemOperandComputationHelper(cond, scratch, label, mask)); 2608 return; 2609 } 2610 2611 Assembler::Delegate(type, instruction, cond, dt, rd, label); 2612} 2613 2614 2615void MacroAssembler::Delegate(InstructionType type, 2616 InstructionCondDtSL instruction, 2617 Condition cond, 2618 DataType dt, 2619 SRegister rd, 2620 Label* label) { 2621 VIXL_ASSERT(type == kVldr); 2622 2623 CONTEXT_SCOPE; 2624 2625 if (label->IsBound()) { 2626 CodeBufferCheckScope scope(this, 4 * kMaxInstructionSizeInBytes); 2627 UseScratchRegisterScope temps(this); 2628 Register scratch = temps.Acquire(); 2629 uint32_t mask = GetOffsetMask(type, Offset); 2630 vldr(dt, rd, MemOperandComputationHelper(cond, scratch, label, mask)); 2631 return; 2632 } 2633 2634 Assembler::Delegate(type, instruction, cond, dt, rd, label); 2635} 2636 2637 2638#undef CONTEXT_SCOPE 2639#undef TOSTRING 2640#undef STRINGIFY 2641 2642// Start of generated code. 2643// End of generated code. 2644} // namespace aarch32 2645} // namespace vixl 2646