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