diff options
Diffstat (limited to 'compiler/utils/arm/assembler_thumb2.cc')
-rw-r--r-- | compiler/utils/arm/assembler_thumb2.cc | 4076 |
1 files changed, 0 insertions, 4076 deletions
diff --git a/compiler/utils/arm/assembler_thumb2.cc b/compiler/utils/arm/assembler_thumb2.cc deleted file mode 100644 index abc36c6adc..0000000000 --- a/compiler/utils/arm/assembler_thumb2.cc +++ /dev/null @@ -1,4076 +0,0 @@ -/* - * Copyright (C) 2014 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <type_traits> - -#include "assembler_thumb2.h" - -#include "base/bit_utils.h" -#include "base/logging.h" -#include "entrypoints/quick/quick_entrypoints.h" -#include "offsets.h" -#include "thread.h" - -namespace art { -namespace arm { - -template <typename Function> -void Thumb2Assembler::Fixup::ForExpandableDependencies(Thumb2Assembler* assembler, Function fn) { - static_assert( - std::is_same<typename std::result_of<Function(FixupId, FixupId)>::type, void>::value, - "Incorrect signature for argument `fn`: expected (FixupId, FixupId) -> void"); - Fixup* fixups = assembler->fixups_.data(); - for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) { - uint32_t target = fixups[fixup_id].target_; - if (target > fixups[fixup_id].location_) { - for (FixupId id = fixup_id + 1u; id != end_id && fixups[id].location_ < target; ++id) { - if (fixups[id].CanExpand()) { - fn(id, fixup_id); - } - } - } else { - for (FixupId id = fixup_id; id != 0u && fixups[id - 1u].location_ >= target; --id) { - if (fixups[id - 1u].CanExpand()) { - fn(id - 1u, fixup_id); - } - } - } - } -} - -void Thumb2Assembler::Fixup::PrepareDependents(Thumb2Assembler* assembler) { - // For each Fixup, it's easy to find the Fixups that it depends on as they are either - // the following or the preceding Fixups until we find the target. However, for fixup - // adjustment we need the reverse lookup, i.e. what Fixups depend on a given Fixup. - // This function creates a compact representation of this relationship, where we have - // all the dependents in a single array and Fixups reference their ranges by start - // index and count. (Instead of having a per-fixup vector.) - - // Count the number of dependents of each Fixup. - Fixup* fixups = assembler->fixups_.data(); - ForExpandableDependencies( - assembler, - [fixups](FixupId dependency, FixupId dependent ATTRIBUTE_UNUSED) { - fixups[dependency].dependents_count_ += 1u; - }); - // Assign index ranges in fixup_dependents_ to individual fixups. Record the end of the - // range in dependents_start_, we shall later decrement it as we fill in fixup_dependents_. - uint32_t number_of_dependents = 0u; - for (FixupId fixup_id = 0u, end_id = assembler->fixups_.size(); fixup_id != end_id; ++fixup_id) { - number_of_dependents += fixups[fixup_id].dependents_count_; - fixups[fixup_id].dependents_start_ = number_of_dependents; - } - if (number_of_dependents == 0u) { - return; - } - // Create and fill in the fixup_dependents_. - assembler->fixup_dependents_.resize(number_of_dependents); - FixupId* dependents = assembler->fixup_dependents_.data(); - ForExpandableDependencies( - assembler, - [fixups, dependents](FixupId dependency, FixupId dependent) { - fixups[dependency].dependents_start_ -= 1u; - dependents[fixups[dependency].dependents_start_] = dependent; - }); -} - -void Thumb2Assembler::BindLabel(Label* label, uint32_t bound_pc) { - CHECK(!label->IsBound()); - - while (label->IsLinked()) { - FixupId fixup_id = label->Position(); // The id for linked Fixup. - Fixup* fixup = GetFixup(fixup_id); // Get the Fixup at this id. - fixup->Resolve(bound_pc); // Fixup can be resolved now. - uint32_t fixup_location = fixup->GetLocation(); - uint16_t next = buffer_.Load<uint16_t>(fixup_location); // Get next in chain. - buffer_.Store<int16_t>(fixup_location, 0); - label->position_ = next; // Move to next. - } - label->BindTo(bound_pc); -} - -uint32_t Thumb2Assembler::BindLiterals() { - // We don't add the padding here, that's done only after adjusting the Fixup sizes. - uint32_t code_size = buffer_.Size(); - for (Literal& lit : literals_) { - Label* label = lit.GetLabel(); - BindLabel(label, code_size); - code_size += lit.GetSize(); - } - return code_size; -} - -void Thumb2Assembler::BindJumpTables(uint32_t code_size) { - for (JumpTable& table : jump_tables_) { - Label* label = table.GetLabel(); - BindLabel(label, code_size); - code_size += table.GetSize(); - } -} - -void Thumb2Assembler::AdjustFixupIfNeeded(Fixup* fixup, uint32_t* current_code_size, - std::deque<FixupId>* fixups_to_recalculate) { - uint32_t adjustment = fixup->AdjustSizeIfNeeded(*current_code_size); - if (adjustment != 0u) { - DCHECK(fixup->CanExpand()); - *current_code_size += adjustment; - for (FixupId dependent_id : fixup->Dependents(*this)) { - Fixup* dependent = GetFixup(dependent_id); - dependent->IncreaseAdjustment(adjustment); - if (buffer_.Load<int16_t>(dependent->GetLocation()) == 0) { - buffer_.Store<int16_t>(dependent->GetLocation(), 1); - fixups_to_recalculate->push_back(dependent_id); - } - } - } -} - -uint32_t Thumb2Assembler::AdjustFixups() { - Fixup::PrepareDependents(this); - uint32_t current_code_size = buffer_.Size(); - std::deque<FixupId> fixups_to_recalculate; - if (kIsDebugBuild) { - // We will use the placeholders in the buffer_ to mark whether the fixup has - // been added to the fixups_to_recalculate. Make sure we start with zeros. - for (Fixup& fixup : fixups_) { - CHECK_EQ(buffer_.Load<int16_t>(fixup.GetLocation()), 0); - } - } - for (Fixup& fixup : fixups_) { - AdjustFixupIfNeeded(&fixup, ¤t_code_size, &fixups_to_recalculate); - } - while (!fixups_to_recalculate.empty()) { - do { - // Pop the fixup. - FixupId fixup_id = fixups_to_recalculate.front(); - fixups_to_recalculate.pop_front(); - Fixup* fixup = GetFixup(fixup_id); - DCHECK_NE(buffer_.Load<int16_t>(fixup->GetLocation()), 0); - buffer_.Store<int16_t>(fixup->GetLocation(), 0); - // See if it needs adjustment. - AdjustFixupIfNeeded(fixup, ¤t_code_size, &fixups_to_recalculate); - } while (!fixups_to_recalculate.empty()); - - if ((current_code_size & 2) != 0 && (!literals_.empty() || !jump_tables_.empty())) { - // If we need to add padding before literals, this may just push some out of range, - // so recalculate all load literals. This makes up for the fact that we don't mark - // load literal as a dependency of all previous Fixups even though it actually is. - for (Fixup& fixup : fixups_) { - if (fixup.IsLoadLiteral()) { - AdjustFixupIfNeeded(&fixup, ¤t_code_size, &fixups_to_recalculate); - } - } - } - } - if (kIsDebugBuild) { - // Check that no fixup is marked as being in fixups_to_recalculate anymore. - for (Fixup& fixup : fixups_) { - CHECK_EQ(buffer_.Load<int16_t>(fixup.GetLocation()), 0); - } - } - - // Adjust literal pool labels for padding. - DCHECK_ALIGNED(current_code_size, 2); - uint32_t literals_adjustment = current_code_size + (current_code_size & 2) - buffer_.Size(); - if (literals_adjustment != 0u) { - for (Literal& literal : literals_) { - Label* label = literal.GetLabel(); - DCHECK(label->IsBound()); - int old_position = label->Position(); - label->Reinitialize(); - label->BindTo(old_position + literals_adjustment); - } - for (JumpTable& table : jump_tables_) { - Label* label = table.GetLabel(); - DCHECK(label->IsBound()); - int old_position = label->Position(); - label->Reinitialize(); - label->BindTo(old_position + literals_adjustment); - } - } - - return current_code_size; -} - -void Thumb2Assembler::EmitFixups(uint32_t adjusted_code_size) { - // Move non-fixup code to its final place and emit fixups. - // Process fixups in reverse order so that we don't repeatedly move the same data. - size_t src_end = buffer_.Size(); - size_t dest_end = adjusted_code_size; - buffer_.Resize(dest_end); - DCHECK_GE(dest_end, src_end); - for (auto i = fixups_.rbegin(), end = fixups_.rend(); i != end; ++i) { - Fixup* fixup = &*i; - size_t old_fixup_location = fixup->GetLocation(); - if (fixup->GetOriginalSize() == fixup->GetSize()) { - // The size of this Fixup didn't change. To avoid moving the data - // in small chunks, emit the code to its original position. - fixup->Finalize(dest_end - src_end); - fixup->Emit(old_fixup_location, &buffer_, adjusted_code_size); - } else { - // Move the data between the end of the fixup and src_end to its final location. - size_t src_begin = old_fixup_location + fixup->GetOriginalSizeInBytes(); - size_t data_size = src_end - src_begin; - size_t dest_begin = dest_end - data_size; - buffer_.Move(dest_begin, src_begin, data_size); - src_end = old_fixup_location; - dest_end = dest_begin - fixup->GetSizeInBytes(); - // Finalize the Fixup and emit the data to the new location. - fixup->Finalize(dest_end - src_end); - fixup->Emit(fixup->GetLocation(), &buffer_, adjusted_code_size); - } - } - CHECK_EQ(src_end, dest_end); -} - -void Thumb2Assembler::EmitLiterals() { - if (!literals_.empty()) { - // Load literal instructions (LDR, LDRD, VLDR) require 4-byte alignment. - // We don't support byte and half-word literals. - uint32_t code_size = buffer_.Size(); - DCHECK_ALIGNED(code_size, 2); - if ((code_size & 2u) != 0u) { - Emit16(0); - } - for (Literal& literal : literals_) { - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - DCHECK_EQ(static_cast<size_t>(literal.GetLabel()->Position()), buffer_.Size()); - DCHECK(literal.GetSize() == 4u || literal.GetSize() == 8u); - for (size_t i = 0, size = literal.GetSize(); i != size; ++i) { - buffer_.Emit<uint8_t>(literal.GetData()[i]); - } - } - } -} - -void Thumb2Assembler::EmitJumpTables() { - if (!jump_tables_.empty()) { - // Jump tables require 4 byte alignment. (We don't support byte and half-word jump tables.) - uint32_t code_size = buffer_.Size(); - DCHECK_ALIGNED(code_size, 2); - if ((code_size & 2u) != 0u) { - Emit16(0); - } - for (JumpTable& table : jump_tables_) { - // Bulk ensure capacity, as this may be large. - size_t orig_size = buffer_.Size(); - size_t required_capacity = orig_size + table.GetSize(); - if (required_capacity > buffer_.Capacity()) { - buffer_.ExtendCapacity(required_capacity); - } -#ifndef NDEBUG - buffer_.has_ensured_capacity_ = true; -#endif - - DCHECK_EQ(static_cast<size_t>(table.GetLabel()->Position()), buffer_.Size()); - int32_t anchor_position = table.GetAnchorLabel()->Position() + 4; - - for (Label* target : table.GetData()) { - // Ensure that the label was tracked, so that it will have the right position. - DCHECK(std::find(tracked_labels_.begin(), tracked_labels_.end(), target) != - tracked_labels_.end()); - - int32_t offset = target->Position() - anchor_position; - buffer_.Emit<int32_t>(offset); - } - -#ifndef NDEBUG - buffer_.has_ensured_capacity_ = false; -#endif - size_t new_size = buffer_.Size(); - DCHECK_LE(new_size - orig_size, table.GetSize()); - } - } -} - -void Thumb2Assembler::PatchCFI() { - if (cfi().NumberOfDelayedAdvancePCs() == 0u) { - return; - } - - typedef DebugFrameOpCodeWriterForAssembler::DelayedAdvancePC DelayedAdvancePC; - const auto data = cfi().ReleaseStreamAndPrepareForDelayedAdvancePC(); - const std::vector<uint8_t>& old_stream = data.first; - const std::vector<DelayedAdvancePC>& advances = data.second; - - // Refill our data buffer with patched opcodes. - cfi().ReserveCFIStream(old_stream.size() + advances.size() + 16); - size_t stream_pos = 0; - for (const DelayedAdvancePC& advance : advances) { - DCHECK_GE(advance.stream_pos, stream_pos); - // Copy old data up to the point where advance was issued. - cfi().AppendRawData(old_stream, stream_pos, advance.stream_pos); - stream_pos = advance.stream_pos; - // Insert the advance command with its final offset. - size_t final_pc = GetAdjustedPosition(advance.pc); - cfi().AdvancePC(final_pc); - } - // Copy the final segment if any. - cfi().AppendRawData(old_stream, stream_pos, old_stream.size()); -} - -inline int16_t Thumb2Assembler::BEncoding16(int32_t offset, Condition cond) { - DCHECK_ALIGNED(offset, 2); - int16_t encoding = static_cast<int16_t>(B15 | B14); - if (cond != AL) { - DCHECK(IsInt<9>(offset)); - encoding |= B12 | (static_cast<int32_t>(cond) << 8) | ((offset >> 1) & 0xff); - } else { - DCHECK(IsInt<12>(offset)); - encoding |= B13 | ((offset >> 1) & 0x7ff); - } - return encoding; -} - -inline int32_t Thumb2Assembler::BEncoding32(int32_t offset, Condition cond) { - DCHECK_ALIGNED(offset, 2); - int32_t s = (offset >> 31) & 1; // Sign bit. - int32_t encoding = B31 | B30 | B29 | B28 | B15 | - (s << 26) | // Sign bit goes to bit 26. - ((offset >> 1) & 0x7ff); // imm11 goes to bits 0-10. - if (cond != AL) { - DCHECK(IsInt<21>(offset)); - // Encode cond, move imm6 from bits 12-17 to bits 16-21 and move J1 and J2. - encoding |= (static_cast<int32_t>(cond) << 22) | ((offset & 0x3f000) << (16 - 12)) | - ((offset & (1 << 19)) >> (19 - 13)) | // Extract J1 from bit 19 to bit 13. - ((offset & (1 << 18)) >> (18 - 11)); // Extract J2 from bit 18 to bit 11. - } else { - DCHECK(IsInt<25>(offset)); - int32_t j1 = ((offset >> 23) ^ s ^ 1) & 1; // Calculate J1 from I1 extracted from bit 23. - int32_t j2 = ((offset >> 22)^ s ^ 1) & 1; // Calculate J2 from I2 extracted from bit 22. - // Move imm10 from bits 12-21 to bits 16-25 and add J1 and J2. - encoding |= B12 | ((offset & 0x3ff000) << (16 - 12)) | - (j1 << 13) | (j2 << 11); - } - return encoding; -} - -inline int16_t Thumb2Assembler::CbxzEncoding16(Register rn, int32_t offset, Condition cond) { - DCHECK(!IsHighRegister(rn)); - DCHECK_ALIGNED(offset, 2); - DCHECK(IsUint<7>(offset)); - DCHECK(cond == EQ || cond == NE); - return B15 | B13 | B12 | B8 | (cond == NE ? B11 : 0) | static_cast<int32_t>(rn) | - ((offset & 0x3e) << (3 - 1)) | // Move imm5 from bits 1-5 to bits 3-7. - ((offset & 0x40) << (9 - 6)); // Move i from bit 6 to bit 11 -} - -inline int16_t Thumb2Assembler::CmpRnImm8Encoding16(Register rn, int32_t value) { - DCHECK(!IsHighRegister(rn)); - DCHECK(IsUint<8>(value)); - return B13 | B11 | (rn << 8) | value; -} - -inline int16_t Thumb2Assembler::AddRdnRmEncoding16(Register rdn, Register rm) { - // The high bit of rn is moved across 4-bit rm. - return B14 | B10 | (static_cast<int32_t>(rm) << 3) | - (static_cast<int32_t>(rdn) & 7) | ((static_cast<int32_t>(rdn) & 8) << 4); -} - -inline int32_t Thumb2Assembler::MovwEncoding32(Register rd, int32_t value) { - DCHECK(IsUint<16>(value)); - return B31 | B30 | B29 | B28 | B25 | B22 | - (static_cast<int32_t>(rd) << 8) | - ((value & 0xf000) << (16 - 12)) | // Move imm4 from bits 12-15 to bits 16-19. - ((value & 0x0800) << (26 - 11)) | // Move i from bit 11 to bit 26. - ((value & 0x0700) << (12 - 8)) | // Move imm3 from bits 8-10 to bits 12-14. - (value & 0xff); // Keep imm8 in bits 0-7. -} - -inline int32_t Thumb2Assembler::MovtEncoding32(Register rd, int32_t value) { - DCHECK_EQ(value & 0xffff, 0); - int32_t movw_encoding = MovwEncoding32(rd, (value >> 16) & 0xffff); - return movw_encoding | B25 | B23; -} - -inline int32_t Thumb2Assembler::MovModImmEncoding32(Register rd, int32_t value) { - uint32_t mod_imm = ModifiedImmediate(value); - DCHECK_NE(mod_imm, kInvalidModifiedImmediate); - return B31 | B30 | B29 | B28 | B22 | B19 | B18 | B17 | B16 | - (static_cast<int32_t>(rd) << 8) | static_cast<int32_t>(mod_imm); -} - -inline int16_t Thumb2Assembler::LdrLitEncoding16(Register rt, int32_t offset) { - DCHECK(!IsHighRegister(rt)); - DCHECK_ALIGNED(offset, 4); - DCHECK(IsUint<10>(offset)); - return B14 | B11 | (static_cast<int32_t>(rt) << 8) | (offset >> 2); -} - -inline int32_t Thumb2Assembler::LdrLitEncoding32(Register rt, int32_t offset) { - // NOTE: We don't support negative offset, i.e. U=0 (B23). - return LdrRtRnImm12Encoding(rt, PC, offset); -} - -inline int32_t Thumb2Assembler::LdrdEncoding32(Register rt, Register rt2, Register rn, int32_t offset) { - DCHECK_ALIGNED(offset, 4); - CHECK(IsUint<10>(offset)); - return B31 | B30 | B29 | B27 | - B24 /* P = 1 */ | B23 /* U = 1 */ | B22 | 0 /* W = 0 */ | B20 | - (static_cast<int32_t>(rn) << 16) | (static_cast<int32_t>(rt) << 12) | - (static_cast<int32_t>(rt2) << 8) | (offset >> 2); -} - -inline int32_t Thumb2Assembler::VldrsEncoding32(SRegister sd, Register rn, int32_t offset) { - DCHECK_ALIGNED(offset, 4); - CHECK(IsUint<10>(offset)); - return B31 | B30 | B29 | B27 | B26 | B24 | - B23 /* U = 1 */ | B20 | B11 | B9 | - (static_cast<int32_t>(rn) << 16) | - ((static_cast<int32_t>(sd) & 0x01) << (22 - 0)) | // Move D from bit 0 to bit 22. - ((static_cast<int32_t>(sd) & 0x1e) << (12 - 1)) | // Move Vd from bits 1-4 to bits 12-15. - (offset >> 2); -} - -inline int32_t Thumb2Assembler::VldrdEncoding32(DRegister dd, Register rn, int32_t offset) { - DCHECK_ALIGNED(offset, 4); - CHECK(IsUint<10>(offset)); - return B31 | B30 | B29 | B27 | B26 | B24 | - B23 /* U = 1 */ | B20 | B11 | B9 | B8 | - (rn << 16) | - ((static_cast<int32_t>(dd) & 0x10) << (22 - 4)) | // Move D from bit 4 to bit 22. - ((static_cast<int32_t>(dd) & 0x0f) << (12 - 0)) | // Move Vd from bits 0-3 to bits 12-15. - (offset >> 2); -} - -inline int16_t Thumb2Assembler::LdrRtRnImm5Encoding16(Register rt, Register rn, int32_t offset) { - DCHECK(!IsHighRegister(rt)); - DCHECK(!IsHighRegister(rn)); - DCHECK_ALIGNED(offset, 4); - DCHECK(IsUint<7>(offset)); - return B14 | B13 | B11 | - (static_cast<int32_t>(rn) << 3) | static_cast<int32_t>(rt) | - (offset << (6 - 2)); // Move imm5 from bits 2-6 to bits 6-10. -} - -int32_t Thumb2Assembler::Fixup::LoadWideOrFpEncoding(Register rbase, int32_t offset) const { - switch (type_) { - case kLoadLiteralWide: - return LdrdEncoding32(rn_, rt2_, rbase, offset); - case kLoadFPLiteralSingle: - return VldrsEncoding32(sd_, rbase, offset); - case kLoadFPLiteralDouble: - return VldrdEncoding32(dd_, rbase, offset); - default: - LOG(FATAL) << "Unexpected type: " << static_cast<int>(type_); - UNREACHABLE(); - } -} - -inline int32_t Thumb2Assembler::LdrRtRnImm12Encoding(Register rt, Register rn, int32_t offset) { - DCHECK(IsUint<12>(offset)); - return B31 | B30 | B29 | B28 | B27 | B23 | B22 | B20 | (rn << 16) | (rt << 12) | offset; -} - -inline int16_t Thumb2Assembler::AdrEncoding16(Register rd, int32_t offset) { - DCHECK(IsUint<10>(offset)); - DCHECK(IsAligned<4>(offset)); - DCHECK(!IsHighRegister(rd)); - return B15 | B13 | (rd << 8) | (offset >> 2); -} - -inline int32_t Thumb2Assembler::AdrEncoding32(Register rd, int32_t offset) { - DCHECK(IsUint<12>(offset)); - // Bit 26: offset[11] - // Bits 14-12: offset[10-8] - // Bits 7-0: offset[7-0] - int32_t immediate_mask = - ((offset & (1 << 11)) << (26 - 11)) | - ((offset & (7 << 8)) << (12 - 8)) | - (offset & 0xFF); - return B31 | B30 | B29 | B28 | B25 | B19 | B18 | B17 | B16 | (rd << 8) | immediate_mask; -} - -void Thumb2Assembler::FinalizeCode() { - ArmAssembler::FinalizeCode(); - uint32_t size_after_literals = BindLiterals(); - BindJumpTables(size_after_literals); - uint32_t adjusted_code_size = AdjustFixups(); - EmitFixups(adjusted_code_size); - EmitLiterals(); - FinalizeTrackedLabels(); - EmitJumpTables(); - PatchCFI(); -} - -bool Thumb2Assembler::ShifterOperandCanAlwaysHold(uint32_t immediate) { - return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; -} - -bool Thumb2Assembler::ShifterOperandCanHold(Register rd ATTRIBUTE_UNUSED, - Register rn ATTRIBUTE_UNUSED, - Opcode opcode, - uint32_t immediate, - SetCc set_cc, - ShifterOperand* shifter_op) { - shifter_op->type_ = ShifterOperand::kImmediate; - shifter_op->immed_ = immediate; - shifter_op->is_shift_ = false; - shifter_op->is_rotate_ = false; - switch (opcode) { - case ADD: - case SUB: - // Less than (or equal to) 12 bits can be done if we don't need to set condition codes. - if (immediate < (1 << 12) && set_cc != kCcSet) { - return true; - } - return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; - - case MOV: - // TODO: Support less than or equal to 12bits. - return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; - - case MVN: - default: - return ArmAssembler::ModifiedImmediate(immediate) != kInvalidModifiedImmediate; - } -} - -void Thumb2Assembler::and_(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, AND, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::eor(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, EOR, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::sub(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, SUB, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::rsb(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, RSB, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::add(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, ADD, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::adc(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, ADC, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::sbc(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, SBC, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::rsc(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, RSC, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::tst(Register rn, const ShifterOperand& so, Condition cond) { - CHECK_NE(rn, PC); // Reserve tst pc instruction for exception handler marker. - EmitDataProcessing(cond, TST, kCcSet, rn, R0, so); -} - - -void Thumb2Assembler::teq(Register rn, const ShifterOperand& so, Condition cond) { - CHECK_NE(rn, PC); // Reserve teq pc instruction for exception handler marker. - EmitDataProcessing(cond, TEQ, kCcSet, rn, R0, so); -} - - -void Thumb2Assembler::cmp(Register rn, const ShifterOperand& so, Condition cond) { - EmitDataProcessing(cond, CMP, kCcSet, rn, R0, so); -} - - -void Thumb2Assembler::cmn(Register rn, const ShifterOperand& so, Condition cond) { - EmitDataProcessing(cond, CMN, kCcSet, rn, R0, so); -} - - -void Thumb2Assembler::orr(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, ORR, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::orn(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, ORN, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::mov(Register rd, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, MOV, set_cc, R0, rd, so); -} - - -void Thumb2Assembler::bic(Register rd, Register rn, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, BIC, set_cc, rn, rd, so); -} - - -void Thumb2Assembler::mvn(Register rd, const ShifterOperand& so, - Condition cond, SetCc set_cc) { - EmitDataProcessing(cond, MVN, set_cc, R0, rd, so); -} - - -void Thumb2Assembler::mul(Register rd, Register rn, Register rm, Condition cond) { - CheckCondition(cond); - - if (rd == rm && !IsHighRegister(rd) && !IsHighRegister(rn) && !force_32bit_) { - // 16 bit. - int16_t encoding = B14 | B9 | B8 | B6 | - rn << 3 | rd; - Emit16(encoding); - } else { - // 32 bit. - uint32_t op1 = 0U /* 0b000 */; - uint32_t op2 = 0U /* 0b00 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | - op1 << 20 | - B15 | B14 | B13 | B12 | - op2 << 4 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); - } -} - - -void Thumb2Assembler::mla(Register rd, Register rn, Register rm, Register ra, - Condition cond) { - CheckCondition(cond); - - uint32_t op1 = 0U /* 0b000 */; - uint32_t op2 = 0U /* 0b00 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | - op1 << 20 | - op2 << 4 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(ra) << 12 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); -} - - -void Thumb2Assembler::mls(Register rd, Register rn, Register rm, Register ra, - Condition cond) { - CheckCondition(cond); - - uint32_t op1 = 0U /* 0b000 */; - uint32_t op2 = 01 /* 0b01 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | - op1 << 20 | - op2 << 4 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(ra) << 12 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); -} - - -void Thumb2Assembler::smull(Register rd_lo, Register rd_hi, Register rn, - Register rm, Condition cond) { - CheckCondition(cond); - - uint32_t op1 = 0U /* 0b000; */; - uint32_t op2 = 0U /* 0b0000 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | - op1 << 20 | - op2 << 4 | - static_cast<uint32_t>(rd_lo) << 12 | - static_cast<uint32_t>(rd_hi) << 8 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); -} - - -void Thumb2Assembler::umull(Register rd_lo, Register rd_hi, Register rn, - Register rm, Condition cond) { - CheckCondition(cond); - - uint32_t op1 = 2U /* 0b010; */; - uint32_t op2 = 0U /* 0b0000 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | - op1 << 20 | - op2 << 4 | - static_cast<uint32_t>(rd_lo) << 12 | - static_cast<uint32_t>(rd_hi) << 8 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); -} - - -void Thumb2Assembler::sdiv(Register rd, Register rn, Register rm, Condition cond) { - CheckCondition(cond); - - uint32_t op1 = 1U /* 0b001 */; - uint32_t op2 = 15U /* 0b1111 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B20 | - op1 << 20 | - op2 << 4 | - 0xf << 12 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); -} - - -void Thumb2Assembler::udiv(Register rd, Register rn, Register rm, Condition cond) { - CheckCondition(cond); - - uint32_t op1 = 1U /* 0b001 */; - uint32_t op2 = 15U /* 0b1111 */; - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B24 | B23 | B21 | B20 | - op1 << 20 | - op2 << 4 | - 0xf << 12 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rm); - - Emit32(encoding); -} - - -void Thumb2Assembler::sbfx(Register rd, Register rn, uint32_t lsb, uint32_t width, Condition cond) { - CheckCondition(cond); - CHECK_LE(lsb, 31U); - CHECK(1U <= width && width <= 32U) << width; - uint32_t widthminus1 = width - 1; - uint32_t imm2 = lsb & (B1 | B0); // Bits 0-1 of `lsb`. - uint32_t imm3 = (lsb & (B4 | B3 | B2)) >> 2; // Bits 2-4 of `lsb`. - - uint32_t op = 20U /* 0b10100 */; - int32_t encoding = B31 | B30 | B29 | B28 | B25 | - op << 20 | - static_cast<uint32_t>(rn) << 16 | - imm3 << 12 | - static_cast<uint32_t>(rd) << 8 | - imm2 << 6 | - widthminus1; - - Emit32(encoding); -} - - -void Thumb2Assembler::ubfx(Register rd, Register rn, uint32_t lsb, uint32_t width, Condition cond) { - CheckCondition(cond); - CHECK_LE(lsb, 31U); - CHECK(1U <= width && width <= 32U) << width; - uint32_t widthminus1 = width - 1; - uint32_t imm2 = lsb & (B1 | B0); // Bits 0-1 of `lsb`. - uint32_t imm3 = (lsb & (B4 | B3 | B2)) >> 2; // Bits 2-4 of `lsb`. - - uint32_t op = 28U /* 0b11100 */; - int32_t encoding = B31 | B30 | B29 | B28 | B25 | - op << 20 | - static_cast<uint32_t>(rn) << 16 | - imm3 << 12 | - static_cast<uint32_t>(rd) << 8 | - imm2 << 6 | - widthminus1; - - Emit32(encoding); -} - - -void Thumb2Assembler::ldr(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, true, false, false, false, rd, ad); -} - - -void Thumb2Assembler::str(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, false, false, false, false, rd, ad); -} - - -void Thumb2Assembler::ldrb(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, true, true, false, false, rd, ad); -} - - -void Thumb2Assembler::strb(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, false, true, false, false, rd, ad); -} - - -void Thumb2Assembler::ldrh(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, true, false, true, false, rd, ad); -} - - -void Thumb2Assembler::strh(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, false, false, true, false, rd, ad); -} - - -void Thumb2Assembler::ldrsb(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, true, true, false, true, rd, ad); -} - - -void Thumb2Assembler::ldrsh(Register rd, const Address& ad, Condition cond) { - EmitLoadStore(cond, true, false, true, true, rd, ad); -} - - -void Thumb2Assembler::ldrd(Register rd, const Address& ad, Condition cond) { - ldrd(rd, Register(rd + 1), ad, cond); -} - - -void Thumb2Assembler::ldrd(Register rd, Register rd2, const Address& ad, Condition cond) { - CheckCondition(cond); - // Encoding T1. - // This is different from other loads. The encoding is like ARM. - int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 | - static_cast<int32_t>(rd) << 12 | - static_cast<int32_t>(rd2) << 8 | - ad.encodingThumbLdrdStrd(); - Emit32(encoding); -} - - -void Thumb2Assembler::strd(Register rd, const Address& ad, Condition cond) { - strd(rd, Register(rd + 1), ad, cond); -} - - -void Thumb2Assembler::strd(Register rd, Register rd2, const Address& ad, Condition cond) { - CheckCondition(cond); - // Encoding T1. - // This is different from other loads. The encoding is like ARM. - int32_t encoding = B31 | B30 | B29 | B27 | B22 | - static_cast<int32_t>(rd) << 12 | - static_cast<int32_t>(rd2) << 8 | - ad.encodingThumbLdrdStrd(); - Emit32(encoding); -} - - -void Thumb2Assembler::ldm(BlockAddressMode am, - Register base, - RegList regs, - Condition cond) { - CHECK_NE(regs, 0u); // Do not use ldm if there's nothing to load. - if (IsPowerOfTwo(regs)) { - // Thumb doesn't support one reg in the list. - // Find the register number. - int reg = CTZ(static_cast<uint32_t>(regs)); - CHECK_LT(reg, 16); - CHECK(am == DB_W); // Only writeback is supported. - ldr(static_cast<Register>(reg), Address(base, kRegisterSize, Address::PostIndex), cond); - } else { - EmitMultiMemOp(cond, am, true, base, regs); - } -} - - -void Thumb2Assembler::stm(BlockAddressMode am, - Register base, - RegList regs, - Condition cond) { - CHECK_NE(regs, 0u); // Do not use stm if there's nothing to store. - if (IsPowerOfTwo(regs)) { - // Thumb doesn't support one reg in the list. - // Find the register number. - int reg = CTZ(static_cast<uint32_t>(regs)); - CHECK_LT(reg, 16); - CHECK(am == IA || am == IA_W); - Address::Mode strmode = am == IA ? Address::PreIndex : Address::Offset; - str(static_cast<Register>(reg), Address(base, -kRegisterSize, strmode), cond); - } else { - EmitMultiMemOp(cond, am, false, base, regs); - } -} - - -bool Thumb2Assembler::vmovs(SRegister sd, float s_imm, Condition cond) { - uint32_t imm32 = bit_cast<uint32_t, float>(s_imm); - if (((imm32 & ((1 << 19) - 1)) == 0) && - ((((imm32 >> 25) & ((1 << 6) - 1)) == (1 << 5)) || - (((imm32 >> 25) & ((1 << 6) - 1)) == ((1 << 5) -1)))) { - uint8_t imm8 = ((imm32 >> 31) << 7) | (((imm32 >> 29) & 1) << 6) | - ((imm32 >> 19) & ((1 << 6) -1)); - EmitVFPsss(cond, B23 | B21 | B20 | ((imm8 >> 4)*B16) | (imm8 & 0xf), - sd, S0, S0); - return true; - } - return false; -} - - -bool Thumb2Assembler::vmovd(DRegister dd, double d_imm, Condition cond) { - uint64_t imm64 = bit_cast<uint64_t, double>(d_imm); - if (((imm64 & ((1LL << 48) - 1)) == 0) && - ((((imm64 >> 54) & ((1 << 9) - 1)) == (1 << 8)) || - (((imm64 >> 54) & ((1 << 9) - 1)) == ((1 << 8) -1)))) { - uint8_t imm8 = ((imm64 >> 63) << 7) | (((imm64 >> 61) & 1) << 6) | - ((imm64 >> 48) & ((1 << 6) -1)); - EmitVFPddd(cond, B23 | B21 | B20 | ((imm8 >> 4)*B16) | B8 | (imm8 & 0xf), - dd, D0, D0); - return true; - } - return false; -} - - -void Thumb2Assembler::vmovs(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vmovd(DRegister dd, DRegister dm, Condition cond) { - EmitVFPddd(cond, B23 | B21 | B20 | B6, dd, D0, dm); -} - - -void Thumb2Assembler::vadds(SRegister sd, SRegister sn, SRegister sm, - Condition cond) { - EmitVFPsss(cond, B21 | B20, sd, sn, sm); -} - - -void Thumb2Assembler::vaddd(DRegister dd, DRegister dn, DRegister dm, - Condition cond) { - EmitVFPddd(cond, B21 | B20, dd, dn, dm); -} - - -void Thumb2Assembler::vsubs(SRegister sd, SRegister sn, SRegister sm, - Condition cond) { - EmitVFPsss(cond, B21 | B20 | B6, sd, sn, sm); -} - - -void Thumb2Assembler::vsubd(DRegister dd, DRegister dn, DRegister dm, - Condition cond) { - EmitVFPddd(cond, B21 | B20 | B6, dd, dn, dm); -} - - -void Thumb2Assembler::vmuls(SRegister sd, SRegister sn, SRegister sm, - Condition cond) { - EmitVFPsss(cond, B21, sd, sn, sm); -} - - -void Thumb2Assembler::vmuld(DRegister dd, DRegister dn, DRegister dm, - Condition cond) { - EmitVFPddd(cond, B21, dd, dn, dm); -} - - -void Thumb2Assembler::vmlas(SRegister sd, SRegister sn, SRegister sm, - Condition cond) { - EmitVFPsss(cond, 0, sd, sn, sm); -} - - -void Thumb2Assembler::vmlad(DRegister dd, DRegister dn, DRegister dm, - Condition cond) { - EmitVFPddd(cond, 0, dd, dn, dm); -} - - -void Thumb2Assembler::vmlss(SRegister sd, SRegister sn, SRegister sm, - Condition cond) { - EmitVFPsss(cond, B6, sd, sn, sm); -} - - -void Thumb2Assembler::vmlsd(DRegister dd, DRegister dn, DRegister dm, - Condition cond) { - EmitVFPddd(cond, B6, dd, dn, dm); -} - - -void Thumb2Assembler::vdivs(SRegister sd, SRegister sn, SRegister sm, - Condition cond) { - EmitVFPsss(cond, B23, sd, sn, sm); -} - - -void Thumb2Assembler::vdivd(DRegister dd, DRegister dn, DRegister dm, - Condition cond) { - EmitVFPddd(cond, B23, dd, dn, dm); -} - - -void Thumb2Assembler::vabss(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B7 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vabsd(DRegister dd, DRegister dm, Condition cond) { - EmitVFPddd(cond, B23 | B21 | B20 | B7 | B6, dd, D0, dm); -} - - -void Thumb2Assembler::vnegs(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B16 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vnegd(DRegister dd, DRegister dm, Condition cond) { - EmitVFPddd(cond, B23 | B21 | B20 | B16 | B6, dd, D0, dm); -} - - -void Thumb2Assembler::vsqrts(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B16 | B7 | B6, sd, S0, sm); -} - -void Thumb2Assembler::vsqrtd(DRegister dd, DRegister dm, Condition cond) { - EmitVFPddd(cond, B23 | B21 | B20 | B16 | B7 | B6, dd, D0, dm); -} - - -void Thumb2Assembler::vcvtsd(SRegister sd, DRegister dm, Condition cond) { - EmitVFPsd(cond, B23 | B21 | B20 | B18 | B17 | B16 | B8 | B7 | B6, sd, dm); -} - - -void Thumb2Assembler::vcvtds(DRegister dd, SRegister sm, Condition cond) { - EmitVFPds(cond, B23 | B21 | B20 | B18 | B17 | B16 | B7 | B6, dd, sm); -} - - -void Thumb2Assembler::vcvtis(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B19 | B18 | B16 | B7 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vcvtid(SRegister sd, DRegister dm, Condition cond) { - EmitVFPsd(cond, B23 | B21 | B20 | B19 | B18 | B16 | B8 | B7 | B6, sd, dm); -} - - -void Thumb2Assembler::vcvtsi(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B19 | B7 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vcvtdi(DRegister dd, SRegister sm, Condition cond) { - EmitVFPds(cond, B23 | B21 | B20 | B19 | B8 | B7 | B6, dd, sm); -} - - -void Thumb2Assembler::vcvtus(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B19 | B18 | B7 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vcvtud(SRegister sd, DRegister dm, Condition cond) { - EmitVFPsd(cond, B23 | B21 | B20 | B19 | B18 | B8 | B7 | B6, sd, dm); -} - - -void Thumb2Assembler::vcvtsu(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B19 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vcvtdu(DRegister dd, SRegister sm, Condition cond) { - EmitVFPds(cond, B23 | B21 | B20 | B19 | B8 | B6, dd, sm); -} - - -void Thumb2Assembler::vcmps(SRegister sd, SRegister sm, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B18 | B6, sd, S0, sm); -} - - -void Thumb2Assembler::vcmpd(DRegister dd, DRegister dm, Condition cond) { - EmitVFPddd(cond, B23 | B21 | B20 | B18 | B6, dd, D0, dm); -} - - -void Thumb2Assembler::vcmpsz(SRegister sd, Condition cond) { - EmitVFPsss(cond, B23 | B21 | B20 | B18 | B16 | B6, sd, S0, S0); -} - - -void Thumb2Assembler::vcmpdz(DRegister dd, Condition cond) { - EmitVFPddd(cond, B23 | B21 | B20 | B18 | B16 | B6, dd, D0, D0); -} - -void Thumb2Assembler::b(Label* label, Condition cond) { - DCHECK_EQ(next_condition_, AL); - EmitBranch(cond, label, false, false); -} - - -void Thumb2Assembler::bl(Label* label, Condition cond) { - CheckCondition(cond); - EmitBranch(cond, label, true, false); -} - - -void Thumb2Assembler::blx(Label* label) { - EmitBranch(AL, label, true, true); -} - - -void Thumb2Assembler::MarkExceptionHandler(Label* label) { - EmitDataProcessing(AL, TST, kCcSet, PC, R0, ShifterOperand(0)); - Label l; - b(&l); - EmitBranch(AL, label, false, false); - Bind(&l); -} - - -void Thumb2Assembler::Emit32(int32_t value) { - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - buffer_.Emit<int16_t>(value >> 16); - buffer_.Emit<int16_t>(value & 0xffff); -} - - -void Thumb2Assembler::Emit16(int16_t value) { - AssemblerBuffer::EnsureCapacity ensured(&buffer_); - buffer_.Emit<int16_t>(value); -} - - -bool Thumb2Assembler::Is32BitDataProcessing(Condition cond, - Opcode opcode, - SetCc set_cc, - Register rn, - Register rd, - const ShifterOperand& so) { - if (force_32bit_) { - return true; - } - - // Check special case for SP relative ADD and SUB immediate. - if ((opcode == ADD || opcode == SUB) && rn == SP && so.IsImmediate() && set_cc != kCcSet) { - // If the immediate is in range, use 16 bit. - if (rd == SP) { - if (so.GetImmediate() < (1 << 9)) { // 9 bit immediate. - return false; - } - } else if (!IsHighRegister(rd) && opcode == ADD) { - if (so.GetImmediate() < (1 << 10)) { // 10 bit immediate. - return false; - } - } - } - - bool can_contain_high_register = - (opcode == CMP) || - (opcode == MOV && set_cc != kCcSet) || - ((opcode == ADD) && (rn == rd) && set_cc != kCcSet); - - if (IsHighRegister(rd) || IsHighRegister(rn)) { - if (!can_contain_high_register) { - return true; - } - - // There are high register instructions available for this opcode. - // However, there is no actual shift available, neither for ADD nor for MOV (ASR/LSR/LSL/ROR). - if (so.IsShift() && (so.GetShift() == RRX || so.GetImmediate() != 0u)) { - return true; - } - - // The ADD and MOV instructions that work with high registers don't have 16-bit - // immediate variants. - if (so.IsImmediate()) { - return true; - } - } - - if (so.IsRegister() && IsHighRegister(so.GetRegister()) && !can_contain_high_register) { - return true; - } - - bool rn_is_valid = true; - - // Check for single operand instructions and ADD/SUB. - switch (opcode) { - case CMP: - case MOV: - case TST: - case MVN: - rn_is_valid = false; // There is no Rn for these instructions. - break; - case TEQ: - case ORN: - return true; - case ADD: - case SUB: - break; - default: - if (so.IsRegister() && rd != rn) { - return true; - } - } - - if (so.IsImmediate()) { - if (opcode == RSB) { - DCHECK(rn_is_valid); - if (so.GetImmediate() != 0u) { - return true; - } - } else if (rn_is_valid && rn != rd) { - // The only thumb1 instructions with a register and an immediate are ADD and SUB - // with a 3-bit immediate, and RSB with zero immediate. - if (opcode == ADD || opcode == SUB) { - if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) { - return true; // Cannot match "setflags". - } - if (!IsUint<3>(so.GetImmediate()) && !IsUint<3>(-so.GetImmediate())) { - return true; - } - } else { - return true; - } - } else { - // ADD, SUB, CMP and MOV may be thumb1 only if the immediate is 8 bits. - if (!(opcode == ADD || opcode == SUB || opcode == MOV || opcode == CMP)) { - return true; - } else if (opcode != CMP && ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) { - return true; // Cannot match "setflags" for ADD, SUB or MOV. - } else { - // For ADD and SUB allow also negative 8-bit immediate as we will emit the oposite opcode. - if (!IsUint<8>(so.GetImmediate()) && - (opcode == MOV || opcode == CMP || !IsUint<8>(-so.GetImmediate()))) { - return true; - } - } - } - } else { - DCHECK(so.IsRegister()); - if (so.IsShift()) { - // Shift operand - check if it is a MOV convertible to a 16-bit shift instruction. - if (opcode != MOV) { - return true; - } - // Check for MOV with an ROR/RRX. There is no 16-bit ROR immediate and no 16-bit RRX. - if (so.GetShift() == ROR || so.GetShift() == RRX) { - return true; - } - // 16-bit shifts set condition codes if and only if outside IT block, - // i.e. if and only if cond == AL. - if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) { - return true; - } - } else { - // Register operand without shift. - switch (opcode) { - case ADD: - // The 16-bit ADD that cannot contain high registers can set condition codes - // if and only if outside IT block, i.e. if and only if cond == AL. - if (!can_contain_high_register && - ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) { - return true; - } - break; - case AND: - case BIC: - case EOR: - case ORR: - case MVN: - case ADC: - case SUB: - case SBC: - // These 16-bit opcodes set condition codes if and only if outside IT block, - // i.e. if and only if cond == AL. - if ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet) { - return true; - } - break; - case RSB: - case RSC: - // No 16-bit RSB/RSC Rd, Rm, Rn. It would be equivalent to SUB/SBC Rd, Rn, Rm. - return true; - case CMP: - default: - break; - } - } - } - - // The instruction can be encoded in 16 bits. - return false; -} - - -void Thumb2Assembler::Emit32BitDataProcessing(Condition cond ATTRIBUTE_UNUSED, - Opcode opcode, - SetCc set_cc, - Register rn, - Register rd, - const ShifterOperand& so) { - uint8_t thumb_opcode = 255U /* 0b11111111 */; - switch (opcode) { - case AND: thumb_opcode = 0U /* 0b0000 */; break; - case EOR: thumb_opcode = 4U /* 0b0100 */; break; - case SUB: thumb_opcode = 13U /* 0b1101 */; break; - case RSB: thumb_opcode = 14U /* 0b1110 */; break; - case ADD: thumb_opcode = 8U /* 0b1000 */; break; - case ADC: thumb_opcode = 10U /* 0b1010 */; break; - case SBC: thumb_opcode = 11U /* 0b1011 */; break; - case RSC: break; - case TST: thumb_opcode = 0U /* 0b0000 */; DCHECK(set_cc == kCcSet); rd = PC; break; - case TEQ: thumb_opcode = 4U /* 0b0100 */; DCHECK(set_cc == kCcSet); rd = PC; break; - case CMP: thumb_opcode = 13U /* 0b1101 */; DCHECK(set_cc == kCcSet); rd = PC; break; - case CMN: thumb_opcode = 8U /* 0b1000 */; DCHECK(set_cc == kCcSet); rd = PC; break; - case ORR: thumb_opcode = 2U /* 0b0010 */; break; - case MOV: thumb_opcode = 2U /* 0b0010 */; rn = PC; break; - case BIC: thumb_opcode = 1U /* 0b0001 */; break; - case MVN: thumb_opcode = 3U /* 0b0011 */; rn = PC; break; - case ORN: thumb_opcode = 3U /* 0b0011 */; break; - default: - break; - } - - if (thumb_opcode == 255U /* 0b11111111 */) { - LOG(FATAL) << "Invalid thumb2 opcode " << opcode; - UNREACHABLE(); - } - - int32_t encoding = 0; - if (so.IsImmediate()) { - // Check special cases. - if ((opcode == SUB || opcode == ADD) && (so.GetImmediate() < (1u << 12)) && - /* Prefer T3 encoding to T4. */ !ShifterOperandCanAlwaysHold(so.GetImmediate())) { - if (set_cc != kCcSet) { - if (opcode == SUB) { - thumb_opcode = 5U; - } else if (opcode == ADD) { - thumb_opcode = 0U; - } - } - uint32_t imm = so.GetImmediate(); - - uint32_t i = (imm >> 11) & 1; - uint32_t imm3 = (imm >> 8) & 7U /* 0b111 */; - uint32_t imm8 = imm & 0xff; - - encoding = B31 | B30 | B29 | B28 | - (set_cc == kCcSet ? B20 : B25) | - thumb_opcode << 21 | - rn << 16 | - rd << 8 | - i << 26 | - imm3 << 12 | - imm8; - } else { - // Modified immediate. - uint32_t imm = ModifiedImmediate(so.encodingThumb()); - if (imm == kInvalidModifiedImmediate) { - LOG(FATAL) << "Immediate value cannot fit in thumb2 modified immediate"; - UNREACHABLE(); - } - encoding = B31 | B30 | B29 | B28 | - thumb_opcode << 21 | - (set_cc == kCcSet ? B20 : 0) | - rn << 16 | - rd << 8 | - imm; - } - } else if (so.IsRegister()) { - // Register (possibly shifted) - encoding = B31 | B30 | B29 | B27 | B25 | - thumb_opcode << 21 | - (set_cc == kCcSet ? B20 : 0) | - rn << 16 | - rd << 8 | - so.encodingThumb(); - } - Emit32(encoding); -} - - -void Thumb2Assembler::Emit16BitDataProcessing(Condition cond, - Opcode opcode, - SetCc set_cc, - Register rn, - Register rd, - const ShifterOperand& so) { - if (opcode == ADD || opcode == SUB) { - Emit16BitAddSub(cond, opcode, set_cc, rn, rd, so); - return; - } - uint8_t thumb_opcode = 255U /* 0b11111111 */; - // Thumb1. - uint8_t dp_opcode = 1U /* 0b01 */; - uint8_t opcode_shift = 6; - uint8_t rd_shift = 0; - uint8_t rn_shift = 3; - uint8_t immediate_shift = 0; - bool use_immediate = false; - uint8_t immediate = 0; - - if (opcode == MOV && so.IsRegister() && so.IsShift()) { - // Convert shifted mov operand2 into 16 bit opcodes. - dp_opcode = 0; - opcode_shift = 11; - - use_immediate = true; - immediate = so.GetImmediate(); - immediate_shift = 6; - - rn = so.GetRegister(); - - switch (so.GetShift()) { - case LSL: - DCHECK_LE(immediate, 31u); - thumb_opcode = 0U /* 0b00 */; - break; - case LSR: - DCHECK(1 <= immediate && immediate <= 32); - immediate &= 31; // 32 is encoded as 0. - thumb_opcode = 1U /* 0b01 */; - break; - case ASR: - DCHECK(1 <= immediate && immediate <= 32); - immediate &= 31; // 32 is encoded as 0. - thumb_opcode = 2U /* 0b10 */; - break; - case ROR: // No 16-bit ROR immediate. - case RRX: // No 16-bit RRX. - default: - LOG(FATAL) << "Unexpected shift: " << so.GetShift(); - UNREACHABLE(); - } - } else { - if (so.IsImmediate()) { - use_immediate = true; - immediate = so.GetImmediate(); - } else { - CHECK(!(so.IsRegister() && so.IsShift() && so.GetSecondRegister() != kNoRegister)) - << "No register-shifted register instruction available in thumb"; - // Adjust rn and rd: only two registers will be emitted. - switch (opcode) { - case AND: - case ORR: - case EOR: - case RSB: - case ADC: - case SBC: - case BIC: { - // Sets condition codes if and only if outside IT block, - // check that it complies with set_cc. - DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet); - if (rn == rd) { - rn = so.GetRegister(); - } else { - CHECK_EQ(rd, so.GetRegister()); - } - break; - } - case CMP: - case CMN: { - CHECK_EQ(rd, 0); - rd = rn; - rn = so.GetRegister(); - break; - } - case MVN: { - // Sets condition codes if and only if outside IT block, - // check that it complies with set_cc. - DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet); - CHECK_EQ(rn, 0); - rn = so.GetRegister(); - break; - } - case TST: - case TEQ: { - DCHECK(set_cc == kCcSet); - CHECK_EQ(rn, 0); - rn = so.GetRegister(); - break; - } - default: - break; - } - } - - switch (opcode) { - case AND: thumb_opcode = 0U /* 0b0000 */; break; - case ORR: thumb_opcode = 12U /* 0b1100 */; break; - case EOR: thumb_opcode = 1U /* 0b0001 */; break; - case RSB: thumb_opcode = 9U /* 0b1001 */; break; - case ADC: thumb_opcode = 5U /* 0b0101 */; break; - case SBC: thumb_opcode = 6U /* 0b0110 */; break; - case BIC: thumb_opcode = 14U /* 0b1110 */; break; - case TST: thumb_opcode = 8U /* 0b1000 */; CHECK(!use_immediate); break; - case MVN: thumb_opcode = 15U /* 0b1111 */; CHECK(!use_immediate); break; - case CMP: { - DCHECK(set_cc == kCcSet); - if (use_immediate) { - // T2 encoding. - dp_opcode = 0; - opcode_shift = 11; - thumb_opcode = 5U /* 0b101 */; - rd_shift = 8; - rn_shift = 8; - } else if (IsHighRegister(rd) || IsHighRegister(rn)) { - // Special cmp for high registers. - dp_opcode = 1U /* 0b01 */; - opcode_shift = 7; - // Put the top bit of rd into the bottom bit of the opcode. - thumb_opcode = 10U /* 0b0001010 */ | static_cast<uint32_t>(rd) >> 3; - rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */); - } else { - thumb_opcode = 10U /* 0b1010 */; - } - - break; - } - case CMN: { - CHECK(!use_immediate); - thumb_opcode = 11U /* 0b1011 */; - break; - } - case MOV: - dp_opcode = 0; - if (use_immediate) { - // T2 encoding. - opcode_shift = 11; - thumb_opcode = 4U /* 0b100 */; - rd_shift = 8; - rn_shift = 8; - } else { - rn = so.GetRegister(); - if (set_cc != kCcSet) { - // Special mov for high registers. - dp_opcode = 1U /* 0b01 */; - opcode_shift = 7; - // Put the top bit of rd into the bottom bit of the opcode. - thumb_opcode = 12U /* 0b0001100 */ | static_cast<uint32_t>(rd) >> 3; - rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */); - } else { - DCHECK(!IsHighRegister(rn)); - DCHECK(!IsHighRegister(rd)); - thumb_opcode = 0; - } - } - break; - - case TEQ: - case RSC: - default: - LOG(FATAL) << "Invalid thumb1 opcode " << opcode; - break; - } - } - - if (thumb_opcode == 255U /* 0b11111111 */) { - LOG(FATAL) << "Invalid thumb1 opcode " << opcode; - UNREACHABLE(); - } - - int16_t encoding = dp_opcode << 14 | - (thumb_opcode << opcode_shift) | - rd << rd_shift | - rn << rn_shift | - (use_immediate ? (immediate << immediate_shift) : 0); - - Emit16(encoding); -} - - -// ADD and SUB are complex enough to warrant their own emitter. -void Thumb2Assembler::Emit16BitAddSub(Condition cond, - Opcode opcode, - SetCc set_cc, - Register rn, - Register rd, - const ShifterOperand& so) { - uint8_t dp_opcode = 0; - uint8_t opcode_shift = 6; - uint8_t rd_shift = 0; - uint8_t rn_shift = 3; - uint8_t immediate_shift = 0; - bool use_immediate = false; - uint32_t immediate = 0; // Should be at most 10 bits but keep the full immediate for CHECKs. - uint8_t thumb_opcode; - - if (so.IsImmediate()) { - use_immediate = true; - immediate = so.GetImmediate(); - if (!IsUint<10>(immediate)) { - // Flip ADD/SUB. - opcode = (opcode == ADD) ? SUB : ADD; - immediate = -immediate; - DCHECK(IsUint<10>(immediate)); // More stringent checks below. - } - } - - switch (opcode) { - case ADD: - if (so.IsRegister()) { - Register rm = so.GetRegister(); - if (rn == rd && set_cc != kCcSet) { - // Can use T2 encoding (allows 4 bit registers) - dp_opcode = 1U /* 0b01 */; - opcode_shift = 10; - thumb_opcode = 1U /* 0b0001 */; - // Make Rn also contain the top bit of rd. - rn = static_cast<Register>(static_cast<uint32_t>(rm) | - (static_cast<uint32_t>(rd) & 8U /* 0b1000 */) << 1); - rd = static_cast<Register>(static_cast<uint32_t>(rd) & 7U /* 0b111 */); - } else { - // T1. - DCHECK(!IsHighRegister(rd)); - DCHECK(!IsHighRegister(rn)); - DCHECK(!IsHighRegister(rm)); - // Sets condition codes if and only if outside IT block, - // check that it complies with set_cc. - DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet); - opcode_shift = 9; - thumb_opcode = 12U /* 0b01100 */; - immediate = static_cast<uint32_t>(so.GetRegister()); - use_immediate = true; - immediate_shift = 6; - } - } else { - // Immediate. - if (rd == SP && rn == SP) { - // ADD sp, sp, #imm - dp_opcode = 2U /* 0b10 */; - thumb_opcode = 3U /* 0b11 */; - opcode_shift = 12; - CHECK(IsUint<9>(immediate)); - CHECK_ALIGNED(immediate, 4); - - // Remove rd and rn from instruction by orring it with immed and clearing bits. - rn = R0; - rd = R0; - rd_shift = 0; - rn_shift = 0; - immediate >>= 2; - } else if (rd != SP && rn == SP) { - // ADD rd, SP, #imm - dp_opcode = 2U /* 0b10 */; - thumb_opcode = 5U /* 0b101 */; - opcode_shift = 11; - CHECK(IsUint<10>(immediate)); - CHECK_ALIGNED(immediate, 4); - - // Remove rn from instruction. - rn = R0; - rn_shift = 0; - rd_shift = 8; - immediate >>= 2; - } else if (rn != rd) { - // Must use T1. - CHECK(IsUint<3>(immediate)); - opcode_shift = 9; - thumb_opcode = 14U /* 0b01110 */; - immediate_shift = 6; - } else { - // T2 encoding. - CHECK(IsUint<8>(immediate)); - opcode_shift = 11; - thumb_opcode = 6U /* 0b110 */; - rd_shift = 8; - rn_shift = 8; - } - } - break; - - case SUB: - if (so.IsRegister()) { - // T1. - Register rm = so.GetRegister(); - DCHECK(!IsHighRegister(rd)); - DCHECK(!IsHighRegister(rn)); - DCHECK(!IsHighRegister(rm)); - // Sets condition codes if and only if outside IT block, - // check that it complies with set_cc. - DCHECK((cond == AL) ? set_cc != kCcKeep : set_cc != kCcSet); - opcode_shift = 9; - thumb_opcode = 13U /* 0b01101 */; - immediate = static_cast<uint32_t>(rm); - use_immediate = true; - immediate_shift = 6; - } else { - if (rd == SP && rn == SP) { - // SUB sp, sp, #imm - dp_opcode = 2U /* 0b10 */; - thumb_opcode = 0x61 /* 0b1100001 */; - opcode_shift = 7; - CHECK(IsUint<9>(immediate)); - CHECK_ALIGNED(immediate, 4); - - // Remove rd and rn from instruction by orring it with immed and clearing bits. - rn = R0; - rd = R0; - rd_shift = 0; - rn_shift = 0; - immediate >>= 2; - } else if (rn != rd) { - // Must use T1. - CHECK(IsUint<3>(immediate)); - opcode_shift = 9; - thumb_opcode = 15U /* 0b01111 */; - immediate_shift = 6; - } else { - // T2 encoding. - CHECK(IsUint<8>(immediate)); - opcode_shift = 11; - thumb_opcode = 7U /* 0b111 */; - rd_shift = 8; - rn_shift = 8; - } - } - break; - default: - LOG(FATAL) << "This opcode is not an ADD or SUB: " << opcode; - UNREACHABLE(); - } - - int16_t encoding = dp_opcode << 14 | - (thumb_opcode << opcode_shift) | - rd << rd_shift | - rn << rn_shift | - (use_immediate ? (immediate << immediate_shift) : 0); - - Emit16(encoding); -} - - -void Thumb2Assembler::EmitDataProcessing(Condition cond, - Opcode opcode, - SetCc set_cc, - Register rn, - Register rd, - const ShifterOperand& so) { - CHECK_NE(rd, kNoRegister); - CheckCondition(cond); - - if (Is32BitDataProcessing(cond, opcode, set_cc, rn, rd, so)) { - Emit32BitDataProcessing(cond, opcode, set_cc, rn, rd, so); - } else { - Emit16BitDataProcessing(cond, opcode, set_cc, rn, rd, so); - } -} - -void Thumb2Assembler::EmitShift(Register rd, - Register rm, - Shift shift, - uint8_t amount, - Condition cond, - SetCc set_cc) { - CHECK_LT(amount, (1 << 5)); - if ((IsHighRegister(rd) || IsHighRegister(rm) || shift == ROR || shift == RRX) || - ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) { - uint16_t opcode = 0; - switch (shift) { - case LSL: opcode = 0U /* 0b00 */; break; - case LSR: opcode = 1U /* 0b01 */; break; - case ASR: opcode = 2U /* 0b10 */; break; - case ROR: opcode = 3U /* 0b11 */; break; - case RRX: opcode = 3U /* 0b11 */; amount = 0; break; - default: - LOG(FATAL) << "Unsupported thumb2 shift opcode"; - UNREACHABLE(); - } - // 32 bit. - int32_t encoding = B31 | B30 | B29 | B27 | B25 | B22 | - 0xf << 16 | (set_cc == kCcSet ? B20 : 0); - uint32_t imm3 = amount >> 2; - uint32_t imm2 = amount & 3U /* 0b11 */; - encoding |= imm3 << 12 | imm2 << 6 | static_cast<int16_t>(rm) | - static_cast<int16_t>(rd) << 8 | opcode << 4; - Emit32(encoding); - } else { - // 16 bit shift - uint16_t opcode = 0; - switch (shift) { - case LSL: opcode = 0U /* 0b00 */; break; - case LSR: opcode = 1U /* 0b01 */; break; - case ASR: opcode = 2U /* 0b10 */; break; - default: - LOG(FATAL) << "Unsupported thumb2 shift opcode"; - UNREACHABLE(); - } - int16_t encoding = opcode << 11 | amount << 6 | static_cast<int16_t>(rm) << 3 | - static_cast<int16_t>(rd); - Emit16(encoding); - } -} - -void Thumb2Assembler::EmitShift(Register rd, - Register rn, - Shift shift, - Register rm, - Condition cond, - SetCc set_cc) { - CHECK_NE(shift, RRX); - bool must_be_32bit = false; - if (IsHighRegister(rd) || IsHighRegister(rm) || IsHighRegister(rn) || rd != rn || - ((cond == AL) ? set_cc == kCcKeep : set_cc == kCcSet)) { - must_be_32bit = true; - } - - if (must_be_32bit) { - uint16_t opcode = 0; - switch (shift) { - case LSL: opcode = 0U /* 0b00 */; break; - case LSR: opcode = 1U /* 0b01 */; break; - case ASR: opcode = 2U /* 0b10 */; break; - case ROR: opcode = 3U /* 0b11 */; break; - default: - LOG(FATAL) << "Unsupported thumb2 shift opcode"; - UNREACHABLE(); - } - // 32 bit. - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | - 0xf << 12 | (set_cc == kCcSet ? B20 : 0); - encoding |= static_cast<int16_t>(rn) << 16 | static_cast<int16_t>(rm) | - static_cast<int16_t>(rd) << 8 | opcode << 21; - Emit32(encoding); - } else { - uint16_t opcode = 0; - switch (shift) { - case LSL: opcode = 2U /* 0b0010 */; break; - case LSR: opcode = 3U /* 0b0011 */; break; - case ASR: opcode = 4U /* 0b0100 */; break; - case ROR: opcode = 7U /* 0b0111 */; break; - default: - LOG(FATAL) << "Unsupported thumb2 shift opcode"; - UNREACHABLE(); - } - int16_t encoding = B14 | opcode << 6 | static_cast<int16_t>(rm) << 3 | - static_cast<int16_t>(rd); - Emit16(encoding); - } -} - -inline size_t Thumb2Assembler::Fixup::SizeInBytes(Size size) { - switch (size) { - case kBranch16Bit: - return 2u; - case kBranch32Bit: - return 4u; - - case kCbxz16Bit: - return 2u; - case kCbxz32Bit: - return 4u; - case kCbxz48Bit: - return 6u; - - case kCodeAddr4KiB: - return 4u; - - case kLiteral1KiB: - return 2u; - case kLiteral4KiB: - return 4u; - case kLiteral64KiB: - return 8u; - case kLiteral1MiB: - return 10u; - case kLiteralFar: - return 14u; - - case kLiteralAddr1KiB: - return 2u; - case kLiteralAddr4KiB: - return 4u; - case kLiteralAddr64KiB: - return 6u; - case kLiteralAddrFar: - return 10u; - - case kLongOrFPLiteral1KiB: - return 4u; - case kLongOrFPLiteral64KiB: - return 10u; - case kLongOrFPLiteralFar: - return 14u; - } - LOG(FATAL) << "Unexpected size: " << static_cast<int>(size); - UNREACHABLE(); -} - -inline uint32_t Thumb2Assembler::Fixup::GetOriginalSizeInBytes() const { - return SizeInBytes(original_size_); -} - -inline uint32_t Thumb2Assembler::Fixup::GetSizeInBytes() const { - return SizeInBytes(size_); -} - -inline size_t Thumb2Assembler::Fixup::LiteralPoolPaddingSize(uint32_t current_code_size) { - // The code size must be a multiple of 2. - DCHECK_ALIGNED(current_code_size, 2); - // If it isn't a multiple of 4, we need to add a 2-byte padding before the literal pool. - return current_code_size & 2; -} - -inline int32_t Thumb2Assembler::Fixup::GetOffset(uint32_t current_code_size) const { - static constexpr int32_t int32_min = std::numeric_limits<int32_t>::min(); - static constexpr int32_t int32_max = std::numeric_limits<int32_t>::max(); - DCHECK_LE(target_, static_cast<uint32_t>(int32_max)); - DCHECK_LE(location_, static_cast<uint32_t>(int32_max)); - DCHECK_LE(adjustment_, static_cast<uint32_t>(int32_max)); - int32_t diff = static_cast<int32_t>(target_) - static_cast<int32_t>(location_); - if (target_ > location_) { - DCHECK_LE(adjustment_, static_cast<uint32_t>(int32_max - diff)); - diff += static_cast<int32_t>(adjustment_); - } else { - DCHECK_LE(int32_min + static_cast<int32_t>(adjustment_), diff); - diff -= static_cast<int32_t>(adjustment_); - } - // The default PC adjustment for Thumb2 is 4 bytes. - DCHECK_GE(diff, int32_min + 4); - diff -= 4; - // Add additional adjustment for instructions preceding the PC usage, padding - // before the literal pool and rounding down the PC for literal loads. - switch (GetSize()) { - case kBranch16Bit: - case kBranch32Bit: - break; - - case kCbxz16Bit: - break; - case kCbxz32Bit: - case kCbxz48Bit: - DCHECK_GE(diff, int32_min + 2); - diff -= 2; // Extra CMP Rn, #0, 16-bit. - break; - - case kCodeAddr4KiB: - // The ADR instruction rounds down the PC+4 to a multiple of 4, so if the PC - // isn't a multiple of 2, we need to adjust. - DCHECK_ALIGNED(diff, 2); - diff += location_ & 2; - // Add the Thumb mode bit. - diff += 1; - break; - - case kLiteral1KiB: - case kLiteral4KiB: - case kLongOrFPLiteral1KiB: - case kLiteralAddr1KiB: - case kLiteralAddr4KiB: - DCHECK(diff >= 0 || (GetSize() == kLiteral1KiB && diff == -2)); - diff += LiteralPoolPaddingSize(current_code_size); - // Load literal instructions round down the PC+4 to a multiple of 4, so if the PC - // isn't a multiple of 2, we need to adjust. Since we already adjusted for the target - // being aligned, current PC alignment can be inferred from diff. - DCHECK_ALIGNED(diff, 2); - diff = diff + (diff & 2); - DCHECK_GE(diff, 0); - break; - case kLiteral64KiB: - case kLiteral1MiB: - case kLongOrFPLiteral64KiB: - case kLiteralAddr64KiB: - DCHECK_GE(diff, 4); // The target must be at least 4 bytes after the ADD rX, PC. - diff -= 4; // One extra 32-bit MOV. - diff += LiteralPoolPaddingSize(current_code_size); - break; - case kLiteralFar: - case kLongOrFPLiteralFar: - case kLiteralAddrFar: - DCHECK_GE(diff, 8); // The target must be at least 4 bytes after the ADD rX, PC. - diff -= 8; // Extra MOVW+MOVT; both 32-bit. - diff += LiteralPoolPaddingSize(current_code_size); - break; - } - return diff; -} - -inline size_t Thumb2Assembler::Fixup::IncreaseSize(Size new_size) { - DCHECK_NE(target_, kUnresolved); - Size old_size = size_; - size_ = new_size; - DCHECK_GT(SizeInBytes(new_size), SizeInBytes(old_size)); - size_t adjustment = SizeInBytes(new_size) - SizeInBytes(old_size); - if (target_ > location_) { - adjustment_ += adjustment; - } - return adjustment; -} - -bool Thumb2Assembler::Fixup::IsCandidateForEmitEarly() const { - DCHECK(size_ == original_size_); - if (target_ == kUnresolved) { - return false; - } - // GetOffset() does not depend on current_code_size for branches, only for literals. - constexpr uint32_t current_code_size = 0u; - switch (GetSize()) { - case kBranch16Bit: - return IsInt(cond_ != AL ? 9 : 12, GetOffset(current_code_size)); - case kBranch32Bit: - // We don't support conditional branches beyond +-1MiB - // or unconditional branches beyond +-16MiB. - return true; - - case kCbxz16Bit: - return IsUint<7>(GetOffset(current_code_size)); - case kCbxz32Bit: - return IsInt<9>(GetOffset(current_code_size)); - case kCbxz48Bit: - // We don't support conditional branches beyond +-1MiB. - return true; - - case kCodeAddr4KiB: - // ADR uses the aligned PC and as such the offset cannot be calculated early. - return false; - - case kLiteral1KiB: - case kLiteral4KiB: - case kLiteral64KiB: - case kLiteral1MiB: - case kLiteralFar: - case kLiteralAddr1KiB: - case kLiteralAddr4KiB: - case kLiteralAddr64KiB: - case kLiteralAddrFar: - case kLongOrFPLiteral1KiB: - case kLongOrFPLiteral64KiB: - case kLongOrFPLiteralFar: - return false; - } -} - -uint32_t Thumb2Assembler::Fixup::AdjustSizeIfNeeded(uint32_t current_code_size) { - uint32_t old_code_size = current_code_size; - switch (GetSize()) { - case kBranch16Bit: - if (IsInt(cond_ != AL ? 9 : 12, GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kBranch32Bit); - FALLTHROUGH_INTENDED; - case kBranch32Bit: - // We don't support conditional branches beyond +-1MiB - // or unconditional branches beyond +-16MiB. - break; - - case kCbxz16Bit: - if (IsUint<7>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kCbxz32Bit); - FALLTHROUGH_INTENDED; - case kCbxz32Bit: - if (IsInt<9>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kCbxz48Bit); - FALLTHROUGH_INTENDED; - case kCbxz48Bit: - // We don't support conditional branches beyond +-1MiB. - break; - - case kCodeAddr4KiB: - // We don't support Code address ADR beyond +4KiB. - break; - - case kLiteral1KiB: - DCHECK(!IsHighRegister(rn_)); - if (IsUint<10>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteral4KiB); - FALLTHROUGH_INTENDED; - case kLiteral4KiB: - if (IsUint<12>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteral64KiB); - FALLTHROUGH_INTENDED; - case kLiteral64KiB: - // Can't handle high register which we can encounter by fall-through from kLiteral4KiB. - if (!IsHighRegister(rn_) && IsUint<16>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteral1MiB); - FALLTHROUGH_INTENDED; - case kLiteral1MiB: - if (IsUint<20>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteralFar); - FALLTHROUGH_INTENDED; - case kLiteralFar: - // This encoding can reach any target. - break; - - case kLiteralAddr1KiB: - DCHECK(!IsHighRegister(rn_)); - if (IsUint<10>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteralAddr4KiB); - FALLTHROUGH_INTENDED; - case kLiteralAddr4KiB: - if (IsUint<12>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteralAddr64KiB); - FALLTHROUGH_INTENDED; - case kLiteralAddr64KiB: - if (IsUint<16>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLiteralAddrFar); - FALLTHROUGH_INTENDED; - case kLiteralAddrFar: - // This encoding can reach any target. - break; - - case kLongOrFPLiteral1KiB: - if (IsUint<10>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLongOrFPLiteral64KiB); - FALLTHROUGH_INTENDED; - case kLongOrFPLiteral64KiB: - if (IsUint<16>(GetOffset(current_code_size))) { - break; - } - current_code_size += IncreaseSize(kLongOrFPLiteralFar); - FALLTHROUGH_INTENDED; - case kLongOrFPLiteralFar: - // This encoding can reach any target. - break; - } - return current_code_size - old_code_size; -} - -void Thumb2Assembler::Fixup::Emit(uint32_t emit_location, - AssemblerBuffer* buffer, - uint32_t code_size) const { - switch (GetSize()) { - case kBranch16Bit: { - DCHECK(type_ == kUnconditional || type_ == kConditional); - DCHECK_EQ(type_ == kConditional, cond_ != AL); - int16_t encoding = BEncoding16(GetOffset(code_size), cond_); - buffer->Store<int16_t>(emit_location, encoding); - break; - } - case kBranch32Bit: { - DCHECK(type_ == kConditional || type_ == kUnconditional || - type_ == kUnconditionalLink || type_ == kUnconditionalLinkX); - DCHECK_EQ(type_ == kConditional, cond_ != AL); - int32_t encoding = BEncoding32(GetOffset(code_size), cond_); - if (type_ == kUnconditionalLink) { - DCHECK_NE(encoding & B12, 0); - encoding |= B14; - } else if (type_ == kUnconditionalLinkX) { - DCHECK_NE(encoding & B12, 0); - encoding ^= B14 | B12; - } - buffer->Store<int16_t>(emit_location, encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff)); - break; - } - - case kCbxz16Bit: { - DCHECK(type_ == kCompareAndBranchXZero); - int16_t encoding = CbxzEncoding16(rn_, GetOffset(code_size), cond_); - buffer->Store<int16_t>(emit_location, encoding); - break; - } - case kCbxz32Bit: { - DCHECK(type_ == kCompareAndBranchXZero); - DCHECK(cond_ == EQ || cond_ == NE); - int16_t cmp_encoding = CmpRnImm8Encoding16(rn_, 0); - int16_t b_encoding = BEncoding16(GetOffset(code_size), cond_); - buffer->Store<int16_t>(emit_location, cmp_encoding); - buffer->Store<int16_t>(emit_location + 2, b_encoding); - break; - } - case kCbxz48Bit: { - DCHECK(type_ == kCompareAndBranchXZero); - DCHECK(cond_ == EQ || cond_ == NE); - int16_t cmp_encoding = CmpRnImm8Encoding16(rn_, 0); - int32_t b_encoding = BEncoding32(GetOffset(code_size), cond_); - buffer->Store<int16_t>(emit_location, cmp_encoding); - buffer->Store<int16_t>(emit_location + 2u, b_encoding >> 16); - buffer->Store<int16_t>(emit_location + 4u, static_cast<int16_t>(b_encoding & 0xffff)); - break; - } - - case kCodeAddr4KiB: { - DCHECK(type_ == kLoadCodeAddr); - int32_t encoding = AdrEncoding32(rn_, GetOffset(code_size)); - buffer->Store<int16_t>(emit_location, encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff)); - break; - } - - case kLiteral1KiB: { - DCHECK(type_ == kLoadLiteralNarrow); - int16_t encoding = LdrLitEncoding16(rn_, GetOffset(code_size)); - buffer->Store<int16_t>(emit_location, encoding); - break; - } - case kLiteral4KiB: { - DCHECK(type_ == kLoadLiteralNarrow); - // GetOffset() uses PC+4 but load literal uses AlignDown(PC+4, 4). Adjust offset accordingly. - int32_t encoding = LdrLitEncoding32(rn_, GetOffset(code_size)); - buffer->Store<int16_t>(emit_location, encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff)); - break; - } - case kLiteral64KiB: { - DCHECK(type_ == kLoadLiteralNarrow); - int32_t mov_encoding = MovwEncoding32(rn_, GetOffset(code_size)); - int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC); - int16_t ldr_encoding = LdrRtRnImm5Encoding16(rn_, rn_, 0); - buffer->Store<int16_t>(location_, mov_encoding >> 16); - buffer->Store<int16_t>(location_ + 2u, static_cast<int16_t>(mov_encoding & 0xffff)); - buffer->Store<int16_t>(location_ + 4u, add_pc_encoding); - buffer->Store<int16_t>(location_ + 6u, ldr_encoding); - break; - } - case kLiteral1MiB: { - DCHECK(type_ == kLoadLiteralNarrow); - int32_t offset = GetOffset(code_size); - int32_t mov_encoding = MovModImmEncoding32(rn_, offset & ~0xfff); - int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC); - int32_t ldr_encoding = LdrRtRnImm12Encoding(rn_, rn_, offset & 0xfff); - buffer->Store<int16_t>(emit_location, mov_encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(mov_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 4u, add_pc_encoding); - buffer->Store<int16_t>(emit_location + 6u, ldr_encoding >> 16); - buffer->Store<int16_t>(emit_location + 8u, static_cast<int16_t>(ldr_encoding & 0xffff)); - break; - } - case kLiteralFar: { - DCHECK(type_ == kLoadLiteralNarrow); - int32_t offset = GetOffset(code_size); - int32_t movw_encoding = MovwEncoding32(rn_, offset & 0xffff); - int32_t movt_encoding = MovtEncoding32(rn_, offset & ~0xffff); - int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC); - int32_t ldr_encoding = LdrRtRnImm12Encoding(rn_, rn_, 0); - buffer->Store<int16_t>(emit_location, movw_encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(movw_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 4u, movt_encoding >> 16); - buffer->Store<int16_t>(emit_location + 6u, static_cast<int16_t>(movt_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 8u, add_pc_encoding); - buffer->Store<int16_t>(emit_location + 10u, ldr_encoding >> 16); - buffer->Store<int16_t>(emit_location + 12u, static_cast<int16_t>(ldr_encoding & 0xffff)); - break; - } - - case kLiteralAddr1KiB: { - DCHECK(type_ == kLoadLiteralAddr); - int16_t encoding = AdrEncoding16(rn_, GetOffset(code_size)); - buffer->Store<int16_t>(emit_location, encoding); - break; - } - case kLiteralAddr4KiB: { - DCHECK(type_ == kLoadLiteralAddr); - int32_t encoding = AdrEncoding32(rn_, GetOffset(code_size)); - buffer->Store<int16_t>(emit_location, encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff)); - break; - } - case kLiteralAddr64KiB: { - DCHECK(type_ == kLoadLiteralAddr); - int32_t mov_encoding = MovwEncoding32(rn_, GetOffset(code_size)); - int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC); - buffer->Store<int16_t>(emit_location, mov_encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(mov_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 4u, add_pc_encoding); - break; - } - case kLiteralAddrFar: { - DCHECK(type_ == kLoadLiteralAddr); - int32_t offset = GetOffset(code_size); - int32_t movw_encoding = MovwEncoding32(rn_, offset & 0xffff); - int32_t movt_encoding = MovtEncoding32(rn_, offset & ~0xffff); - int16_t add_pc_encoding = AddRdnRmEncoding16(rn_, PC); - buffer->Store<int16_t>(emit_location, movw_encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(movw_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 4u, movt_encoding >> 16); - buffer->Store<int16_t>(emit_location + 6u, static_cast<int16_t>(movt_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 8u, add_pc_encoding); - break; - } - - case kLongOrFPLiteral1KiB: { - int32_t encoding = LoadWideOrFpEncoding(PC, GetOffset(code_size)); // DCHECKs type_. - buffer->Store<int16_t>(emit_location, encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(encoding & 0xffff)); - break; - } - case kLongOrFPLiteral64KiB: { - int32_t mov_encoding = MovwEncoding32(IP, GetOffset(code_size)); - int16_t add_pc_encoding = AddRdnRmEncoding16(IP, PC); - int32_t ldr_encoding = LoadWideOrFpEncoding(IP, 0u); // DCHECKs type_. - buffer->Store<int16_t>(emit_location, mov_encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(mov_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 4u, add_pc_encoding); - buffer->Store<int16_t>(emit_location + 6u, ldr_encoding >> 16); - buffer->Store<int16_t>(emit_location + 8u, static_cast<int16_t>(ldr_encoding & 0xffff)); - break; - } - case kLongOrFPLiteralFar: { - int32_t offset = GetOffset(code_size); - int32_t movw_encoding = MovwEncoding32(IP, offset & 0xffff); - int32_t movt_encoding = MovtEncoding32(IP, offset & ~0xffff); - int16_t add_pc_encoding = AddRdnRmEncoding16(IP, PC); - int32_t ldr_encoding = LoadWideOrFpEncoding(IP, 0); // DCHECKs type_. - buffer->Store<int16_t>(emit_location, movw_encoding >> 16); - buffer->Store<int16_t>(emit_location + 2u, static_cast<int16_t>(movw_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 4u, movt_encoding >> 16); - buffer->Store<int16_t>(emit_location + 6u, static_cast<int16_t>(movt_encoding & 0xffff)); - buffer->Store<int16_t>(emit_location + 8u, add_pc_encoding); - buffer->Store<int16_t>(emit_location + 10u, ldr_encoding >> 16); - buffer->Store<int16_t>(emit_location + 12u, static_cast<int16_t>(ldr_encoding & 0xffff)); - break; - } - } -} - -uint16_t Thumb2Assembler::EmitCompareAndBranch(Register rn, uint16_t prev, bool n) { - CHECK(IsLowRegister(rn)); - uint32_t location = buffer_.Size(); - - // This is always unresolved as it must be a forward branch. - Emit16(prev); // Previous link. - return AddFixup(Fixup::CompareAndBranch(location, rn, n ? NE : EQ)); -} - - -// NOTE: this only support immediate offsets, not [rx,ry]. -// TODO: support [rx,ry] instructions. -void Thumb2Assembler::EmitLoadStore(Condition cond, - bool load, - bool byte, - bool half, - bool is_signed, - Register rd, - const Address& ad) { - CHECK_NE(rd, kNoRegister); - CheckCondition(cond); - bool must_be_32bit = force_32bit_; - if (IsHighRegister(rd)) { - must_be_32bit = true; - } - - Register rn = ad.GetRegister(); - if (IsHighRegister(rn) && (byte || half || (rn != SP && rn != PC))) { - must_be_32bit = true; - } - - if (is_signed || ad.GetOffset() < 0 || ad.GetMode() != Address::Offset) { - must_be_32bit = true; - } - - if (ad.IsImmediate()) { - // Immediate offset - int32_t offset = ad.GetOffset(); - - if (byte) { - // 5 bit offset, no shift. - if ((offset & ~0x1f) != 0) { - must_be_32bit = true; - } - } else if (half) { - // 5 bit offset, shifted by 1. - if ((offset & ~(0x1f << 1)) != 0) { - must_be_32bit = true; - } - } else if (rn == SP || rn == PC) { - // The 16 bit SP/PC relative instruction can only have an (imm8 << 2) offset. - if ((offset & ~(0xff << 2)) != 0) { - must_be_32bit = true; - } - } else { - // 5 bit offset, shifted by 2. - if ((offset & ~(0x1f << 2)) != 0) { - must_be_32bit = true; - } - } - - if (must_be_32bit) { - int32_t encoding = B31 | B30 | B29 | B28 | B27 | - (load ? B20 : 0) | - (is_signed ? B24 : 0) | - static_cast<uint32_t>(rd) << 12 | - ad.encodingThumb(true) | - (byte ? 0 : half ? B21 : B22); - Emit32(encoding); - } else { - // 16 bit thumb1. - uint8_t opA = 0; - bool sp_or_pc_relative = false; - - if (byte) { - opA = 7U /* 0b0111 */; - } else if (half) { - opA = 8U /* 0b1000 */; - } else { - if (rn == SP) { - opA = 9U /* 0b1001 */; - sp_or_pc_relative = true; - } else if (rn == PC) { - opA = 4U; - sp_or_pc_relative = true; - } else { - opA = 6U /* 0b0110 */; - } - } - int16_t encoding = opA << 12 | - (load ? B11 : 0); - - CHECK_GE(offset, 0); - if (sp_or_pc_relative) { - // SP relative, 10 bit offset. - CHECK_LT(offset, (1 << 10)); - CHECK_ALIGNED(offset, 4); - encoding |= rd << 8 | offset >> 2; - } else { - // No SP relative. The offset is shifted right depending on - // the size of the load/store. - encoding |= static_cast<uint32_t>(rd); - - if (byte) { - // 5 bit offset, no shift. - CHECK_LT(offset, (1 << 5)); - } else if (half) { - // 6 bit offset, shifted by 1. - CHECK_LT(offset, (1 << 6)); - CHECK_ALIGNED(offset, 2); - offset >>= 1; - } else { - // 7 bit offset, shifted by 2. - CHECK_LT(offset, (1 << 7)); - CHECK_ALIGNED(offset, 4); - offset >>= 2; - } - encoding |= rn << 3 | offset << 6; - } - - Emit16(encoding); - } - } else { - // Register shift. - CHECK_NE(ad.GetRegister(), PC); - if (ad.GetShiftCount() != 0) { - // If there is a shift count this must be 32 bit. - must_be_32bit = true; - } else if (IsHighRegister(ad.GetRegisterOffset())) { - must_be_32bit = true; - } - - if (must_be_32bit) { - int32_t encoding = 0x1f << 27 | (load ? B20 : 0) | static_cast<uint32_t>(rd) << 12 | - ad.encodingThumb(true); - if (half) { - encoding |= B21; - } else if (!byte) { - encoding |= B22; - } - if (load && is_signed && (byte || half)) { - encoding |= B24; - } - Emit32(encoding); - } else { - // 16 bit register offset. - int32_t encoding = B14 | B12 | (load ? B11 : 0) | static_cast<uint32_t>(rd) | - ad.encodingThumb(false); - if (byte) { - encoding |= B10; - } else if (half) { - encoding |= B9; - } - Emit16(encoding); - } - } -} - - -void Thumb2Assembler::EmitMultiMemOp(Condition cond, - BlockAddressMode bam, - bool load, - Register base, - RegList regs) { - CHECK_NE(base, kNoRegister); - CheckCondition(cond); - bool must_be_32bit = force_32bit_; - - if (!must_be_32bit && base == SP && bam == (load ? IA_W : DB_W) && - (regs & 0xff00 & ~(1 << (load ? PC : LR))) == 0) { - // Use 16-bit PUSH/POP. - int16_t encoding = B15 | B13 | B12 | (load ? B11 : 0) | B10 | - ((regs & (1 << (load ? PC : LR))) != 0 ? B8 : 0) | (regs & 0x00ff); - Emit16(encoding); - return; - } - - if ((regs & 0xff00) != 0) { - must_be_32bit = true; - } - - bool w_bit = bam == IA_W || bam == DB_W || bam == DA_W || bam == IB_W; - // 16 bit always uses writeback. - if (!w_bit) { - must_be_32bit = true; - } - - if (must_be_32bit) { - uint32_t op = 0; - switch (bam) { - case IA: - case IA_W: - op = 1U /* 0b01 */; - break; - case DB: - case DB_W: - op = 2U /* 0b10 */; - break; - case DA: - case IB: - case DA_W: - case IB_W: - LOG(FATAL) << "LDM/STM mode not supported on thumb: " << bam; - UNREACHABLE(); - } - if (load) { - // Cannot have SP in the list. - CHECK_EQ((regs & (1 << SP)), 0); - } else { - // Cannot have PC or SP in the list. - CHECK_EQ((regs & (1 << PC | 1 << SP)), 0); - } - int32_t encoding = B31 | B30 | B29 | B27 | - (op << 23) | - (load ? B20 : 0) | - base << 16 | - regs | - (w_bit << 21); - Emit32(encoding); - } else { - int16_t encoding = B15 | B14 | - (load ? B11 : 0) | - base << 8 | - regs; - Emit16(encoding); - } -} - -void Thumb2Assembler::EmitBranch(Condition cond, Label* label, bool link, bool x) { - bool use32bit = IsForced32Bit() || !CanRelocateBranches(); - uint32_t pc = buffer_.Size(); - Fixup::Type branch_type; - if (cond == AL) { - if (link) { - use32bit = true; - if (x) { - branch_type = Fixup::kUnconditionalLinkX; // BLX. - } else { - branch_type = Fixup::kUnconditionalLink; // BX. - } - } else { - branch_type = Fixup::kUnconditional; // B. - // The T2 encoding offset is `SignExtend(imm11:'0', 32)` and there is a PC adjustment of 4. - static constexpr size_t kMaxT2BackwardDistance = (1u << 11) - 4u; - if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT2BackwardDistance) { - use32bit = true; - } - } - } else { - branch_type = Fixup::kConditional; // B<cond>. - // The T1 encoding offset is `SignExtend(imm8:'0', 32)` and there is a PC adjustment of 4. - static constexpr size_t kMaxT1BackwardDistance = (1u << 8) - 4u; - if (!use32bit && label->IsBound() && pc - label->Position() > kMaxT1BackwardDistance) { - use32bit = true; - } - } - - Fixup::Size size = use32bit ? Fixup::kBranch32Bit : Fixup::kBranch16Bit; - FixupId branch_id = AddFixup(Fixup::Branch(pc, branch_type, size, cond)); - - if (label->IsBound()) { - // The branch is to a bound label which means that it's a backwards branch. - GetFixup(branch_id)->Resolve(label->Position()); - Emit16(0); - } else { - // Branch target is an unbound label. Add it to a singly-linked list maintained within - // the code with the label serving as the head. - Emit16(static_cast<uint16_t>(label->position_)); - label->LinkTo(branch_id); - } - - if (use32bit) { - Emit16(0); - } - DCHECK_EQ(buffer_.Size() - pc, GetFixup(branch_id)->GetSizeInBytes()); -} - - -void Thumb2Assembler::Emit32Miscellaneous(uint8_t op1, - uint8_t op2, - uint32_t rest_encoding) { - int32_t encoding = B31 | B30 | B29 | B28 | B27 | B25 | B23 | - op1 << 20 | - 0xf << 12 | - B7 | - op2 << 4 | - rest_encoding; - Emit32(encoding); -} - - -void Thumb2Assembler::Emit16Miscellaneous(uint32_t rest_encoding) { - int16_t encoding = B15 | B13 | B12 | - rest_encoding; - Emit16(encoding); -} - -void Thumb2Assembler::clz(Register rd, Register rm, Condition cond) { - CHECK_NE(rd, kNoRegister); - CHECK_NE(rm, kNoRegister); - CheckCondition(cond); - CHECK_NE(rd, PC); - CHECK_NE(rm, PC); - int32_t encoding = - static_cast<uint32_t>(rm) << 16 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(rm); - Emit32Miscellaneous(0b11, 0b00, encoding); -} - - -void Thumb2Assembler::movw(Register rd, uint16_t imm16, Condition cond) { - CheckCondition(cond); - // Always 32 bits, encoding T3. (Other encondings are called MOV, not MOVW.) - uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */; - uint32_t i = (imm16 >> 11) & 1U /* 0b1 */; - uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */; - uint32_t imm8 = imm16 & 0xff; - int32_t encoding = B31 | B30 | B29 | B28 | - B25 | B22 | - static_cast<uint32_t>(rd) << 8 | - i << 26 | - imm4 << 16 | - imm3 << 12 | - imm8; - Emit32(encoding); -} - - -void Thumb2Assembler::movt(Register rd, uint16_t imm16, Condition cond) { - CheckCondition(cond); - // Always 32 bits. - uint32_t imm4 = (imm16 >> 12) & 15U /* 0b1111 */; - uint32_t i = (imm16 >> 11) & 1U /* 0b1 */; - uint32_t imm3 = (imm16 >> 8) & 7U /* 0b111 */; - uint32_t imm8 = imm16 & 0xff; - int32_t encoding = B31 | B30 | B29 | B28 | - B25 | B23 | B22 | - static_cast<uint32_t>(rd) << 8 | - i << 26 | - imm4 << 16 | - imm3 << 12 | - imm8; - Emit32(encoding); -} - - -void Thumb2Assembler::rbit(Register rd, Register rm, Condition cond) { - CHECK_NE(rd, kNoRegister); - CHECK_NE(rm, kNoRegister); - CheckCondition(cond); - CHECK_NE(rd, PC); - CHECK_NE(rm, PC); - CHECK_NE(rd, SP); - CHECK_NE(rm, SP); - int32_t encoding = - static_cast<uint32_t>(rm) << 16 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(rm); - - Emit32Miscellaneous(0b01, 0b10, encoding); -} - - -void Thumb2Assembler::EmitReverseBytes(Register rd, Register rm, - uint32_t op) { - CHECK_NE(rd, kNoRegister); - CHECK_NE(rm, kNoRegister); - CHECK_NE(rd, PC); - CHECK_NE(rm, PC); - CHECK_NE(rd, SP); - CHECK_NE(rm, SP); - - if (!IsHighRegister(rd) && !IsHighRegister(rm) && !force_32bit_) { - uint16_t t1_op = B11 | B9 | (op << 6); - int16_t encoding = t1_op | - static_cast<uint16_t>(rm) << 3 | - static_cast<uint16_t>(rd); - Emit16Miscellaneous(encoding); - } else { - int32_t encoding = - static_cast<uint32_t>(rm) << 16 | - static_cast<uint32_t>(rd) << 8 | - static_cast<uint32_t>(rm); - Emit32Miscellaneous(0b01, op, encoding); - } -} - - -void Thumb2Assembler::rev(Register rd, Register rm, Condition cond) { - CheckCondition(cond); - EmitReverseBytes(rd, rm, 0b00); -} - - -void Thumb2Assembler::rev16(Register rd, Register rm, Condition cond) { - CheckCondition(cond); - EmitReverseBytes(rd, rm, 0b01); -} - - -void Thumb2Assembler::revsh(Register rd, Register rm, Condition cond) { - CheckCondition(cond); - EmitReverseBytes(rd, rm, 0b11); -} - - -void Thumb2Assembler::ldrex(Register rt, Register rn, uint16_t imm, Condition cond) { - CHECK_NE(rn, kNoRegister); - CHECK_NE(rt, kNoRegister); - CheckCondition(cond); - CHECK_LT(imm, (1u << 10)); - - int32_t encoding = B31 | B30 | B29 | B27 | B22 | B20 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rt) << 12 | - 0xf << 8 | - imm >> 2; - Emit32(encoding); -} - - -void Thumb2Assembler::ldrex(Register rt, Register rn, Condition cond) { - ldrex(rt, rn, 0, cond); -} - - -void Thumb2Assembler::strex(Register rd, - Register rt, - Register rn, - uint16_t imm, - Condition cond) { - CHECK_NE(rn, kNoRegister); - CHECK_NE(rd, kNoRegister); - CHECK_NE(rt, kNoRegister); - CheckCondition(cond); - CHECK_LT(imm, (1u << 10)); - - int32_t encoding = B31 | B30 | B29 | B27 | B22 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rt) << 12 | - static_cast<uint32_t>(rd) << 8 | - imm >> 2; - Emit32(encoding); -} - - -void Thumb2Assembler::ldrexd(Register rt, Register rt2, Register rn, Condition cond) { - CHECK_NE(rn, kNoRegister); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt2, kNoRegister); - CHECK_NE(rt, rt2); - CheckCondition(cond); - - int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 | B20 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rt) << 12 | - static_cast<uint32_t>(rt2) << 8 | - B6 | B5 | B4 | B3 | B2 | B1 | B0; - Emit32(encoding); -} - - -void Thumb2Assembler::strex(Register rd, - Register rt, - Register rn, - Condition cond) { - strex(rd, rt, rn, 0, cond); -} - - -void Thumb2Assembler::strexd(Register rd, Register rt, Register rt2, Register rn, Condition cond) { - CHECK_NE(rd, kNoRegister); - CHECK_NE(rn, kNoRegister); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt2, kNoRegister); - CHECK_NE(rt, rt2); - CHECK_NE(rd, rt); - CHECK_NE(rd, rt2); - CheckCondition(cond); - - int32_t encoding = B31 | B30 | B29 | B27 | B23 | B22 | - static_cast<uint32_t>(rn) << 16 | - static_cast<uint32_t>(rt) << 12 | - static_cast<uint32_t>(rt2) << 8 | - B6 | B5 | B4 | - static_cast<uint32_t>(rd); - Emit32(encoding); -} - - -void Thumb2Assembler::clrex(Condition cond) { - CheckCondition(cond); - int32_t encoding = B31 | B30 | B29 | B28 | B25 | B24 | B23 | - B21 | B20 | - 0xf << 16 | - B15 | - 0xf << 8 | - B5 | - 0xf; - Emit32(encoding); -} - - -void Thumb2Assembler::nop(Condition cond) { - CheckCondition(cond); - uint16_t encoding = B15 | B13 | B12 | - B11 | B10 | B9 | B8; - Emit16(static_cast<int16_t>(encoding)); -} - - -void Thumb2Assembler::vmovsr(SRegister sn, Register rt, Condition cond) { - CHECK_NE(sn, kNoSRegister); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt, SP); - CHECK_NE(rt, PC); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | - ((static_cast<int32_t>(sn) >> 1)*B16) | - (static_cast<int32_t>(rt)*B12) | B11 | B9 | - ((static_cast<int32_t>(sn) & 1)*B7) | B4; - Emit32(encoding); -} - - -void Thumb2Assembler::vmovrs(Register rt, SRegister sn, Condition cond) { - CHECK_NE(sn, kNoSRegister); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt, SP); - CHECK_NE(rt, PC); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | B20 | - ((static_cast<int32_t>(sn) >> 1)*B16) | - (static_cast<int32_t>(rt)*B12) | B11 | B9 | - ((static_cast<int32_t>(sn) & 1)*B7) | B4; - Emit32(encoding); -} - - -void Thumb2Assembler::vmovsrr(SRegister sm, Register rt, Register rt2, - Condition cond) { - CHECK_NE(sm, kNoSRegister); - CHECK_NE(sm, S31); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt, SP); - CHECK_NE(rt, PC); - CHECK_NE(rt2, kNoRegister); - CHECK_NE(rt2, SP); - CHECK_NE(rt2, PC); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B22 | - (static_cast<int32_t>(rt2)*B16) | - (static_cast<int32_t>(rt)*B12) | B11 | B9 | - ((static_cast<int32_t>(sm) & 1)*B5) | B4 | - (static_cast<int32_t>(sm) >> 1); - Emit32(encoding); -} - - -void Thumb2Assembler::vmovrrs(Register rt, Register rt2, SRegister sm, - Condition cond) { - CHECK_NE(sm, kNoSRegister); - CHECK_NE(sm, S31); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt, SP); - CHECK_NE(rt, PC); - CHECK_NE(rt2, kNoRegister); - CHECK_NE(rt2, SP); - CHECK_NE(rt2, PC); - CHECK_NE(rt, rt2); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B22 | B20 | - (static_cast<int32_t>(rt2)*B16) | - (static_cast<int32_t>(rt)*B12) | B11 | B9 | - ((static_cast<int32_t>(sm) & 1)*B5) | B4 | - (static_cast<int32_t>(sm) >> 1); - Emit32(encoding); -} - - -void Thumb2Assembler::vmovdrr(DRegister dm, Register rt, Register rt2, - Condition cond) { - CHECK_NE(dm, kNoDRegister); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt, SP); - CHECK_NE(rt, PC); - CHECK_NE(rt2, kNoRegister); - CHECK_NE(rt2, SP); - CHECK_NE(rt2, PC); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B22 | - (static_cast<int32_t>(rt2)*B16) | - (static_cast<int32_t>(rt)*B12) | B11 | B9 | B8 | - ((static_cast<int32_t>(dm) >> 4)*B5) | B4 | - (static_cast<int32_t>(dm) & 0xf); - Emit32(encoding); -} - - -void Thumb2Assembler::vmovrrd(Register rt, Register rt2, DRegister dm, - Condition cond) { - CHECK_NE(dm, kNoDRegister); - CHECK_NE(rt, kNoRegister); - CHECK_NE(rt, SP); - CHECK_NE(rt, PC); - CHECK_NE(rt2, kNoRegister); - CHECK_NE(rt2, SP); - CHECK_NE(rt2, PC); - CHECK_NE(rt, rt2); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B22 | B20 | - (static_cast<int32_t>(rt2)*B16) | - (static_cast<int32_t>(rt)*B12) | B11 | B9 | B8 | - ((static_cast<int32_t>(dm) >> 4)*B5) | B4 | - (static_cast<int32_t>(dm) & 0xf); - Emit32(encoding); -} - - -void Thumb2Assembler::vldrs(SRegister sd, const Address& ad, Condition cond) { - const Address& addr = static_cast<const Address&>(ad); - CHECK_NE(sd, kNoSRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B24 | B20 | - ((static_cast<int32_t>(sd) & 1)*B22) | - ((static_cast<int32_t>(sd) >> 1)*B12) | - B11 | B9 | addr.vencoding(); - Emit32(encoding); -} - - -void Thumb2Assembler::vstrs(SRegister sd, const Address& ad, Condition cond) { - const Address& addr = static_cast<const Address&>(ad); - CHECK_NE(static_cast<Register>(addr.encodingArm() & (0xf << kRnShift)), PC); - CHECK_NE(sd, kNoSRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B24 | - ((static_cast<int32_t>(sd) & 1)*B22) | - ((static_cast<int32_t>(sd) >> 1)*B12) | - B11 | B9 | addr.vencoding(); - Emit32(encoding); -} - - -void Thumb2Assembler::vldrd(DRegister dd, const Address& ad, Condition cond) { - const Address& addr = static_cast<const Address&>(ad); - CHECK_NE(dd, kNoDRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B24 | B20 | - ((static_cast<int32_t>(dd) >> 4)*B22) | - ((static_cast<int32_t>(dd) & 0xf)*B12) | - B11 | B9 | B8 | addr.vencoding(); - Emit32(encoding); -} - - -void Thumb2Assembler::vstrd(DRegister dd, const Address& ad, Condition cond) { - const Address& addr = static_cast<const Address&>(ad); - CHECK_NE(static_cast<Register>(addr.encodingArm() & (0xf << kRnShift)), PC); - CHECK_NE(dd, kNoDRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B24 | - ((static_cast<int32_t>(dd) >> 4)*B22) | - ((static_cast<int32_t>(dd) & 0xf)*B12) | - B11 | B9 | B8 | addr.vencoding(); - Emit32(encoding); -} - - -void Thumb2Assembler::vpushs(SRegister reg, int nregs, Condition cond) { - EmitVPushPop(static_cast<uint32_t>(reg), nregs, true, false, cond); -} - - -void Thumb2Assembler::vpushd(DRegister reg, int nregs, Condition cond) { - EmitVPushPop(static_cast<uint32_t>(reg), nregs, true, true, cond); -} - - -void Thumb2Assembler::vpops(SRegister reg, int nregs, Condition cond) { - EmitVPushPop(static_cast<uint32_t>(reg), nregs, false, false, cond); -} - - -void Thumb2Assembler::vpopd(DRegister reg, int nregs, Condition cond) { - EmitVPushPop(static_cast<uint32_t>(reg), nregs, false, true, cond); -} - - -void Thumb2Assembler::vldmiad(Register base_reg, DRegister reg, int nregs, Condition cond) { - int32_t rest = B23; - EmitVLdmOrStm(rest, - static_cast<uint32_t>(reg), - nregs, - base_reg, - /*is_load*/ true, - /*dbl*/ true, - cond); -} - - -void Thumb2Assembler::vstmiad(Register base_reg, DRegister reg, int nregs, Condition cond) { - int32_t rest = B23; - EmitVLdmOrStm(rest, - static_cast<uint32_t>(reg), - nregs, - base_reg, - /*is_load*/ false, - /*dbl*/ true, - cond); -} - - -void Thumb2Assembler::EmitVPushPop(uint32_t reg, int nregs, bool push, bool dbl, Condition cond) { - int32_t rest = B21 | (push ? B24 : B23); - EmitVLdmOrStm(rest, reg, nregs, SP, /*is_load*/ !push, dbl, cond); -} - - -void Thumb2Assembler::EmitVLdmOrStm(int32_t rest, - uint32_t reg, - int nregs, - Register rn, - bool is_load, - bool dbl, - Condition cond) { - CheckCondition(cond); - - DCHECK_GT(nregs, 0); - DCHECK_LE(reg + nregs, 32u); - DCHECK(!dbl || (nregs <= 16)); - - uint32_t D; - uint32_t Vd; - if (dbl) { - // Encoded as D:Vd. - D = (reg >> 4) & 1; - Vd = reg & 15U /* 0b1111 */; - } else { - // Encoded as Vd:D. - D = reg & 1; - Vd = (reg >> 1) & 15U /* 0b1111 */; - } - - int32_t encoding = rest | - 14U /* 0b1110 */ << 28 | - B27 | B26 | B11 | B9 | - (is_load ? B20 : 0) | - static_cast<int16_t>(rn) << 16 | - D << 22 | - Vd << 12 | - (dbl ? B8 : 0) | - nregs << (dbl ? 1 : 0); - - Emit32(encoding); -} - - -void Thumb2Assembler::EmitVFPsss(Condition cond, int32_t opcode, - SRegister sd, SRegister sn, SRegister sm) { - CHECK_NE(sd, kNoSRegister); - CHECK_NE(sn, kNoSRegister); - CHECK_NE(sm, kNoSRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | B11 | B9 | opcode | - ((static_cast<int32_t>(sd) & 1)*B22) | - ((static_cast<int32_t>(sn) >> 1)*B16) | - ((static_cast<int32_t>(sd) >> 1)*B12) | - ((static_cast<int32_t>(sn) & 1)*B7) | - ((static_cast<int32_t>(sm) & 1)*B5) | - (static_cast<int32_t>(sm) >> 1); - Emit32(encoding); -} - - -void Thumb2Assembler::EmitVFPddd(Condition cond, int32_t opcode, - DRegister dd, DRegister dn, DRegister dm) { - CHECK_NE(dd, kNoDRegister); - CHECK_NE(dn, kNoDRegister); - CHECK_NE(dm, kNoDRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | B11 | B9 | B8 | opcode | - ((static_cast<int32_t>(dd) >> 4)*B22) | - ((static_cast<int32_t>(dn) & 0xf)*B16) | - ((static_cast<int32_t>(dd) & 0xf)*B12) | - ((static_cast<int32_t>(dn) >> 4)*B7) | - ((static_cast<int32_t>(dm) >> 4)*B5) | - (static_cast<int32_t>(dm) & 0xf); - Emit32(encoding); -} - - -void Thumb2Assembler::EmitVFPsd(Condition cond, int32_t opcode, - SRegister sd, DRegister dm) { - CHECK_NE(sd, kNoSRegister); - CHECK_NE(dm, kNoDRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | B11 | B9 | opcode | - ((static_cast<int32_t>(sd) & 1)*B22) | - ((static_cast<int32_t>(sd) >> 1)*B12) | - ((static_cast<int32_t>(dm) >> 4)*B5) | - (static_cast<int32_t>(dm) & 0xf); - Emit32(encoding); -} - - -void Thumb2Assembler::EmitVFPds(Condition cond, int32_t opcode, - DRegister dd, SRegister sm) { - CHECK_NE(dd, kNoDRegister); - CHECK_NE(sm, kNoSRegister); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | B11 | B9 | opcode | - ((static_cast<int32_t>(dd) >> 4)*B22) | - ((static_cast<int32_t>(dd) & 0xf)*B12) | - ((static_cast<int32_t>(sm) & 1)*B5) | - (static_cast<int32_t>(sm) >> 1); - Emit32(encoding); -} - - -void Thumb2Assembler::vmstat(Condition cond) { // VMRS APSR_nzcv, FPSCR. - CHECK_NE(cond, kNoCondition); - CheckCondition(cond); - int32_t encoding = (static_cast<int32_t>(cond) << kConditionShift) | - B27 | B26 | B25 | B23 | B22 | B21 | B20 | B16 | - (static_cast<int32_t>(PC)*B12) | - B11 | B9 | B4; - Emit32(encoding); -} - -void Thumb2Assembler::vcntd(DRegister dd, DRegister dm) { - uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) | - ((static_cast<int32_t>(dd) >> 4) * B22) | - ((static_cast<uint32_t>(dd) & 0xf) * B12) | - (B10 | B8) | - ((static_cast<int32_t>(dm) >> 4) * B5) | - (static_cast<uint32_t>(dm) & 0xf); - - Emit32(encoding); -} - -void Thumb2Assembler::vpaddld(DRegister dd, DRegister dm, int32_t size, bool is_unsigned) { - CHECK(size == 8 || size == 16 || size == 32) << size; - uint32_t encoding = (B31 | B30 | B29 | B28 | B27 | B26 | B25 | B24 | B23 | B21 | B20) | - ((static_cast<uint32_t>(size >> 4) & 0x3) * B18) | - ((static_cast<int32_t>(dd) >> 4) * B22) | - ((static_cast<uint32_t>(dd) & 0xf) * B12) | - (B9) | - (is_unsigned ? B7 : 0) | - ((static_cast<int32_t>(dm) >> 4) * B5) | - (static_cast<uint32_t>(dm) & 0xf); - - Emit32(encoding); -} - -void Thumb2Assembler::svc(uint32_t imm8) { - CHECK(IsUint<8>(imm8)) << imm8; - int16_t encoding = B15 | B14 | B12 | - B11 | B10 | B9 | B8 | - imm8; - Emit16(encoding); -} - - -void Thumb2Assembler::bkpt(uint16_t imm8) { - CHECK(IsUint<8>(imm8)) << imm8; - int16_t encoding = B15 | B13 | B12 | - B11 | B10 | B9 | - imm8; - Emit16(encoding); -} - -// Convert the given IT state to a mask bit given bit 0 of the first -// condition and a shift position. -static uint8_t ToItMask(ItState s, uint8_t firstcond0, uint8_t shift) { - switch (s) { - case kItOmitted: return 1 << shift; - case kItThen: return firstcond0 << shift; - case kItElse: return !firstcond0 << shift; - } - return 0; -} - - -// Set the IT condition in the given position for the given state. This is used -// to check that conditional instructions match the preceding IT statement. -void Thumb2Assembler::SetItCondition(ItState s, Condition cond, uint8_t index) { - switch (s) { - case kItOmitted: it_conditions_[index] = AL; break; - case kItThen: it_conditions_[index] = cond; break; - case kItElse: - it_conditions_[index] = static_cast<Condition>(static_cast<uint8_t>(cond) ^ 1); - break; - } -} - - -void Thumb2Assembler::it(Condition firstcond, ItState i1, ItState i2, ItState i3) { - CheckCondition(AL); // Not allowed in IT block. - uint8_t firstcond0 = static_cast<uint8_t>(firstcond) & 1; - - // All conditions to AL. - for (uint8_t i = 0; i < 4; ++i) { - it_conditions_[i] = AL; - } - - SetItCondition(kItThen, firstcond, 0); - uint8_t mask = ToItMask(i1, firstcond0, 3); - SetItCondition(i1, firstcond, 1); - - if (i1 != kItOmitted) { - mask |= ToItMask(i2, firstcond0, 2); - SetItCondition(i2, firstcond, 2); - if (i2 != kItOmitted) { - mask |= ToItMask(i3, firstcond0, 1); - SetItCondition(i3, firstcond, 3); - if (i3 != kItOmitted) { - mask |= 1U /* 0b0001 */; - } - } - } - - // Start at first condition. - it_cond_index_ = 0; - next_condition_ = it_conditions_[0]; - uint16_t encoding = B15 | B13 | B12 | - B11 | B10 | B9 | B8 | - firstcond << 4 | - mask; - Emit16(encoding); -} - - -void Thumb2Assembler::cbz(Register rn, Label* label) { - CheckCondition(AL); - if (label->IsBound()) { - LOG(FATAL) << "cbz can only be used to branch forwards"; - UNREACHABLE(); - } else if (IsHighRegister(rn)) { - LOG(FATAL) << "cbz can only be used with low registers"; - UNREACHABLE(); - } else { - uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), false); - label->LinkTo(branchid); - } -} - - -void Thumb2Assembler::cbnz(Register rn, Label* label) { - CheckCondition(AL); - if (label->IsBound()) { - LOG(FATAL) << "cbnz can only be used to branch forwards"; - UNREACHABLE(); - } else if (IsHighRegister(rn)) { - LOG(FATAL) << "cbnz can only be used with low registers"; - UNREACHABLE(); - } else { - uint16_t branchid = EmitCompareAndBranch(rn, static_cast<uint16_t>(label->position_), true); - label->LinkTo(branchid); - } -} - - -void Thumb2Assembler::blx(Register rm, Condition cond) { - CHECK_NE(rm, kNoRegister); - CheckCondition(cond); - int16_t encoding = B14 | B10 | B9 | B8 | B7 | static_cast<int16_t>(rm) << 3; - Emit16(encoding); -} - - -void Thumb2Assembler::bx(Register rm, Condition cond) { - CHECK_NE(rm, kNoRegister); - CheckCondition(cond); - int16_t encoding = B14 | B10 | B9 | B8 | static_cast<int16_t>(rm) << 3; - Emit16(encoding); -} - - -void Thumb2Assembler::AdrCode(Register rt, Label* label) { - uint32_t pc = buffer_.Size(); - FixupId branch_id = AddFixup(Fixup::LoadCodeAddress(pc, rt)); - CHECK(!label->IsBound()); - // ADR target must be an unbound label. Add it to a singly-linked list maintained within - // the code with the label serving as the head. - Emit16(static_cast<uint16_t>(label->position_)); - label->LinkTo(branch_id); - Emit16(0); - DCHECK_EQ(buffer_.Size() - pc, GetFixup(branch_id)->GetSizeInBytes()); -} - - -void Thumb2Assembler::Push(Register rd, Condition cond) { - str(rd, Address(SP, -kRegisterSize, Address::PreIndex), cond); -} - - -void Thumb2Assembler::Pop(Register rd, Condition cond) { - ldr(rd, Address(SP, kRegisterSize, Address::PostIndex), cond); -} - - -void Thumb2Assembler::PushList(RegList regs, Condition cond) { - stm(DB_W, SP, regs, cond); -} - - -void Thumb2Assembler::PopList(RegList regs, Condition cond) { - ldm(IA_W, SP, regs, cond); -} - -void Thumb2Assembler::StoreList(RegList regs, size_t stack_offset) { - DCHECK_NE(regs, 0u); - DCHECK_EQ(regs & (1u << IP), 0u); - if (IsPowerOfTwo(regs)) { - Register reg = static_cast<Register>(CTZ(static_cast<uint32_t>(regs))); - str(reg, Address(SP, stack_offset)); - } else { - add(IP, SP, ShifterOperand(stack_offset)); - stm(IA, IP, regs); - } -} - -void Thumb2Assembler::LoadList(RegList regs, size_t stack_offset) { - DCHECK_NE(regs, 0u); - DCHECK_EQ(regs & (1u << IP), 0u); - if (IsPowerOfTwo(regs)) { - Register reg = static_cast<Register>(CTZ(static_cast<uint32_t>(regs))); - ldr(reg, Address(SP, stack_offset)); - } else { - Register lowest_reg = static_cast<Register>(CTZ(static_cast<uint32_t>(regs))); - add(lowest_reg, SP, ShifterOperand(stack_offset)); - ldm(IA, lowest_reg, regs); - } -} - -void Thumb2Assembler::Mov(Register rd, Register rm, Condition cond) { - if (cond != AL || rd != rm) { - mov(rd, ShifterOperand(rm), cond); - } -} - - -void Thumb2Assembler::Bind(Label* label) { - BindLabel(label, buffer_.Size()); - - // Try to emit some Fixups now to reduce the memory needed during the branch fixup later. - while (!fixups_.empty() && fixups_.back().IsCandidateForEmitEarly()) { - const Fixup& last_fixup = fixups_.back(); - // Fixups are ordered by location, so the candidate can surely be emitted if it is - // a forward branch. If it's a backward branch, it may go over any number of other - // fixups. We could check for any number of emit early candidates but we want this - // heuristics to be quick, so check just one. - uint32_t target = last_fixup.GetTarget(); - if (target < last_fixup.GetLocation() && - fixups_.size() >= 2u && - fixups_[fixups_.size() - 2u].GetLocation() >= target) { - const Fixup& prev_fixup = fixups_[fixups_.size() - 2u]; - if (!prev_fixup.IsCandidateForEmitEarly()) { - break; - } - uint32_t min_target = std::min(target, prev_fixup.GetTarget()); - if (fixups_.size() >= 3u && fixups_[fixups_.size() - 3u].GetLocation() >= min_target) { - break; - } - } - last_fixup.Emit(last_fixup.GetLocation(), &buffer_, buffer_.Size()); - fixups_.pop_back(); - } -} - - -void Thumb2Assembler::Lsl(Register rd, Register rm, uint32_t shift_imm, - Condition cond, SetCc set_cc) { - CHECK_LE(shift_imm, 31u); - CheckCondition(cond); - EmitShift(rd, rm, LSL, shift_imm, cond, set_cc); -} - - -void Thumb2Assembler::Lsr(Register rd, Register rm, uint32_t shift_imm, - Condition cond, SetCc set_cc) { - CHECK(1u <= shift_imm && shift_imm <= 32u); - if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. - CheckCondition(cond); - EmitShift(rd, rm, LSR, shift_imm, cond, set_cc); -} - - -void Thumb2Assembler::Asr(Register rd, Register rm, uint32_t shift_imm, - Condition cond, SetCc set_cc) { - CHECK(1u <= shift_imm && shift_imm <= 32u); - if (shift_imm == 32) shift_imm = 0; // Comply to UAL syntax. - CheckCondition(cond); - EmitShift(rd, rm, ASR, shift_imm, cond, set_cc); -} - - -void Thumb2Assembler::Ror(Register rd, Register rm, uint32_t shift_imm, - Condition cond, SetCc set_cc) { - CHECK(1u <= shift_imm && shift_imm <= 31u); - CheckCondition(cond); - EmitShift(rd, rm, ROR, shift_imm, cond, set_cc); -} - - -void Thumb2Assembler::Rrx(Register rd, Register rm, Condition cond, SetCc set_cc) { - CheckCondition(cond); - EmitShift(rd, rm, RRX, 0, cond, set_cc); -} - - -void Thumb2Assembler::Lsl(Register rd, Register rm, Register rn, - Condition cond, SetCc set_cc) { - CheckCondition(cond); - EmitShift(rd, rm, LSL, rn, cond, set_cc); -} - - -void Thumb2Assembler::Lsr(Register rd, Register rm, Register rn, - Condition cond, SetCc set_cc) { - CheckCondition(cond); - EmitShift(rd, rm, LSR, rn, cond, set_cc); -} - - -void Thumb2Assembler::Asr(Register rd, Register rm, Register rn, - Condition cond, SetCc set_cc) { - CheckCondition(cond); - EmitShift(rd, rm, ASR, rn, cond, set_cc); -} - - -void Thumb2Assembler::Ror(Register rd, Register rm, Register rn, - Condition cond, SetCc set_cc) { - CheckCondition(cond); - EmitShift(rd, rm, ROR, rn, cond, set_cc); -} - - -int32_t Thumb2Assembler::EncodeBranchOffset(int32_t offset, int32_t inst) { - // The offset is off by 4 due to the way the ARM CPUs read PC. - offset -= 4; - offset >>= 1; - - uint32_t value = 0; - // There are two different encodings depending on the value of bit 12. In one case - // intermediate values are calculated using the sign bit. - if ((inst & B12) == B12) { - // 25 bits of offset. - uint32_t signbit = (offset >> 31) & 0x1; - uint32_t i1 = (offset >> 22) & 0x1; - uint32_t i2 = (offset >> 21) & 0x1; - uint32_t imm10 = (offset >> 11) & 0x03ff; - uint32_t imm11 = offset & 0x07ff; - uint32_t j1 = (i1 ^ signbit) ? 0 : 1; - uint32_t j2 = (i2 ^ signbit) ? 0 : 1; - value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm10 << 16) | - imm11; - // Remove the offset from the current encoding. - inst &= ~(0x3ff << 16 | 0x7ff); - } else { - uint32_t signbit = (offset >> 31) & 0x1; - uint32_t imm6 = (offset >> 11) & 0x03f; - uint32_t imm11 = offset & 0x07ff; - uint32_t j1 = (offset >> 19) & 1; - uint32_t j2 = (offset >> 17) & 1; - value = (signbit << 26) | (j1 << 13) | (j2 << 11) | (imm6 << 16) | - imm11; - // Remove the offset from the current encoding. - inst &= ~(0x3f << 16 | 0x7ff); - } - // Mask out offset bits in current instruction. - inst &= ~(B26 | B13 | B11); - inst |= value; - return inst; -} - - -int Thumb2Assembler::DecodeBranchOffset(int32_t instr) { - int32_t imm32; - if ((instr & B12) == B12) { - uint32_t S = (instr >> 26) & 1; - uint32_t J2 = (instr >> 11) & 1; - uint32_t J1 = (instr >> 13) & 1; - uint32_t imm10 = (instr >> 16) & 0x3FF; - uint32_t imm11 = instr & 0x7FF; - - uint32_t I1 = ~(J1 ^ S) & 1; - uint32_t I2 = ~(J2 ^ S) & 1; - imm32 = (S << 24) | (I1 << 23) | (I2 << 22) | (imm10 << 12) | (imm11 << 1); - imm32 = (imm32 << 8) >> 8; // sign extend 24 bit immediate. - } else { - uint32_t S = (instr >> 26) & 1; - uint32_t J2 = (instr >> 11) & 1; - uint32_t J1 = (instr >> 13) & 1; - uint32_t imm6 = (instr >> 16) & 0x3F; - uint32_t imm11 = instr & 0x7FF; - - imm32 = (S << 20) | (J2 << 19) | (J1 << 18) | (imm6 << 12) | (imm11 << 1); - imm32 = (imm32 << 11) >> 11; // sign extend 21 bit immediate. - } - imm32 += 4; - return imm32; -} - -uint32_t Thumb2Assembler::GetAdjustedPosition(uint32_t old_position) { - // We can reconstruct the adjustment by going through all the fixups from the beginning - // up to the old_position. Since we expect AdjustedPosition() to be called in a loop - // with increasing old_position, we can use the data from last AdjustedPosition() to - // continue where we left off and the whole loop should be O(m+n) where m is the number - // of positions to adjust and n is the number of fixups. - if (old_position < last_old_position_) { - last_position_adjustment_ = 0u; - last_old_position_ = 0u; - last_fixup_id_ = 0u; - } - while (last_fixup_id_ != fixups_.size()) { - Fixup* fixup = GetFixup(last_fixup_id_); - if (fixup->GetLocation() >= old_position + last_position_adjustment_) { - break; - } - if (fixup->GetSize() != fixup->GetOriginalSize()) { - last_position_adjustment_ += fixup->GetSizeInBytes() - fixup->GetOriginalSizeInBytes(); - } - ++last_fixup_id_; - } - last_old_position_ = old_position; - return old_position + last_position_adjustment_; -} - -Literal* Thumb2Assembler::NewLiteral(size_t size, const uint8_t* data) { - DCHECK(size == 4u || size == 8u) << size; - literals_.emplace_back(size, data); - return &literals_.back(); -} - -void Thumb2Assembler::LoadLiteral(Register rt, Literal* literal) { - DCHECK_EQ(literal->GetSize(), 4u); - DCHECK(!literal->GetLabel()->IsBound()); - bool use32bit = IsForced32Bit() || IsHighRegister(rt); - uint32_t location = buffer_.Size(); - Fixup::Size size = use32bit ? Fixup::kLiteral4KiB : Fixup::kLiteral1KiB; - FixupId fixup_id = AddFixup(Fixup::LoadNarrowLiteral(location, rt, size)); - Emit16(static_cast<uint16_t>(literal->GetLabel()->position_)); - literal->GetLabel()->LinkTo(fixup_id); - if (use32bit) { - Emit16(0); - } - DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size()); -} - -void Thumb2Assembler::LoadLiteral(Register rt, Register rt2, Literal* literal) { - DCHECK_EQ(literal->GetSize(), 8u); - DCHECK(!literal->GetLabel()->IsBound()); - uint32_t location = buffer_.Size(); - FixupId fixup_id = - AddFixup(Fixup::LoadWideLiteral(location, rt, rt2, Fixup::kLongOrFPLiteral1KiB)); - Emit16(static_cast<uint16_t>(literal->GetLabel()->position_)); - literal->GetLabel()->LinkTo(fixup_id); - Emit16(0); - DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size()); -} - -void Thumb2Assembler::LoadLiteral(SRegister sd, Literal* literal) { - DCHECK_EQ(literal->GetSize(), 4u); - DCHECK(!literal->GetLabel()->IsBound()); - uint32_t location = buffer_.Size(); - FixupId fixup_id = AddFixup(Fixup::LoadSingleLiteral(location, sd, Fixup::kLongOrFPLiteral1KiB)); - Emit16(static_cast<uint16_t>(literal->GetLabel()->position_)); - literal->GetLabel()->LinkTo(fixup_id); - Emit16(0); - DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size()); -} - -void Thumb2Assembler::LoadLiteral(DRegister dd, Literal* literal) { - DCHECK_EQ(literal->GetSize(), 8u); - DCHECK(!literal->GetLabel()->IsBound()); - uint32_t location = buffer_.Size(); - FixupId fixup_id = AddFixup(Fixup::LoadDoubleLiteral(location, dd, Fixup::kLongOrFPLiteral1KiB)); - Emit16(static_cast<uint16_t>(literal->GetLabel()->position_)); - literal->GetLabel()->LinkTo(fixup_id); - Emit16(0); - DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size()); -} - - -void Thumb2Assembler::AddConstant(Register rd, Register rn, int32_t value, - Condition cond, SetCc set_cc) { - if (value == 0 && set_cc != kCcSet) { - if (rd != rn) { - mov(rd, ShifterOperand(rn), cond); - } - return; - } - // We prefer to select the shorter code sequence rather than selecting add for - // positive values and sub for negatives ones, which would slightly improve - // the readability of generated code for some constants. - ShifterOperand shifter_op; - if (ShifterOperandCanHold(rd, rn, ADD, value, set_cc, &shifter_op)) { - add(rd, rn, shifter_op, cond, set_cc); - } else if (ShifterOperandCanHold(rd, rn, SUB, -value, set_cc, &shifter_op)) { - sub(rd, rn, shifter_op, cond, set_cc); - } else { - CHECK(rn != IP); - // If rd != rn, use rd as temp. This alows 16-bit ADD/SUB in more situations than using IP. - Register temp = (rd != rn) ? rd : IP; - if (ShifterOperandCanHold(temp, kNoRegister, MVN, ~value, kCcKeep, &shifter_op)) { - mvn(temp, shifter_op, cond, kCcKeep); - add(rd, rn, ShifterOperand(temp), cond, set_cc); - } else if (ShifterOperandCanHold(temp, kNoRegister, MVN, ~(-value), kCcKeep, &shifter_op)) { - mvn(temp, shifter_op, cond, kCcKeep); - sub(rd, rn, ShifterOperand(temp), cond, set_cc); - } else if (High16Bits(-value) == 0) { - movw(temp, Low16Bits(-value), cond); - sub(rd, rn, ShifterOperand(temp), cond, set_cc); - } else { - movw(temp, Low16Bits(value), cond); - uint16_t value_high = High16Bits(value); - if (value_high != 0) { - movt(temp, value_high, cond); - } - add(rd, rn, ShifterOperand(temp), cond, set_cc); - } - } -} - -void Thumb2Assembler::CmpConstant(Register rn, int32_t value, Condition cond) { - // We prefer to select the shorter code sequence rather than using plain cmp and cmn - // which would slightly improve the readability of generated code for some constants. - ShifterOperand shifter_op; - if (ShifterOperandCanHold(kNoRegister, rn, CMP, value, kCcSet, &shifter_op)) { - cmp(rn, shifter_op, cond); - } else if (ShifterOperandCanHold(kNoRegister, rn, CMN, -value, kCcSet, &shifter_op)) { - cmn(rn, shifter_op, cond); - } else { - CHECK(rn != IP); - if (ShifterOperandCanHold(IP, kNoRegister, MVN, ~value, kCcKeep, &shifter_op)) { - mvn(IP, shifter_op, cond, kCcKeep); - cmp(rn, ShifterOperand(IP), cond); - } else if (ShifterOperandCanHold(IP, kNoRegister, MVN, ~(-value), kCcKeep, &shifter_op)) { - mvn(IP, shifter_op, cond, kCcKeep); - cmn(rn, ShifterOperand(IP), cond); - } else if (High16Bits(-value) == 0) { - movw(IP, Low16Bits(-value), cond); - cmn(rn, ShifterOperand(IP), cond); - } else { - movw(IP, Low16Bits(value), cond); - uint16_t value_high = High16Bits(value); - if (value_high != 0) { - movt(IP, value_high, cond); - } - cmp(rn, ShifterOperand(IP), cond); - } - } -} - -void Thumb2Assembler::LoadImmediate(Register rd, int32_t value, Condition cond) { - ShifterOperand shifter_op; - if (ShifterOperandCanHold(rd, R0, MOV, value, &shifter_op)) { - mov(rd, shifter_op, cond); - } else if (ShifterOperandCanHold(rd, R0, MVN, ~value, &shifter_op)) { - mvn(rd, shifter_op, cond); - } else { - movw(rd, Low16Bits(value), cond); - uint16_t value_high = High16Bits(value); - if (value_high != 0) { - movt(rd, value_high, cond); - } - } -} - -void Thumb2Assembler::LoadDImmediate(DRegister dd, double value, Condition cond) { - if (!vmovd(dd, value, cond)) { - uint64_t int_value = bit_cast<uint64_t, double>(value); - if (int_value == bit_cast<uint64_t, double>(0.0)) { - // 0.0 is quite common, so we special case it by loading - // 2.0 in `dd` and then subtracting it. - bool success = vmovd(dd, 2.0, cond); - CHECK(success); - vsubd(dd, dd, dd, cond); - } else { - Literal* literal = literal64_dedupe_map_.GetOrCreate( - int_value, - [this, int_value]() { return NewLiteral<uint64_t>(int_value); }); - LoadLiteral(dd, literal); - } - } -} - -int32_t Thumb2Assembler::GetAllowedLoadOffsetBits(LoadOperandType type) { - switch (type) { - case kLoadSignedByte: - case kLoadSignedHalfword: - case kLoadUnsignedHalfword: - case kLoadUnsignedByte: - case kLoadWord: - // We can encode imm12 offset. - return 0xfffu; - case kLoadSWord: - case kLoadDWord: - case kLoadWordPair: - // We can encode imm8:'00' offset. - return 0xff << 2; - default: - LOG(FATAL) << "UNREACHABLE"; - UNREACHABLE(); - } -} - -int32_t Thumb2Assembler::GetAllowedStoreOffsetBits(StoreOperandType type) { - switch (type) { - case kStoreHalfword: - case kStoreByte: - case kStoreWord: - // We can encode imm12 offset. - return 0xfff; - case kStoreSWord: - case kStoreDWord: - case kStoreWordPair: - // We can encode imm8:'00' offset. - return 0xff << 2; - default: - LOG(FATAL) << "UNREACHABLE"; - UNREACHABLE(); - } -} - -bool Thumb2Assembler::CanSplitLoadStoreOffset(int32_t allowed_offset_bits, - int32_t offset, - /*out*/ int32_t* add_to_base, - /*out*/ int32_t* offset_for_load_store) { - int32_t other_bits = offset & ~allowed_offset_bits; - if (ShifterOperandCanAlwaysHold(other_bits) || ShifterOperandCanAlwaysHold(-other_bits)) { - *add_to_base = offset & ~allowed_offset_bits; - *offset_for_load_store = offset & allowed_offset_bits; - return true; - } - return false; -} - -int32_t Thumb2Assembler::AdjustLoadStoreOffset(int32_t allowed_offset_bits, - Register temp, - Register base, - int32_t offset, - Condition cond) { - DCHECK_NE(offset & ~allowed_offset_bits, 0); - int32_t add_to_base, offset_for_load; - if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) { - AddConstant(temp, base, add_to_base, cond, kCcKeep); - return offset_for_load; - } else { - LoadImmediate(temp, offset, cond); - add(temp, temp, ShifterOperand(base), cond, kCcKeep); - return 0; - } -} - -// Implementation note: this method must emit at most one instruction when -// Address::CanHoldLoadOffsetThumb. -void Thumb2Assembler::LoadFromOffset(LoadOperandType type, - Register reg, - Register base, - int32_t offset, - Condition cond) { - if (!Address::CanHoldLoadOffsetThumb(type, offset)) { - CHECK_NE(base, IP); - // Inlined AdjustLoadStoreOffset() allows us to pull a few more tricks. - int32_t allowed_offset_bits = GetAllowedLoadOffsetBits(type); - DCHECK_NE(offset & ~allowed_offset_bits, 0); - int32_t add_to_base, offset_for_load; - if (CanSplitLoadStoreOffset(allowed_offset_bits, offset, &add_to_base, &offset_for_load)) { - // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load. - AddConstant(reg, base, add_to_base, cond, kCcKeep); - base = reg; - offset = offset_for_load; - } else { - Register temp = (reg == base) ? IP : reg; - LoadImmediate(temp, offset, cond); - // TODO: Implement indexed load (not available for LDRD) and use it here to avoid the ADD. - // Use reg for the adjusted base. If it's low reg, we may end up using 16-bit load. - add(reg, reg, ShifterOperand((reg == base) ? IP : base), cond, kCcKeep); - base = reg; - offset = 0; - } - } - DCHECK(Address::CanHoldLoadOffsetThumb(type, offset)); - switch (type) { - case kLoadSignedByte: - ldrsb(reg, Address(base, offset), cond); - break; - case kLoadUnsignedByte: - ldrb(reg, Address(base, offset), cond); - break; - case kLoadSignedHalfword: - ldrsh(reg, Address(base, offset), cond); - break; - case kLoadUnsignedHalfword: - ldrh(reg, Address(base, offset), cond); - break; - case kLoadWord: - ldr(reg, Address(base, offset), cond); - break; - case kLoadWordPair: - ldrd(reg, Address(base, offset), cond); - break; - default: - LOG(FATAL) << "UNREACHABLE"; - UNREACHABLE(); - } -} - -// Implementation note: this method must emit at most one instruction when -// Address::CanHoldLoadOffsetThumb, as expected by JIT::GuardedLoadFromOffset. -void Thumb2Assembler::LoadSFromOffset(SRegister reg, - Register base, - int32_t offset, - Condition cond) { - if (!Address::CanHoldLoadOffsetThumb(kLoadSWord, offset)) { - CHECK_NE(base, IP); - offset = AdjustLoadStoreOffset(GetAllowedLoadOffsetBits(kLoadSWord), IP, base, offset, cond); - base = IP; - } - DCHECK(Address::CanHoldLoadOffsetThumb(kLoadSWord, offset)); - vldrs(reg, Address(base, offset), cond); -} - - -// Implementation note: this method must emit at most one instruction when -// Address::CanHoldLoadOffsetThumb, as expected by JIT::GuardedLoadFromOffset. -void Thumb2Assembler::LoadDFromOffset(DRegister reg, - Register base, - int32_t offset, - Condition cond) { - if (!Address::CanHoldLoadOffsetThumb(kLoadDWord, offset)) { - CHECK_NE(base, IP); - offset = AdjustLoadStoreOffset(GetAllowedLoadOffsetBits(kLoadDWord), IP, base, offset, cond); - base = IP; - } - DCHECK(Address::CanHoldLoadOffsetThumb(kLoadDWord, offset)); - vldrd(reg, Address(base, offset), cond); -} - - -// Implementation note: this method must emit at most one instruction when -// Address::CanHoldStoreOffsetThumb. -void Thumb2Assembler::StoreToOffset(StoreOperandType type, - Register reg, - Register base, - int32_t offset, - Condition cond) { - Register tmp_reg = kNoRegister; - if (!Address::CanHoldStoreOffsetThumb(type, offset)) { - CHECK_NE(base, IP); - if ((reg != IP) && - ((type != kStoreWordPair) || (reg + 1 != IP))) { - tmp_reg = IP; - } else { - // Be careful not to use IP twice (for `reg` (or `reg` + 1 in - // the case of a word-pair store) and `base`) to build the - // Address object used by the store instruction(s) below. - // Instead, save R5 on the stack (or R6 if R5 is already used by - // `base`), use it as secondary temporary register, and restore - // it after the store instruction has been emitted. - tmp_reg = (base != R5) ? R5 : R6; - Push(tmp_reg); - if (base == SP) { - offset += kRegisterSize; - } - } - // TODO: Implement indexed store (not available for STRD), inline AdjustLoadStoreOffset() - // and in the "unsplittable" path get rid of the "add" by using the store indexed instead. - offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(type), tmp_reg, base, offset, cond); - base = tmp_reg; - } - DCHECK(Address::CanHoldStoreOffsetThumb(type, offset)); - switch (type) { - case kStoreByte: - strb(reg, Address(base, offset), cond); - break; - case kStoreHalfword: - strh(reg, Address(base, offset), cond); - break; - case kStoreWord: - str(reg, Address(base, offset), cond); - break; - case kStoreWordPair: - strd(reg, Address(base, offset), cond); - break; - default: - LOG(FATAL) << "UNREACHABLE"; - UNREACHABLE(); - } - if ((tmp_reg != kNoRegister) && (tmp_reg != IP)) { - CHECK((tmp_reg == R5) || (tmp_reg == R6)); - Pop(tmp_reg); - } -} - - -// Implementation note: this method must emit at most one instruction when -// Address::CanHoldStoreOffsetThumb, as expected by JIT::GuardedStoreToOffset. -void Thumb2Assembler::StoreSToOffset(SRegister reg, - Register base, - int32_t offset, - Condition cond) { - if (!Address::CanHoldStoreOffsetThumb(kStoreSWord, offset)) { - CHECK_NE(base, IP); - offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(kStoreSWord), IP, base, offset, cond); - base = IP; - } - DCHECK(Address::CanHoldStoreOffsetThumb(kStoreSWord, offset)); - vstrs(reg, Address(base, offset), cond); -} - - -// Implementation note: this method must emit at most one instruction when -// Address::CanHoldStoreOffsetThumb, as expected by JIT::GuardedStoreSToOffset. -void Thumb2Assembler::StoreDToOffset(DRegister reg, - Register base, - int32_t offset, - Condition cond) { - if (!Address::CanHoldStoreOffsetThumb(kStoreDWord, offset)) { - CHECK_NE(base, IP); - offset = AdjustLoadStoreOffset(GetAllowedStoreOffsetBits(kStoreDWord), IP, base, offset, cond); - base = IP; - } - DCHECK(Address::CanHoldStoreOffsetThumb(kStoreDWord, offset)); - vstrd(reg, Address(base, offset), cond); -} - - -void Thumb2Assembler::dmb(DmbOptions flavor) { - int32_t encoding = 0xf3bf8f50; // dmb in T1 encoding. - Emit32(encoding | flavor); -} - - -void Thumb2Assembler::CompareAndBranchIfZero(Register r, Label* label) { - if (CanRelocateBranches() && IsLowRegister(r) && !label->IsBound()) { - cbz(r, label); - } else { - cmp(r, ShifterOperand(0)); - b(label, EQ); - } -} - - -void Thumb2Assembler::CompareAndBranchIfNonZero(Register r, Label* label) { - if (CanRelocateBranches() && IsLowRegister(r) && !label->IsBound()) { - cbnz(r, label); - } else { - cmp(r, ShifterOperand(0)); - b(label, NE); - } -} - -JumpTable* Thumb2Assembler::CreateJumpTable(std::vector<Label*>&& labels, Register base_reg) { - jump_tables_.emplace_back(std::move(labels)); - JumpTable* table = &jump_tables_.back(); - DCHECK(!table->GetLabel()->IsBound()); - - bool use32bit = IsForced32Bit() || IsHighRegister(base_reg); - uint32_t location = buffer_.Size(); - Fixup::Size size = use32bit ? Fixup::kLiteralAddr4KiB : Fixup::kLiteralAddr1KiB; - FixupId fixup_id = AddFixup(Fixup::LoadLiteralAddress(location, base_reg, size)); - Emit16(static_cast<uint16_t>(table->GetLabel()->position_)); - table->GetLabel()->LinkTo(fixup_id); - if (use32bit) { - Emit16(0); - } - DCHECK_EQ(location + GetFixup(fixup_id)->GetSizeInBytes(), buffer_.Size()); - - return table; -} - -void Thumb2Assembler::EmitJumpTableDispatch(JumpTable* jump_table, Register displacement_reg) { - CHECK(!IsForced32Bit()) << "Forced 32-bit dispatch not implemented yet"; - // 32-bit ADD doesn't support PC as an input, so we need a two-instruction sequence: - // SUB ip, ip, #0 - // ADD pc, ip, reg - // TODO: Implement. - - // The anchor's position needs to be fixed up before we can compute offsets - so make it a tracked - // label. - BindTrackedLabel(jump_table->GetAnchorLabel()); - - add(PC, PC, ShifterOperand(displacement_reg)); -} - -} // namespace arm -} // namespace art |