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