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